3 # Copyright (c) 2003 Intel Corporation
6 # Copyright (c) 2004-2006 The Trustees of Princeton University
10 import sys, os, traceback
15 from Exceptions import *
16 import notify_messages
17 import BootServerRequest
19 # all output is written to this file
20 LOG_FILE= "/tmp/bm.log"
21 UPLOAD_LOG_PATH = "/alpina-logs/upload.php"
23 # the new contents of PATH when the boot manager is running
24 BIN_PATH= ('/usr/local/bin',
30 '/usr/local/planetlab/bin')
33 # the set of valid node run states
38 format="%H:%M:%S(%Z) "
40 def __init__( self, OutputFilePath= None ):
43 self.OutputFilePath= OutputFilePath
44 self.OutputFile= gzip.GzipFile( OutputFilePath, "w", 9 )
46 print( "Unable to open output file for log, continuing" )
50 def LogEntry( self, str, inc_newline= 1, display_screen= 1 ):
51 now=time.strftime(log.format, time.localtime())
53 self.OutputFile.write( now+str )
55 sys.stdout.write( now+str )
59 sys.stdout.write( "\n" )
61 self.OutputFile.write( "\n" )
64 self.OutputFile.flush()
68 def write( self, str ):
70 make log behave like a writable file object (for traceback
73 self.LogEntry( str, 0, 1 )
79 upload the contents of the log to the server
82 if self.OutputFile is not None:
83 self.LogEntry( "Uploading logs to %s" % UPLOAD_LOG_PATH )
85 self.OutputFile.close()
88 bs_request = BootServerRequest.BootServerRequest()
89 bs_request.MakeRequest(PartialPath = UPLOAD_LOG_PATH,
90 GetVars = None, PostVars = None,
91 FormData = ["log=@" + self.OutputFilePath],
92 DoSSL = True, DoCertCheck = True)
101 # file containing initial variables/constants
102 VARS_FILE = "configuration"
105 def __init__(self, log, forceState):
106 # override machine's current state from the command line
107 self.forceState = forceState
109 # the main logging point
112 # set to 1 if we can run after initialization
115 # read in and store all variables in VARS_FILE into each line
116 # is in the format name=val (any whitespace around the = is
117 # removed. everything after the = to the end of the line is
120 vars_file= file(self.VARS_FILE,'r')
122 for line in vars_file:
123 # if its a comment or a whitespace line, ignore
124 if line[:1] == "#" or string.strip(line) == "":
127 parts= string.split(line,"=")
129 self.LOG.LogEntry( "Invalid line in vars file: %s" % line )
130 validConfFile = False
133 name= string.strip(parts[0])
134 value= string.strip(parts[1])
138 if not validConfFile:
139 self.LOG.LogEntry( "Unable to read configuration vars." )
142 # find out which directory we are running it, and set a variable
143 # for that. future steps may need to get files out of the bootmanager
145 current_dir= os.getcwd()
146 vars['BM_SOURCE_DIR']= current_dir
148 # not sure what the current PATH is set to, replace it with what
149 # we know will work with all the boot cds
150 os.environ['PATH']= string.join(BIN_PATH,":")
152 # this contains a set of information used and updated
160 core boot manager logic.
162 the way errors are handled is as such: if any particular step
163 cannot continue or unexpectibly fails, an exception is thrown.
164 in this case, the boot manager cannot continue running.
166 these step functions can also return a 0/1 depending on whether
167 or not it succeeded. In the case of steps like ConfirmInstallWithUser,
168 a 0 is returned and no exception is thrown if the user chose not
169 to confirm the install. The same goes with the CheckHardwareRequirements.
170 If requriements not met, but tests were succesfull, return 0.
172 for steps that run within the installer, they are expected to either
173 complete succesfully and return 1, or throw an execption.
175 For exact return values and expected operations, see the comments
176 at the top of each of the invididual step functions.
179 def _nodeNotInstalled():
180 # called by the _xxxState() functions below upon failure
181 self.VARS['BOOT_STATE']= 'failboot'
182 self.VARS['STATE_CHANGE_NOTIFY']= 1
183 self.VARS['STATE_CHANGE_NOTIFY_MESSAGE']= \
184 notify_messages.MSG_NODE_NOT_INSTALLED
185 raise BootManagerException, \
186 notify_messages.MSG_NODE_NOT_INSTALLED
189 # implements the boot logic, which consists of first
190 # double checking that the node was properly installed,
191 # checking whether someone added or changed disks, and
192 # then finally chain boots.
194 InstallInit.Run( self.VARS, self.LOG )
195 if ValidateNodeInstall.Run( self.VARS, self.LOG ):
196 WriteModprobeConfig.Run( self.VARS, self.LOG )
197 MakeInitrd.Run( self.VARS, self.LOG )
198 WriteNetworkConfig.Run( self.VARS, self.LOG )
199 CheckForNewDisks.Run( self.VARS, self.LOG )
200 SendHardwareConfigToPLC.Run( self.VARS, self.LOG )
201 ChainBootNode.Run( self.VARS, self.LOG )
206 # implements the reinstall logic, which will check whether
207 # the min. hardware requirements are met, install the
208 # software, and upon correct installation will switch too
209 # 'boot' state and chainboot into the production system
210 if not CheckHardwareRequirements.Run( self.VARS, self.LOG ):
211 self.VARS['BOOT_STATE']= 'failboot'
212 raise BootManagerException, "Hardware requirements not met."
215 InstallInit.Run( self.VARS, self.LOG )
216 InstallPartitionDisks.Run( self.VARS, self.LOG )
217 InstallBootstrapFS.Run( self.VARS, self.LOG )
218 InstallWriteConfig.Run( self.VARS, self.LOG )
219 InstallUninitHardware.Run( self.VARS, self.LOG )
220 self.VARS['BOOT_STATE']= 'boot'
221 self.VARS['STATE_CHANGE_NOTIFY']= 1
222 self.VARS['STATE_CHANGE_NOTIFY_MESSAGE']= \
223 notify_messages.MSG_INSTALL_FINISHED
224 UpdateBootStateWithPLC.Run( self.VARS, self.LOG )
228 # implements the new install logic, which will first check
229 # with the user whether it is ok to install on this
230 # machine, switch to 'reinstall' state and then invoke the reinstall
231 # logic. See reinstallState logic comments for further
233 if not ConfirmInstallWithUser.Run( self.VARS, self.LOG ):
235 self.VARS['BOOT_STATE']= 'reinstall'
236 UpdateBootStateWithPLC.Run( self.VARS, self.LOG )
239 def _debugRun(state='failboot'):
240 # implements debug logic, which just starts the sshd
241 # and just waits around
242 self.VARS['BOOT_STATE']=state
243 UpdateBootStateWithPLC.Run( self.VARS, self.LOG )
244 StartDebug.Run( self.VARS, self.LOG )
247 # should never happen; log event
248 self.LOG.write( "\nInvalid BOOT_STATE = %s\n" % self.VARS['BOOT_STATE'])
252 # setup state -> function hash table
253 NodeRunStates['install'] = _newRun
254 NodeRunStates['reinstall'] = _reinstallRun
255 NodeRunStates['boot'] = _bootRun
256 NodeRunStates['failboot'] = _bootRun # should always try to boot.
257 NodeRunStates['safeboot'] = lambda : _debugRun('safeboot')
258 NodeRunStates['disabled'] = lambda : _debugRun('disabled')
262 InitializeBootManager.Run( self.VARS, self.LOG )
263 ReadNodeConfiguration.Run( self.VARS, self.LOG )
264 AuthenticateWithPLC.Run( self.VARS, self.LOG )
265 GetAndUpdateNodeDetails.Run( self.VARS, self.LOG )
267 # override machine's current state from the command line
268 if self.forceState is not None:
269 self.VARS['BOOT_STATE']= self.forceState
270 UpdateBootStateWithPLC.Run( self.VARS, self.LOG )
272 stateRun = NodeRunStates.get(self.VARS['BOOT_STATE'],_badRun)
277 self.LOG.write( "\n\nKeyError while running: %s\n" % str(e) )
278 except BootManagerException, e:
279 self.LOG.write( "\n\nException while running: %s\n" % str(e) )
281 self.LOG.write( "\n\nImplementation Error\n")
282 traceback.print_exc(file=self.LOG.OutputFile)
283 traceback.print_exc()
288 except BootManagerException, e:
289 self.LOG.write( "\n\nException while running: %s\n" % str(e) )
291 self.LOG.write( "\n\nImplementation Error\n")
292 traceback.print_exc(file=self.LOG.OutputFile)
293 traceback.print_exc()
301 utils.prompt_for_breakpoint_mode()
303 #utils.breakpoint ("Entering BootManager::main")
306 NodeRunStates = {'install':None,
313 # set to 1 if error occurred
316 # all output goes through this class so we can save it and post
317 # the data back to PlanetLab central
320 LOG.LogEntry( "BootManager started at: %s" % \
321 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) )
327 if NodeRunStates.has_key(fState):
330 LOG.LogEntry("FATAL: cannot force node run state to=%s" % fState)
333 traceback.print_exc(file=LOG.OutputFile)
334 traceback.print_exc()
337 LOG.LogEntry( "BootManager finished at: %s" % \
338 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) )
343 bm= BootManager(LOG,forceState)
345 LOG.LogEntry( "Unable to initialize BootManager." )
347 LOG.LogEntry( "Running version %s of BootManager." %
351 LOG.LogEntry( "\nDone!" );
353 LOG.LogEntry( "\nError occurred!" );
356 traceback.print_exc(file=LOG.OutputFile)
357 traceback.print_exc()
359 LOG.LogEntry( "BootManager finished at: %s" % \
360 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) )
366 if __name__ == "__main__":
367 error = main(sys.argv)