140a360c2SBrad Bishop#!/usr/bin/python -u 240a360c2SBrad Bishop 340a360c2SBrad Bishopimport gobject 440a360c2SBrad Bishopimport dbus 540a360c2SBrad Bishopimport dbus.service 640a360c2SBrad Bishopimport dbus.mainloop.glib 7*0c8c5d4aSMilton Millerimport subprocess 8*0c8c5d4aSMilton Millerimport tempfile 940a360c2SBrad Bishopimport shutil 1040a360c2SBrad Bishopimport tarfile 1140a360c2SBrad Bishopimport os 1240a360c2SBrad Bishopfrom obmc.dbuslib.bindings import get_dbus, DbusProperties, DbusObjectManager 1340a360c2SBrad Bishop 1440a360c2SBrad BishopDBUS_NAME = 'org.openbmc.control.BmcFlash' 1540a360c2SBrad BishopOBJ_NAME = '/org/openbmc/control/flash/bmc' 1640a360c2SBrad BishopFLASH_INTF = 'org.openbmc.Flash' 1740a360c2SBrad BishopDOWNLOAD_INTF = 'org.openbmc.managers.Download' 1840a360c2SBrad Bishop 19*0c8c5d4aSMilton MillerBMC_DBUS_NAME = 'org.openbmc.control.Bmc' 20*0c8c5d4aSMilton MillerBMC_OBJ_NAME = '/org/openbmc/control/bmc0' 21*0c8c5d4aSMilton Miller 2240a360c2SBrad BishopUPDATE_PATH = '/run/initramfs' 2340a360c2SBrad Bishop 2440a360c2SBrad Bishop 2540a360c2SBrad Bishopdef doExtract(members,files): 2640a360c2SBrad Bishop for tarinfo in members: 2740a360c2SBrad Bishop if files.has_key(tarinfo.name) == True: 2840a360c2SBrad Bishop yield tarinfo 2940a360c2SBrad Bishop 3040a360c2SBrad Bishop 3140a360c2SBrad Bishopclass BmcFlashControl(DbusProperties,DbusObjectManager): 3240a360c2SBrad Bishop def __init__(self,bus,name): 3340a360c2SBrad Bishop self.dbus_objects = { } 3440a360c2SBrad Bishop DbusProperties.__init__(self) 3540a360c2SBrad Bishop DbusObjectManager.__init__(self) 3640a360c2SBrad Bishop dbus.service.Object.__init__(self,bus,name) 3740a360c2SBrad Bishop 3840a360c2SBrad Bishop self.Set(DBUS_NAME,"status","Idle") 3940a360c2SBrad Bishop self.Set(DBUS_NAME,"filename","") 40c8094109SMilton Miller self.Set(DBUS_NAME,"preserve_network_settings",True) 4140a360c2SBrad Bishop self.Set(DBUS_NAME,"restore_application_defaults",False) 4240a360c2SBrad Bishop self.Set(DBUS_NAME,"update_kernel_and_apps",False) 4340a360c2SBrad Bishop self.Set(DBUS_NAME,"clear_persistent_files",False) 44*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"auto_apply",False) 4540a360c2SBrad Bishop 4640a360c2SBrad Bishop bus.add_signal_receiver(self.download_error_handler,signal_name = "DownloadError") 4740a360c2SBrad Bishop bus.add_signal_receiver(self.download_complete_handler,signal_name = "DownloadComplete") 4840a360c2SBrad Bishop 49*0c8c5d4aSMilton Miller self.update_process = None 50*0c8c5d4aSMilton Miller self.progress_name = None 51*0c8c5d4aSMilton Miller 5240a360c2SBrad Bishop self.InterfacesAdded(name,self.properties) 5340a360c2SBrad Bishop 5440a360c2SBrad Bishop 5540a360c2SBrad Bishop @dbus.service.method(DBUS_NAME, 5640a360c2SBrad Bishop in_signature='ss', out_signature='') 5740a360c2SBrad Bishop def updateViaTftp(self,ip,filename): 5840a360c2SBrad Bishop self.Set(DBUS_NAME,"status","Downloading") 59*0c8c5d4aSMilton Miller self.TftpDownload(ip,filename) 6040a360c2SBrad Bishop 6140a360c2SBrad Bishop @dbus.service.method(DBUS_NAME, 6240a360c2SBrad Bishop in_signature='s', out_signature='') 6340a360c2SBrad Bishop def update(self,filename): 6440a360c2SBrad Bishop self.Set(DBUS_NAME,"filename",filename) 6540a360c2SBrad Bishop self.download_complete_handler(filename, filename) 6640a360c2SBrad Bishop 6740a360c2SBrad Bishop @dbus.service.signal(DOWNLOAD_INTF,signature='ss') 6840a360c2SBrad Bishop def TftpDownload(self,ip,filename): 6940a360c2SBrad Bishop self.Set(DBUS_NAME,"filename",filename) 7040a360c2SBrad Bishop pass 7140a360c2SBrad Bishop 7240a360c2SBrad Bishop 7340a360c2SBrad Bishop ## Signal handler 7440a360c2SBrad Bishop def download_error_handler(self,filename): 7540a360c2SBrad Bishop if (filename == self.Get(DBUS_NAME,"filename")): 7640a360c2SBrad Bishop self.Set(DBUS_NAME,"status","Download Error") 7740a360c2SBrad Bishop 7840a360c2SBrad Bishop def download_complete_handler(self,outfile,filename): 7940a360c2SBrad Bishop ## do update 8040a360c2SBrad Bishop if (filename != self.Get(DBUS_NAME,"filename")): 8140a360c2SBrad Bishop return 8240a360c2SBrad Bishop 8340a360c2SBrad Bishop print "Download complete. Updating..." 8440a360c2SBrad Bishop 8540a360c2SBrad Bishop self.Set(DBUS_NAME,"status","Download Complete") 8640a360c2SBrad Bishop copy_files = {} 8740a360c2SBrad Bishop 8840a360c2SBrad Bishop ## determine needed files 8940a360c2SBrad Bishop if (self.Get(DBUS_NAME,"update_kernel_and_apps") == False): 9040a360c2SBrad Bishop copy_files["image-bmc"] = True 9140a360c2SBrad Bishop else: 9240a360c2SBrad Bishop copy_files["image-kernel"] = True 9340a360c2SBrad Bishop copy_files["image-initramfs"] = True 9440a360c2SBrad Bishop copy_files["image-rofs"] = True 9540a360c2SBrad Bishop 9640a360c2SBrad Bishop if (self.Get(DBUS_NAME,"restore_application_defaults") == True): 9740a360c2SBrad Bishop copy_files["image-rwfs"] = True 9840a360c2SBrad Bishop 9940a360c2SBrad Bishop 10040a360c2SBrad Bishop ## make sure files exist in archive 10140a360c2SBrad Bishop try: 10240a360c2SBrad Bishop tar = tarfile.open(outfile,"r") 10340a360c2SBrad Bishop files = {} 10440a360c2SBrad Bishop for f in tar.getnames(): 10540a360c2SBrad Bishop files[f] = True 10640a360c2SBrad Bishop tar.close() 10740a360c2SBrad Bishop for f in copy_files.keys(): 10840a360c2SBrad Bishop if (files.has_key(f) == False): 10940a360c2SBrad Bishop raise Exception("ERROR: File not found in update archive: "+f) 11040a360c2SBrad Bishop 11140a360c2SBrad Bishop except Exception as e: 11240a360c2SBrad Bishop print e 113*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Unpack Error") 11440a360c2SBrad Bishop return 11540a360c2SBrad Bishop 11640a360c2SBrad Bishop try: 11740a360c2SBrad Bishop tar = tarfile.open(outfile,"r") 11840a360c2SBrad Bishop tar.extractall(UPDATE_PATH,members=doExtract(tar,copy_files)) 11940a360c2SBrad Bishop tar.close() 12040a360c2SBrad Bishop 12140a360c2SBrad Bishop if (self.Get(DBUS_NAME,"clear_persistent_files") == True): 12240a360c2SBrad Bishop print "Removing persistent files" 123b6cfc545SMilton Miller try: 12440a360c2SBrad Bishop os.unlink(UPDATE_PATH+"/whitelist") 125b6cfc545SMilton Miller except OSError as e: 126b6cfc545SMilton Miller if (e.errno == errno.EISDIR): 127b6cfc545SMilton Miller pass 128b6cfc545SMilton Miller elif (e.errno == errno.ENOENT): 129b6cfc545SMilton Miller pass 130b6cfc545SMilton Miller else: 131b6cfc545SMilton Miller raise 132b6cfc545SMilton Miller 133b6cfc545SMilton Miller try: 134b6cfc545SMilton Miller wldir = UPDATE_PATH + "/whitelist.d" 135b6cfc545SMilton Miller 136b6cfc545SMilton Miller for file in os.listdir(wldir): 137b6cfc545SMilton Miller os.unlink(os.path.join(wldir,file)) 138b6cfc545SMilton Miller except OSError as e: 139b6cfc545SMilton Miller if (e.errno == errno.EISDIR): 140b6cfc545SMilton Miller pass 141b6cfc545SMilton Miller else: 142b6cfc545SMilton Miller raise 143b6cfc545SMilton Miller 14440a360c2SBrad Bishop if (self.Get(DBUS_NAME,"preserve_network_settings") == True): 14540a360c2SBrad Bishop print "Preserving network settings" 146c8094109SMilton Miller shutil.copy2("/run/fw_env",UPDATE_PATH+"/image-u-boot-env") 14740a360c2SBrad Bishop 14840a360c2SBrad Bishop except Exception as e: 14940a360c2SBrad Bishop print e 150*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Unpack Error") 151*0c8c5d4aSMilton Miller 152*0c8c5d4aSMilton Miller self.Verify() 153*0c8c5d4aSMilton Miller 154*0c8c5d4aSMilton Miller def Verify(self): 155*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Checking Image") 156*0c8c5d4aSMilton Miller try: 157*0c8c5d4aSMilton Miller subprocess.check_call([ 158*0c8c5d4aSMilton Miller "/run/initramfs/update", 159*0c8c5d4aSMilton Miller "--no-flash", 160*0c8c5d4aSMilton Miller "--no-save-files", 161*0c8c5d4aSMilton Miller "--no-restore-files", 162*0c8c5d4aSMilton Miller "--no-clean-saved-files" ]) 163*0c8c5d4aSMilton Miller 164*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Image ready to apply.") 165*0c8c5d4aSMilton Miller if (self.Get(DBUS_NAME,"auto_apply")): 166*0c8c5d4aSMilton Miller self.Apply() 167*0c8c5d4aSMilton Miller except: 168*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"auto_apply",False) 169*0c8c5d4aSMilton Miller try: 170*0c8c5d4aSMilton Miller subprocess.check_output([ 171*0c8c5d4aSMilton Miller "/run/initramfs/update", 172*0c8c5d4aSMilton Miller "--no-flash", 173*0c8c5d4aSMilton Miller "--ignore-mount", 174*0c8c5d4aSMilton Miller "--no-save-files", 175*0c8c5d4aSMilton Miller "--no-restore-files", 176*0c8c5d4aSMilton Miller "--no-clean-saved-files" ], 177*0c8c5d4aSMilton Miller stderr = subprocess.STDOUT) 178*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Deferred for mounted filesystem. reboot BMC to apply.") 179*0c8c5d4aSMilton Miller except subprocess.CalledProcessError as e: 180*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Verify error: %s" 181*0c8c5d4aSMilton Miller % e.output) 182*0c8c5d4aSMilton Miller except OSError as e: 183*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Verify error: problem calling update: %s" % e.strerror) 18440a360c2SBrad Bishop 18540a360c2SBrad Bishop 186*0c8c5d4aSMilton Miller def Cleanup(self): 187*0c8c5d4aSMilton Miller if self.progress_name: 188*0c8c5d4aSMilton Miller try: 189*0c8c5d4aSMilton Miller os.unlink(self.progress_name) 190*0c8c5d4aSMilton Miller self.progress_name = None 191*0c8c5d4aSMilton Miller except oserror as e: 192*0c8c5d4aSMilton Miller if e.errno == EEXIST: 193*0c8c5d4aSMilton Miller pass 194*0c8c5d4aSMilton Miller raise 195*0c8c5d4aSMilton Miller self.update_process = None 196*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Idle") 19740a360c2SBrad Bishop 198*0c8c5d4aSMilton Miller @dbus.service.method(DBUS_NAME, 199*0c8c5d4aSMilton Miller in_signature='', out_signature='') 200*0c8c5d4aSMilton Miller def Abort(self): 201*0c8c5d4aSMilton Miller if self.update_process: 202*0c8c5d4aSMilton Miller try: 203*0c8c5d4aSMilton Miller self.update_process.kill() 204*0c8c5d4aSMilton Miller except: 205*0c8c5d4aSMilton Miller pass 206*0c8c5d4aSMilton Miller for file in os.listdir(UPDATE_PATH): 207*0c8c5d4aSMilton Miller if file.startswith('image-'): 208*0c8c5d4aSMilton Miller os.unlink(os.path.join(UPDATE_PATH,file)) 209*0c8c5d4aSMilton Miller 210*0c8c5d4aSMilton Miller self.Cleanup(); 211*0c8c5d4aSMilton Miller 212*0c8c5d4aSMilton Miller @dbus.service.method(DBUS_NAME, 213*0c8c5d4aSMilton Miller in_signature='', out_signature='s') 214*0c8c5d4aSMilton Miller def GetUpdateProgress(self): 215*0c8c5d4aSMilton Miller msg = "" 216*0c8c5d4aSMilton Miller 217*0c8c5d4aSMilton Miller if self.update_process and self.update_process.returncode is None: 218*0c8c5d4aSMilton Miller self.update_process.poll() 219*0c8c5d4aSMilton Miller 220*0c8c5d4aSMilton Miller if (self.update_process is None): 221*0c8c5d4aSMilton Miller pass 222*0c8c5d4aSMilton Miller elif (self.update_process.returncode > 0): 223*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Apply failed") 224*0c8c5d4aSMilton Miller elif (self.update_process.returncode is None): 225*0c8c5d4aSMilton Miller pass 226*0c8c5d4aSMilton Miller else: # (self.update_process.returncode == 0) 227*0c8c5d4aSMilton Miller files = "" 228*0c8c5d4aSMilton Miller for file in os.listdir(UPDATE_PATH): 229*0c8c5d4aSMilton Miller if file.startswith('image-'): 230*0c8c5d4aSMilton Miller files = files + file; 231*0c8c5d4aSMilton Miller if files == "": 232*0c8c5d4aSMilton Miller msg = "Apply Complete. Reboot to take effect." 233*0c8c5d4aSMilton Miller else: 234*0c8c5d4aSMilton Miller msg = "Apply Incomplete, Remaining:" + files 235*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status", msg) 236*0c8c5d4aSMilton Miller 237*0c8c5d4aSMilton Miller msg = self.Get(DBUS_NAME,"status") + "\n"; 238*0c8c5d4aSMilton Miller if self.progress_name: 239*0c8c5d4aSMilton Miller try: 240*0c8c5d4aSMilton Miller prog = open(self.progress_name,'r') 241*0c8c5d4aSMilton Miller for line in prog: 242*0c8c5d4aSMilton Miller # strip off initial sets of xxx\r here 243*0c8c5d4aSMilton Miller # ignore crlf at the end 244*0c8c5d4aSMilton Miller # cr will be -1 if no '\r' is found 245*0c8c5d4aSMilton Miller cr = line.rfind("\r", 0, -2) 246*0c8c5d4aSMilton Miller msg = msg + line[cr + 1: ] 247*0c8c5d4aSMilton Miller except OSError as e: 248*0c8c5d4aSMilton Miller if (e.error == EEXIST): 249*0c8c5d4aSMilton Miller pass 250*0c8c5d4aSMilton Miller raise 251*0c8c5d4aSMilton Miller return msg 252*0c8c5d4aSMilton Miller 253*0c8c5d4aSMilton Miller @dbus.service.method(DBUS_NAME, 254*0c8c5d4aSMilton Miller in_signature='', out_signature='') 255*0c8c5d4aSMilton Miller def Apply(self): 256*0c8c5d4aSMilton Miller progress = None 257*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Writing images to flash") 258*0c8c5d4aSMilton Miller try: 259*0c8c5d4aSMilton Miller progress = tempfile.NamedTemporaryFile( 260*0c8c5d4aSMilton Miller delete = False, prefix="progress." ) 261*0c8c5d4aSMilton Miller self.progress_name = progress.name 262*0c8c5d4aSMilton Miller self.update_process = subprocess.Popen([ 263*0c8c5d4aSMilton Miller "/run/initramfs/update" ], 264*0c8c5d4aSMilton Miller stdout = progress.file, 265*0c8c5d4aSMilton Miller stderr = subprocess.STDOUT ) 266*0c8c5d4aSMilton Miller except Exception as e: 267*0c8c5d4aSMilton Miller try: 268*0c8c5d4aSMilton Miller progress.close() 269*0c8c5d4aSMilton Miller os.unlink(progress.name) 270*0c8c5d4aSMilton Miller self.progress_name = None 271*0c8c5d4aSMilton Miller except: 272*0c8c5d4aSMilton Miller pass 273*0c8c5d4aSMilton Miller raise 274*0c8c5d4aSMilton Miller 275*0c8c5d4aSMilton Miller try: 276*0c8c5d4aSMilton Miller progress.close() 277*0c8c5d4aSMilton Miller except: 278*0c8c5d4aSMilton Miller pass 279*0c8c5d4aSMilton Miller 280*0c8c5d4aSMilton Miller @dbus.service.method(DBUS_NAME, 281*0c8c5d4aSMilton Miller in_signature='', out_signature='') 282*0c8c5d4aSMilton Miller def PrepareForUpdate(self): 283*0c8c5d4aSMilton Miller subprocess.call([ 284*0c8c5d4aSMilton Miller "fw_setenv", 285*0c8c5d4aSMilton Miller "openbmconce", 286*0c8c5d4aSMilton Miller "copy-files-to-ram copy-base-filesystem-to-ram"]) 287*0c8c5d4aSMilton Miller self.Set(DBUS_NAME,"status","Switch to update mode in progress") 288*0c8c5d4aSMilton Miller o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME) 289*0c8c5d4aSMilton Miller intf = dbus.Interface(o, BMC_DBUS_NAME) 290*0c8c5d4aSMilton Miller intf.warmReset() 29140a360c2SBrad Bishop 29240a360c2SBrad Bishop 29340a360c2SBrad Bishopif __name__ == '__main__': 29440a360c2SBrad Bishop dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 29540a360c2SBrad Bishop 29640a360c2SBrad Bishop bus = get_dbus() 29740a360c2SBrad Bishop name = dbus.service.BusName(DBUS_NAME, bus) 29840a360c2SBrad Bishop obj = BmcFlashControl(bus, OBJ_NAME) 29940a360c2SBrad Bishop mainloop = gobject.MainLoop() 30040a360c2SBrad Bishop 30140a360c2SBrad Bishop print "Running Bmc Flash Control" 30240a360c2SBrad Bishop mainloop.run() 30340a360c2SBrad Bishop 304