Beispiel #1
0
class InstrumentDriverTestCase(IonIntegrationTestCase):
    """
    Base class for instrument driver tests
    """

    # configuration singleton
    _test_config = InstrumentDriverTestConfig()

    @classmethod
    def initialize(cls, *args, **kwargs):
        """
        Initialize the test_configuration singleton
        """
        cls._test_config.initialize(*args, **kwargs)

    # Port agent process object.
    port_agent = None

    def setUp(self):
        """
        @brief Setup test cases.
        """
        log.debug("InstrumentDriverTestCase setUp")

        # Test to ensure we have initialized our test config
        if not self._test_config.initialized:
            return TestNotInitialized(
                msg=
                "Tests non initialized. Missing InstrumentDriverTestCase.initalize(...)?"
            )

        self.clear_events()
        self.init_comm_config()

    def tearDown(self):
        """
        @brief Test teardown
        """
        log.debug("InstrumentDriverTestCase tearDown")

    def clear_events(self):
        """
        @brief Clear the event list.
        """
        self.events = []

    def event_received(self, evt):
        """
        @brief Simple callback to catch events from the driver for verification.
        """
        self.events.append(evt)

    def comm_config_file(self):
        """
        @brief Return the path the the driver comm config yaml file.
        @return if comm_config.yml exists return the full path
        """
        repo_dir = Config().get('working_repo')
        driver_path = self._test_config.driver_module
        p = re.compile('\.')
        driver_path = p.sub('/', driver_path)
        abs_path = "%s/%s/%s" % (repo_dir, os.path.dirname(driver_path),
                                 CommConfig.config_filename())

        log.debug(abs_path)
        return abs_path

    def init_comm_config(self):
        """
        @brief Create the comm config object by reading the comm_config.yml file.
        """
        log.info("Initialize comm config")
        config_file = self.comm_config_file()

        log.debug(" -- reading comm config from: %s" % config_file)
        if not os.path.exists(config_file):
            raise TestNoCommConfig(
                msg=
                "Missing comm config.  Try running start_driver or switch_driver"
            )

        self.comm_config = CommConfig.get_config_from_file(config_file)

    def init_port_agent(self):
        """
        @brief Launch the driver process and driver client.  This is used in the
        integration and qualification tests.  The port agent abstracts the physical
        interface with the instrument.
        @retval return the pid to the logger process
        """
        log.info("Startup Port Agent")
        # Create port agent object.
        this_pid = os.getpid()

        # Working dir and delim are hard coded here because this launch process
        # will change with the new port agent.
        self.port_agent = EthernetDeviceLogger.launch_process(
            self.comm_config.device_addr, self.comm_config.device_port,
            self._test_config.working_dir, self._test_config.delimeter,
            this_pid)

        pid = self.port_agent.get_pid()
        while not pid:
            gevent.sleep(.1)
            pid = self.port_agent.get_pid()

        port = self.port_agent.get_port()

        while not port:
            gevent.sleep(.1)
            port = self.port_agent.get_port()

        log.info('Started port agent pid %d listening at port %d' %
                 (pid, port))
        return port

    def stop_port_agent(self):
        """
        Stop the port agent.
        """
        if self.port_agent:
            pid = self.port_agent.get_pid()
            if pid:
                log.info('Stopping pagent pid %i' % pid)
                self.port_agent.stop()
            else:
                log.info('No port agent running.')

    def init_driver_process_client(self):
        """
        @brief Launch the driver process and driver client
        @retval return driver process and driver client object
        """
        log.info("Startup Driver Process")

        this_pid = os.getpid()
        (dvr_proc, cmd_port, evt_port) = ZmqDriverProcess.launch_process(
            self._test_config.driver_module, self._test_config.driver_class,
            self._test_config.working_dir, this_pid)
        self.driver_process = dvr_proc
        log.info('Started driver process for %d %d %s %s' %
                 (cmd_port, evt_port, self._test_config.driver_module,
                  self._test_config.driver_class))
        log.info('Driver process pid %d' % self.driver_process.pid)

        # Create driver client.
        self.driver_client = ZmqDriverClient('localhost', cmd_port, evt_port)
        log.info('Created driver client for %d %d %s %s' %
                 (cmd_port, evt_port, self._test_config.driver_module,
                  self._test_config.driver_class))

        # Start client messaging.
        self.driver_client.start_messaging(self.event_received)
        log.info('Driver messaging started.')
        gevent.sleep(.5)

    def stop_driver_process_client(self):
        """
        Stop the driver_process.
        """
        if self.driver_process:
            log.info('Stopping driver process pid %d' %
                     self.driver_process.pid)
            if self.driver_client:
                self.driver_client.done()
                self.driver_process.wait()
                self.driver_client = None

            else:
                try:
                    log.info('Killing driver process.')
                    self.driver_process.kill()
                except OSError:
                    pass
            self.driver_process = None

    ###
    #   Private Methods
    ###
    def _kill_process(self):
        """
        @brief Ensure a driver process has been killed 
        """
        process = self._driver_process
        pid = process.pid

        log.debug("Killing driver process. PID: %d" % pid)
        # For some reason process.kill and process.terminate didn't actually kill the process.
        # that's whay we had to use the os kill command.  We have to call process.wait so that
        # the returncode attribute is updated which could be blocking.  process.poll didn't
        # seem to work.

        for sig in [signal.SIGTERM, signal.SIGKILL]:
            if (process.returncode != None):
                break
            else:
                log.debug("Sending signal %s to driver process" % sig)
                os.kill(pid, sig)
                process.wait()

        if (process.returncode == None):
            raise Exception(
                "TODO: Better exception.  Failed to kill driver process. PID: %d"
                % self._driver_process.pid)
