blob: 8e3d43d1b0ef1a9e59f9ce1df1fe6636818748d3 [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'
16FLASH_INTF = 'org.openbmc.Flash'
17DOWNLOAD_INTF = 'org.openbmc.managers.Download'
18
Milton Miller0c8c5d42016-06-29 17:41:36 -050019BMC_DBUS_NAME = 'org.openbmc.control.Bmc'
20BMC_OBJ_NAME = '/org/openbmc/control/bmc0'
21
Norman James3d6a06f2016-01-30 13:03:45 -060022UPDATE_PATH = '/run/initramfs'
23
24
25def doExtract(members,files):
26 for tarinfo in members:
27 if files.has_key(tarinfo.name) == True:
28 yield tarinfo
29
30
Brad Bishop84e73b52016-05-12 15:57:52 -040031class BmcFlashControl(DbusProperties,DbusObjectManager):
Norman James3d6a06f2016-01-30 13:03:45 -060032 def __init__(self,bus,name):
33 self.dbus_objects = { }
Brad Bishop84e73b52016-05-12 15:57:52 -040034 DbusProperties.__init__(self)
35 DbusObjectManager.__init__(self)
Norman James3d6a06f2016-01-30 13:03:45 -060036 dbus.service.Object.__init__(self,bus,name)
37
38 self.Set(DBUS_NAME,"status","Idle")
39 self.Set(DBUS_NAME,"filename","")
Milton Millerc8094102016-06-16 13:11:31 -050040 self.Set(DBUS_NAME,"preserve_network_settings",True)
Norman James3d6a06f2016-01-30 13:03:45 -060041 self.Set(DBUS_NAME,"restore_application_defaults",False)
Norman James8a65a542016-02-03 19:04:44 -060042 self.Set(DBUS_NAME,"update_kernel_and_apps",False)
43 self.Set(DBUS_NAME,"clear_persistent_files",False)
Milton Miller0c8c5d42016-06-29 17:41:36 -050044 self.Set(DBUS_NAME,"auto_apply",False)
45
Norman James3d6a06f2016-01-30 13:03:45 -060046 bus.add_signal_receiver(self.download_error_handler,signal_name = "DownloadError")
47 bus.add_signal_receiver(self.download_complete_handler,signal_name = "DownloadComplete")
48
Milton Miller0c8c5d42016-06-29 17:41:36 -050049 self.update_process = None
50 self.progress_name = None
51
Norman James3d6a06f2016-01-30 13:03:45 -060052 self.InterfacesAdded(name,self.properties)
53
54
55 @dbus.service.method(DBUS_NAME,
56 in_signature='ss', out_signature='')
57 def updateViaTftp(self,ip,filename):
Norman James3d6a06f2016-01-30 13:03:45 -060058 self.Set(DBUS_NAME,"status","Downloading")
Milton Miller0c8c5d42016-06-29 17:41:36 -050059 self.TftpDownload(ip,filename)
60
Chris Austenea5b4012016-05-20 16:41:40 -050061 @dbus.service.method(DBUS_NAME,
62 in_signature='s', out_signature='')
63 def update(self,filename):
64 self.Set(DBUS_NAME,"filename",filename)
65 self.download_complete_handler(filename, filename)
66
Norman James3d6a06f2016-01-30 13:03:45 -060067 @dbus.service.signal(DOWNLOAD_INTF,signature='ss')
68 def TftpDownload(self,ip,filename):
69 self.Set(DBUS_NAME,"filename",filename)
70 pass
71
72
73 ## Signal handler
74 def download_error_handler(self,filename):
75 if (filename == self.Get(DBUS_NAME,"filename")):
76 self.Set(DBUS_NAME,"status","Download Error")
77
78 def download_complete_handler(self,outfile,filename):
79 ## do update
80 if (filename != self.Get(DBUS_NAME,"filename")):
81 return
82
83 print "Download complete. Updating..."
84
85 self.Set(DBUS_NAME,"status","Download Complete")
86 copy_files = {}
87
88 ## determine needed files
Norman James8a65a542016-02-03 19:04:44 -060089 if (self.Get(DBUS_NAME,"update_kernel_and_apps") == False):
Norman James3d6a06f2016-01-30 13:03:45 -060090 copy_files["image-bmc"] = True
91 else:
92 copy_files["image-kernel"] = True
93 copy_files["image-initramfs"] = True
94 copy_files["image-rofs"] = True
95
96 if (self.Get(DBUS_NAME,"restore_application_defaults") == True):
Norman James8a65a542016-02-03 19:04:44 -060097 copy_files["image-rwfs"] = True
Norman James3d6a06f2016-01-30 13:03:45 -060098
99
100 ## make sure files exist in archive
101 try:
102 tar = tarfile.open(outfile,"r")
103 files = {}
104 for f in tar.getnames():
105 files[f] = True
106 tar.close()
107 for f in copy_files.keys():
108 if (files.has_key(f) == False):
109 raise Exception("ERROR: File not found in update archive: "+f)
110
111 except Exception as e:
112 print e
Milton Miller0c8c5d42016-06-29 17:41:36 -0500113 self.Set(DBUS_NAME,"status","Unpack Error")
Norman James3d6a06f2016-01-30 13:03:45 -0600114 return
115
116 try:
117 tar = tarfile.open(outfile,"r")
118 tar.extractall(UPDATE_PATH,members=doExtract(tar,copy_files))
119 tar.close()
Norman James8a65a542016-02-03 19:04:44 -0600120
Adi Gangidi4d27c1b2016-05-11 18:27:43 -0500121 if (self.Get(DBUS_NAME,"clear_persistent_files") == True):
Norman James8a65a542016-02-03 19:04:44 -0600122 print "Removing persistent files"
Milton Millerb6cfc542016-06-17 11:43:40 -0500123 try:
124 os.unlink(UPDATE_PATH+"/whitelist")
125 except OSError as e:
126 if (e.errno == errno.EISDIR):
127 pass
128 elif (e.errno == errno.ENOENT):
129 pass
130 else:
131 raise
132
133 try:
134 wldir = UPDATE_PATH + "/whitelist.d"
135
136 for file in os.listdir(wldir):
137 os.unlink(os.path.join(wldir,file))
138 except OSError as e:
139 if (e.errno == errno.EISDIR):
140 pass
141 else:
142 raise
143
Norman James3d6a06f2016-01-30 13:03:45 -0600144 if (self.Get(DBUS_NAME,"preserve_network_settings") == True):
Norman James8a65a542016-02-03 19:04:44 -0600145 print "Preserving network settings"
Milton Millerc8094102016-06-16 13:11:31 -0500146 shutil.copy2("/run/fw_env",UPDATE_PATH+"/image-u-boot-env")
Norman James3d6a06f2016-01-30 13:03:45 -0600147
148 except Exception as e:
149 print e
Milton Miller0c8c5d42016-06-29 17:41:36 -0500150 self.Set(DBUS_NAME,"status","Unpack Error")
Norman James3d6a06f2016-01-30 13:03:45 -0600151
Milton Miller0c8c5d42016-06-29 17:41:36 -0500152 self.Verify()
153
154 def Verify(self):
155 self.Set(DBUS_NAME,"status","Checking Image")
156 try:
157 subprocess.check_call([
158 "/run/initramfs/update",
159 "--no-flash",
160 "--no-save-files",
161 "--no-restore-files",
162 "--no-clean-saved-files" ])
163
164 self.Set(DBUS_NAME,"status","Image ready to apply.")
165 if (self.Get(DBUS_NAME,"auto_apply")):
166 self.Apply()
167 except:
168 self.Set(DBUS_NAME,"auto_apply",False)
169 try:
170 subprocess.check_output([
171 "/run/initramfs/update",
172 "--no-flash",
173 "--ignore-mount",
174 "--no-save-files",
175 "--no-restore-files",
176 "--no-clean-saved-files" ],
177 stderr = subprocess.STDOUT)
178 self.Set(DBUS_NAME,"status","Deferred for mounted filesystem. reboot BMC to apply.")
179 except subprocess.CalledProcessError as e:
180 self.Set(DBUS_NAME,"status","Verify error: %s"
181 % e.output)
182 except OSError as e:
183 self.Set(DBUS_NAME,"status","Verify error: problem calling update: %s" % e.strerror)
184
185
186 def Cleanup(self):
187 if self.progress_name:
188 try:
189 os.unlink(self.progress_name)
190 self.progress_name = None
191 except oserror as e:
192 if e.errno == EEXIST:
193 pass
194 raise
195 self.update_process = None
196 self.Set(DBUS_NAME,"status","Idle")
197
198 @dbus.service.method(DBUS_NAME,
199 in_signature='', out_signature='')
200 def Abort(self):
201 if self.update_process:
202 try:
203 self.update_process.kill()
204 except:
205 pass
206 for file in os.listdir(UPDATE_PATH):
207 if file.startswith('image-'):
208 os.unlink(os.path.join(UPDATE_PATH,file))
209
210 self.Cleanup();
211
212 @dbus.service.method(DBUS_NAME,
213 in_signature='', out_signature='s')
214 def GetUpdateProgress(self):
215 msg = ""
216
217 if self.update_process and self.update_process.returncode is None:
218 self.update_process.poll()
219
220 if (self.update_process is None):
221 pass
222 elif (self.update_process.returncode > 0):
223 self.Set(DBUS_NAME,"status","Apply failed")
224 elif (self.update_process.returncode is None):
225 pass
226 else: # (self.update_process.returncode == 0)
227 files = ""
228 for file in os.listdir(UPDATE_PATH):
229 if file.startswith('image-'):
230 files = files + file;
231 if files == "":
232 msg = "Apply Complete. Reboot to take effect."
233 else:
234 msg = "Apply Incomplete, Remaining:" + files
235 self.Set(DBUS_NAME,"status", msg)
236
237 msg = self.Get(DBUS_NAME,"status") + "\n";
238 if self.progress_name:
239 try:
240 prog = open(self.progress_name,'r')
241 for line in prog:
242 # strip off initial sets of xxx\r here
243 # ignore crlf at the end
244 # cr will be -1 if no '\r' is found
245 cr = line.rfind("\r", 0, -2)
246 msg = msg + line[cr + 1: ]
247 except OSError as e:
248 if (e.error == EEXIST):
249 pass
250 raise
251 return msg
252
253 @dbus.service.method(DBUS_NAME,
254 in_signature='', out_signature='')
255 def Apply(self):
256 progress = None
257 self.Set(DBUS_NAME,"status","Writing images to flash")
258 try:
259 progress = tempfile.NamedTemporaryFile(
260 delete = False, prefix="progress." )
261 self.progress_name = progress.name
262 self.update_process = subprocess.Popen([
263 "/run/initramfs/update" ],
264 stdout = progress.file,
265 stderr = subprocess.STDOUT )
266 except Exception as e:
267 try:
268 progress.close()
269 os.unlink(progress.name)
270 self.progress_name = None
271 except:
272 pass
273 raise
274
275 try:
276 progress.close()
277 except:
278 pass
279
280 @dbus.service.method(DBUS_NAME,
281 in_signature='', out_signature='')
282 def PrepareForUpdate(self):
283 subprocess.call([
284 "fw_setenv",
285 "openbmconce",
286 "copy-files-to-ram copy-base-filesystem-to-ram"])
287 self.Set(DBUS_NAME,"status","Switch to update mode in progress")
288 o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME)
289 intf = dbus.Interface(o, BMC_DBUS_NAME)
290 intf.warmReset()
291
Norman James3d6a06f2016-01-30 13:03:45 -0600292
293if __name__ == '__main__':
294 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
295
Brad Bishop84e73b52016-05-12 15:57:52 -0400296 bus = get_dbus()
Norman James3d6a06f2016-01-30 13:03:45 -0600297 name = dbus.service.BusName(DBUS_NAME, bus)
298 obj = BmcFlashControl(bus, OBJ_NAME)
299 mainloop = gobject.MainLoop()
300
301 print "Running Bmc Flash Control"
302 mainloop.run()
303