blob: c1c04ec6d7c89348c54bfb7be83f2b7030537ae7 [file] [log] [blame]
Brad Bishop34c3bf92016-08-30 20:34:51 -04001#!/usr/bin/env python
Norman James3d6a06f2016-01-30 13:03:45 -06002
3import gobject
4import dbus
5import dbus.service
6import dbus.mainloop.glib
Milton Miller0c8c5d42016-06-29 17:41:36 -05007import subprocess
8import tempfile
Norman James3d6a06f2016-01-30 13:03:45 -06009import shutil
10import tarfile
Norman James8a65a542016-02-03 19:04:44 -060011import os
Brad Bishop84e73b52016-05-12 15:57:52 -040012from obmc.dbuslib.bindings import get_dbus, DbusProperties, DbusObjectManager
Norman James3d6a06f2016-01-30 13:03:45 -060013
14DBUS_NAME = 'org.openbmc.control.BmcFlash'
15OBJ_NAME = '/org/openbmc/control/flash/bmc'
Norman James3d6a06f2016-01-30 13:03:45 -060016DOWNLOAD_INTF = 'org.openbmc.managers.Download'
17
Milton Miller0c8c5d42016-06-29 17:41:36 -050018BMC_DBUS_NAME = 'org.openbmc.control.Bmc'
19BMC_OBJ_NAME = '/org/openbmc/control/bmc0'
20
Norman James3d6a06f2016-01-30 13:03:45 -060021UPDATE_PATH = '/run/initramfs'
22
23
Brad Bishop34c3bf92016-08-30 20:34:51 -040024def doExtract(members, files):
Norman James3d6a06f2016-01-30 13:03:45 -060025 for tarinfo in members:
Brad Bishop34c3bf92016-08-30 20:34:51 -040026 if tarinfo.name in files:
Norman James3d6a06f2016-01-30 13:03:45 -060027 yield tarinfo
28
29
Brad Bishop34c3bf92016-08-30 20:34:51 -040030class BmcFlashControl(DbusProperties, DbusObjectManager):
31 def __init__(self, bus, name):
Brad Bishopf47f5fa2016-09-08 22:29:01 -040032 super(BmcFlashControl, self).__init__(
33 conn=bus,
34 object_path=name)
Milton Miller0c8c5d42016-06-29 17:41:36 -050035
Brad Bishop34c3bf92016-08-30 20:34:51 -040036 self.Set(DBUS_NAME, "status", "Idle")
37 self.Set(DBUS_NAME, "filename", "")
38 self.Set(DBUS_NAME, "preserve_network_settings", True)
39 self.Set(DBUS_NAME, "restore_application_defaults", False)
40 self.Set(DBUS_NAME, "update_kernel_and_apps", False)
41 self.Set(DBUS_NAME, "clear_persistent_files", False)
42 self.Set(DBUS_NAME, "auto_apply", False)
Norman James3d6a06f2016-01-30 13:03:45 -060043
Brad Bishop34c3bf92016-08-30 20:34:51 -040044 bus.add_signal_receiver(
45 self.download_error_handler, signal_name="DownloadError")
46 bus.add_signal_receiver(
47 self.download_complete_handler, signal_name="DownloadComplete")
Milton Miller0c8c5d42016-06-29 17:41:36 -050048
Brad Bishop34c3bf92016-08-30 20:34:51 -040049 self.update_process = None
50 self.progress_name = None
Norman James3d6a06f2016-01-30 13:03:45 -060051
Brad Bishop34c3bf92016-08-30 20:34:51 -040052 @dbus.service.method(
53 DBUS_NAME, in_signature='ss', out_signature='')
54 def updateViaTftp(self, ip, filename):
55 self.Set(DBUS_NAME, "status", "Downloading")
56 self.TftpDownload(ip, filename)
Milton Miller0c8c5d42016-06-29 17:41:36 -050057
Brad Bishop34c3bf92016-08-30 20:34:51 -040058 @dbus.service.method(
59 DBUS_NAME, in_signature='s', out_signature='')
60 def update(self, filename):
61 self.Set(DBUS_NAME, "filename", filename)
62 self.download_complete_handler(filename, filename)
Chris Austenea5b4012016-05-20 16:41:40 -050063
Brad Bishop34c3bf92016-08-30 20:34:51 -040064 @dbus.service.signal(DOWNLOAD_INTF, signature='ss')
65 def TftpDownload(self, ip, filename):
66 self.Set(DBUS_NAME, "filename", filename)
67 pass
Norman James3d6a06f2016-01-30 13:03:45 -060068
Brad Bishop34c3bf92016-08-30 20:34:51 -040069 ## Signal handler
70 def download_error_handler(self, filename):
71 if (filename == self.Get(DBUS_NAME, "filename")):
72 self.Set(DBUS_NAME, "status", "Download Error")
Norman James3d6a06f2016-01-30 13:03:45 -060073
Brad Bishop34c3bf92016-08-30 20:34:51 -040074 def download_complete_handler(self, outfile, filename):
75 ## do update
76 if (filename != self.Get(DBUS_NAME, "filename")):
77 return
Norman James3d6a06f2016-01-30 13:03:45 -060078
Brad Bishop34c3bf92016-08-30 20:34:51 -040079 print "Download complete. Updating..."
Norman James3d6a06f2016-01-30 13:03:45 -060080
Brad Bishop34c3bf92016-08-30 20:34:51 -040081 self.Set(DBUS_NAME, "status", "Download Complete")
82 copy_files = {}
Norman James3d6a06f2016-01-30 13:03:45 -060083
Brad Bishop34c3bf92016-08-30 20:34:51 -040084 ## determine needed files
85 if not self.Get(DBUS_NAME, "update_kernel_and_apps"):
86 copy_files["image-bmc"] = True
87 else:
88 copy_files["image-kernel"] = True
Brad Bishop34c3bf92016-08-30 20:34:51 -040089 copy_files["image-rofs"] = True
Norman James3d6a06f2016-01-30 13:03:45 -060090
Brad Bishop34c3bf92016-08-30 20:34:51 -040091 if self.Get(DBUS_NAME, "restore_application_defaults"):
92 copy_files["image-rwfs"] = True
Norman James8a65a542016-02-03 19:04:44 -060093
Brad Bishop34c3bf92016-08-30 20:34:51 -040094 ## make sure files exist in archive
95 try:
96 tar = tarfile.open(outfile, "r")
97 files = {}
98 for f in tar.getnames():
99 files[f] = True
100 tar.close()
101 for f in copy_files.keys():
102 if f not in files:
103 raise Exception(
104 "ERROR: File not found in update archive: "+f)
Milton Millerb6cfc542016-06-17 11:43:40 -0500105
Brad Bishop34c3bf92016-08-30 20:34:51 -0400106 except Exception as e:
107 print e
108 self.Set(DBUS_NAME, "status", "Unpack Error")
109 return
Milton Millerb6cfc542016-06-17 11:43:40 -0500110
Brad Bishop34c3bf92016-08-30 20:34:51 -0400111 try:
112 tar = tarfile.open(outfile, "r")
113 tar.extractall(UPDATE_PATH, members=doExtract(tar, copy_files))
114 tar.close()
Milton Millerb6cfc542016-06-17 11:43:40 -0500115
Brad Bishop34c3bf92016-08-30 20:34:51 -0400116 if self.Get(DBUS_NAME, "clear_persistent_files"):
117 print "Removing persistent files"
118 try:
119 os.unlink(UPDATE_PATH+"/whitelist")
120 except OSError as e:
121 if (e.errno == errno.EISDIR):
122 pass
123 elif (e.errno == errno.ENOENT):
124 pass
125 else:
126 raise
Norman James3d6a06f2016-01-30 13:03:45 -0600127
Brad Bishop34c3bf92016-08-30 20:34:51 -0400128 try:
129 wldir = UPDATE_PATH + "/whitelist.d"
Milton Miller0c8c5d42016-06-29 17:41:36 -0500130
Brad Bishop34c3bf92016-08-30 20:34:51 -0400131 for file in os.listdir(wldir):
132 os.unlink(os.path.join(wldir, file))
133 except OSError as e:
134 if (e.errno == errno.EISDIR):
135 pass
136 else:
137 raise
Milton Miller0c8c5d42016-06-29 17:41:36 -0500138
Brad Bishop34c3bf92016-08-30 20:34:51 -0400139 if self.Get(DBUS_NAME, "preserve_network_settings"):
140 print "Preserving network settings"
141 shutil.copy2("/run/fw_env", UPDATE_PATH+"/image-u-boot-env")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500142
Brad Bishop34c3bf92016-08-30 20:34:51 -0400143 except Exception as e:
144 print e
145 self.Set(DBUS_NAME, "status", "Unpack Error")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500146
Brad Bishop34c3bf92016-08-30 20:34:51 -0400147 self.Verify()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500148
Brad Bishop34c3bf92016-08-30 20:34:51 -0400149 def Verify(self):
150 self.Set(DBUS_NAME, "status", "Checking Image")
151 try:
152 subprocess.check_call([
153 "/run/initramfs/update",
154 "--no-flash",
155 "--no-save-files",
156 "--no-restore-files",
157 "--no-clean-saved-files"])
Milton Miller0c8c5d42016-06-29 17:41:36 -0500158
Brad Bishop34c3bf92016-08-30 20:34:51 -0400159 self.Set(DBUS_NAME, "status", "Image ready to apply.")
160 if (self.Get(DBUS_NAME, "auto_apply")):
161 self.Apply()
162 except:
163 self.Set(DBUS_NAME, "auto_apply", False)
164 try:
165 subprocess.check_output([
166 "/run/initramfs/update",
167 "--no-flash",
168 "--ignore-mount",
169 "--no-save-files",
170 "--no-restore-files",
171 "--no-clean-saved-files"],
172 stderr=subprocess.STDOUT)
173 self.Set(
174 DBUS_NAME, "status",
175 "Deferred for mounted filesystem. reboot BMC to apply.")
176 except subprocess.CalledProcessError as e:
177 self.Set(
178 DBUS_NAME, "status", "Verify error: %s" % e.output)
179 except OSError as e:
180 self.Set(
181 DBUS_NAME, "status",
182 "Verify error: problem calling update: %s" % e.strerror)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500183
Brad Bishop34c3bf92016-08-30 20:34:51 -0400184 def Cleanup(self):
185 if self.progress_name:
186 try:
187 os.unlink(self.progress_name)
188 self.progress_name = None
189 except oserror as e:
190 if e.errno == EEXIST:
191 pass
192 raise
193 self.update_process = None
194 self.Set(DBUS_NAME, "status", "Idle")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500195
Brad Bishop34c3bf92016-08-30 20:34:51 -0400196 @dbus.service.method(
197 DBUS_NAME, in_signature='', out_signature='')
198 def Abort(self):
199 if self.update_process:
200 try:
201 self.update_process.kill()
202 except:
203 pass
204 for file in os.listdir(UPDATE_PATH):
205 if file.startswith('image-'):
206 os.unlink(os.path.join(UPDATE_PATH, file))
Milton Miller0c8c5d42016-06-29 17:41:36 -0500207
Brad Bishop34c3bf92016-08-30 20:34:51 -0400208 self.Cleanup()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500209
Brad Bishop34c3bf92016-08-30 20:34:51 -0400210 @dbus.service.method(
211 DBUS_NAME, in_signature='', out_signature='s')
212 def GetUpdateProgress(self):
213 msg = ""
Milton Miller0c8c5d42016-06-29 17:41:36 -0500214
Brad Bishop34c3bf92016-08-30 20:34:51 -0400215 if self.update_process and self.update_process.returncode is None:
216 self.update_process.poll()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500217
Brad Bishop34c3bf92016-08-30 20:34:51 -0400218 if (self.update_process is None):
219 pass
220 elif (self.update_process.returncode > 0):
221 self.Set(DBUS_NAME, "status", "Apply failed")
222 elif (self.update_process.returncode is None):
223 pass
224 else: # (self.update_process.returncode == 0)
225 files = ""
226 for file in os.listdir(UPDATE_PATH):
227 if file.startswith('image-'):
228 files = files + file
229 if files == "":
230 msg = "Apply Complete. Reboot to take effect."
231 else:
232 msg = "Apply Incomplete, Remaining:" + files
233 self.Set(DBUS_NAME, "status", msg)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500234
Brad Bishop34c3bf92016-08-30 20:34:51 -0400235 msg = self.Get(DBUS_NAME, "status") + "\n"
236 if self.progress_name:
237 try:
238 prog = open(self.progress_name, 'r')
239 for line in prog:
240 # strip off initial sets of xxx\r here
241 # ignore crlf at the end
242 # cr will be -1 if no '\r' is found
243 cr = line.rfind("\r", 0, -2)
244 msg = msg + line[cr + 1:]
245 except OSError as e:
246 if (e.error == EEXIST):
247 pass
248 raise
249 return msg
250
251 @dbus.service.method(
252 DBUS_NAME, in_signature='', out_signature='')
253 def Apply(self):
254 progress = None
255 self.Set(DBUS_NAME, "status", "Writing images to flash")
256 try:
257 progress = tempfile.NamedTemporaryFile(
258 delete=False, prefix="progress.")
259 self.progress_name = progress.name
260 self.update_process = subprocess.Popen([
261 "/run/initramfs/update"],
262 stdout=progress.file,
263 stderr=subprocess.STDOUT)
264 except Exception as e:
265 try:
266 progress.close()
267 os.unlink(progress.name)
268 self.progress_name = None
269 except:
270 pass
271 raise
272
273 try:
274 progress.close()
275 except:
276 pass
277
278 @dbus.service.method(
279 DBUS_NAME, in_signature='', out_signature='')
280 def PrepareForUpdate(self):
281 subprocess.call([
282 "fw_setenv",
283 "openbmconce",
284 "copy-files-to-ram copy-base-filesystem-to-ram"])
285 self.Set(DBUS_NAME, "status", "Switch to update mode in progress")
286 o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME)
287 intf = dbus.Interface(o, BMC_DBUS_NAME)
288 intf.warmReset()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500289
Norman James3d6a06f2016-01-30 13:03:45 -0600290
291if __name__ == '__main__':
292 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
293
Brad Bishop84e73b52016-05-12 15:57:52 -0400294 bus = get_dbus()
Norman James3d6a06f2016-01-30 13:03:45 -0600295 obj = BmcFlashControl(bus, OBJ_NAME)
296 mainloop = gobject.MainLoop()
Brad Bishopf0f3efe2016-06-29 23:20:24 -0400297
298 obj.unmask_signals()
Brad Bishop70852a32016-06-29 22:58:51 -0400299 name = dbus.service.BusName(DBUS_NAME, bus)
Brad Bishop34c3bf92016-08-30 20:34:51 -0400300
Norman James3d6a06f2016-01-30 13:03:45 -0600301 print "Running Bmc Flash Control"
302 mainloop.run()
Brad Bishop53066752016-09-21 08:48:04 -0400303
304# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4