move code block to write out /etc/planetlab/session to ChainBootNode, since
[bootmanager.git] / source / steps / ChainBootNode.py
1 import string
2 import re
3
4 from Exceptions import *
5 import utils
6 import compatibility
7 from systeminfo import systeminfo
8 import BootAPI
9
10
11 def Run( vars, log ):
12     """
13     Load the kernel off of a node and boot to it.
14     This step assumes the disks are mounted on SYSIMG_PATH.
15     
16     Expect the following variables:
17     BOOT_CD_VERSION       A tuple of the current bootcd version
18     SYSIMG_PATH           the path where the system image will be mounted
19                           (always starts with TEMP_PATH)
20     ROOT_MOUNTED          the node root file system is mounted
21     NODE_SESSION             the unique session val set when we requested
22                              the current boot state
23     PLCONF_DIR               The directory to store PL configuration files in
24     
25     Sets the following variables:
26     ROOT_MOUNTED          the node root file system is mounted
27     """
28
29     log.write( "\n\nStep: Chain booting node.\n" )
30
31     # make sure we have the variables we need
32     try:
33         BOOT_CD_VERSION= vars["BOOT_CD_VERSION"]
34         if BOOT_CD_VERSION == "":
35             raise ValueError, "BOOT_CD_VERSION"
36
37         SYSIMG_PATH= vars["SYSIMG_PATH"]
38         if SYSIMG_PATH == "":
39             raise ValueError, "SYSIMG_PATH"
40
41         PLCONF_DIR= vars["PLCONF_DIR"]
42         if PLCONF_DIR == "":
43             raise ValueError, "PLCONF_DIR"
44
45         # its ok if this is blank
46         NODE_SESSION= vars["NODE_SESSION"]
47
48     except KeyError, var:
49         raise BootManagerException, "Missing variable in vars: %s\n" % var
50     except ValueError, var:
51         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
52
53     ROOT_MOUNTED= 0
54     if 'ROOT_MOUNTED' in vars.keys():
55         ROOT_MOUNTED= vars['ROOT_MOUNTED']
56     
57     if ROOT_MOUNTED == 0:
58         log.write( "Mounting node partitions\n" )
59
60         # old cds need extra utilities to run lvm
61         if BOOT_CD_VERSION[0] == 2:
62             compatibility.setup_lvm_2x_cd( vars, log )
63             
64         # simply creating an instance of this class and listing the system
65         # block devices will make them show up so vgscan can find the planetlab
66         # volume group
67         systeminfo().get_block_device_list()
68         
69         utils.sysexec( "vgscan", log )
70         utils.sysexec( "vgchange -ay planetlab", log )
71
72         utils.makedirs( SYSIMG_PATH )
73
74         utils.sysexec( "mount /dev/planetlab/root %s" % SYSIMG_PATH, log )
75         utils.sysexec( "mount /dev/planetlab/vservers %s/vservers" %
76                        SYSIMG_PATH, log )
77
78         ROOT_MOUNTED= 1
79         vars['ROOT_MOUNTED']= 1
80         
81
82     node_update_cmd= "/usr/local/planetlab/bin/NodeUpdate.py start noreboot"
83
84     log.write( "Running node update.\n" )
85     utils.sysexec( "chroot %s %s" % (SYSIMG_PATH,node_update_cmd), log )
86
87     log.write( "Updating ssh public host key with PLC.\n" )
88     ssh_host_key= ""
89     try:
90         ssh_host_key_file= file("%s/etc/ssh/ssh_host_rsa_key.pub"%SYSIMG_PATH,"r")
91         ssh_host_key= ssh_host_key_file.read().strip()
92         ssh_host_key_file.close()
93         ssh_host_key_file= None
94     except IOError, e:
95         pass
96
97     # write out the session value /etc/planetlab/session
98     try:
99         session_file_path= "%s/%s/session" % (SYSIMG_PATH,PLCONF_DIR)
100         session_file= file( session_file_path, "w" )
101         session_file.write( str(NODE_SESSION) )
102         session_file.close()
103         session_file= None
104         log.write( "Updated /etc/planetlab/session\n" )
105     except IOError, e:
106         log.write( "Unable to write out /etc/planetlab/session, continuing anyway\n" )
107
108     update_vals= {}
109     update_vals['ssh_host_key']= ssh_host_key
110     BootAPI.call_api_function( vars, "BootUpdateNode", (update_vals,) )
111
112
113     log.write( "Copying kernel and initrd for booting.\n" )
114     utils.sysexec( "cp %s/boot/kernel-boot /tmp/kernel" % SYSIMG_PATH, log )
115     utils.sysexec( "cp %s/boot/initrd-boot /tmp/initrd" % SYSIMG_PATH, log )
116
117     log.write( "Unmounting disks.\n" )
118     utils.sysexec_noerr( "chroot %s umount /rcfs" % SYSIMG_PATH, log )
119     utils.sysexec_noerr( "umount -r /dev/planetlab/vservers", log )
120     utils.sysexec_noerr( "umount -r /dev/planetlab/root", log )
121     utils.sysexec_noerr( "vgchange -an", log )
122
123     ROOT_MOUNTED= 0
124     vars['ROOT_MOUNTED']= 0
125
126     log.write( "Unloading modules and chain booting to new kernel.\n" )
127
128     # further use of log after Upload will only output to screen
129     log.Upload()
130
131     # regardless of whether kexec works or not, we need to stop trying to
132     # run anything
133     cancel_boot_flag= "/tmp/CANCEL_BOOT"
134     utils.sysexec( "touch %s" % cancel_boot_flag, log )
135
136     # on 2.x cds (2.4 kernel) for sure, we need to shutdown everything
137     # to get kexec to work correctly. Even on 3.x cds (2.6 kernel),
138     # there are a few buggy drivers that don't disable their hardware
139     # correctly unless they are first unloaded.
140     
141     utils.sysexec_noerr( "ifconfig eth0 down", log )
142
143     if BOOT_CD_VERSION[0] == 2:
144         utils.sysexec_noerr( "killall dhcpcd", log )
145     elif BOOT_CD_VERSION[0] == 3:
146         utils.sysexec_noerr( "killall dhclient", log )
147         
148     utils.sysexec_noerr( "umount -a -r -t ext2,ext3", log )
149     utils.sysexec_noerr( "modprobe -r lvm-mod", log )
150     
151     try:
152         modules= file("/tmp/loadedmodules","r")
153         
154         for line in modules:
155             module= string.strip(line)
156             if module != "":
157                 log.write( "Unloading %s\n" % module )
158                 utils.sysexec_noerr( "modprobe -r %s" % module, log )
159
160         modules.close()
161     except IOError:
162         log.write( "Couldn't read /tmp/loadedmodules, continuing.\n" )
163
164     try:
165         modules= file("/proc/modules", "r")
166
167         # Get usage count for USB
168         usb_usage = 0
169         for line in modules:
170             try:
171                 # Module Size UsageCount UsedBy State LoadAddress
172                 parts= string.split(line)
173
174                 if parts[0] == "usb_storage":
175                     usb_usage += int(parts[2])
176             except IndexError, e:
177                 log.write( "Couldn't parse /proc/modules, continuing.\n" )
178
179         modules.seek(0)
180
181         for line in modules:
182             try:
183                 # Module Size UsageCount UsedBy State LoadAddress
184                 parts= string.split(line)
185
186                 # While we would like to remove all "unused" modules,
187                 # you can't trust usage count, especially for things
188                 # like network drivers or RAID array drivers. Just try
189                 # and unload a few specific modules that we know cause
190                 # problems during chain boot, such as USB host
191                 # controller drivers (HCDs) (PL6577).
192                 # if int(parts[2]) == 0:
193                 if re.search('_hcd$', parts[0]):
194                     if usb_usage > 0:
195                         log.write( "NOT unloading %s since USB may be in use\n" % parts[0] )
196                     else:
197                         log.write( "Unloading %s\n" % parts[0] )
198                         utils.sysexec_noerr( "modprobe -r %s" % parts[0], log )
199             except IndexError, e:
200                 log.write( "Couldn't parse /proc/modules, continuing.\n" )
201     except IOError:
202         log.write( "Couldn't read /proc/modules, continuing.\n" )
203
204
205     kargs = "ramdisk_size=8192"
206     try:
207         kargsfb = open("/kargs.txt","r")
208         moreargs = kargsfb.readline()
209         kargsfb.close()
210         moreargs = moreargs.strip()
211         log.write( 'Parsed in "%s" kexec args from /kargs.txt\n' % moreargs )
212         kargs = kargs + " " + moreargs
213     except IOError:
214         # /kargs.txt does not exist, which is fine. Just kexec with default
215         # kargs, which is ramdisk_size=8192
216         pass 
217
218     try:
219         utils.sysexec( 'kexec --force --initrd=/tmp/initrd ' \
220                        '--append="%s" /tmp/kernel' % kargs)
221     except BootManagerException, e:
222         # if kexec fails, we've shut the machine down to a point where nothing
223         # can run usefully anymore (network down, all modules unloaded, file
224         # systems unmounted. write out the error, and cancel the boot process
225
226         log.write( "\n\n" )
227         log.write( "-------------------------------------------------------\n" )
228         log.write( "kexec failed with the following error. Please report\n" )
229         log.write( "this problem to support@planet-lab.org.\n\n" )
230         log.write( str(e) + "\n\n" )
231         log.write( "The boot process has been canceled.\n" )
232         log.write( "-------------------------------------------------------\n\n" )
233
234     return