blob: 147f0c36c8e7b36e25d99784470a1db326b23f45 [file] [log] [blame]
Norman James3d6a06f2016-01-30 13:03:45 -06001#!/usr/bin/python -u
2
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
24def doExtract(members,files):
25 for tarinfo in members:
26 if files.has_key(tarinfo.name) == True:
27 yield tarinfo
28
29
Brad Bishop84e73b52016-05-12 15:57:52 -040030class BmcFlashControl(DbusProperties,DbusObjectManager):
Norman James3d6a06f2016-01-30 13:03:45 -060031 def __init__(self,bus,name):
32 self.dbus_objects = { }
Brad Bishop84e73b52016-05-12 15:57:52 -040033 DbusProperties.__init__(self)
34 DbusObjectManager.__init__(self)
Norman James3d6a06f2016-01-30 13:03:45 -060035 dbus.service.Object.__init__(self,bus,name)
36
37 self.Set(DBUS_NAME,"status","Idle")
38 self.Set(DBUS_NAME,"filename","")
Milton Millerc8094102016-06-16 13:11:31 -050039 self.Set(DBUS_NAME,"preserve_network_settings",True)
Norman James3d6a06f2016-01-30 13:03:45 -060040 self.Set(DBUS_NAME,"restore_application_defaults",False)
Norman James8a65a542016-02-03 19:04:44 -060041 self.Set(DBUS_NAME,"update_kernel_and_apps",False)
42 self.Set(DBUS_NAME,"clear_persistent_files",False)
Milton Miller0c8c5d42016-06-29 17:41:36 -050043 self.Set(DBUS_NAME,"auto_apply",False)
44
Norman James3d6a06f2016-01-30 13:03:45 -060045 bus.add_signal_receiver(self.download_error_handler,signal_name = "DownloadError")
46 bus.add_signal_receiver(self.download_complete_handler,signal_name = "DownloadComplete")
47
Milton Miller0c8c5d42016-06-29 17:41:36 -050048 self.update_process = None
49 self.progress_name = None
50
Norman James3d6a06f2016-01-30 13:03:45 -060051
52 @dbus.service.method(DBUS_NAME,
53 in_signature='ss', out_signature='')
54 def updateViaTftp(self,ip,filename):
Norman James3d6a06f2016-01-30 13:03:45 -060055 self.Set(DBUS_NAME,"status","Downloading")
Milton Miller0c8c5d42016-06-29 17:41:36 -050056 self.TftpDownload(ip,filename)
57
Chris Austenea5b4012016-05-20 16:41:40 -050058 @dbus.service.method(DBUS_NAME,
59 in_signature='s', out_signature='')
60 def update(self,filename):
61 self.Set(DBUS_NAME,"filename",filename)
62 self.download_complete_handler(filename, filename)
63
Norman James3d6a06f2016-01-30 13:03:45 -060064 @dbus.service.signal(DOWNLOAD_INTF,signature='ss')
65 def TftpDownload(self,ip,filename):
66 self.Set(DBUS_NAME,"filename",filename)
67 pass
68
69
70 ## Signal handler
71 def download_error_handler(self,filename):
72 if (filename == self.Get(DBUS_NAME,"filename")):
73 self.Set(DBUS_NAME,"status","Download Error")
74
75 def download_complete_handler(self,outfile,filename):
76 ## do update
77 if (filename != self.Get(DBUS_NAME,"filename")):
78 return
79
80 print "Download complete. Updating..."
81
82 self.Set(DBUS_NAME,"status","Download Complete")
83 copy_files = {}
84
85 ## determine needed files
Norman James8a65a542016-02-03 19:04:44 -060086 if (self.Get(DBUS_NAME,"update_kernel_and_apps") == False):
Norman James3d6a06f2016-01-30 13:03:45 -060087 copy_files["image-bmc"] = True
88 else:
89 copy_files["image-kernel"] = True
90 copy_files["image-initramfs"] = True
91 copy_files["image-rofs"] = True
92
93 if (self.Get(DBUS_NAME,"restore_application_defaults") == True):
Norman James8a65a542016-02-03 19:04:44 -060094 copy_files["image-rwfs"] = True
Norman James3d6a06f2016-01-30 13:03:45 -060095
96
97 ## make sure files exist in archive
98 try:
99 tar = tarfile.open(outfile,"r")
100 files = {}
101 for f in tar.getnames():
102 files[f] = True
103 tar.close()
104 for f in copy_files.keys():
105 if (files.has_key(f) == False):
106 raise Exception("ERROR: File not found in update archive: "+f)
107
108 except Exception as e:
109 print e
Milton Miller0c8c5d42016-06-29 17:41:36 -0500110 self.Set(DBUS_NAME,"status","Unpack Error")
Norman James3d6a06f2016-01-30 13:03:45 -0600111 return
112
113 try:
114 tar = tarfile.open(outfile,"r")
115 tar.extractall(UPDATE_PATH,members=doExtract(tar,copy_files))
116 tar.close()
Norman James8a65a542016-02-03 19:04:44 -0600117
Adi Gangidi4d27c1b2016-05-11 18:27:43 -0500118 if (self.Get(DBUS_NAME,"clear_persistent_files") == True):
Norman James8a65a542016-02-03 19:04:44 -0600119 print "Removing persistent files"
Milton Millerb6cfc542016-06-17 11:43:40 -0500120 try:
121 os.unlink(UPDATE_PATH+"/whitelist")
122 except OSError as e:
123 if (e.errno == errno.EISDIR):
124 pass
125 elif (e.errno == errno.ENOENT):
126 pass
127 else:
128 raise
129
130 try:
131 wldir = UPDATE_PATH + "/whitelist.d"
132
133 for file in os.listdir(wldir):
134 os.unlink(os.path.join(wldir,file))
135 except OSError as e:
136 if (e.errno == errno.EISDIR):
137 pass
138 else:
139 raise
140
Norman James3d6a06f2016-01-30 13:03:45 -0600141 if (self.Get(DBUS_NAME,"preserve_network_settings") == True):
Norman James8a65a542016-02-03 19:04:44 -0600142 print "Preserving network settings"
Milton Millerc8094102016-06-16 13:11:31 -0500143 shutil.copy2("/run/fw_env",UPDATE_PATH+"/image-u-boot-env")
Norman James3d6a06f2016-01-30 13:03:45 -0600144
145 except Exception as e:
146 print e
Milton Miller0c8c5d42016-06-29 17:41:36 -0500147 self.Set(DBUS_NAME,"status","Unpack Error")
Norman James3d6a06f2016-01-30 13:03:45 -0600148
Milton Miller0c8c5d42016-06-29 17:41:36 -0500149 self.Verify()
150
151 def Verify(self):
152 self.Set(DBUS_NAME,"status","Checking Image")
153 try:
154 subprocess.check_call([
155 "/run/initramfs/update",
156 "--no-flash",
157 "--no-save-files",
158 "--no-restore-files",
159 "--no-clean-saved-files" ])
160
161 self.Set(DBUS_NAME,"status","Image ready to apply.")
162 if (self.Get(DBUS_NAME,"auto_apply")):
163 self.Apply()
164 except:
165 self.Set(DBUS_NAME,"auto_apply",False)
166 try:
167 subprocess.check_output([
168 "/run/initramfs/update",
169 "--no-flash",
170 "--ignore-mount",
171 "--no-save-files",
172 "--no-restore-files",
173 "--no-clean-saved-files" ],
174 stderr = subprocess.STDOUT)
175 self.Set(DBUS_NAME,"status","Deferred for mounted filesystem. reboot BMC to apply.")
176 except subprocess.CalledProcessError as e:
177 self.Set(DBUS_NAME,"status","Verify error: %s"
178 % e.output)
179 except OSError as e:
180 self.Set(DBUS_NAME,"status","Verify error: problem calling update: %s" % e.strerror)
181
182
183 def Cleanup(self):
184 if self.progress_name:
185 try:
186 os.unlink(self.progress_name)
187 self.progress_name = None
188 except oserror as e:
189 if e.errno == EEXIST:
190 pass
191 raise
192 self.update_process = None
193 self.Set(DBUS_NAME,"status","Idle")
194
195 @dbus.service.method(DBUS_NAME,
196 in_signature='', out_signature='')
197 def Abort(self):
198 if self.update_process:
199 try:
200 self.update_process.kill()
201 except:
202 pass
203 for file in os.listdir(UPDATE_PATH):
204 if file.startswith('image-'):
205 os.unlink(os.path.join(UPDATE_PATH,file))
206
207 self.Cleanup();
208
209 @dbus.service.method(DBUS_NAME,
210 in_signature='', out_signature='s')
211 def GetUpdateProgress(self):
212 msg = ""
213
214 if self.update_process and self.update_process.returncode is None:
215 self.update_process.poll()
216
217 if (self.update_process is None):
218 pass
219 elif (self.update_process.returncode > 0):
220 self.Set(DBUS_NAME,"status","Apply failed")
221 elif (self.update_process.returncode is None):
222 pass
223 else: # (self.update_process.returncode == 0)
224 files = ""
225 for file in os.listdir(UPDATE_PATH):
226 if file.startswith('image-'):
227 files = files + file;
228 if files == "":
229 msg = "Apply Complete. Reboot to take effect."
230 else:
231 msg = "Apply Incomplete, Remaining:" + files
232 self.Set(DBUS_NAME,"status", msg)
233
234 msg = self.Get(DBUS_NAME,"status") + "\n";
235 if self.progress_name:
236 try:
237 prog = open(self.progress_name,'r')
238 for line in prog:
239 # strip off initial sets of xxx\r here
240 # ignore crlf at the end
241 # cr will be -1 if no '\r' is found
242 cr = line.rfind("\r", 0, -2)
243 msg = msg + line[cr + 1: ]
244 except OSError as e:
245 if (e.error == EEXIST):
246 pass
247 raise
248 return msg
249
250 @dbus.service.method(DBUS_NAME,
251 in_signature='', out_signature='')
252 def Apply(self):
253 progress = None
254 self.Set(DBUS_NAME,"status","Writing images to flash")
255 try:
256 progress = tempfile.NamedTemporaryFile(
257 delete = False, prefix="progress." )
258 self.progress_name = progress.name
259 self.update_process = subprocess.Popen([
260 "/run/initramfs/update" ],
261 stdout = progress.file,
262 stderr = subprocess.STDOUT )
263 except Exception as e:
264 try:
265 progress.close()
266 os.unlink(progress.name)
267 self.progress_name = None
268 except:
269 pass
270 raise
271
272 try:
273 progress.close()
274 except:
275 pass
276
277 @dbus.service.method(DBUS_NAME,
278 in_signature='', out_signature='')
279 def PrepareForUpdate(self):
280 subprocess.call([
281 "fw_setenv",
282 "openbmconce",
283 "copy-files-to-ram copy-base-filesystem-to-ram"])
284 self.Set(DBUS_NAME,"status","Switch to update mode in progress")
285 o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME)
286 intf = dbus.Interface(o, BMC_DBUS_NAME)
287 intf.warmReset()
288
Norman James3d6a06f2016-01-30 13:03:45 -0600289
290if __name__ == '__main__':
291 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
292
Brad Bishop84e73b52016-05-12 15:57:52 -0400293 bus = get_dbus()
Norman James3d6a06f2016-01-30 13:03:45 -0600294 obj = BmcFlashControl(bus, OBJ_NAME)
295 mainloop = gobject.MainLoop()
Brad Bishopf0f3efe2016-06-29 23:20:24 -0400296
297 obj.unmask_signals()
Brad Bishop70852a32016-06-29 22:58:51 -0400298 name = dbus.service.BusName(DBUS_NAME, bus)
Norman James3d6a06f2016-01-30 13:03:45 -0600299
300 print "Running Bmc Flash Control"
301 mainloop.run()
302