Beispiel #2
0
class InstrumentDriverTestCase(IonIntegrationTestCase):
    """
    Base class for instrument driver tests
    """

    # configuration singleton
    _test_config = InstrumentDriverTestConfig()

    @classmethod
    def initialize(cls, *args, **kwargs):
        """
        Initialize the test_configuration singleton
        """
        cls._test_config.initialize(*args, **kwargs)

    # Port agent process object.
    port_agent = None

    def setUp(self):
        """
        @brief Setup test cases.
        """
        log.debug("InstrumentDriverTestCase setUp")

        # Test to ensure we have initialized our test config
        if not self._test_config.initialized:
            return TestNotInitialized(msg="Tests non initialized. Missing InstrumentDriverTestCase.initalize(...)?")

        self.clear_events()
        self.init_comm_config()

    def tearDown(self):
        """
        @brief Test teardown
        """
        log.debug("InstrumentDriverTestCase tearDown")

    def clear_events(self):
        """
        @brief Clear the event list.
        """
        self.events = []

    def event_received(self, evt):
        """
        @brief Simple callback to catch events from the driver for verification.
        """
        self.events.append(evt)

    def comm_config_file(self):
        """
        @brief Return the path the the driver comm config yaml file.
        @return if comm_config.yml exists return the full path
        """
        repo_dir = Config().get("working_repo")
        driver_path = self._test_config.driver_module
        p = re.compile("\.")
        driver_path = p.sub("/", driver_path)
        abs_path = "%s/%s/%s" % (repo_dir, os.path.dirname(driver_path), CommConfig.config_filename())

        log.debug(abs_path)
        return abs_path

    def init_comm_config(self):
        """
        @brief Create the comm config object by reading the comm_config.yml file.
        """
        log.info("Initialize comm config")
        config_file = self.comm_config_file()

        log.debug(" -- reading comm config from: %s" % config_file)
        if not os.path.exists(config_file):
            raise TestNoCommConfig(msg="Missing comm config.  Try running start_driver or switch_driver")

        self.comm_config = CommConfig.get_config_from_file(config_file)

    def init_port_agent(self):
        """
        @brief Launch the driver process and driver client.  This is used in the
        integration and qualification tests.  The port agent abstracts the physical
        interface with the instrument.
        @retval return the pid to the logger process
        """
        log.info("Startup Port Agent")
        # Create port agent object.
        this_pid = os.getpid()

        # Working dir and delim are hard coded here because this launch process
        # will change with the new port agent.
        self.port_agent = EthernetDeviceLogger.launch_process(
            self.comm_config.device_addr,
            self.comm_config.device_port,
            self._test_config.working_dir,
            self._test_config.delimeter,
            this_pid,
        )

        pid = self.port_agent.get_pid()
        while not pid:
            gevent.sleep(0.1)
            pid = self.port_agent.get_pid()

        port = self.port_agent.get_port()

        while not port:
            gevent.sleep(0.1)
            port = self.port_agent.get_port()

        log.info("Started port agent pid %d listening at port %d" % (pid, port))
        return port

    def stop_port_agent(self):
        """
        Stop the port agent.
        """
        if self.port_agent:
            pid = self.port_agent.get_pid()
            if pid:
                log.info("Stopping pagent pid %i" % pid)
                self.port_agent.stop()
            else:
                log.info("No port agent running.")

    def init_driver_process_client(self):
        """
        @brief Launch the driver process and driver client
        @retval return driver process and driver client object
        """
        log.info("Startup Driver Process")

        this_pid = os.getpid()
        (dvr_proc, cmd_port, evt_port) = ZmqDriverProcess.launch_process(
            self._test_config.driver_module, self._test_config.driver_class, self._test_config.working_dir, this_pid
        )
        self.driver_process = dvr_proc
        log.info(
            "Started driver process for %d %d %s %s"
            % (cmd_port, evt_port, self._test_config.driver_module, self._test_config.driver_class)
        )
        log.info("Driver process pid %d" % self.driver_process.pid)

        # Create driver client.
        self.driver_client = ZmqDriverClient("localhost", cmd_port, evt_port)
        log.info(
            "Created driver client for %d %d %s %s"
            % (cmd_port, evt_port, self._test_config.driver_module, self._test_config.driver_class)
        )

        # Start client messaging.
        self.driver_client.start_messaging(self.event_received)
        log.info("Driver messaging started.")
        gevent.sleep(0.5)

    def stop_driver_process_client(self):
        """
        Stop the driver_process.
        """
        if self.driver_process:
            log.info("Stopping driver process pid %d" % self.driver_process.pid)
            if self.driver_client:
                self.driver_client.done()
                self.driver_process.wait()
                self.driver_client = None

            else:
                try:
                    log.info("Killing driver process.")
                    self.driver_process.kill()
                except OSError:
                    pass
            self.driver_process = None

    ###
    #   Private Methods
    ###
    def _kill_process(self):
        """
        @brief Ensure a driver process has been killed 
        """
        process = self._driver_process
        pid = process.pid

        log.debug("Killing driver process. PID: %d" % pid)
        # For some reason process.kill and process.terminate didn't actually kill the process.
        # that's whay we had to use the os kill command.  We have to call process.wait so that
        # the returncode attribute is updated which could be blocking.  process.poll didn't
        # seem to work.

        for sig in [signal.SIGTERM, signal.SIGKILL]:
            if process.returncode != None:
                break
            else:
                log.debug("Sending signal %s to driver process" % sig)
                os.kill(pid, sig)
                process.wait()

        if process.returncode == None:
            raise Exception(
                "TODO: Better exception.  Failed to kill driver process. PID: %d" % self._driver_process.pid
            )
