6 # Copyright (c) 2003 Intel Corporation
9 # Copyright (c) 2004-2006 The Trustees of Princeton University
10 # All rights reserved.
13 import sys, os, traceback
18 from Exceptions import *
19 import notify_messages
20 import BootServerRequest
22 # all output is written to this file
23 BM_NODE_LOG= "/tmp/bm.log"
24 VARS_FILE = "configuration"
26 # the new contents of PATH when the boot manager is running
27 BIN_PATH= ('/usr/local/bin',
34 def read_configuration_file(filename):
35 # read in and store all variables in VARS_FILE into each line
36 # is in the format name=val (any whitespace around the = is
37 # removed. everything after the = to the end of the line is
40 vars_file= file(filename,'r')
42 for line in vars_file:
43 # if its a comment or a whitespace line, ignore
44 if line[:1] == "#" or string.strip(line) == "":
47 parts= string.split(line,"=")
50 raise Exception( "Invalid line in vars file: %s" % line )
52 name= string.strip(parts[0])
53 value= string.strip(parts[1])
54 value= value.replace("'", "") # remove quotes
55 value= value.replace('"', "") # remove quotes
60 raise Exception( "Unable to read configuration vars." )
62 # find out which directory we are running it, and set a variable
63 # for that. future steps may need to get files out of the bootmanager
65 current_dir= os.getcwd()
66 vars['BM_SOURCE_DIR']= current_dir
70 ##############################
73 format="%H:%M:%S(%Z) "
75 def __init__( self, OutputFilePath= None ):
77 self.OutputFile= open( OutputFilePath, "w")
78 self.OutputFilePath= OutputFilePath
80 print( "bootmanager log : Unable to open output file %r, continuing"%OutputFilePath )
85 vars = read_configuration_file(VARS_FILE)
88 self.LogEntry( str(e) )
91 def LogEntry( self, str, inc_newline= 1, display_screen= 1 ):
92 now=time.strftime(log.format, time.localtime())
94 self.OutputFile.write( now+str )
96 sys.stdout.write( now+str )
100 sys.stdout.write( "\n" )
102 self.OutputFile.write( "\n" )
105 self.OutputFile.flush()
107 def write( self, str ):
109 make log behave like a writable file object (for traceback
112 self.LogEntry( str, 0, 1 )
114 # bm log uploading is available back again, as of nodeconfig-5.0-2
117 upload the contents of the log to the server
119 if self.OutputFile is not None:
120 self.OutputFile.flush()
122 self.LogEntry( "Uploading logs to %s" % self.VARS['UPLOAD_LOG_SCRIPT'] )
124 self.OutputFile.close()
125 self.OutputFile= None
127 hostname= self.VARS['INTERFACE_SETTINGS']['hostname'] + "." + \
128 self.VARS['INTERFACE_SETTINGS']['domainname']
129 bs_request = BootServerRequest.BootServerRequest(self.VARS)
131 # this was working until f10
132 bs_request.MakeRequest(PartialPath = self.VARS['UPLOAD_LOG_SCRIPT'],
133 GetVars = None, PostVars = None,
134 DoSSL = True, DoCertCheck = True,
135 FormData = ["log=@" + self.OutputFilePath,
136 "hostname=" + hostname,
141 bs_request.MakeRequest(PartialPath = self.VARS['UPLOAD_LOG_SCRIPT'],
142 GetVars = None, PostVars = None,
143 DoSSL = True, DoCertCheck = True,
144 FormData = [('log',(pycurl.FORM_FILE, self.OutputFilePath)),
145 ("hostname",hostname),
149 ##############################
152 # file containing initial variables/constants
154 # the set of valid node run states
155 NodeRunStates = {'reinstall':None,
161 def __init__(self, log, forceState):
162 # override machine's current state from the command line
163 self.forceState = forceState
165 # the main logging point
168 # set to 1 if we can run after initialization
172 # this contains a set of information used and updated by each step
177 # not sure what the current PATH is set to, replace it with what
178 # we know will work with all the boot cds
179 os.environ['PATH']= string.join(BIN_PATH,":")
185 core boot manager logic.
187 the way errors are handled is as such: if any particular step
188 cannot continue or unexpectibly fails, an exception is thrown.
189 in this case, the boot manager cannot continue running.
191 these step functions can also return a 0/1 depending on whether
192 or not it succeeded. In the case of steps like ConfirmInstallWithUser,
193 a 0 is returned and no exception is thrown if the user chose not
194 to confirm the install. The same goes with the CheckHardwareRequirements.
195 If requriements not met, but tests were succesfull, return 0.
197 for steps that run within the installer, they are expected to either
198 complete succesfully and return 1, or throw an execption.
200 For exact return values and expected operations, see the comments
201 at the top of each of the invididual step functions.
204 def _nodeNotInstalled(message='MSG_NODE_NOT_INSTALLED'):
205 # called by the _xxxState() functions below upon failure
206 self.VARS['RUN_LEVEL']= 'failboot'
207 notify = getattr(notify_messages, message)
208 self.VARS['STATE_CHANGE_NOTIFY']= 1
209 self.VARS['STATE_CHANGE_NOTIFY_MESSAGE']= notify
210 raise BootManagerException, notify
213 # implements the boot logic, which consists of first
214 # double checking that the node was properly installed,
215 # checking whether someone added or changed disks, and
216 # then finally chain boots.
218 # starting the fallback/debug ssh daemon for safety:
219 # if the node install somehow hangs, or if it simply takes ages,
220 # we can still enter and investigate
222 StartDebug.Run(self.VARS, self.LOG, last_resort = False)
226 InstallInit.Run( self.VARS, self.LOG )
227 ret = ValidateNodeInstall.Run( self.VARS, self.LOG )
229 WriteModprobeConfig.Run( self.VARS, self.LOG )
230 MakeInitrd.Run( self.VARS, self.LOG )
231 WriteNetworkConfig.Run( self.VARS, self.LOG )
232 CheckForNewDisks.Run( self.VARS, self.LOG )
233 SendHardwareConfigToPLC.Run( self.VARS, self.LOG )
234 ChainBootNode.Run( self.VARS, self.LOG )
236 _nodeNotInstalled('MSG_NODE_FILESYSTEM_CORRUPT')
238 _nodeNotInstalled('MSG_NODE_MOUNT_FAILED')
240 _nodeNotInstalled('MSG_NODE_MISSING_KERNEL')
246 # starting the fallback/debug ssh daemon for safety:
247 # if the node install somehow hangs, or if it simply takes ages,
248 # we can still enter and investigate
250 StartDebug.Run(self.VARS, self.LOG, last_resort = False)
254 # implements the reinstall logic, which will check whether
255 # the min. hardware requirements are met, install the
256 # software, and upon correct installation will switch too
257 # 'boot' state and chainboot into the production system
258 if not CheckHardwareRequirements.Run( self.VARS, self.LOG ):
259 self.VARS['RUN_LEVEL']= 'failboot'
260 raise BootManagerException, "Hardware requirements not met."
263 InstallInit.Run( self.VARS, self.LOG )
264 InstallPartitionDisks.Run( self.VARS, self.LOG )
265 InstallBootstrapFS.Run( self.VARS, self.LOG )
266 InstallWriteConfig.Run( self.VARS, self.LOG )
267 InstallUninitHardware.Run( self.VARS, self.LOG )
268 self.VARS['BOOT_STATE']= 'boot'
269 self.VARS['STATE_CHANGE_NOTIFY']= 1
270 self.VARS['STATE_CHANGE_NOTIFY_MESSAGE']= \
271 notify_messages.MSG_INSTALL_FINISHED
272 UpdateBootStateWithPLC.Run( self.VARS, self.LOG )
276 # implements the new install logic, which will first check
277 # with the user whether it is ok to install on this
278 # machine, switch to 'reinstall' state and then invoke the reinstall
279 # logic. See reinstallState logic comments for further
281 if not ConfirmInstallWithUser.Run( self.VARS, self.LOG ):
283 self.VARS['BOOT_STATE']= 'reinstall'
284 UpdateRunLevelWithPLC.Run( self.VARS, self.LOG )
287 def _debugRun(state='failboot'):
288 # implements debug logic, which starts the sshd and just waits around
289 self.VARS['RUN_LEVEL']=state
290 UpdateRunLevelWithPLC.Run( self.VARS, self.LOG )
291 StartDebug.Run( self.VARS, self.LOG )
292 # fsck/mount fs if present, and ignore return value if it's not.
293 ValidateNodeInstall.Run( self.VARS, self.LOG )
296 # should never happen; log event
297 self.LOG.write( "\nInvalid BOOT_STATE = %s\n" % self.VARS['BOOT_STATE'])
300 # setup state -> function hash table
301 BootManager.NodeRunStates['reinstall'] = _reinstallRun
302 BootManager.NodeRunStates['boot'] = _bootRun
303 BootManager.NodeRunStates['safeboot'] = lambda : _debugRun('safeboot')
304 BootManager.NodeRunStates['disabled'] = lambda : _debugRun('disabled')
308 InitializeBootManager.Run( self.VARS, self.LOG )
309 ReadNodeConfiguration.Run( self.VARS, self.LOG )
310 AuthenticateWithPLC.Run( self.VARS, self.LOG )
311 StartRunlevelAgent.Run( self.VARS, self.LOG )
312 GetAndUpdateNodeDetails.Run( self.VARS, self.LOG )
314 # override machine's current state from the command line
315 if self.forceState is not None:
316 self.VARS['BOOT_STATE']= self.forceState
317 UpdateBootStateWithPLC.Run( self.VARS, self.LOG )
318 UpdateRunLevelWithPLC.Run( self.VARS, self.LOG )
320 stateRun = BootManager.NodeRunStates.get(self.VARS['BOOT_STATE'],_badstateRun)
325 self.LOG.write( "\n\nKeyError while running: %s\n" % str(e) )
326 except BootManagerException, e:
327 self.LOG.write( "\n\nException while running: %s\n" % str(e) )
328 except BootManagerAuthenticationException, e:
329 self.LOG.write( "\n\nFailed to Authenticate Node: %s\n" % str(e) )
330 # sets /tmp/CANCEL_BOOT flag
331 StartDebug.Run(self.VARS, self.LOG )
332 # Return immediately b/c any other calls to API will fail
335 self.LOG.write( "\n\nImplementation Error\n")
336 traceback.print_exc(file=self.LOG.OutputFile)
337 traceback.print_exc()
342 except BootManagerException, e:
343 self.LOG.write( "\n\nException while running: %s\n" % str(e) )
345 self.LOG.write( "\n\nImplementation Error\n")
346 traceback.print_exc(file=self.LOG.OutputFile)
347 traceback.print_exc()
355 utils.prompt_for_breakpoint_mode()
357 utils.breakpoint ("Entering BootManager::main")
359 # set to 1 if error occurred
362 # all output goes through this class so we can save it and post
363 # the data back to PlanetLab central
364 LOG= log( BM_NODE_LOG )
366 LOG.LogEntry( "BootManager started at: %s" % \
367 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) )
373 if BootManager.NodeRunStates.has_key(fState):
376 LOG.LogEntry("FATAL: cannot force node run state to=%s" % fState)
379 traceback.print_exc(file=LOG.OutputFile)
380 traceback.print_exc()
383 LOG.LogEntry( "BootManager finished at: %s" % \
384 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) )
389 bm= BootManager(LOG,forceState)
391 LOG.LogEntry( "Unable to initialize BootManager." )
393 LOG.LogEntry( "Running version %s of BootManager." % bm.VARS['VERSION'] )
396 LOG.LogEntry( "\nDone!" );
398 LOG.LogEntry( "\nError occurred!" );
401 traceback.print_exc(file=LOG.OutputFile)
402 traceback.print_exc()
404 LOG.LogEntry( "BootManager finished at: %s" % \
405 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) )
411 if __name__ == "__main__":
412 error = main(sys.argv)