blob: 71f04fa5ce7dd1956c1a6fe8766c8eb33105f4e4 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# BitBake ToasterUI Implementation
3# based on (No)TTY UI Implementation by Richard Purdie
4#
5# Handling output to TTYs or files (no TTY)
6#
7# Copyright (C) 2006-2012 Richard Purdie
8# Copyright (C) 2013 Intel Corporation
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License version 2 as
12# published by the Free Software Foundation.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License along
20# with this program; if not, write to the Free Software Foundation, Inc.,
21# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23from __future__ import division
Patrick Williamsf1e5d692016-03-30 15:21:19 -050024import time
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025import sys
26try:
27 import bb
28except RuntimeError as exc:
29 sys.exit(str(exc))
30
31from bb.ui import uihelper
32from bb.ui.buildinfohelper import BuildInfoHelper
33
34import bb.msg
35import logging
36import os
37
38# pylint: disable=invalid-name
39# module properties for UI modules are read by bitbake and the contract should not be broken
40
41
Patrick Williamsc0f7c042017-02-23 20:41:17 -060042featureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING, bb.cooker.CookerFeatures.SEND_SANITYEVENTS]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043
44logger = logging.getLogger("ToasterLogger")
45interactive = sys.stdout.isatty()
46
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047def _log_settings_from_server(server):
48 # Get values of variables which control our output
49 includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
50 if error:
51 logger.error("Unable to get the value of BBINCLUDELOGS variable: %s", error)
52 raise BaseException(error)
53 loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
54 if error:
55 logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s", error)
56 raise BaseException(error)
57 consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"])
58 if error:
59 logger.error("Unable to get the value of BB_CONSOLELOG variable: %s", error)
60 raise BaseException(error)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050061 return consolelogfile
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062
Patrick Williamsf1e5d692016-03-30 15:21:19 -050063# create a log file for a single build and direct the logger at it;
64# log file name is timestamped to the millisecond (depending
65# on system clock accuracy) to ensure it doesn't overlap with
66# other log file names
67#
68# returns (log file, path to log file) for a build
69def _open_build_log(log_dir):
70 format_str = "%(levelname)s: %(message)s"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071
Patrick Williamsf1e5d692016-03-30 15:21:19 -050072 now = time.time()
73 now_ms = int((now - int(now)) * 1000)
74 time_str = time.strftime('build_%Y%m%d_%H%M%S', time.localtime(now))
75 log_file_name = time_str + ('.%d.log' % now_ms)
76 build_log_file_path = os.path.join(log_dir, log_file_name)
77
78 build_log = logging.FileHandler(build_log_file_path)
79
80 logformat = bb.msg.BBLogFormatter(format_str)
81 build_log.setFormatter(logformat)
82
83 bb.msg.addDefaultlogFilter(build_log)
84 logger.addHandler(build_log)
85
86 return (build_log, build_log_file_path)
87
88# stop logging to the build log if it exists
89def _close_build_log(build_log):
90 if build_log:
91 build_log.flush()
92 build_log.close()
93 logger.removeHandler(build_log)
94
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050095_evt_list = [
96 "bb.build.TaskBase",
97 "bb.build.TaskFailed",
98 "bb.build.TaskFailedSilent",
99 "bb.build.TaskStarted",
100 "bb.build.TaskSucceeded",
101 "bb.command.CommandCompleted",
102 "bb.command.CommandExit",
103 "bb.command.CommandFailed",
104 "bb.cooker.CookerExit",
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105 "bb.event.BuildInit",
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500106 "bb.event.BuildCompleted",
107 "bb.event.BuildStarted",
108 "bb.event.CacheLoadCompleted",
109 "bb.event.CacheLoadProgress",
110 "bb.event.CacheLoadStarted",
111 "bb.event.ConfigParsed",
112 "bb.event.DepTreeGenerated",
113 "bb.event.LogExecTTY",
114 "bb.event.MetadataEvent",
115 "bb.event.MultipleProviders",
116 "bb.event.NoProvider",
117 "bb.event.ParseCompleted",
118 "bb.event.ParseProgress",
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600119 "bb.event.ParseStarted",
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500120 "bb.event.RecipeParsed",
121 "bb.event.SanityCheck",
122 "bb.event.SanityCheckPassed",
123 "bb.event.TreeDataPreparationCompleted",
124 "bb.event.TreeDataPreparationStarted",
125 "bb.runqueue.runQueueTaskCompleted",
126 "bb.runqueue.runQueueTaskFailed",
127 "bb.runqueue.runQueueTaskSkipped",
128 "bb.runqueue.runQueueTaskStarted",
129 "bb.runqueue.sceneQueueTaskCompleted",
130 "bb.runqueue.sceneQueueTaskFailed",
131 "bb.runqueue.sceneQueueTaskStarted",
132 "logging.LogRecord"]
133
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500134def main(server, eventHandler, params):
135 # set to a logging.FileHandler instance when a build starts;
136 # see _open_build_log()
137 build_log = None
138
139 # set to the log path when a build starts
140 build_log_file_path = None
141
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500142 helper = uihelper.BBUIHelper()
143
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500144 # TODO don't use log output to determine when bitbake has started
145 #
146 # WARNING: this log handler cannot be removed, as localhostbecontroller
147 # relies on output in the toaster_ui.log file to determine whether
148 # the bitbake server has started, which only happens if
149 # this logger is setup here (see the TODO in the loop below)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150 console = logging.StreamHandler(sys.stdout)
151 format_str = "%(levelname)s: %(message)s"
152 formatter = bb.msg.BBLogFormatter(format_str)
153 bb.msg.addDefaultlogFilter(console)
154 console.setFormatter(formatter)
155 logger.addHandler(console)
156 logger.setLevel(logging.INFO)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500157 llevel, debug_domains = bb.msg.constructLogOptions()
158 result, error = server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
159 if not result or error:
160 logger.error("can't set event mask: %s", error)
161 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500163 # verify and warn
164 build_history_enabled = True
165 inheritlist, _ = server.runCommand(["getVariable", "INHERIT"])
166
167 if not "buildhistory" in inheritlist.split(" "):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600168 logger.warning("buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 build_history_enabled = False
170
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500171 if not "buildstats" in inheritlist.split(" "):
172 logger.warning("buildstats is not enabled. Please enable INHERIT += \"buildstats\" to generate build statistics.")
173
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174 if not params.observe_only:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500175 params.updateFromServer(server)
176 params.updateToServer(server, os.environ.copy())
177 cmdline = params.parseActions()
178 if not cmdline:
179 print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
180 return 1
181 if 'msg' in cmdline and cmdline['msg']:
182 logger.error(cmdline['msg'])
183 return 1
184
185 ret, error = server.runCommand(cmdline['action'])
186 if error:
187 logger.error("Command '%s' failed: %s" % (cmdline, error))
188 return 1
189 elif ret != True:
190 logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
191 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500192
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500193 # set to 1 when toasterui needs to shut down
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194 main.shutdown = 0
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500195
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500196 interrupted = False
197 return_value = 0
198 errors = 0
199 warnings = 0
200 taskfailures = []
201 first = True
202
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500203 buildinfohelper = BuildInfoHelper(server, build_history_enabled,
204 os.getenv('TOASTER_BRBE'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500205
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500206 # write our own log files into bitbake's log directory;
207 # we're only interested in the path to the parent directory of
208 # this file, as we're writing our own logs into the same directory
209 consolelogfile = _log_settings_from_server(server)
210 log_dir = os.path.dirname(consolelogfile)
211 bb.utils.mkdirhier(log_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212
213 while True:
214 try:
215 event = eventHandler.waitEvent(0.25)
216 if first:
217 first = False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500218
219 # TODO don't use log output to determine when bitbake has started
220 #
221 # this is the line localhostbecontroller needs to
222 # see in toaster_ui.log which it uses to decide whether
223 # the bitbake server has started...
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224 logger.info("ToasterUI waiting for events")
225
226 if event is None:
227 if main.shutdown > 0:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500228 # if shutting down, close any open build log first
229 _close_build_log(build_log)
230
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231 break
232 continue
233
234 helper.eventHandler(event)
235
236 # pylint: disable=protected-access
237 # the code will look into the protected variables of the event; no easy way around this
238
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500239 if isinstance(event, bb.event.HeartbeatEvent):
240 continue
241
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500242 if isinstance(event, bb.event.ParseStarted):
243 if not (build_log and build_log_file_path):
244 build_log, build_log_file_path = _open_build_log(log_dir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600245
246 buildinfohelper.store_started_build()
247 buildinfohelper.save_build_log_file_path(build_log_file_path)
248 buildinfohelper.set_recipes_to_parse(event.total)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500249 continue
250
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600251 # create a build object in buildinfohelper from either BuildInit
252 # (if available) or BuildStarted (for jethro and previous versions)
253 if isinstance(event, (bb.event.BuildStarted, bb.event.BuildInit)):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500254 if not (build_log and build_log_file_path):
255 build_log, build_log_file_path = _open_build_log(log_dir)
256
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600257 buildinfohelper.save_build_targets(event)
258 buildinfohelper.save_build_log_file_path(build_log_file_path)
259
260 # get additional data from BuildStarted
261 if isinstance(event, bb.event.BuildStarted):
262 buildinfohelper.save_build_layers_and_variables()
263 continue
264
265 if isinstance(event, bb.event.ParseProgress):
266 buildinfohelper.set_recipes_parsed(event.current)
267 continue
268
269 if isinstance(event, bb.event.ParseCompleted):
270 buildinfohelper.set_recipes_parsed(event.total)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500271 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272
273 if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
274 buildinfohelper.update_and_store_task(event)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500275 logger.info("Logfile for task %s", event.logfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276 continue
277
278 if isinstance(event, bb.build.TaskBase):
279 logger.info(event._message)
280
281 if isinstance(event, bb.event.LogExecTTY):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500282 logger.info(event.msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283 continue
284
285 if isinstance(event, logging.LogRecord):
286 if event.levelno == -1:
287 event.levelno = formatter.ERROR
288
289 buildinfohelper.store_log_event(event)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500290
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 if event.levelno >= formatter.ERROR:
292 errors = errors + 1
293 elif event.levelno == formatter.WARNING:
294 warnings = warnings + 1
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500295
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296 # For "normal" logging conditions, don't show note logs from tasks
297 # but do show them if the user has changed the default log level to
298 # include verbose/debug messages
299 if event.taskpid != 0 and event.levelno <= formatter.NOTE:
300 continue
301
302 logger.handle(event)
303 continue
304
305 if isinstance(event, bb.build.TaskFailed):
306 buildinfohelper.update_and_store_task(event)
307 logfile = event.logfile
308 if logfile and os.path.exists(logfile):
309 bb.error("Logfile of failure stored in: %s" % logfile)
310 continue
311
312 # these events are unprocessed now, but may be used in the future to log
313 # timing and error informations from the parsing phase in Toaster
314 if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)):
315 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500316 if isinstance(event, bb.event.CacheLoadStarted):
317 continue
318 if isinstance(event, bb.event.CacheLoadProgress):
319 continue
320 if isinstance(event, bb.event.CacheLoadCompleted):
321 continue
322 if isinstance(event, bb.event.MultipleProviders):
323 logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "",
324 event._item,
325 ", ".join(event._candidates))
326 logger.info("consider defining a PREFERRED_PROVIDER entry to match %s", event._item)
327 continue
328
329 if isinstance(event, bb.event.NoProvider):
330 errors = errors + 1
331 if event._runtime:
332 r = "R"
333 else:
334 r = ""
335
336 if event._dependees:
337 text = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % (r, event._item, ", ".join(event._dependees), r)
338 else:
339 text = "Nothing %sPROVIDES '%s'" % (r, event._item)
340
341 logger.error(text)
342 if event._reasons:
343 for reason in event._reasons:
344 logger.error("%s", reason)
345 text += reason
346 buildinfohelper.store_log_error(text)
347 continue
348
349 if isinstance(event, bb.event.ConfigParsed):
350 continue
351 if isinstance(event, bb.event.RecipeParsed):
352 continue
353
354 # end of saved events
355
356 if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)):
357 buildinfohelper.store_started_task(event)
358 continue
359
360 if isinstance(event, bb.runqueue.runQueueTaskCompleted):
361 buildinfohelper.update_and_store_task(event)
362 continue
363
364 if isinstance(event, bb.runqueue.runQueueTaskFailed):
365 buildinfohelper.update_and_store_task(event)
366 taskfailures.append(event.taskstring)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 logger.error("Task (%s) failed with exit code '%s'",
368 event.taskstring, event.exitcode)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369 continue
370
371 if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)):
372 buildinfohelper.update_and_store_task(event)
373 continue
374
375
376 if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)):
377 continue
378
379 if isinstance(event, (bb.event.BuildCompleted, bb.command.CommandFailed)):
380
381 errorcode = 0
382 if isinstance(event, bb.command.CommandFailed):
383 errors += 1
384 errorcode = 1
385 logger.error("Command execution failed: %s", event.error)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600386 elif isinstance(event, bb.event.BuildCompleted):
387 buildinfohelper.scan_image_artifacts()
388 buildinfohelper.clone_required_sdk_artifacts()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500390 # turn off logging to the current build log
391 _close_build_log(build_log)
392
393 # reset ready for next BuildStarted
394 build_log = None
395
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396 # update the build info helper on BuildCompleted, not on CommandXXX
397 buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500398
399 brbe = buildinfohelper.brbe
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400 buildinfohelper.close(errorcode)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500401
402 # we start a new build info
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500403 if params.observe_only:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404 logger.debug("ToasterUI prepared for new build")
405 errors = 0
406 warnings = 0
407 taskfailures = []
408 buildinfohelper = BuildInfoHelper(server, build_history_enabled)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500409 else:
410 main.shutdown = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500411
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500412 logger.info("ToasterUI build done, brbe: %s", brbe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413 continue
414
415 if isinstance(event, (bb.command.CommandCompleted,
416 bb.command.CommandFailed,
417 bb.command.CommandExit)):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500418 if params.observe_only:
419 errorcode = 0
420 else:
421 main.shutdown = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422
423 continue
424
425 if isinstance(event, bb.event.MetadataEvent):
426 if event.type == "SinglePackageInfo":
427 buildinfohelper.store_build_package_information(event)
428 elif event.type == "LayerInfo":
429 buildinfohelper.store_layer_info(event)
430 elif event.type == "BuildStatsList":
431 buildinfohelper.store_tasks_stats(event)
432 elif event.type == "ImagePkgList":
433 buildinfohelper.store_target_package_data(event)
434 elif event.type == "MissedSstate":
435 buildinfohelper.store_missed_state_tasks(event)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600436 elif event.type == "SDKArtifactInfo":
437 buildinfohelper.scan_sdk_artifacts(event)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500438 elif event.type == "SetBRBE":
439 buildinfohelper.brbe = buildinfohelper._get_data_from_event(event)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600440 elif event.type == "TaskArtifacts":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500441 buildinfohelper.scan_task_artifacts(event)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500442 elif event.type == "OSErrorException":
443 logger.error(event)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600445 logger.error("Unprocessed MetadataEvent %s", event.type)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 continue
447
448 if isinstance(event, bb.cooker.CookerExit):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500449 # shutdown when bitbake server shuts down
450 main.shutdown = 1
451 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500452
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453 if isinstance(event, bb.event.DepTreeGenerated):
454 buildinfohelper.store_dependency_information(event)
455 continue
456
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600457 logger.warning("Unknown event: %s", event)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458 return_value += 1
459
460 except EnvironmentError as ioerror:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600461 logger.warning("EnvironmentError: %s" % ioerror)
462 # ignore interrupted io system calls
463 if ioerror.args[0] == 4: # errno 4 is EINTR
464 logger.warning("Skipped EINTR: %s" % ioerror)
465 else:
466 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500467 except KeyboardInterrupt:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600468 if params.observe_only:
469 print("\nKeyboard Interrupt, exiting observer...")
470 main.shutdown = 2
471 if not params.observe_only and main.shutdown == 1:
472 print("\nSecond Keyboard Interrupt, stopping...\n")
473 _, error = server.runCommand(["stateForceShutdown"])
474 if error:
475 logger.error("Unable to cleanly stop: %s" % error)
476 if not params.observe_only and main.shutdown == 0:
477 print("\nKeyboard Interrupt, closing down...\n")
478 interrupted = True
479 _, error = server.runCommand(["stateShutdown"])
480 if error:
481 logger.error("Unable to cleanly shutdown: %s" % error)
482 buildinfohelper.cancel_cli_build()
483 main.shutdown = main.shutdown + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 except Exception as e:
485 # print errors to log
486 import traceback
487 from pprint import pformat
488 exception_data = traceback.format_exc()
489 logger.error("%s\n%s" , e, exception_data)
490
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 # save them to database, if possible; if it fails, we already logged to console.
492 try:
493 buildinfohelper.store_log_exception("%s\n%s" % (str(e), exception_data))
494 except Exception as ce:
495 logger.error("CRITICAL - Failed to to save toaster exception to the database: %s", str(ce))
496
497 # make sure we return with an error
498 return_value += 1
499
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500500 if interrupted and return_value == 0:
501 return_value += 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503 logger.warning("Return value is %d", return_value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500504 return return_value