blob: aad703d182193806f84bb8bd1b3c07446d44f182 [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
89 copy_files["image-initramfs"] = True
90 copy_files["image-rofs"] = True
Norman James3d6a06f2016-01-30 13:03:45 -060091
Brad Bishop34c3bf92016-08-30 20:34:51 -040092 if self.Get(DBUS_NAME, "restore_application_defaults"):
93 copy_files["image-rwfs"] = True
Norman James8a65a542016-02-03 19:04:44 -060094
Brad Bishop34c3bf92016-08-30 20:34:51 -040095 ## make sure files exist in archive
96 try:
97 tar = tarfile.open(outfile, "r")
98 files = {}
99 for f in tar.getnames():
100 files[f] = True
101 tar.close()
102 for f in copy_files.keys():
103 if f not in files:
104 raise Exception(
105 "ERROR: File not found in update archive: "+f)
Milton Millerb6cfc542016-06-17 11:43:40 -0500106
Brad Bishop34c3bf92016-08-30 20:34:51 -0400107 except Exception as e:
108 print e
109 self.Set(DBUS_NAME, "status", "Unpack Error")
110 return
Milton Millerb6cfc542016-06-17 11:43:40 -0500111
Brad Bishop34c3bf92016-08-30 20:34:51 -0400112 try:
113 tar = tarfile.open(outfile, "r")
114 tar.extractall(UPDATE_PATH, members=doExtract(tar, copy_files))
115 tar.close()
Milton Millerb6cfc542016-06-17 11:43:40 -0500116
Brad Bishop34c3bf92016-08-30 20:34:51 -0400117 if self.Get(DBUS_NAME, "clear_persistent_files"):
118 print "Removing persistent files"
119 try:
120 os.unlink(UPDATE_PATH+"/whitelist")
121 except OSError as e:
122 if (e.errno == errno.EISDIR):
123 pass
124 elif (e.errno == errno.ENOENT):
125 pass
126 else:
127 raise
Norman James3d6a06f2016-01-30 13:03:45 -0600128
Brad Bishop34c3bf92016-08-30 20:34:51 -0400129 try:
130 wldir = UPDATE_PATH + "/whitelist.d"
Milton Miller0c8c5d42016-06-29 17:41:36 -0500131
Brad Bishop34c3bf92016-08-30 20:34:51 -0400132 for file in os.listdir(wldir):
133 os.unlink(os.path.join(wldir, file))
134 except OSError as e:
135 if (e.errno == errno.EISDIR):
136 pass
137 else:
138 raise
Milton Miller0c8c5d42016-06-29 17:41:36 -0500139
Brad Bishop34c3bf92016-08-30 20:34:51 -0400140 if self.Get(DBUS_NAME, "preserve_network_settings"):
141 print "Preserving network settings"
142 shutil.copy2("/run/fw_env", UPDATE_PATH+"/image-u-boot-env")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500143
Brad Bishop34c3bf92016-08-30 20:34:51 -0400144 except Exception as e:
145 print e
146 self.Set(DBUS_NAME, "status", "Unpack Error")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500147
Brad Bishop34c3bf92016-08-30 20:34:51 -0400148 self.Verify()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500149
Brad Bishop34c3bf92016-08-30 20:34:51 -0400150 def Verify(self):
151 self.Set(DBUS_NAME, "status", "Checking Image")
152 try:
153 subprocess.check_call([
154 "/run/initramfs/update",
155 "--no-flash",
156 "--no-save-files",
157 "--no-restore-files",
158 "--no-clean-saved-files"])
Milton Miller0c8c5d42016-06-29 17:41:36 -0500159
Brad Bishop34c3bf92016-08-30 20:34:51 -0400160 self.Set(DBUS_NAME, "status", "Image ready to apply.")
161 if (self.Get(DBUS_NAME, "auto_apply")):
162 self.Apply()
163 except:
164 self.Set(DBUS_NAME, "auto_apply", False)
165 try:
166 subprocess.check_output([
167 "/run/initramfs/update",
168 "--no-flash",
169 "--ignore-mount",
170 "--no-save-files",
171 "--no-restore-files",
172 "--no-clean-saved-files"],
173 stderr=subprocess.STDOUT)
174 self.Set(
175 DBUS_NAME, "status",
176 "Deferred for mounted filesystem. reboot BMC to apply.")
177 except subprocess.CalledProcessError as e:
178 self.Set(
179 DBUS_NAME, "status", "Verify error: %s" % e.output)
180 except OSError as e:
181 self.Set(
182 DBUS_NAME, "status",
183 "Verify error: problem calling update: %s" % e.strerror)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500184
Brad Bishop34c3bf92016-08-30 20:34:51 -0400185 def Cleanup(self):
186 if self.progress_name:
187 try:
188 os.unlink(self.progress_name)
189 self.progress_name = None
190 except oserror as e:
191 if e.errno == EEXIST:
192 pass
193 raise
194 self.update_process = None
195 self.Set(DBUS_NAME, "status", "Idle")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500196
Brad Bishop34c3bf92016-08-30 20:34:51 -0400197 @dbus.service.method(
198 DBUS_NAME, in_signature='', out_signature='')
199 def Abort(self):
200 if self.update_process:
201 try:
202 self.update_process.kill()
203 except:
204 pass
205 for file in os.listdir(UPDATE_PATH):
206 if file.startswith('image-'):
207 os.unlink(os.path.join(UPDATE_PATH, file))
Milton Miller0c8c5d42016-06-29 17:41:36 -0500208
Brad Bishop34c3bf92016-08-30 20:34:51 -0400209 self.Cleanup()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500210
Brad Bishop34c3bf92016-08-30 20:34:51 -0400211 @dbus.service.method(
212 DBUS_NAME, in_signature='', out_signature='s')
213 def GetUpdateProgress(self):
214 msg = ""
Milton Miller0c8c5d42016-06-29 17:41:36 -0500215
Brad Bishop34c3bf92016-08-30 20:34:51 -0400216 if self.update_process and self.update_process.returncode is None:
217 self.update_process.poll()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500218
Brad Bishop34c3bf92016-08-30 20:34:51 -0400219 if (self.update_process is None):
220 pass
221 elif (self.update_process.returncode > 0):
222 self.Set(DBUS_NAME, "status", "Apply failed")
223 elif (self.update_process.returncode is None):
224 pass
225 else: # (self.update_process.returncode == 0)
226 files = ""
227 for file in os.listdir(UPDATE_PATH):
228 if file.startswith('image-'):
229 files = files + file
230 if files == "":
231 msg = "Apply Complete. Reboot to take effect."
232 else:
233 msg = "Apply Incomplete, Remaining:" + files
234 self.Set(DBUS_NAME, "status", msg)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500235
Brad Bishop34c3bf92016-08-30 20:34:51 -0400236 msg = self.Get(DBUS_NAME, "status") + "\n"
237 if self.progress_name:
238 try:
239 prog = open(self.progress_name, 'r')
240 for line in prog:
241 # strip off initial sets of xxx\r here
242 # ignore crlf at the end
243 # cr will be -1 if no '\r' is found
244 cr = line.rfind("\r", 0, -2)
245 msg = msg + line[cr + 1:]
246 except OSError as e:
247 if (e.error == EEXIST):
248 pass
249 raise
250 return msg
251
252 @dbus.service.method(
253 DBUS_NAME, in_signature='', out_signature='')
254 def Apply(self):
255 progress = None
256 self.Set(DBUS_NAME, "status", "Writing images to flash")
257 try:
258 progress = tempfile.NamedTemporaryFile(
259 delete=False, prefix="progress.")
260 self.progress_name = progress.name
261 self.update_process = subprocess.Popen([
262 "/run/initramfs/update"],
263 stdout=progress.file,
264 stderr=subprocess.STDOUT)
265 except Exception as e:
266 try:
267 progress.close()
268 os.unlink(progress.name)
269 self.progress_name = None
270 except:
271 pass
272 raise
273
274 try:
275 progress.close()
276 except:
277 pass
278
279 @dbus.service.method(
280 DBUS_NAME, in_signature='', out_signature='')
281 def PrepareForUpdate(self):
282 subprocess.call([
283 "fw_setenv",
284 "openbmconce",
285 "copy-files-to-ram copy-base-filesystem-to-ram"])
286 self.Set(DBUS_NAME, "status", "Switch to update mode in progress")
287 o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME)
288 intf = dbus.Interface(o, BMC_DBUS_NAME)
289 intf.warmReset()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500290
Norman James3d6a06f2016-01-30 13:03:45 -0600291
292if __name__ == '__main__':
293 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
294
Brad Bishop84e73b52016-05-12 15:57:52 -0400295 bus = get_dbus()
Norman James3d6a06f2016-01-30 13:03:45 -0600296 obj = BmcFlashControl(bus, OBJ_NAME)
297 mainloop = gobject.MainLoop()
Brad Bishopf0f3efe2016-06-29 23:20:24 -0400298
299 obj.unmask_signals()
Brad Bishop70852a32016-06-29 22:58:51 -0400300 name = dbus.service.BusName(DBUS_NAME, bus)
Brad Bishop34c3bf92016-08-30 20:34:51 -0400301
Norman James3d6a06f2016-01-30 13:03:45 -0600302 print "Running Bmc Flash Control"
303 mainloop.run()
Brad Bishop53066752016-09-21 08:48:04 -0400304
305# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4