134c3bf9eSBrad Bishop#!/usr/bin/env python 240a360c2SBrad Bishop 340a360c2SBrad Bishopimport gobject 440a360c2SBrad Bishopimport dbus 540a360c2SBrad Bishopimport dbus.service 640a360c2SBrad Bishopimport dbus.mainloop.glib 70c8c5d4aSMilton Millerimport subprocess 80c8c5d4aSMilton 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 BishopDOWNLOAD_INTF = 'org.openbmc.managers.Download' 1740a360c2SBrad Bishop 180c8c5d4aSMilton MillerBMC_DBUS_NAME = 'org.openbmc.control.Bmc' 190c8c5d4aSMilton MillerBMC_OBJ_NAME = '/org/openbmc/control/bmc0' 200c8c5d4aSMilton Miller 2140a360c2SBrad BishopUPDATE_PATH = '/run/initramfs' 2240a360c2SBrad Bishop 2340a360c2SBrad Bishop 2440a360c2SBrad Bishopdef doExtract(members, files): 2540a360c2SBrad Bishop for tarinfo in members: 2634c3bf9eSBrad Bishop if tarinfo.name in files: 2740a360c2SBrad Bishop yield tarinfo 2840a360c2SBrad Bishop 2940a360c2SBrad Bishop 3040a360c2SBrad Bishopclass BmcFlashControl(DbusProperties, DbusObjectManager): 3140a360c2SBrad Bishop def __init__(self, bus, name): 32*f47f5faaSBrad Bishop super(BmcFlashControl, self).__init__( 33*f47f5faaSBrad Bishop conn=bus, 34*f47f5faaSBrad Bishop object_path=name) 3540a360c2SBrad Bishop 3640a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Idle") 3740a360c2SBrad Bishop self.Set(DBUS_NAME, "filename", "") 38c8094109SMilton Miller self.Set(DBUS_NAME, "preserve_network_settings", True) 3940a360c2SBrad Bishop self.Set(DBUS_NAME, "restore_application_defaults", False) 4040a360c2SBrad Bishop self.Set(DBUS_NAME, "update_kernel_and_apps", False) 4140a360c2SBrad Bishop self.Set(DBUS_NAME, "clear_persistent_files", False) 420c8c5d4aSMilton Miller self.Set(DBUS_NAME, "auto_apply", False) 4340a360c2SBrad Bishop 4434c3bf9eSBrad Bishop bus.add_signal_receiver( 4534c3bf9eSBrad Bishop self.download_error_handler, signal_name="DownloadError") 4634c3bf9eSBrad Bishop bus.add_signal_receiver( 4734c3bf9eSBrad Bishop self.download_complete_handler, signal_name="DownloadComplete") 4840a360c2SBrad Bishop 490c8c5d4aSMilton Miller self.update_process = None 500c8c5d4aSMilton Miller self.progress_name = None 510c8c5d4aSMilton Miller 5234c3bf9eSBrad Bishop @dbus.service.method( 5334c3bf9eSBrad Bishop DBUS_NAME, in_signature='ss', out_signature='') 5440a360c2SBrad Bishop def updateViaTftp(self, ip, filename): 5540a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Downloading") 560c8c5d4aSMilton Miller self.TftpDownload(ip, filename) 5740a360c2SBrad Bishop 5834c3bf9eSBrad Bishop @dbus.service.method( 5934c3bf9eSBrad Bishop DBUS_NAME, in_signature='s', out_signature='') 6040a360c2SBrad Bishop def update(self, filename): 6140a360c2SBrad Bishop self.Set(DBUS_NAME, "filename", filename) 6240a360c2SBrad Bishop self.download_complete_handler(filename, filename) 6340a360c2SBrad Bishop 6440a360c2SBrad Bishop @dbus.service.signal(DOWNLOAD_INTF, signature='ss') 6540a360c2SBrad Bishop def TftpDownload(self, ip, filename): 6640a360c2SBrad Bishop self.Set(DBUS_NAME, "filename", filename) 6740a360c2SBrad Bishop pass 6840a360c2SBrad Bishop 6940a360c2SBrad Bishop ## Signal handler 7040a360c2SBrad Bishop def download_error_handler(self, filename): 7140a360c2SBrad Bishop if (filename == self.Get(DBUS_NAME, "filename")): 7240a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Download Error") 7340a360c2SBrad Bishop 7440a360c2SBrad Bishop def download_complete_handler(self, outfile, filename): 7540a360c2SBrad Bishop ## do update 7640a360c2SBrad Bishop if (filename != self.Get(DBUS_NAME, "filename")): 7740a360c2SBrad Bishop return 7840a360c2SBrad Bishop 7940a360c2SBrad Bishop print "Download complete. Updating..." 8040a360c2SBrad Bishop 8140a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Download Complete") 8240a360c2SBrad Bishop copy_files = {} 8340a360c2SBrad Bishop 8440a360c2SBrad Bishop ## determine needed files 8534c3bf9eSBrad Bishop if not self.Get(DBUS_NAME, "update_kernel_and_apps"): 8640a360c2SBrad Bishop copy_files["image-bmc"] = True 8740a360c2SBrad Bishop else: 8840a360c2SBrad Bishop copy_files["image-kernel"] = True 8940a360c2SBrad Bishop copy_files["image-initramfs"] = True 9040a360c2SBrad Bishop copy_files["image-rofs"] = True 9140a360c2SBrad Bishop 9234c3bf9eSBrad Bishop if self.Get(DBUS_NAME, "restore_application_defaults"): 9340a360c2SBrad Bishop copy_files["image-rwfs"] = True 9440a360c2SBrad Bishop 9540a360c2SBrad Bishop ## make sure files exist in archive 9640a360c2SBrad Bishop try: 9740a360c2SBrad Bishop tar = tarfile.open(outfile, "r") 9840a360c2SBrad Bishop files = {} 9940a360c2SBrad Bishop for f in tar.getnames(): 10040a360c2SBrad Bishop files[f] = True 10140a360c2SBrad Bishop tar.close() 10240a360c2SBrad Bishop for f in copy_files.keys(): 10334c3bf9eSBrad Bishop if f not in files: 10434c3bf9eSBrad Bishop raise Exception( 10534c3bf9eSBrad Bishop "ERROR: File not found in update archive: "+f) 10640a360c2SBrad Bishop 10740a360c2SBrad Bishop except Exception as e: 10840a360c2SBrad Bishop print e 1090c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Unpack Error") 11040a360c2SBrad Bishop return 11140a360c2SBrad Bishop 11240a360c2SBrad Bishop try: 11340a360c2SBrad Bishop tar = tarfile.open(outfile, "r") 11440a360c2SBrad Bishop tar.extractall(UPDATE_PATH, members=doExtract(tar, copy_files)) 11540a360c2SBrad Bishop tar.close() 11640a360c2SBrad Bishop 11734c3bf9eSBrad Bishop if self.Get(DBUS_NAME, "clear_persistent_files"): 11840a360c2SBrad Bishop print "Removing persistent files" 119b6cfc545SMilton Miller try: 12040a360c2SBrad Bishop os.unlink(UPDATE_PATH+"/whitelist") 121b6cfc545SMilton Miller except OSError as e: 122b6cfc545SMilton Miller if (e.errno == errno.EISDIR): 123b6cfc545SMilton Miller pass 124b6cfc545SMilton Miller elif (e.errno == errno.ENOENT): 125b6cfc545SMilton Miller pass 126b6cfc545SMilton Miller else: 127b6cfc545SMilton Miller raise 128b6cfc545SMilton Miller 129b6cfc545SMilton Miller try: 130b6cfc545SMilton Miller wldir = UPDATE_PATH + "/whitelist.d" 131b6cfc545SMilton Miller 132b6cfc545SMilton Miller for file in os.listdir(wldir): 133b6cfc545SMilton Miller os.unlink(os.path.join(wldir, file)) 134b6cfc545SMilton Miller except OSError as e: 135b6cfc545SMilton Miller if (e.errno == errno.EISDIR): 136b6cfc545SMilton Miller pass 137b6cfc545SMilton Miller else: 138b6cfc545SMilton Miller raise 139b6cfc545SMilton Miller 14034c3bf9eSBrad Bishop if self.Get(DBUS_NAME, "preserve_network_settings"): 14140a360c2SBrad Bishop print "Preserving network settings" 142c8094109SMilton Miller shutil.copy2("/run/fw_env", UPDATE_PATH+"/image-u-boot-env") 14340a360c2SBrad Bishop 14440a360c2SBrad Bishop except Exception as e: 14540a360c2SBrad Bishop print e 1460c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Unpack Error") 1470c8c5d4aSMilton Miller 1480c8c5d4aSMilton Miller self.Verify() 1490c8c5d4aSMilton Miller 1500c8c5d4aSMilton Miller def Verify(self): 1510c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Checking Image") 1520c8c5d4aSMilton Miller try: 1530c8c5d4aSMilton Miller subprocess.check_call([ 1540c8c5d4aSMilton Miller "/run/initramfs/update", 1550c8c5d4aSMilton Miller "--no-flash", 1560c8c5d4aSMilton Miller "--no-save-files", 1570c8c5d4aSMilton Miller "--no-restore-files", 1580c8c5d4aSMilton Miller "--no-clean-saved-files"]) 1590c8c5d4aSMilton Miller 1600c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Image ready to apply.") 1610c8c5d4aSMilton Miller if (self.Get(DBUS_NAME, "auto_apply")): 1620c8c5d4aSMilton Miller self.Apply() 1630c8c5d4aSMilton Miller except: 1640c8c5d4aSMilton Miller self.Set(DBUS_NAME, "auto_apply", False) 1650c8c5d4aSMilton Miller try: 1660c8c5d4aSMilton Miller subprocess.check_output([ 1670c8c5d4aSMilton Miller "/run/initramfs/update", 1680c8c5d4aSMilton Miller "--no-flash", 1690c8c5d4aSMilton Miller "--ignore-mount", 1700c8c5d4aSMilton Miller "--no-save-files", 1710c8c5d4aSMilton Miller "--no-restore-files", 1720c8c5d4aSMilton Miller "--no-clean-saved-files"], 1730c8c5d4aSMilton Miller stderr=subprocess.STDOUT) 17434c3bf9eSBrad Bishop self.Set( 17534c3bf9eSBrad Bishop DBUS_NAME, "status", 17634c3bf9eSBrad Bishop "Deferred for mounted filesystem. reboot BMC to apply.") 1770c8c5d4aSMilton Miller except subprocess.CalledProcessError as e: 17834c3bf9eSBrad Bishop self.Set( 17934c3bf9eSBrad Bishop DBUS_NAME, "status", "Verify error: %s" % e.output) 1800c8c5d4aSMilton Miller except OSError as e: 18134c3bf9eSBrad Bishop self.Set( 18234c3bf9eSBrad Bishop DBUS_NAME, "status", 18334c3bf9eSBrad Bishop "Verify error: problem calling update: %s" % e.strerror) 18440a360c2SBrad Bishop 1850c8c5d4aSMilton Miller def Cleanup(self): 1860c8c5d4aSMilton Miller if self.progress_name: 1870c8c5d4aSMilton Miller try: 1880c8c5d4aSMilton Miller os.unlink(self.progress_name) 1890c8c5d4aSMilton Miller self.progress_name = None 1900c8c5d4aSMilton Miller except oserror as e: 1910c8c5d4aSMilton Miller if e.errno == EEXIST: 1920c8c5d4aSMilton Miller pass 1930c8c5d4aSMilton Miller raise 1940c8c5d4aSMilton Miller self.update_process = None 1950c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Idle") 19640a360c2SBrad Bishop 19734c3bf9eSBrad Bishop @dbus.service.method( 19834c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='') 1990c8c5d4aSMilton Miller def Abort(self): 2000c8c5d4aSMilton Miller if self.update_process: 2010c8c5d4aSMilton Miller try: 2020c8c5d4aSMilton Miller self.update_process.kill() 2030c8c5d4aSMilton Miller except: 2040c8c5d4aSMilton Miller pass 2050c8c5d4aSMilton Miller for file in os.listdir(UPDATE_PATH): 2060c8c5d4aSMilton Miller if file.startswith('image-'): 2070c8c5d4aSMilton Miller os.unlink(os.path.join(UPDATE_PATH, file)) 2080c8c5d4aSMilton Miller 20934c3bf9eSBrad Bishop self.Cleanup() 2100c8c5d4aSMilton Miller 21134c3bf9eSBrad Bishop @dbus.service.method( 21234c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='s') 2130c8c5d4aSMilton Miller def GetUpdateProgress(self): 2140c8c5d4aSMilton Miller msg = "" 2150c8c5d4aSMilton Miller 2160c8c5d4aSMilton Miller if self.update_process and self.update_process.returncode is None: 2170c8c5d4aSMilton Miller self.update_process.poll() 2180c8c5d4aSMilton Miller 2190c8c5d4aSMilton Miller if (self.update_process is None): 2200c8c5d4aSMilton Miller pass 2210c8c5d4aSMilton Miller elif (self.update_process.returncode > 0): 2220c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Apply failed") 2230c8c5d4aSMilton Miller elif (self.update_process.returncode is None): 2240c8c5d4aSMilton Miller pass 2250c8c5d4aSMilton Miller else: # (self.update_process.returncode == 0) 2260c8c5d4aSMilton Miller files = "" 2270c8c5d4aSMilton Miller for file in os.listdir(UPDATE_PATH): 2280c8c5d4aSMilton Miller if file.startswith('image-'): 22934c3bf9eSBrad Bishop files = files + file 2300c8c5d4aSMilton Miller if files == "": 2310c8c5d4aSMilton Miller msg = "Apply Complete. Reboot to take effect." 2320c8c5d4aSMilton Miller else: 2330c8c5d4aSMilton Miller msg = "Apply Incomplete, Remaining:" + files 2340c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", msg) 2350c8c5d4aSMilton Miller 23634c3bf9eSBrad Bishop msg = self.Get(DBUS_NAME, "status") + "\n" 2370c8c5d4aSMilton Miller if self.progress_name: 2380c8c5d4aSMilton Miller try: 2390c8c5d4aSMilton Miller prog = open(self.progress_name, 'r') 2400c8c5d4aSMilton Miller for line in prog: 2410c8c5d4aSMilton Miller # strip off initial sets of xxx\r here 2420c8c5d4aSMilton Miller # ignore crlf at the end 2430c8c5d4aSMilton Miller # cr will be -1 if no '\r' is found 2440c8c5d4aSMilton Miller cr = line.rfind("\r", 0, -2) 2450c8c5d4aSMilton Miller msg = msg + line[cr + 1:] 2460c8c5d4aSMilton Miller except OSError as e: 2470c8c5d4aSMilton Miller if (e.error == EEXIST): 2480c8c5d4aSMilton Miller pass 2490c8c5d4aSMilton Miller raise 2500c8c5d4aSMilton Miller return msg 2510c8c5d4aSMilton Miller 25234c3bf9eSBrad Bishop @dbus.service.method( 25334c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='') 2540c8c5d4aSMilton Miller def Apply(self): 2550c8c5d4aSMilton Miller progress = None 2560c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Writing images to flash") 2570c8c5d4aSMilton Miller try: 2580c8c5d4aSMilton Miller progress = tempfile.NamedTemporaryFile( 2590c8c5d4aSMilton Miller delete=False, prefix="progress.") 2600c8c5d4aSMilton Miller self.progress_name = progress.name 2610c8c5d4aSMilton Miller self.update_process = subprocess.Popen([ 2620c8c5d4aSMilton Miller "/run/initramfs/update"], 2630c8c5d4aSMilton Miller stdout=progress.file, 2640c8c5d4aSMilton Miller stderr=subprocess.STDOUT) 2650c8c5d4aSMilton Miller except Exception as e: 2660c8c5d4aSMilton Miller try: 2670c8c5d4aSMilton Miller progress.close() 2680c8c5d4aSMilton Miller os.unlink(progress.name) 2690c8c5d4aSMilton Miller self.progress_name = None 2700c8c5d4aSMilton Miller except: 2710c8c5d4aSMilton Miller pass 2720c8c5d4aSMilton Miller raise 2730c8c5d4aSMilton Miller 2740c8c5d4aSMilton Miller try: 2750c8c5d4aSMilton Miller progress.close() 2760c8c5d4aSMilton Miller except: 2770c8c5d4aSMilton Miller pass 2780c8c5d4aSMilton Miller 27934c3bf9eSBrad Bishop @dbus.service.method( 28034c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='') 2810c8c5d4aSMilton Miller def PrepareForUpdate(self): 2820c8c5d4aSMilton Miller subprocess.call([ 2830c8c5d4aSMilton Miller "fw_setenv", 2840c8c5d4aSMilton Miller "openbmconce", 2850c8c5d4aSMilton Miller "copy-files-to-ram copy-base-filesystem-to-ram"]) 2860c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Switch to update mode in progress") 2870c8c5d4aSMilton Miller o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME) 2880c8c5d4aSMilton Miller intf = dbus.Interface(o, BMC_DBUS_NAME) 2890c8c5d4aSMilton Miller intf.warmReset() 29040a360c2SBrad Bishop 29140a360c2SBrad Bishop 29240a360c2SBrad Bishopif __name__ == '__main__': 29340a360c2SBrad Bishop dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 29440a360c2SBrad Bishop 29540a360c2SBrad Bishop bus = get_dbus() 29640a360c2SBrad Bishop obj = BmcFlashControl(bus, OBJ_NAME) 29740a360c2SBrad Bishop mainloop = gobject.MainLoop() 298f0f3efe1SBrad Bishop 299f0f3efe1SBrad Bishop obj.unmask_signals() 30070852a38SBrad Bishop name = dbus.service.BusName(DBUS_NAME, bus) 30140a360c2SBrad Bishop 30240a360c2SBrad Bishop print "Running Bmc Flash Control" 30340a360c2SBrad Bishop mainloop.run() 304