Beispiel #3
0
class CiLogger(object):
    """
    ResourceAgent derived class for the CI Logger. This class
    interfaces CG components to driver resources in the ION
    system. It provides support for the CG DCL_Control and Data_Mgr interfaces 
    and translates/forwards these on to the CI driver and CG_Logger.
    """
    def __init__(self):
        log.debug("CiLogger.__init__:")

        # start driver
        driver_config = {
            'svr_addr': 'localhost',
            'cmd_port': 5556,
            'evt_port': 5557,
            'dvr_mod': 'ion.agents.instrument.drivers.sbe37.sbe37_driver',
            'dvr_cls': 'SBE37Driver'
        }
        result = self._start_driver(driver_config)
        if not isinstance(result, int):
            log.error("CiLogger.__init__: " + result[1])
            return
        log.info("CiLogger.__init__: started driver with process id of " +
                 str(result))

        # configure driver and have it start the port agent
        comms_config = {
            SBE37Channel.CTD: {
                'method': 'ethernet',
                'device_addr': 'sbe37-simulator.oceanobservatories.org',
                'device_port': 4001,
                'server_addr': 'localhost',
                'server_port': 8888
            }
        }
        cfg_result = self._dvr_client.cmd_dvr('configure', comms_config)
        channels = [
            key for (key, val) in cfg_result.iteritems()
            if not InstErrorCode.is_error(val)
        ]
        con_result = self._dvr_client.cmd_dvr('connect', channels)
        result = cfg_result.copy()
        try:
            for (key, val) in con_result.iteritems():
                result[key] = val
        except:
            log.error("CiLogger.__init__: driver connection failure - " +
                      str(con_result))
            return
        self._active_channels = self._dvr_client.cmd_dvr('get_active_channels')
        if len(self._active_channels) <= 0:
            log.error(
                "CiLogger.__init__: driver connection failure - no active channels"
            )
            return
        log.info("CiLogger.__init__: driver connected to instrument " +
                 comms_config[SBE37Channel.CTD]['device_addr'])

    def start_streaming(self):
        log.info("CiLogger.start_streaming():")
        return CgMsgTypes.ACK, "Instrument started"

    ###############################################################################
    # Event callback and handling.
    ###############################################################################

    def evt_recv(self, evt):
        """
        Callback to receive asynchronous driver events.
        @param evt The driver event received.
        """
        log.info('CiLogger.evt_recv(): received driver event %s', str(evt))
        # TODO: send 'sample' events to the CG_Logger

    ###############################################################################
    # Private helpers.
    ###############################################################################

    def _go_inactive(self, *args, **kwargs):
        """
        Handler for go_inactive agent command in idle state.
        Attempt to disconnect and initialize all active driver channels.
        Swtich to inactive state if successful.
        """

        channels = self._dvr_client.cmd_dvr('get_active_channels')
        dis_result = self._dvr_client.cmd_dvr('disconnect', channels)

        [
            key for (key, val) in dis_result.iteritems()
            if not InstErrorCode.is_error(val)
        ]
        self._stop_driver()

    def _go_streaming(self, *args, **kwargs):
        """
        Handler for go_streaming agent command in observatory state.
        Send start autosample command to driver and switch to streaming
        state if successful.
        """
        result = self._dvr_client.cmd_dvr('start_autosample', *args, **kwargs)

        if isinstance(result, dict):
            if any([val == None for val in result.values()]):
                next_state = InstrumentAgentState.STREAMING

    def _go_observatory(self, *args, **kwargs):
        """
        Handler for go_observatory agent command within streaming state. Command
        driver to stop autosampling, and switch to observatory mode if
        successful.
        """

        result = self._dvr_client.cmd_dvr('stop_autosample', *args, **kwargs)

        if isinstance(result, dict):
            if all([val == None for val in result.values()]):
                next_state = InstrumentAgentState.OBSERVATORY

    def _start_driver(self, dvr_config):
        """
        Start the driver process and driver client.
        @param dvr_config The driver configuration.
        @param comms_config The driver communications configuration.
        @retval None or error.
        """
        try:
            cmd_port = dvr_config['cmd_port']
            evt_port = dvr_config['evt_port']
            dvr_mod = dvr_config['dvr_mod']
            dvr_cls = dvr_config['dvr_cls']
            svr_addr = dvr_config['svr_addr']

        except (TypeError, KeyError):
            # Not a dict. or missing required parameter.
            log.error(
                'CiLogger._start_driver(): missing required parameter in start_driver.'
            )
            return InstErrorCode.REQUIRED_PARAMETER

        # Launch driver process.
        self._dvr_proc = ZmqDriverProcess.launch_process(
            cmd_port, evt_port, dvr_mod, dvr_cls)

        self._dvr_proc.poll()
        if self._dvr_proc.returncode:
            # Error proc didn't start.
            log.error(
                'CiLogger._start_driver(): driver process did not launch.')
            return InstErrorCode.AGENT_INIT_FAILED

        log.info('CiLogger._start_driver(): launched driver process.')

        # Create client and start messaging.
        self._dvr_client = ZmqDriverClient(svr_addr, cmd_port, evt_port)
        self._dvr_client.start_messaging(self.evt_recv)
        log.info('CiLogger._start_driver(): driver process client started.')
        time.sleep(1)

        try:
            retval = self._dvr_client.cmd_dvr('process_echo', 'Test.')
            log.info(
                'CiLogger._start_driver(): driver process echo test: %s.' %
                str(retval))

        except Exception:
            self._dvr_proc.terminate()
            self._dvr_proc.wait()
            self._dvr_proc = None
            self._dvr_client = None
            log.error(
                'CiLogger._start_driver(): error commanding driver process.')
            return InstErrorCode.AGENT_INIT_FAILED

        else:
            log.info('CiLogger._start_driver(): started its driver.')

        return self._dvr_proc.pid

    def _stop_driver(self):
        """
        Stop the driver process and driver client.
        @retval None.
        """
        if self._dvr_client:
            self._dvr_client.done()
            self._dvr_proc.wait()
            self._dvr_proc = None
            self._dvr_client = None
            log.info('CiLogger._stop_driver(): stopped its driver.')
        time.sleep(1)
