Merge pull request #111 from mdmillerii/flash-bmc

Flash bmc updates
diff --git a/pyflashbmc/bmc_update.py b/pyflashbmc/bmc_update.py
index 9fa54ac..8e3d43d 100644
--- a/pyflashbmc/bmc_update.py
+++ b/pyflashbmc/bmc_update.py
@@ -4,6 +4,8 @@
 import dbus
 import dbus.service
 import dbus.mainloop.glib
+import subprocess
+import tempfile
 import shutil
 import tarfile
 import os
@@ -14,6 +16,9 @@
 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'
 
 
@@ -32,23 +37,27 @@
 		
 		self.Set(DBUS_NAME,"status","Idle")
 		self.Set(DBUS_NAME,"filename","")
-		self.Set(DBUS_NAME,"preserve_network_settings",False)
+		self.Set(DBUS_NAME,"preserve_network_settings",True)
 		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 @@
 
 		except Exception as e:
 			print e
-			self.Set(DBUS_NAME,"status","Update Error")
+			self.Set(DBUS_NAME,"status","Unpack Error")
 			return
 
 		try:
@@ -111,19 +120,175 @@
 
 			if (self.Get(DBUS_NAME,"clear_persistent_files") == True):
 				print "Removing persistent files"
-				os.unlink(UPDATE_PATH+"/whitelist")
+				try:
+					os.unlink(UPDATE_PATH+"/whitelist")
+				except OSError as e:
+					if (e.errno == errno.EISDIR):
+						pass
+					elif (e.errno == errno.ENOENT):
+						pass
+					else:
+						raise
+
+				try:
+					wldir = UPDATE_PATH + "/whitelist.d"
+
+					for file in os.listdir(wldir):
+						os.unlink(os.path.join(wldir,file))
+				except OSError as e:
+					if (e.errno == errno.EISDIR):
+						pass
+					else:
+						raise
+
 			if (self.Get(DBUS_NAME,"preserve_network_settings") == True):
 				print "Preserving network settings"
-				shutil.copy2("/dev/mtd2",UPDATE_PATH+"/image-u-boot-env")
+				shutil.copy2("/run/fw_env",UPDATE_PATH+"/image-u-boot-env")
 				
 		except Exception as e:
 			print e
-			self.Set(DBUS_NAME,"status","Update Error")
-				
-		
+			self.Set(DBUS_NAME,"status","Unpack Error")
 
-		self.Set(DBUS_NAME,"status","Update Success.  Please reboot.")
-		
+		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()
+
 
 if __name__ == '__main__':
     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)