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