diff options
author | Milton Miller <miltonm@us.ibm.com> | 2016-06-29 17:41:36 -0500 |
---|---|---|
committer | Milton Miller <miltonm@us.ibm.com> | 2016-06-30 18:22:33 -0500 |
commit | 0c8c5d4a4983d1785d0adffbe53f17b876ade7e3 (patch) | |
tree | 11d9298f4bd5d225b03dea7a370d55607fec8db7 /pyflashbmc/bmc_update.py | |
parent | b6cfc545a97ac6453b143dc150949d56ad5e52d2 (diff) | |
download | blackbird-skeleton-0c8c5d4a4983d1785d0adffbe53f17b876ade7e3.tar.gz blackbird-skeleton-0c8c5d4a4983d1785d0adffbe53f17b876ade7e3.zip |
flash_bmc: Add Apply method to flash updates from runtime
This commit adds methods to Validate (not exposed), Apply,
GetUpdateProgress, and Abort along with an auto_apply attribute.
A PrepareForUpdate method is also added that will write bootloader
environment to instruct the initramfs init script to copy necessary
file system state into RAM to allow the flash to be updated.
Together these methods take the prior Update method and chain
on the Validate phase which checks the image can be applied
concurrently (and could be extended to check signatures). If
validation fails auto_apply is cleared. If auto_apply remains
true then Apply is called which will perform the check again
(implicitly in the update script) and update the flash contents,
saving its output to a temporary file.
The GetUpdateProgress will return the status variable followed
by the output from the update script, with status that was
overwritten with a carriage return stripped. As each new phase
(erase, write, and verify) of each image is encountered the
message will be extended.
An Abort method will kill an in-progress update, erase the logfile,
and remove any pending images. A new image can be unpacked at this
time.
Note: the current method to clear all persistent files removes the
whitelist and all future calls will clear all files from the read
write layer. This maybe addressed in a future update.
Signed-off-by: Milton Miller <miltonm@us.ibm.com>
Diffstat (limited to 'pyflashbmc/bmc_update.py')
-rw-r--r-- | pyflashbmc/bmc_update.py | 163 |
1 files changed, 154 insertions, 9 deletions
diff --git a/pyflashbmc/bmc_update.py b/pyflashbmc/bmc_update.py index 50042c1..8e3d43d 100644 --- a/pyflashbmc/bmc_update.py +++ b/pyflashbmc/bmc_update.py @@ -4,6 +4,8 @@ import gobject import dbus import dbus.service import dbus.mainloop.glib +import subprocess +import tempfile import shutil import tarfile import os @@ -14,6 +16,9 @@ OBJ_NAME = '/org/openbmc/control/flash/bmc' FLASH_INTF = 'org.openbmc.Flash' DOWNLOAD_INTF = 'org.openbmc.managers.Download' +BMC_DBUS_NAME = 'org.openbmc.control.Bmc' +BMC_OBJ_NAME = '/org/openbmc/control/bmc0' + UPDATE_PATH = '/run/initramfs' @@ -36,19 +41,23 @@ class BmcFlashControl(DbusProperties,DbusObjectManager): self.Set(DBUS_NAME,"restore_application_defaults",False) self.Set(DBUS_NAME,"update_kernel_and_apps",False) self.Set(DBUS_NAME,"clear_persistent_files",False) - + self.Set(DBUS_NAME,"auto_apply",False) + bus.add_signal_receiver(self.download_error_handler,signal_name = "DownloadError") bus.add_signal_receiver(self.download_complete_handler,signal_name = "DownloadComplete") + self.update_process = None + self.progress_name = None + self.InterfacesAdded(name,self.properties) @dbus.service.method(DBUS_NAME, in_signature='ss', out_signature='') def updateViaTftp(self,ip,filename): - self.TftpDownload(ip,filename) self.Set(DBUS_NAME,"status","Downloading") - + self.TftpDownload(ip,filename) + @dbus.service.method(DBUS_NAME, in_signature='s', out_signature='') def update(self,filename): @@ -101,7 +110,7 @@ class BmcFlashControl(DbusProperties,DbusObjectManager): except Exception as e: print e - self.Set(DBUS_NAME,"status","Update Error") + self.Set(DBUS_NAME,"status","Unpack Error") return try: @@ -138,12 +147,148 @@ class BmcFlashControl(DbusProperties,DbusObjectManager): except Exception as e: print e - self.Set(DBUS_NAME,"status","Update Error") - - + self.Set(DBUS_NAME,"status","Unpack Error") + + self.Verify() + + def Verify(self): + self.Set(DBUS_NAME,"status","Checking Image") + try: + subprocess.check_call([ + "/run/initramfs/update", + "--no-flash", + "--no-save-files", + "--no-restore-files", + "--no-clean-saved-files" ]) + + self.Set(DBUS_NAME,"status","Image ready to apply.") + if (self.Get(DBUS_NAME,"auto_apply")): + self.Apply() + except: + self.Set(DBUS_NAME,"auto_apply",False) + try: + subprocess.check_output([ + "/run/initramfs/update", + "--no-flash", + "--ignore-mount", + "--no-save-files", + "--no-restore-files", + "--no-clean-saved-files" ], + stderr = subprocess.STDOUT) + self.Set(DBUS_NAME,"status","Deferred for mounted filesystem. reboot BMC to apply.") + except subprocess.CalledProcessError as e: + self.Set(DBUS_NAME,"status","Verify error: %s" + % e.output) + except OSError as e: + self.Set(DBUS_NAME,"status","Verify error: problem calling update: %s" % e.strerror) + + + def Cleanup(self): + if self.progress_name: + try: + os.unlink(self.progress_name) + self.progress_name = None + except oserror as e: + if e.errno == EEXIST: + pass + raise + self.update_process = None + self.Set(DBUS_NAME,"status","Idle") + + @dbus.service.method(DBUS_NAME, + in_signature='', out_signature='') + def Abort(self): + if self.update_process: + try: + self.update_process.kill() + except: + pass + for file in os.listdir(UPDATE_PATH): + if file.startswith('image-'): + os.unlink(os.path.join(UPDATE_PATH,file)) + + self.Cleanup(); + + @dbus.service.method(DBUS_NAME, + in_signature='', out_signature='s') + def GetUpdateProgress(self): + msg = "" + + if self.update_process and self.update_process.returncode is None: + self.update_process.poll() + + if (self.update_process is None): + pass + elif (self.update_process.returncode > 0): + self.Set(DBUS_NAME,"status","Apply failed") + elif (self.update_process.returncode is None): + pass + else: # (self.update_process.returncode == 0) + files = "" + for file in os.listdir(UPDATE_PATH): + if file.startswith('image-'): + files = files + file; + if files == "": + msg = "Apply Complete. Reboot to take effect." + else: + msg = "Apply Incomplete, Remaining:" + files + self.Set(DBUS_NAME,"status", msg) + + msg = self.Get(DBUS_NAME,"status") + "\n"; + if self.progress_name: + try: + prog = open(self.progress_name,'r') + for line in prog: + # strip off initial sets of xxx\r here + # ignore crlf at the end + # cr will be -1 if no '\r' is found + cr = line.rfind("\r", 0, -2) + msg = msg + line[cr + 1: ] + except OSError as e: + if (e.error == EEXIST): + pass + raise + return msg + + @dbus.service.method(DBUS_NAME, + in_signature='', out_signature='') + def Apply(self): + progress = None + self.Set(DBUS_NAME,"status","Writing images to flash") + try: + progress = tempfile.NamedTemporaryFile( + delete = False, prefix="progress." ) + self.progress_name = progress.name + self.update_process = subprocess.Popen([ + "/run/initramfs/update" ], + stdout = progress.file, + stderr = subprocess.STDOUT ) + except Exception as e: + try: + progress.close() + os.unlink(progress.name) + self.progress_name = None + except: + pass + raise + + try: + progress.close() + except: + pass + + @dbus.service.method(DBUS_NAME, + in_signature='', out_signature='') + def PrepareForUpdate(self): + subprocess.call([ + "fw_setenv", + "openbmconce", + "copy-files-to-ram copy-base-filesystem-to-ram"]) + self.Set(DBUS_NAME,"status","Switch to update mode in progress") + o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME) + intf = dbus.Interface(o, BMC_DBUS_NAME) + intf.warmReset() - self.Set(DBUS_NAME,"status","Update Success. Please reboot.") - if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |