# Path: nitro/nitro.py
# class Nitro:
# 
#     def __init__(self, domain, introspection=True, syscall_filtering=True):
#         self.listener = Listener(domain)
#         self.introspection = introspection
#         self.backend = None
#         if self.introspection:
#             self.backend = get_backend(domain, self.listener, syscall_filtering)
# 
#     def listen(self):
#         yield from self.listener.listen()
# 
#     def __enter__(self):
#         return self
# 
#     def __exit__(self, *args, **kwargs):
#         self.stop()
# 
#     def stop(self, synchronous=True):
#         self.listener.stop(synchronous)
# 
# Path: nitro/event.py
# class SyscallDirection(Enum):
#     """System call direction"""
#     enter = 0
#     exit = 1
# 
# Path: nitro/backends/backend.py
# class Backend:
#     """
#     Base class for Backends. ``Backend`` provides functionality for dispatching
#     hooks and keeping statistics about processed events.
#     """
# 
#     __slots__ = (
#         "domain",
#         "libvmi",
#         "hooks",
#         "stats",
#         "listener",
#         "syscall_filtering"
#     )
# 
#     def __init__(self, domain, libvmi, listener, syscall_filtering=True):
#         """Create a new ``Backend``"""
# 
#         #: libvirt domain associated with the backend
#         self.domain = domain
#         #: handle to libvmi
#         self.libvmi = libvmi
#         #: ``Listener`` associated with the ``Backend``
#         self.listener = listener
#         #: Is system call filtering enabled for the backend
#         self.syscall_filtering = syscall_filtering
#         #: Event hooks
#         self.hooks = {
#             SyscallDirection.enter: {},
#             SyscallDirection.exit: {}
#         }
#         #: Statistics about the backend
#         self.stats = defaultdict(int)
# 
#     def dispatch_hooks(self, syscall):
#         # TODO: don't dispatch if the process is None
#         if syscall.process is None:
#             return
# 
#         try:
#             hook = self.hooks[syscall.event.direction][syscall.name]
#         except KeyError:
#             pass
#         else:
#             try:
#                 logging.debug('Processing hook %s - %s',
#                               syscall.event.direction.name, hook.__name__)
#                 hook(syscall, self)
#             # FIXME: There should be a way for OS specific backends to report these
#             # except InconsistentMemoryError: #
#             #     self.stats['memory_access_error'] += 1
#             #     logging.exception('Memory access error')
#             except LibvmiError:
#                 self.stats['libvmi_failure'] += 1
#                 logging.exception('VMI_FAILURE')
#             # misc failures
#             except ValueError:
#                 self.stats['misc_error'] += 1
#                 logging.exception('Misc error')
#             except Exception:
#                 logging.exception('Unknown error while processing hook')
#             else:
#                 self.stats['hooks_completed'] += 1
#             finally:
#                 self.stats['hooks_processed'] += 1
# 
#     def define_hook(self, name, callback, direction=SyscallDirection.enter):
#         """
#         Register a new system call hook with the ``Backend``.
#         
#         :param str name: Name of the system call to hook.
#         :param callable callback: Callable to call when the hook is fired.
#         :param SyscallDirection direction: Should the hook fire when system call is entered or exited.
#         """
#         logging.info('Defining %s hook on %s', direction.name, name)
#         self.hooks[direction][name] = callback
# 
#     def undefine_hook(self, name, direction=SyscallDirection.enter):
#         """
#         Unregister a hook.
#         """
#         logging.info('Removing hook on %s', name)
#         self.hooks[direction].pop(name)
# 
#     def __enter__(self):
#         return self
# 
#     def __exit__(self, *args):
#         self.stop()
# 
#     def stop(self):
#         """Stop the backend"""
#         logging.info(json.dumps(self.stats, indent=4))
#         self.libvmi.destroy()


import os
import sys
import logging
import time
import libvirt
import socket
import datetime
import xml.etree.ElementTree as tree
from functools import partial
from threading import Thread, Event
from nitro.nitro import Nitro
from nitro.libvmi import LibvmiError
from nitro.event import SyscallDirection
from nitro.backends.backend import Backend
from cdrom import WindowsCDROM, LinuxCDROM
        source_elem = cdrom_elem.find('./source')
        if source_elem is None:
            tree.SubElement(cdrom_elem, 'source')
            source_elem = cdrom_elem.find('./source')
        source_elem.set('file', cdrom_path)
        new_xml = tree.tostring(cdrom_elem).decode('utf-8')
        result = self.domain.updateDeviceFlags(new_xml, libvirt.VIR_DOMAIN_AFFECT_LIVE)
        logging.debug("updateDeviceFlags returned %s", result)

    def run_test(self, wait=True, analyze=True, enter_hooks=None, exit_hooks=None):
        """Run the test by mounting the cdrom into the guest
        if wait is True, it will run the Nitro thread and wait for the test to terminate.
        if wait is False, it will return an Event which will be set when the test will terminate"""
        # get iso
        cdrom_iso = self.cdrom.generate_iso()
        logging.debug("ISO path: %s", cdrom_iso)
        if wait:
            # run nitro before inserting CDROM
            ready = Event()
            nitro = NitroThread(self.domain, analyze, enter_hooks, exit_hooks, ready)
            nitro.start()
            # wait for nitro to attach before mounting the CDROM
            ready.wait()
            # mount the cdrom
            # the test is executed
            self.mount_cdrom(cdrom_iso)
            # wait on monitered to be closed
            self.wait(self.ip, False, self.sleep_amount)
            # wait for nitro thread to terminate properly
            nitro.stop()