Beispiel #4
0
class CiLogger(object):
    """
    ResourceAgent derived class for the CI Logger. This class
    interfaces CG components to driver resources in the ION
    system. It provides support for the CG DCL_Control and Data_Mgr interfaces 
    and translates/forwards these on to the CI driver and CG_Logger.
    """

    def __init__(self):
        log.debug("CiLogger.__init__:")
        
        # start driver
        driver_config = {'svr_addr': 'localhost',
                         'cmd_port': 5556,
                         'evt_port': 5557,
                         'dvr_mod': 'ion.agents.instrument.drivers.sbe37.sbe37_driver',
                         'dvr_cls': 'SBE37Driver'}
        result = self._start_driver(driver_config)
        if not isinstance(result, int):
            log.error("CiLogger.__init__: " + result[1])
            return
        log.info("CiLogger.__init__: started driver with process id of " + str(result))
        
        # configure driver and have it start the port agent
        comms_config = {SBE37Channel.CTD: {'method':'ethernet',
                                           'device_addr': 'sbe37-simulator.oceanobservatories.org',
                                           'device_port': 4001,
                                           'server_addr': 'localhost',
                                           'server_port': 8888} 
                       }               
        cfg_result = self._dvr_client.cmd_dvr('configure', comms_config)
        channels = [key for (key, val) in cfg_result.iteritems() if not
            InstErrorCode.is_error(val)]
        con_result = self._dvr_client.cmd_dvr('connect', channels)
        result = cfg_result.copy()
        try:
            for (key, val) in con_result.iteritems():
                result[key] = val
        except:
            log.error("CiLogger.__init__: driver connection failure - " + str(con_result))
            return
        self._active_channels = self._dvr_client.cmd_dvr('get_active_channels')
        if len(self._active_channels) <= 0:
            log.error("CiLogger.__init__: driver connection failure - no active channels")
            return
        log.info("CiLogger.__init__: driver connected to instrument " + comms_config[SBE37Channel.CTD]['device_addr'])

    def start_streaming(self):
        log.info("CiLogger.start_streaming():")
        return CgMsgTypes.ACK, "Instrument started"
    
    
    ###############################################################################
    # Event callback and handling.
    ###############################################################################

    def evt_recv(self, evt):
        """
        Callback to receive asynchronous driver events.
        @param evt The driver event received.
        """
        log.info('CiLogger.evt_recv(): received driver event %s', str(evt))
        # TODO: send 'sample' events to the CG_Logger
        
    ###############################################################################
    # Private helpers.
    ###############################################################################

    def _go_inactive(self, *args, **kwargs):
        """
        Handler for go_inactive agent command in idle state.
        Attempt to disconnect and initialize all active driver channels.
        Swtich to inactive state if successful.
        """
        
        channels = self._dvr_client.cmd_dvr('get_active_channels')
        dis_result = self._dvr_client.cmd_dvr('disconnect', channels)
        
        [key for (key, val) in dis_result.iteritems() if not
            InstErrorCode.is_error(val)]
        self._stop_driver()
        
    def _go_streaming(self,  *args, **kwargs):
        """
        Handler for go_streaming agent command in observatory state.
        Send start autosample command to driver and switch to streaming
        state if successful.
        """
        result = self._dvr_client.cmd_dvr('start_autosample', *args, **kwargs)
    
        if isinstance(result, dict):
            if any([val == None for val in result.values()]):
                next_state = InstrumentAgentState.STREAMING

    def _go_observatory(self,  *args, **kwargs):
        """
        Handler for go_observatory agent command within streaming state. Command
        driver to stop autosampling, and switch to observatory mode if
        successful.
        """
        
        result = self._dvr_client.cmd_dvr('stop_autosample', *args, **kwargs)
        
        if isinstance(result, dict):
            if all([val == None for val in result.values()]):
                next_state = InstrumentAgentState.OBSERVATORY
                               
    def _start_driver(self, dvr_config):
        """
        Start the driver process and driver client.
        @param dvr_config The driver configuration.
        @param comms_config The driver communications configuration.
        @retval None or error.
        """
        try:        
            cmd_port = dvr_config['cmd_port']
            evt_port = dvr_config['evt_port']
            dvr_mod = dvr_config['dvr_mod']
            dvr_cls = dvr_config['dvr_cls']
            svr_addr = dvr_config['svr_addr']
            
        except (TypeError, KeyError):
            # Not a dict. or missing required parameter.
            log.error('CiLogger._start_driver(): missing required parameter in start_driver.')            
            return InstErrorCode.REQUIRED_PARAMETER
                
        # Launch driver process.
        self._dvr_proc = ZmqDriverProcess.launch_process(cmd_port, evt_port,
                                                         dvr_mod,  dvr_cls)

        self._dvr_proc.poll()
        if self._dvr_proc.returncode:
            # Error proc didn't start.
            log.error('CiLogger._start_driver(): driver process did not launch.')
            return InstErrorCode.AGENT_INIT_FAILED

        log.info('CiLogger._start_driver(): launched driver process.')
        
        # Create client and start messaging.
        self._dvr_client = ZmqDriverClient(svr_addr, cmd_port, evt_port)
        self._dvr_client.start_messaging(self.evt_recv)
        log.info('CiLogger._start_driver(): driver process client started.')
        time.sleep(1)

        try:        
            retval = self._dvr_client.cmd_dvr('process_echo', 'Test.')
            log.info('CiLogger._start_driver(): driver process echo test: %s.' %str(retval))
            
        except Exception:
            self._dvr_proc.terminate()
            self._dvr_proc.wait()
            self._dvr_proc = None
            self._dvr_client = None
            log.error('CiLogger._start_driver(): error commanding driver process.')            
            return InstErrorCode.AGENT_INIT_FAILED

        else:
            log.info('CiLogger._start_driver(): started its driver.')

        return self._dvr_proc.pid
        
    def _stop_driver(self):
        """
        Stop the driver process and driver client.
        @retval None.
        """
        if self._dvr_client:
            self._dvr_client.done()
            self._dvr_proc.wait()
            self._dvr_proc = None
            self._dvr_client = None
            log.info('CiLogger._stop_driver(): stopped its driver.')            
        time.sleep(1)
