b6038c974ee8eee64cc03b980af5ea76bc1bcb53
[bootmanager.git] / source / steps / InstallWriteConfig.py
1 # Copyright (c) 2003 Intel Corporation
2 # All rights reserved.
3
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7
8 #     * Redistributions of source code must retain the above copyright
9 #       notice, this list of conditions and the following disclaimer.
10
11 #     * Redistributions in binary form must reproduce the above
12 #       copyright notice, this list of conditions and the following
13 #       disclaimer in the documentation and/or other materials provided
14 #       with the distribution.
15
16 #     * Neither the name of the Intel Corporation nor the names of its
17 #       contributors may be used to endorse or promote products derived
18 #       from this software without specific prior written permission.
19
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
24 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 # EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF
33 # YOUR JURISDICTION. It is licensee's responsibility to comply with any
34 # export regulations applicable in licensee's jurisdiction. Under
35 # CURRENT (May 2000) U.S. export regulations this software is eligible
36 # for export from the U.S. and can be downloaded by or otherwise
37 # exported or reexported worldwide EXCEPT to U.S. embargoed destinations
38 # which include Cuba, Iraq, Libya, North Korea, Iran, Syria, Sudan,
39 # Afghanistan and any other country to which the U.S. has embargoed
40 # goods and services.
41
42
43 import os, string
44
45 from Exceptions import *
46 import utils
47 from systeminfo import systeminfo
48 import BootAPI
49
50 from GetAndUpdateNodeDetails import SMP_OPT
51
52 def Run( vars, log ):
53
54     """
55     Writes out the following configuration files for the node:
56     /etc/fstab
57     /etc/hosts
58     /etc/sysconfig/network-scripts/ifcfg-eth0
59     /etc/resolv.conf (if applicable)
60     /etc/sysconfig/network
61     /etc/modprobe.conf
62     /etc/ssh/ssh_host_key
63     /etc/ssh/ssh_host_rsa_key
64     /etc/ssh/ssh_host_dsa_key
65     
66     Expect the following variables from the store:
67     VERSION                 the version of the install
68     SYSIMG_PATH             the path where the system image will be mounted
69                             (always starts with TEMP_PATH)
70     PARTITIONS              dictionary of generic part. types (root/swap)
71                             and their associated devices.
72     PLCONF_DIR              The directory to store the configuration file in
73     NETWORK_SETTINGS  A dictionary of the values from the network
74                                 configuration file
75     BOOT_CD_VERSION          A tuple of the current bootcd version
76     
77     Sets the following variables:
78     None
79     
80     """
81
82     log.write( "\n\nStep: Install: Writing configuration files.\n" )
83     
84     # make sure we have the variables we need
85     try:
86         VERSION= vars["VERSION"]
87         if VERSION == "":
88             raise ValueError, "VERSION"
89
90         SYSIMG_PATH= vars["SYSIMG_PATH"]
91         if SYSIMG_PATH == "":
92             raise ValueError, "SYSIMG_PATH"
93
94         PARTITIONS= vars["PARTITIONS"]
95         if PARTITIONS == None:
96             raise ValueError, "PARTITIONS"
97
98         PLCONF_DIR= vars["PLCONF_DIR"]
99         if PLCONF_DIR == "":
100             raise ValueError, "PLCONF_DIR"
101
102         NETWORK_SETTINGS= vars["NETWORK_SETTINGS"]
103         if NETWORK_SETTINGS == "":
104             raise ValueError, "NETWORK_SETTINGS"
105
106         BOOT_CD_VERSION= vars["BOOT_CD_VERSION"]
107         if BOOT_CD_VERSION == "":
108             raise ValueError, "BOOT_CD_VERSION"
109
110         NODE_MODEL_OPTIONS= vars["NODE_MODEL_OPTIONS"]
111
112     except KeyError, var:
113         raise BootManagerException, "Missing variable in vars: %s\n" % var
114     except ValueError, var:
115         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
116
117     try:
118         # we need to keys in PARTITIONS, root and swap, make sure
119         # they exist
120         val= PARTITIONS["root"]
121         val= PARTITIONS["swap"]
122         val= PARTITIONS["vservers"]
123         val= PARTITIONS["mapper-root"]
124         val= PARTITIONS["mapper-swap"]
125         val= PARTITIONS["mapper-vservers"]
126     except KeyError, part:
127         log.write( "Missing partition in PARTITIONS: %s\n" % part )
128         return 0
129     
130
131     log.write( "Setting local time to UTC\n" )
132     utils.sysexec( "chroot %s ln -sf /usr/share/zoneinfo/UTC /etc/localtime" % \
133                    SYSIMG_PATH, log )
134
135
136     log.write( "Enabling ntp at boot\n" )
137     utils.sysexec( "chroot %s chkconfig ntpd on" % SYSIMG_PATH, log )
138
139     log.write( "Creating system directory %s\n" % PLCONF_DIR )
140     if not utils.makedirs( "%s/%s" % (SYSIMG_PATH,PLCONF_DIR) ):
141         log.write( "Unable to create directory\n" )
142         return 0
143
144
145     log.write( "Writing network configuration\n" )
146     write_network_configuration( vars, log )
147
148     # write out the modprobe.conf file for the system. make sure
149     # the order of the ethernet devices are listed in the same order
150     # as the boot cd loaded the modules. this is found in /tmp/loadedmodules
151     # ultimately, the order will only match the boot cd order if
152     # the kernel modules have the same name - which should be true for the later
153     # version boot cds because they use the same kernel version.
154     # older boot cds use a 2.4.19 kernel, and its possible some of the network
155     # module names have changed, in which case the system might not boot
156     # if the network modules are activated in a different order that the
157     # boot cd.
158     log.write( "Writing /etc/modprobe.conf\n" )
159     write_modprobeconf_file( vars, log )
160     
161     # dump the modprobe.conf file to the log (not to screen)
162     log.write( "Contents of new modprobe.conf file:\n" )
163     modulesconf_file= file("%s/etc/modprobe.conf" % SYSIMG_PATH, "r" )
164     contents= modulesconf_file.read()
165     log.write( contents + "\n" )
166     modulesconf_file.close()
167     modulesconf_file= None
168     log.write( "End contents of new modprobe.conf file.\n" )
169
170     log.write( "Writing system /etc/fstab\n" )
171     fstab= file( "%s/etc/fstab" % SYSIMG_PATH, "w" )
172     fstab.write( "%s           none        swap      sw        0 0\n" % \
173                  PARTITIONS["mapper-swap"] )
174     fstab.write( "%s           /           ext3      defaults  0 0\n" % \
175                  PARTITIONS["mapper-root"] )
176     fstab.write( "%s           /vservers   ext3      tagxid,defaults  0 0\n" % \
177                  PARTITIONS["mapper-vservers"] )
178     fstab.write( "none         /proc       proc      defaults  0 0\n" )
179     fstab.write( "none         /dev/shm    tmpfs     defaults  0 0\n" )
180     fstab.write( "none         /dev/pts    devpts    defaults  0 0\n" )
181     # no longer needed
182     # fstab.write( "none         /rcfs       rcfs      defaults  0 0\n" )
183     fstab.close()
184
185
186     log.write( "Writing system /etc/issue\n" )
187     issue= file( "%s/etc/issue" % SYSIMG_PATH, "w" )
188     issue.write( "PlanetLab Node: \\n\n" )
189     issue.write( "Kernel \\r on an \\m\n" )
190     issue.write( "http://www.planet-lab.org\n\n" )
191     issue.close()
192
193     log.write( "Setting up authentication (non-ssh)\n" )
194     utils.sysexec( "chroot %s authconfig --nostart --kickstart --enablemd5 " \
195                    "--enableshadow" % SYSIMG_PATH, log )
196     utils.sysexec( "sed -e 's/^root\:\:/root\:*\:/g' " \
197                    "%s/etc/shadow > %s/etc/shadow.new" % \
198                    (SYSIMG_PATH,SYSIMG_PATH), log )
199     utils.sysexec( "chroot %s mv " \
200                    "/etc/shadow.new /etc/shadow" % SYSIMG_PATH, log )
201     utils.sysexec( "chroot %s chmod 400 /etc/shadow" % SYSIMG_PATH, log )
202
203     # if we are setup with dhcp, copy the current /etc/resolv.conf into
204     # the system image so we can run programs inside that need network access
205     method= ""
206     try:
207         method= vars['NETWORK_SETTINGS']['method']
208     except:
209         pass
210     
211     if method == "dhcp":
212         utils.sysexec( "cp /etc/resolv.conf %s/etc/" % SYSIMG_PATH, log )
213
214     # the kernel rpm should have already done this, so don't fail the
215     # install if it fails
216     log.write( "Mounting /proc in system image\n" )
217     utils.sysexec_noerr( "mount -t proc proc %s/proc" % SYSIMG_PATH, log )
218
219     # mkinitrd references both /etc/modprobe.conf and /etc/fstab
220     # as well as /proc/lvm/global. The kernel RPM installation
221     # likely created an improper initrd since these files did not
222     # yet exist. Re-create the initrd here.
223     log.write( "Making initrd\n" )
224
225     # trick mkinitrd in case the current environment does not have device mapper
226     fake_root_lvm= 0
227     if not os.path.exists( "%s/%s" % (SYSIMG_PATH,PARTITIONS["mapper-root"]) ):
228         fake_root_lvm= 1
229         utils.makedirs( "%s/dev/mapper" % SYSIMG_PATH )
230         rootdev= file( "%s/%s" % (SYSIMG_PATH,PARTITIONS["mapper-root"]), "w" )
231         rootdev.close()
232
233     option = ''
234     if NODE_MODEL_OPTIONS & SMP_OPT:
235         option = 'smp'
236     initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
237     kernel_version= initrd.replace("initrd-", "").replace(".img", "")
238     utils.removefile( "%s/boot/%s" % (SYSIMG_PATH, initrd) )
239     utils.sysexec( "chroot %s mkinitrd /boot/initrd-%s.img %s" % \
240                    (SYSIMG_PATH, kernel_version, kernel_version), log )
241
242     if fake_root_lvm == 1:
243         utils.removefile( "%s/%s" % (SYSIMG_PATH,PARTITIONS["mapper-root"]) )
244
245     log.write( "Writing node install version\n" )
246     utils.makedirs( "%s/etc/planetlab" % SYSIMG_PATH )
247     ver= file( "%s/etc/planetlab/install_version" % SYSIMG_PATH, "w" )
248     ver.write( "%s\n" % VERSION )
249     ver.close()
250
251     log.write( "Creating ssh host keys\n" )
252     key_gen_prog= "/usr/bin/ssh-keygen"
253
254     log.write( "Generating SSH1 RSA host key:\n" )
255     key_file= "/etc/ssh/ssh_host_key"
256     utils.sysexec( "chroot %s %s -q -t rsa1 -f %s -C '' -N ''" %
257                    (SYSIMG_PATH,key_gen_prog,key_file), log )
258     utils.sysexec( "chmod 600 %s/%s" % (SYSIMG_PATH,key_file), log )
259     utils.sysexec( "chmod 644 %s/%s.pub" % (SYSIMG_PATH,key_file), log )
260     
261     log.write( "Generating SSH2 RSA host key:\n" )
262     key_file= "/etc/ssh/ssh_host_rsa_key"
263     utils.sysexec( "chroot %s %s -q -t rsa -f %s -C '' -N ''" %
264                    (SYSIMG_PATH,key_gen_prog,key_file), log )
265     utils.sysexec( "chmod 600 %s/%s" % (SYSIMG_PATH,key_file), log )
266     utils.sysexec( "chmod 644 %s/%s.pub" % (SYSIMG_PATH,key_file), log )
267     
268     log.write( "Generating SSH2 DSA host key:\n" )
269     key_file= "/etc/ssh/ssh_host_dsa_key"
270     utils.sysexec( "chroot %s %s -q -t dsa -f %s -C '' -N ''" %
271                    (SYSIMG_PATH,key_gen_prog,key_file), log )
272     utils.sysexec( "chmod 600 %s/%s" % (SYSIMG_PATH,key_file), log )
273     utils.sysexec( "chmod 644 %s/%s.pub" % (SYSIMG_PATH,key_file), log )
274
275     return 1
276
277
278
279 def write_network_configuration( vars, log ):
280     """
281     Write out the network configuration for this machine:
282     /etc/hosts
283     /etc/sysconfig/network-scripts/ifcfg-eth0
284     /etc/resolv.conf (if applicable)
285     /etc/sysconfig/network
286
287     It is assumed the caller mounted the root partition and the vserver partition
288     starting on SYSIMG_PATH - it is not checked here.
289
290     The values to be used for the network settings are to be set in vars
291     in the variable 'NETWORK_SETTINGS', which is a dictionary
292     with keys:
293
294      Key               Used by this function
295      -----------------------------------------------
296      node_id
297      node_key
298      method            x
299      ip                x
300      mac               x (optional)
301      gateway           x
302      network           x
303      broadcast         x
304      netmask           x
305      dns1              x
306      dns2              x (optional)
307      hostname          x
308      domainname        x
309     """
310
311     try:
312         SYSIMG_PATH= vars["SYSIMG_PATH"]
313         if SYSIMG_PATH == "":
314             raise ValueError, "SYSIMG_PATH"
315
316     except KeyError, var:
317         raise BootManagerException, "Missing variable in vars: %s\n" % var
318     except ValueError, var:
319         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
320
321
322     try:
323         network_settings= vars['NETWORK_SETTINGS']
324     except KeyError, e:
325         raise BootManagerException, "No network settings found in vars."
326
327     try:
328         hostname= network_settings['hostname']
329         domainname= network_settings['domainname']
330         method= network_settings['method']
331         ip= network_settings['ip']
332         gateway= network_settings['gateway']
333         network= network_settings['network']
334         netmask= network_settings['netmask']
335         dns1= network_settings['dns1']
336         mac= network_settings['mac']
337     except KeyError, e:
338         raise BootManagerException, "Missing value %s in network settings." % str(e)
339
340     try:
341         dns2= ''
342         dns2= network_settings['dns2']
343     except KeyError, e:
344         pass
345
346         
347     log.write( "Writing /etc/hosts\n" )
348     hosts_file= file("%s/etc/hosts" % SYSIMG_PATH, "w" )    
349     hosts_file.write( "127.0.0.1       localhost\n" )
350     if method == "static":
351         hosts_file.write( "%s %s.%s\n" % (ip, hostname, domainname) )
352     hosts_file.close()
353     hosts_file= None
354     
355
356     log.write( "Writing /etc/sysconfig/network-scripts/ifcfg-eth0\n" )
357     eth0_file= file("%s/etc/sysconfig/network-scripts/ifcfg-eth0" %
358                     SYSIMG_PATH, "w" )
359     eth0_file.write( "DEVICE=eth0\n" )
360     if method == "static":
361         eth0_file.write( "BOOTPROTO=static\n" )
362         eth0_file.write( "IPADDR=%s\n" % ip )
363         eth0_file.write( "NETMASK=%s\n" % netmask )
364         eth0_file.write( "GATEWAY=%s\n" % gateway )
365     else:
366         eth0_file.write( "BOOTPROTO=dhcp\n" )
367         eth0_file.write( "DHCP_HOSTNAME=%s\n" % hostname )
368     if mac != "":
369         eth0_file.write( "HWADDR=%s\n" % mac )
370     eth0_file.write( "ONBOOT=yes\n" )
371     eth0_file.write( "USERCTL=no\n" )
372     eth0_file.close()
373     eth0_file= None
374
375     if method == "static":
376         log.write( "Writing /etc/resolv.conf\n" )
377         resolv_file= file("%s/etc/resolv.conf" % SYSIMG_PATH, "w" )
378         if dns1 != "":
379             resolv_file.write( "nameserver %s\n" % dns1 )
380         if dns2 != "":
381             resolv_file.write( "nameserver %s\n" % dns2 )
382         resolv_file.write( "search %s\n" % domainname )
383         resolv_file.close()
384         resolv_file= None
385
386     log.write( "Writing /etc/sysconfig/network\n" )
387     network_file= file("%s/etc/sysconfig/network" % SYSIMG_PATH, "w" )
388     network_file.write( "NETWORKING=yes\n" )
389     network_file.write( "HOSTNAME=%s.%s\n" % (hostname, domainname) )
390     if method == "static":
391         network_file.write( "GATEWAY=%s\n" % gateway )
392     network_file.close()
393     network_file= None
394
395
396
397 def write_modprobeconf_file( vars, log, filename = "/etc/modprobe.conf"):
398     """
399     write out the system file /etc/modprobe.conf with the current
400     set of modules.
401
402     returns a tuple of the number of network driver lines and storage
403     driver lines written as (networkcount,storagecount)
404     """
405
406     # make sure we have this class loaded
407     from systeminfo import systeminfo
408     
409     try:
410         SYSIMG_PATH= vars["SYSIMG_PATH"]
411         if SYSIMG_PATH == "":
412             raise ValueError, "SYSIMG_PATH"
413
414         NODE_MODEL_OPTIONS= vars["NODE_MODEL_OPTIONS"]
415
416     except KeyError, var:
417         raise BootManagerException, "Missing variable in vars: %s\n" % var
418     except ValueError, var:
419         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
420
421     
422     # get the kernel version
423     option = ''
424     if NODE_MODEL_OPTIONS & SMP_OPT:
425         option = 'smp'
426     initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
427     kernel_version= initrd.replace("initrd-", "").replace(".img", "")
428
429     sysinfo= systeminfo()
430     sysmods= sysinfo.get_system_modules(SYSIMG_PATH, kernel_version)
431     if sysmods is None:
432         raise BootManagerException, "Unable to get list of system modules."
433         
434     eth_count= 0
435     scsi_count= 0
436
437     modulesconf_file= file("%s/%s" % (SYSIMG_PATH,filename), "w" )
438
439     for type in sysmods:
440         if type == sysinfo.MODULE_CLASS_SCSI:
441             for a_mod in sysmods[type]:
442                 if scsi_count == 0:
443                     modulesconf_file.write( "alias scsi_hostadapter %s\n" %
444                                             a_mod )
445                 else:
446                     modulesconf_file.write( "alias scsi_hostadapter%d %s\n" %
447                                             (scsi_count,a_mod) )
448                 scsi_count= scsi_count + 1
449
450         elif type == sysinfo.MODULE_CLASS_NETWORK:
451             for a_mod in sysmods[type]:
452                 modulesconf_file.write( "alias eth%d %s\n" %
453                                         (eth_count,a_mod) )
454                 eth_count= eth_count + 1
455
456     modulesconf_file.close()
457     modulesconf_file= None
458
459     return (eth_count,scsi_count)
460