# Path: nitro/nitro.py
# class Nitro:
# 
#     def __init__(self, domain, introspection=True, syscall_filtering=True):
#         self.listener = Listener(domain)
#         self.introspection = introspection
#         self.backend = None
#         if self.introspection:
#             self.backend = get_backend(domain, self.listener, syscall_filtering)
# 
#     def listen(self):
#         yield from self.listener.listen()
# 
#     def __enter__(self):
#         return self
# 
#     def __exit__(self, *args, **kwargs):
#         self.stop()
# 
#     def stop(self, synchronous=True):
#         self.listener.stop(synchronous)
# 
# Path: nitro/event.py
# class SyscallDirection(Enum):
#     """System call direction"""
#     enter = 0
#     exit = 1
# 
# Path: nitro/backends/backend.py
# class Backend:
#     """
#     Base class for Backends. ``Backend`` provides functionality for dispatching
#     hooks and keeping statistics about processed events.
#     """
# 
#     __slots__ = (
#         "domain",
#         "libvmi",
#         "hooks",
#         "stats",
#         "listener",
#         "syscall_filtering"
#     )
# 
#     def __init__(self, domain, libvmi, listener, syscall_filtering=True):
#         """Create a new ``Backend``"""
# 
#         #: libvirt domain associated with the backend
#         self.domain = domain
#         #: handle to libvmi
#         self.libvmi = libvmi
#         #: ``Listener`` associated with the ``Backend``
#         self.listener = listener
#         #: Is system call filtering enabled for the backend
#         self.syscall_filtering = syscall_filtering
#         #: Event hooks
#         self.hooks = {
#             SyscallDirection.enter: {},
#             SyscallDirection.exit: {}
#         }
#         #: Statistics about the backend
#         self.stats = defaultdict(int)
# 
#     def dispatch_hooks(self, syscall):
#         # TODO: don't dispatch if the process is None
#         if syscall.process is None:
#             return
# 
#         try:
#             hook = self.hooks[syscall.event.direction][syscall.name]
#         except KeyError:
#             pass
#         else:
#             try:
#                 logging.debug('Processing hook %s - %s',
#                               syscall.event.direction.name, hook.__name__)
#                 hook(syscall, self)
#             # FIXME: There should be a way for OS specific backends to report these
#             # except InconsistentMemoryError: #
#             #     self.stats['memory_access_error'] += 1
#             #     logging.exception('Memory access error')
#             except LibvmiError:
#                 self.stats['libvmi_failure'] += 1
#                 logging.exception('VMI_FAILURE')
#             # misc failures
#             except ValueError:
#                 self.stats['misc_error'] += 1
#                 logging.exception('Misc error')
#             except Exception:
#                 logging.exception('Unknown error while processing hook')
#             else:
#                 self.stats['hooks_completed'] += 1
#             finally:
#                 self.stats['hooks_processed'] += 1
# 
#     def define_hook(self, name, callback, direction=SyscallDirection.enter):
#         """
#         Register a new system call hook with the ``Backend``.
#         
#         :param str name: Name of the system call to hook.
#         :param callable callback: Callable to call when the hook is fired.
#         :param SyscallDirection direction: Should the hook fire when system call is entered or exited.
#         """
#         logging.info('Defining %s hook on %s', direction.name, name)
#         self.hooks[direction][name] = callback
# 
#     def undefine_hook(self, name, direction=SyscallDirection.enter):
#         """
#         Unregister a hook.
#         """
#         logging.info('Removing hook on %s', name)
#         self.hooks[direction].pop(name)
# 
#     def __enter__(self):
#         return self
# 
#     def __exit__(self, *args):
#         self.stop()
# 
#     def stop(self):
#         """Stop the backend"""
#         logging.info(json.dumps(self.stats, indent=4))
#         self.libvmi.destroy()


# Path: tests/vmtest_helper.py
import os
import sys
import logging
import time
import libvirt
import socket
import datetime
import xml.etree.ElementTree as tree
from functools import partial
from threading import Thread, Event
from nitro.nitro import Nitro
from nitro.libvmi import LibvmiError
from nitro.event import SyscallDirection
from nitro.backends.backend import Backend
from cdrom import WindowsCDROM, LinuxCDROM

        source_elem = cdrom_elem.find('./source')
        if source_elem is None:
            tree.SubElement(cdrom_elem, 'source')
            source_elem = cdrom_elem.find('./source')
        source_elem.set('file', cdrom_path)
        new_xml = tree.tostring(cdrom_elem).decode('utf-8')
        result = self.domain.updateDeviceFlags(new_xml, libvirt.VIR_DOMAIN_AFFECT_LIVE)
        logging.debug("updateDeviceFlags returned %s", result)

    def run_test(self, wait=True, analyze=True, enter_hooks=None, exit_hooks=None):
        """Run the test by mounting the cdrom into the guest
        if wait is True, it will run the Nitro thread and wait for the test to terminate.
        if wait is False, it will return an Event which will be set when the test will terminate"""
        # get iso
        cdrom_iso = self.cdrom.generate_iso()
        logging.debug("ISO path: %s", cdrom_iso)
        if wait:
            # run nitro before inserting CDROM
            ready = Event()
            nitro = NitroThread(self.domain, analyze, enter_hooks, exit_hooks, ready)
            nitro.start()
            # wait for nitro to attach before mounting the CDROM
            ready.wait()
            # mount the cdrom
            # the test is executed
            self.mount_cdrom(cdrom_iso)
            # wait on monitered to be closed
            self.wait(self.ip, False, self.sleep_amount)
            # wait for nitro thread to terminate properly
            nitro.stop()

            return (nitro.events, nitro.total_time)