class DriverIntegrationTestSupport(object):
    """
    Common functionality helpful for driver integration testing.
    """

    def __init__(self, driver_module, driver_class,
                 device_addr, device_port, delim=None,
                 work_dir='/tmp/'):

        """
        @param driver_module
        @param driver_class
        @param device_addr
        @param device_port
        @param delim 2-element delimiter to indicate traffic from the driver
               in the logfile. See EthernetDeviceLogger.launch_process for
               default value.
        @param work_dir by default, '/tmp/'
        """

        object.__init__(self)

        self.driver_module = driver_module
        self.driver_class = driver_class

        self.device_addr = device_addr
        self.device_port = device_port
        self.delim = delim
        self.work_dir = work_dir

        # Should clear this in setup
        self._events = []
        self._dvr_client = None

    def start_pagent(self):
        """
        Construct and start the port agent.
        @retval port Port that was used for connection to agent
        """
        # Create port agent object.
        this_pid = os.getpid()
        self._pagent = EthernetDeviceLogger.launch_process(self.device_addr,
                                                           self.device_port,
                                                           self.work_dir,
                                                           self.delim,
                                                           this_pid)

        pid = self._pagent.get_pid()
        while not pid:
            gevent.sleep(.1)
            pid = self._pagent.get_pid()
        port = self._pagent.get_port()
        while not port:
            gevent.sleep(.1)
            port = self._pagent.get_port()

        mi_logger.info('Started port agent pid %d listening at port %d', pid, port)
        return port

    def stop_pagent(self):
        """
        Stop the port agent.
        """
        if self._pagent:
            pid = self._pagent.get_pid()
            if pid:
                mi_logger.info('Stopping pagent pid %i', pid)
                self._pagent.stop()
            else:
                mi_logger.info('No port agent running.')

    def start_driver(self):
        """
        Start the driver process.
        """

        # Launch driver process based on test config.
        this_pid = os.getpid()
        (dvr_proc, cmd_port, evt_port) = ZmqDriverProcess.launch_process(self.driver_module,
                                                                         self.driver_class,
                                                                         self.work_dir,
                                                                         this_pid)
        self._dvr_proc = dvr_proc
        mi_logger.info('Started driver process for %d %d %s %s', cmd_port,
            evt_port, self.driver_module, self.driver_class)
        mi_logger.info('Driver process pid %d', self._dvr_proc.pid)

        # Create driver client.
        self._dvr_client = ZmqDriverClient('localhost', cmd_port,
            evt_port)
        mi_logger.info('Created driver client for %d %d %s %s', cmd_port,
            evt_port, self.driver_module, self.driver_class)

        # Start client messaging.
        self._dvr_client.start_messaging(self.evt_recd)
        mi_logger.info('Driver messaging started.')
        gevent.sleep(.5)

    def stop_driver(self):
        """
        Method to shut down the driver process. Attempt normal shutdown,
        and kill the process if unsuccessful.
        """

        if self._dvr_proc:
            mi_logger.info('Stopping driver process pid %d', self._dvr_proc.pid)
            if self._dvr_client:
                self._dvr_client.done()
                self._dvr_proc.wait()
                self._dvr_client = None

            else:
                try:
                    mi_logger.info('Killing driver process.')
                    self._dvr_proc.kill()
                except OSError:
                    pass
            self._dvr_proc = None

    def evt_recd(self, evt):
        """
        Simple callback to catch events from the driver for verification.
        """
        self._events.append(evt)
