1""" 2BitBake 'Event' implementation 3 4Classes and functions for manipulating 'events' in the 5BitBake build tools. 6""" 7 8# Copyright (C) 2003, 2004 Chris Larson 9# 10# SPDX-License-Identifier: GPL-2.0-only 11# 12 13import ast 14import atexit 15import collections 16import logging 17import pickle 18import sys 19import threading 20import traceback 21 22import bb.utils 23 24# This is the pid for which we should generate the event. This is set when 25# the runqueue forks off. 26worker_pid = 0 27worker_fire = None 28 29logger = logging.getLogger('BitBake.Event') 30 31class Event(object): 32 """Base class for events""" 33 34 def __init__(self): 35 self.pid = worker_pid 36 37 38class HeartbeatEvent(Event): 39 """Triggered at regular time intervals of 10 seconds. Other events can fire much more often 40 (runQueueTaskStarted when there are many short tasks) or not at all for long periods 41 of time (again runQueueTaskStarted, when there is just one long-running task), so this 42 event is more suitable for doing some task-independent work occasionally.""" 43 def __init__(self, time): 44 Event.__init__(self) 45 self.time = time 46 47Registered = 10 48AlreadyRegistered = 14 49 50def get_class_handlers(): 51 return _handlers 52 53def set_class_handlers(h): 54 global _handlers 55 _handlers = h 56 57def clean_class_handlers(): 58 return collections.OrderedDict() 59 60# Internal 61_handlers = clean_class_handlers() 62_ui_handlers = {} 63_ui_logfilters = {} 64_ui_handler_seq = 0 65_event_handler_map = {} 66_catchall_handlers = {} 67_eventfilter = None 68_uiready = False 69_thread_lock = threading.Lock() 70_heartbeat_enabled = False 71_should_exit = threading.Event() 72 73def enable_threadlock(): 74 # Always needed now 75 return 76 77def disable_threadlock(): 78 # Always needed now 79 return 80 81def enable_heartbeat(): 82 global _heartbeat_enabled 83 _heartbeat_enabled = True 84 85def disable_heartbeat(): 86 global _heartbeat_enabled 87 _heartbeat_enabled = False 88 89# 90# In long running code, this function should be called periodically 91# to check if we should exit due to an interuption (.e.g Ctrl+C from the UI) 92# 93def check_for_interrupts(d): 94 global _should_exit 95 if _should_exit.is_set(): 96 bb.warn("Exiting due to interrupt.") 97 raise bb.BBHandledException() 98 99def execute_handler(name, handler, event, d): 100 event.data = d 101 try: 102 ret = handler(event, d) 103 except (bb.parse.SkipRecipe, bb.BBHandledException): 104 raise 105 except Exception: 106 etype, value, tb = sys.exc_info() 107 logger.error("Execution of event handler '%s' failed" % name, 108 exc_info=(etype, value, tb.tb_next)) 109 raise 110 except SystemExit as exc: 111 if exc.code != 0: 112 logger.error("Execution of event handler '%s' failed" % name) 113 raise 114 finally: 115 del event.data 116 117 118def fire_class_handlers(event, d): 119 if isinstance(event, logging.LogRecord): 120 return 121 122 eid = str(event.__class__)[8:-2] 123 evt_hmap = _event_handler_map.get(eid, {}) 124 for name, handler in list(_handlers.items()): 125 if name in _catchall_handlers or name in evt_hmap: 126 if _eventfilter: 127 if not _eventfilter(name, handler, event, d): 128 continue 129 if d is not None and not name in (d.getVar("__BBHANDLERS_MC") or set()): 130 continue 131 execute_handler(name, handler, event, d) 132 133ui_queue = [] 134@atexit.register 135def print_ui_queue(): 136 global ui_queue 137 """If we're exiting before a UI has been spawned, display any queued 138 LogRecords to the console.""" 139 logger = logging.getLogger("BitBake") 140 if not _uiready: 141 from bb.msg import BBLogFormatter 142 # Flush any existing buffered content 143 try: 144 sys.stdout.flush() 145 except: 146 pass 147 try: 148 sys.stderr.flush() 149 except: 150 pass 151 stdout = logging.StreamHandler(sys.stdout) 152 stderr = logging.StreamHandler(sys.stderr) 153 formatter = BBLogFormatter("%(levelname)s: %(message)s") 154 stdout.setFormatter(formatter) 155 stderr.setFormatter(formatter) 156 157 # First check to see if we have any proper messages 158 msgprint = False 159 msgerrs = False 160 161 # Should we print to stderr? 162 for event in ui_queue[:]: 163 if isinstance(event, logging.LogRecord) and event.levelno >= logging.WARNING: 164 msgerrs = True 165 break 166 167 if msgerrs: 168 logger.addHandler(stderr) 169 else: 170 logger.addHandler(stdout) 171 172 for event in ui_queue[:]: 173 if isinstance(event, logging.LogRecord): 174 if event.levelno > logging.DEBUG: 175 logger.handle(event) 176 msgprint = True 177 178 # Nope, so just print all of the messages we have (including debug messages) 179 if not msgprint: 180 for event in ui_queue[:]: 181 if isinstance(event, logging.LogRecord): 182 logger.handle(event) 183 if msgerrs: 184 logger.removeHandler(stderr) 185 else: 186 logger.removeHandler(stdout) 187 ui_queue = [] 188 189def fire_ui_handlers(event, d): 190 global _thread_lock 191 192 if not _uiready: 193 # No UI handlers registered yet, queue up the messages 194 ui_queue.append(event) 195 return 196 197 with bb.utils.lock_timeout_nocheck(_thread_lock) as lock: 198 if not lock: 199 # If we can't get the lock, we may be recursively called, queue and return 200 ui_queue.append(event) 201 return 202 203 errors = [] 204 for h in _ui_handlers: 205 #print "Sending event %s" % event 206 try: 207 if not _ui_logfilters[h].filter(event): 208 continue 209 # We use pickle here since it better handles object instances 210 # which xmlrpc's marshaller does not. Events *must* be serializable 211 # by pickle. 212 if hasattr(_ui_handlers[h].event, "sendpickle"): 213 _ui_handlers[h].event.sendpickle((pickle.dumps(event))) 214 else: 215 _ui_handlers[h].event.send(event) 216 except: 217 errors.append(h) 218 for h in errors: 219 del _ui_handlers[h] 220 221 while ui_queue: 222 fire_ui_handlers(ui_queue.pop(), d) 223 224def fire(event, d): 225 """Fire off an Event""" 226 227 # We can fire class handlers in the worker process context and this is 228 # desired so they get the task based datastore. 229 # UI handlers need to be fired in the server context so we defer this. They 230 # don't have a datastore so the datastore context isn't a problem. 231 232 fire_class_handlers(event, d) 233 if worker_fire: 234 worker_fire(event, d) 235 else: 236 # If messages have been queued up, clear the queue 237 global _uiready, ui_queue 238 if _uiready and ui_queue: 239 with bb.utils.lock_timeout_nocheck(_thread_lock): 240 queue = ui_queue 241 ui_queue = [] 242 for queue_event in queue: 243 fire_ui_handlers(queue_event, d) 244 245 fire_ui_handlers(event, d) 246 247def fire_from_worker(event, d): 248 fire_ui_handlers(event, d) 249 250noop = lambda _: None 251def register(name, handler, mask=None, filename=None, lineno=None, data=None): 252 """Register an Event handler""" 253 254 if data is not None and data.getVar("BB_CURRENT_MC"): 255 mc = data.getVar("BB_CURRENT_MC") 256 name = '%s%s' % (mc.replace('-', '_'), name) 257 258 # already registered 259 if name in _handlers: 260 if data is not None: 261 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set()) 262 bbhands_mc.add(name) 263 data.setVar("__BBHANDLERS_MC", bbhands_mc) 264 return AlreadyRegistered 265 266 if handler is not None: 267 # handle string containing python code 268 if isinstance(handler, str): 269 tmp = "def %s(e, d):\n%s" % (name, handler) 270 # Inject empty lines to make code match lineno in filename 271 if lineno is not None: 272 tmp = "\n" * (lineno-1) + tmp 273 try: 274 code = bb.methodpool.compile_cache(tmp) 275 if not code: 276 if filename is None: 277 filename = "%s(e, d)" % name 278 code = compile(tmp, filename, "exec", ast.PyCF_ONLY_AST) 279 code = compile(code, filename, "exec") 280 bb.methodpool.compile_cache_add(tmp, code) 281 except SyntaxError: 282 logger.error("Unable to register event handler '%s':\n%s", name, 283 ''.join(traceback.format_exc(limit=0))) 284 _handlers[name] = noop 285 return 286 env = {} 287 bb.utils.better_exec(code, env) 288 func = bb.utils.better_eval(name, env) 289 _handlers[name] = func 290 else: 291 _handlers[name] = handler 292 293 if not mask or '*' in mask: 294 _catchall_handlers[name] = True 295 else: 296 for m in mask: 297 if _event_handler_map.get(m, None) is None: 298 _event_handler_map[m] = {} 299 _event_handler_map[m][name] = True 300 301 if data is not None: 302 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set()) 303 bbhands_mc.add(name) 304 data.setVar("__BBHANDLERS_MC", bbhands_mc) 305 306 return Registered 307 308def remove(name, handler, data=None): 309 """Remove an Event handler""" 310 if data is not None: 311 if data.getVar("BB_CURRENT_MC"): 312 mc = data.getVar("BB_CURRENT_MC") 313 name = '%s%s' % (mc.replace('-', '_'), name) 314 315 _handlers.pop(name) 316 if name in _catchall_handlers: 317 _catchall_handlers.pop(name) 318 for event in _event_handler_map.keys(): 319 if name in _event_handler_map[event]: 320 _event_handler_map[event].pop(name) 321 322 if data is not None: 323 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set()) 324 if name in bbhands_mc: 325 bbhands_mc.remove(name) 326 data.setVar("__BBHANDLERS_MC", bbhands_mc) 327 328def get_handlers(): 329 return _handlers 330 331def set_handlers(handlers): 332 global _handlers 333 _handlers = handlers 334 335def set_eventfilter(func): 336 global _eventfilter 337 _eventfilter = func 338 339def register_UIHhandler(handler, mainui=False): 340 with bb.utils.lock_timeout(_thread_lock): 341 bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 342 _ui_handlers[_ui_handler_seq] = handler 343 level, debug_domains = bb.msg.constructLogOptions() 344 _ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains) 345 if mainui: 346 global _uiready 347 _uiready = _ui_handler_seq 348 return _ui_handler_seq 349 350def unregister_UIHhandler(handlerNum, mainui=False): 351 if mainui: 352 global _uiready 353 _uiready = False 354 with bb.utils.lock_timeout(_thread_lock): 355 if handlerNum in _ui_handlers: 356 del _ui_handlers[handlerNum] 357 return 358 359def get_uihandler(): 360 if _uiready is False: 361 return None 362 return _uiready 363 364# Class to allow filtering of events and specific filtering of LogRecords *before* we put them over the IPC 365class UIEventFilter(object): 366 def __init__(self, level, debug_domains): 367 self.update(None, level, debug_domains) 368 369 def update(self, eventmask, level, debug_domains): 370 self.eventmask = eventmask 371 self.stdlevel = level 372 self.debug_domains = debug_domains 373 374 def filter(self, event): 375 if isinstance(event, logging.LogRecord): 376 if event.levelno >= self.stdlevel: 377 return True 378 if event.name in self.debug_domains and event.levelno >= self.debug_domains[event.name]: 379 return True 380 return False 381 eid = str(event.__class__)[8:-2] 382 if self.eventmask and eid not in self.eventmask: 383 return False 384 return True 385 386def set_UIHmask(handlerNum, level, debug_domains, mask): 387 if not handlerNum in _ui_handlers: 388 return False 389 if '*' in mask: 390 _ui_logfilters[handlerNum].update(None, level, debug_domains) 391 else: 392 _ui_logfilters[handlerNum].update(mask, level, debug_domains) 393 return True 394 395def getName(e): 396 """Returns the name of a class or class instance""" 397 if getattr(e, "__name__", None) is None: 398 return e.__class__.__name__ 399 else: 400 return e.__name__ 401 402class OperationStarted(Event): 403 """An operation has begun""" 404 def __init__(self, msg = "Operation Started"): 405 Event.__init__(self) 406 self.msg = msg 407 408class OperationCompleted(Event): 409 """An operation has completed""" 410 def __init__(self, total, msg = "Operation Completed"): 411 Event.__init__(self) 412 self.total = total 413 self.msg = msg 414 415class OperationProgress(Event): 416 """An operation is in progress""" 417 def __init__(self, current, total, msg = "Operation in Progress"): 418 Event.__init__(self) 419 self.current = current 420 self.total = total 421 self.msg = msg + ": %s/%s" % (current, total); 422 423class ConfigParsed(Event): 424 """Configuration Parsing Complete""" 425 426class MultiConfigParsed(Event): 427 """Multi-Config Parsing Complete""" 428 def __init__(self, mcdata): 429 self.mcdata = mcdata 430 Event.__init__(self) 431 432class RecipeEvent(Event): 433 def __init__(self, fn): 434 self.fn = fn 435 Event.__init__(self) 436 437class RecipePreDeferredInherits(RecipeEvent): 438 """ 439 Called before deferred inherits are processed so code can snoop on class extensions for example 440 Limitations: It won't see inherits of inherited classes and the data is unexpanded 441 """ 442 def __init__(self, fn, inherits): 443 self.fn = fn 444 self.inherits = inherits 445 Event.__init__(self) 446 447class RecipePreFinalise(RecipeEvent): 448 """ Recipe Parsing Complete but not yet finalised""" 449 450class RecipePostKeyExpansion(RecipeEvent): 451 """ Recipe Parsing Complete but not yet finalised""" 452 453 454class RecipeTaskPreProcess(RecipeEvent): 455 """ 456 Recipe Tasks about to be finalised 457 The list of tasks should be final at this point and handlers 458 are only able to change interdependencies 459 """ 460 def __init__(self, fn, tasklist): 461 self.fn = fn 462 self.tasklist = tasklist 463 Event.__init__(self) 464 465class RecipeParsed(RecipeEvent): 466 """ Recipe Parsing Complete """ 467 468class BuildBase(Event): 469 """Base class for bitbake build events""" 470 471 def __init__(self, n, p, failures = 0): 472 self._name = n 473 self._pkgs = p 474 Event.__init__(self) 475 self._failures = failures 476 477 def getPkgs(self): 478 return self._pkgs 479 480 def setPkgs(self, pkgs): 481 self._pkgs = pkgs 482 483 def getName(self): 484 return self._name 485 486 def setName(self, name): 487 self._name = name 488 489 def getFailures(self): 490 """ 491 Return the number of failed packages 492 """ 493 return self._failures 494 495 pkgs = property(getPkgs, setPkgs, None, "pkgs property") 496 name = property(getName, setName, None, "name property") 497 498class BuildInit(BuildBase): 499 """buildFile or buildTargets was invoked""" 500 def __init__(self, p=[]): 501 name = None 502 BuildBase.__init__(self, name, p) 503 504class BuildStarted(BuildBase, OperationStarted): 505 """Event when builds start""" 506 def __init__(self, n, p, failures = 0): 507 OperationStarted.__init__(self, "Building Started") 508 BuildBase.__init__(self, n, p, failures) 509 510class BuildCompleted(BuildBase, OperationCompleted): 511 """Event when builds have completed""" 512 def __init__(self, total, n, p, failures=0, interrupted=0): 513 if not failures: 514 OperationCompleted.__init__(self, total, "Building Succeeded") 515 else: 516 OperationCompleted.__init__(self, total, "Building Failed") 517 self._interrupted = interrupted 518 BuildBase.__init__(self, n, p, failures) 519 520class DiskFull(Event): 521 """Disk full case build halted""" 522 def __init__(self, dev, type, freespace, mountpoint): 523 Event.__init__(self) 524 self._dev = dev 525 self._type = type 526 self._free = freespace 527 self._mountpoint = mountpoint 528 529class DiskUsageSample: 530 def __init__(self, available_bytes, free_bytes, total_bytes): 531 # Number of bytes available to non-root processes. 532 self.available_bytes = available_bytes 533 # Number of bytes available to root processes. 534 self.free_bytes = free_bytes 535 # Total capacity of the volume. 536 self.total_bytes = total_bytes 537 538class MonitorDiskEvent(Event): 539 """If BB_DISKMON_DIRS is set, then this event gets triggered each time disk space is checked. 540 Provides information about devices that are getting monitored.""" 541 def __init__(self, disk_usage): 542 Event.__init__(self) 543 # hash of device root path -> DiskUsageSample 544 self.disk_usage = disk_usage 545 546class NoProvider(Event): 547 """No Provider for an Event""" 548 549 def __init__(self, item, runtime=False, dependees=None, reasons=None, close_matches=None): 550 Event.__init__(self) 551 self._item = item 552 self._runtime = runtime 553 self._dependees = dependees 554 self._reasons = reasons 555 self._close_matches = close_matches 556 557 def getItem(self): 558 return self._item 559 560 def isRuntime(self): 561 return self._runtime 562 563 def __str__(self): 564 msg = '' 565 if self._runtime: 566 r = "R" 567 else: 568 r = "" 569 570 extra = '' 571 if not self._reasons: 572 if self._close_matches: 573 extra = ". Close matches:\n %s" % '\n '.join(sorted(set(self._close_matches))) 574 575 if self._dependees: 576 msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s" % (r, self._item, ", ".join(self._dependees), r, extra) 577 else: 578 msg = "Nothing %sPROVIDES '%s'%s" % (r, self._item, extra) 579 if self._reasons: 580 for reason in self._reasons: 581 msg += '\n' + reason 582 return msg 583 584 585class MultipleProviders(Event): 586 """Multiple Providers""" 587 588 def __init__(self, item, candidates, runtime = False): 589 Event.__init__(self) 590 self._item = item 591 self._candidates = candidates 592 self._is_runtime = runtime 593 594 def isRuntime(self): 595 """ 596 Is this a runtime issue? 597 """ 598 return self._is_runtime 599 600 def getItem(self): 601 """ 602 The name for the to be build item 603 """ 604 return self._item 605 606 def getCandidates(self): 607 """ 608 Get the possible Candidates for a PROVIDER. 609 """ 610 return self._candidates 611 612 def __str__(self): 613 msg = "Multiple providers are available for %s%s (%s)" % (self._is_runtime and "runtime " or "", 614 self._item, 615 ", ".join(self._candidates)) 616 rtime = "" 617 if self._is_runtime: 618 rtime = "R" 619 msg += "\nConsider defining a PREFERRED_%sPROVIDER entry to match %s" % (rtime, self._item) 620 return msg 621 622class ParseStarted(OperationStarted): 623 """Recipe parsing for the runqueue has begun""" 624 def __init__(self, total): 625 OperationStarted.__init__(self, "Recipe parsing Started") 626 self.total = total 627 628class ParseCompleted(OperationCompleted): 629 """Recipe parsing for the runqueue has completed""" 630 def __init__(self, cached, parsed, skipped, masked, virtuals, errors, total): 631 OperationCompleted.__init__(self, total, "Recipe parsing Completed") 632 self.cached = cached 633 self.parsed = parsed 634 self.skipped = skipped 635 self.virtuals = virtuals 636 self.masked = masked 637 self.errors = errors 638 self.sofar = cached + parsed 639 640class ParseProgress(OperationProgress): 641 """Recipe parsing progress""" 642 def __init__(self, current, total): 643 OperationProgress.__init__(self, current, total, "Recipe parsing") 644 645 646class CacheLoadStarted(OperationStarted): 647 """Loading of the dependency cache has begun""" 648 def __init__(self, total): 649 OperationStarted.__init__(self, "Loading cache Started") 650 self.total = total 651 652class CacheLoadProgress(OperationProgress): 653 """Cache loading progress""" 654 def __init__(self, current, total): 655 OperationProgress.__init__(self, current, total, "Loading cache") 656 657class CacheLoadCompleted(OperationCompleted): 658 """Cache loading is complete""" 659 def __init__(self, total, num_entries): 660 OperationCompleted.__init__(self, total, "Loading cache Completed") 661 self.num_entries = num_entries 662 663class TreeDataPreparationStarted(OperationStarted): 664 """Tree data preparation started""" 665 def __init__(self): 666 OperationStarted.__init__(self, "Preparing tree data Started") 667 668class TreeDataPreparationProgress(OperationProgress): 669 """Tree data preparation is in progress""" 670 def __init__(self, current, total): 671 OperationProgress.__init__(self, current, total, "Preparing tree data") 672 673class TreeDataPreparationCompleted(OperationCompleted): 674 """Tree data preparation completed""" 675 def __init__(self, total): 676 OperationCompleted.__init__(self, total, "Preparing tree data Completed") 677 678class DepTreeGenerated(Event): 679 """ 680 Event when a dependency tree has been generated 681 """ 682 683 def __init__(self, depgraph): 684 Event.__init__(self) 685 self._depgraph = depgraph 686 687class TargetsTreeGenerated(Event): 688 """ 689 Event when a set of buildable targets has been generated 690 """ 691 def __init__(self, model): 692 Event.__init__(self) 693 self._model = model 694 695class ReachableStamps(Event): 696 """ 697 An event listing all stamps reachable after parsing 698 which the metadata may use to clean up stale data 699 """ 700 701 def __init__(self, stamps): 702 Event.__init__(self) 703 self.stamps = stamps 704 705class StaleSetSceneTasks(Event): 706 """ 707 An event listing setscene tasks which are 'stale' and will 708 be rerun. The metadata may use to clean up stale data. 709 tasks is a mapping of tasks and matching stale stamps. 710 """ 711 712 def __init__(self, tasks): 713 Event.__init__(self) 714 self.tasks = tasks 715 716class FilesMatchingFound(Event): 717 """ 718 Event when a list of files matching the supplied pattern has 719 been generated 720 """ 721 def __init__(self, pattern, matches): 722 Event.__init__(self) 723 self._pattern = pattern 724 self._matches = matches 725 726class ConfigFilesFound(Event): 727 """ 728 Event when a list of appropriate config files has been generated 729 """ 730 def __init__(self, variable, values): 731 Event.__init__(self) 732 self._variable = variable 733 self._values = values 734 735class ConfigFilePathFound(Event): 736 """ 737 Event when a path for a config file has been found 738 """ 739 def __init__(self, path): 740 Event.__init__(self) 741 self._path = path 742 743class MsgBase(Event): 744 """Base class for messages""" 745 746 def __init__(self, msg): 747 self._message = msg 748 Event.__init__(self) 749 750class MsgDebug(MsgBase): 751 """Debug Message""" 752 753class MsgNote(MsgBase): 754 """Note Message""" 755 756class MsgWarn(MsgBase): 757 """Warning Message""" 758 759class MsgError(MsgBase): 760 """Error Message""" 761 762class MsgFatal(MsgBase): 763 """Fatal Message""" 764 765class MsgPlain(MsgBase): 766 """General output""" 767 768class LogExecTTY(Event): 769 """Send event containing program to spawn on tty of the logger""" 770 def __init__(self, msg, prog, sleep_delay, retries): 771 Event.__init__(self) 772 self.msg = msg 773 self.prog = prog 774 self.sleep_delay = sleep_delay 775 self.retries = retries 776 777class LogHandler(logging.Handler): 778 """Dispatch logging messages as bitbake events""" 779 780 def emit(self, record): 781 if record.exc_info: 782 record.bb_exc_formatted = traceback.format_exception(*record.exc_info) 783 record.exc_info = None 784 fire(record, None) 785 786 def filter(self, record): 787 record.taskpid = worker_pid 788 return True 789 790class MetadataEvent(Event): 791 """ 792 Generic event that target for OE-Core classes 793 to report information during asynchronous execution 794 """ 795 def __init__(self, eventtype, eventdata): 796 Event.__init__(self) 797 self.type = eventtype 798 self._localdata = eventdata 799 800class ProcessStarted(Event): 801 """ 802 Generic process started event (usually part of the initial startup) 803 where further progress events will be delivered 804 """ 805 def __init__(self, processname, total): 806 Event.__init__(self) 807 self.processname = processname 808 self.total = total 809 810class ProcessProgress(Event): 811 """ 812 Generic process progress event (usually part of the initial startup) 813 """ 814 def __init__(self, processname, progress): 815 Event.__init__(self) 816 self.processname = processname 817 self.progress = progress 818 819class ProcessFinished(Event): 820 """ 821 Generic process finished event (usually part of the initial startup) 822 """ 823 def __init__(self, processname): 824 Event.__init__(self) 825 self.processname = processname 826 827class SanityCheck(Event): 828 """ 829 Event to run sanity checks, either raise errors or generate events as return status. 830 """ 831 def __init__(self, generateevents = True): 832 Event.__init__(self) 833 self.generateevents = generateevents 834 835class SanityCheckPassed(Event): 836 """ 837 Event to indicate sanity check has passed 838 """ 839 840class SanityCheckFailed(Event): 841 """ 842 Event to indicate sanity check has failed 843 """ 844 def __init__(self, msg, network_error=False): 845 Event.__init__(self) 846 self._msg = msg 847 self._network_error = network_error 848 849class NetworkTest(Event): 850 """ 851 Event to run network connectivity tests, either raise errors or generate events as return status. 852 """ 853 def __init__(self, generateevents = True): 854 Event.__init__(self) 855 self.generateevents = generateevents 856 857class NetworkTestPassed(Event): 858 """ 859 Event to indicate network test has passed 860 """ 861 862class NetworkTestFailed(Event): 863 """ 864 Event to indicate network test has failed 865 """ 866 867class FindSigInfoResult(Event): 868 """ 869 Event to return results from findSigInfo command 870 """ 871 def __init__(self, result): 872 Event.__init__(self) 873 self.result = result 874 875class GetTaskSignatureResult(Event): 876 """ 877 Event to return results from GetTaskSignatures command 878 """ 879 def __init__(self, sig): 880 Event.__init__(self) 881 self.sig = sig 882 883class ParseError(Event): 884 """ 885 Event to indicate parse failed 886 """ 887 def __init__(self, msg): 888 super().__init__() 889 self._msg = msg 890