class DriverIntegrationTestSupport(object):
    """
    Common functionality helpful for driver integration testing.
    """
    def __init__(self,
                 driver_module,
                 driver_class,
                 device_addr,
                 device_port,
                 delim=None,
                 work_dir='/tmp/'):
        """
        @param driver_module
        @param driver_class
        @param device_addr
        @param device_port
        @param delim 2-element delimiter to indicate traffic from the driver
               in the logfile. See EthernetDeviceLogger.launch_process for
               default value.
        @param work_dir by default, '/tmp/'
        """

        object.__init__(self)

        self.driver_module = driver_module
        self.driver_class = driver_class

        self.device_addr = device_addr
        self.device_port = device_port
        self.delim = delim
        self.work_dir = work_dir

        # Should clear this in setup
        self._events = []
        self._dvr_client = None

    def start_pagent(self):
        """
        Construct and start the port agent.
        @retval port Port that was used for connection to agent
        """
        # Create port agent object.
        this_pid = os.getpid()
        self._pagent = EthernetDeviceLogger.launch_process(
            self.device_addr, self.device_port, self.work_dir, self.delim,
            this_pid)

        pid = self._pagent.get_pid()
        while not pid:
            gevent.sleep(.1)
            pid = self._pagent.get_pid()
        port = self._pagent.get_port()
        while not port:
            gevent.sleep(.1)
            port = self._pagent.get_port()

        mi_logger.info('Started port agent pid %d listening at port %d', pid,
                       port)
        return port

    def stop_pagent(self):
        """
        Stop the port agent.
        """
        if self._pagent:
            pid = self._pagent.get_pid()
            if pid:
                mi_logger.info('Stopping pagent pid %i', pid)
                self._pagent.stop()
            else:
                mi_logger.info('No port agent running.')

    def start_driver(self):
        """
        Start the driver process.
        """

        # Launch driver process based on test config.
        this_pid = os.getpid()
        (dvr_proc, cmd_port,
         evt_port) = ZmqDriverProcess.launch_process(self.driver_module,
                                                     self.driver_class,
                                                     self.work_dir, this_pid)
        self._dvr_proc = dvr_proc
        mi_logger.info('Started driver process for %d %d %s %s', cmd_port,
                       evt_port, self.driver_module, self.driver_class)
        mi_logger.info('Driver process pid %d', self._dvr_proc.pid)

        # Create driver client.
        self._dvr_client = ZmqDriverClient('localhost', cmd_port, evt_port)
        mi_logger.info('Created driver client for %d %d %s %s', cmd_port,
                       evt_port, self.driver_module, self.driver_class)

        # Start client messaging.
        self._dvr_client.start_messaging(self.evt_recd)
        mi_logger.info('Driver messaging started.')
        gevent.sleep(.5)

    def stop_driver(self):
        """
        Method to shut down the driver process. Attempt normal shutdown,
        and kill the process if unsuccessful.
        """

        if self._dvr_proc:
            mi_logger.info('Stopping driver process pid %d',
                           self._dvr_proc.pid)
            if self._dvr_client:
                self._dvr_client.done()
                self._dvr_proc.wait()
                self._dvr_client = None

            else:
                try:
                    mi_logger.info('Killing driver process.')
                    self._dvr_proc.kill()
                except OSError:
                    pass
            self._dvr_proc = None

    def evt_recd(self, evt):
        """
        Simple callback to catch events from the driver for verification.
        """
        self._events.append(evt)