예제 #1
0
    def test_pub_on_different_subtypes(self):
        ar = event.AsyncResult()
        gq = queue.Queue()
        self.count = 0

        def cb(event, *args, **kwargs):
            self.count += 1
            gq.put(event)
            if event.description == "end":
                ar.set()

        sub = EventSubscriber(event_type="ResourceModifiedEvent",
                              sub_type="st1",
                              callback=cb)
        sub.start()

        pub1 = EventPublisher(event_type="ResourceModifiedEvent")
        pub2 = EventPublisher(event_type="ContainerLifecycleEvent")

        pub1.publish_event(origin="two", sub_type="st2", description="2")
        pub2.publish_event(origin="three", sub_type="st1", description="3")
        pub1.publish_event(origin="one", sub_type="st1", description="1")
        pub1.publish_event(origin="four", sub_type="st1", description="end")

        ar.get(timeout=5)
        sub.stop()

        res = []
        for x in xrange(self.count):
            res.append(gq.get(timeout=5))

        self.assertEquals(len(res), 2)
        self.assertEquals(res[0].description, "1")
class NotificationSubscription(object):
    """
    Ties a notification's info to it's event subscriber
    """

    def  __init__(self, notification_request=None, callback=None):
        self._res_obj = notification_request  # The Notification Request Resource Object
        self.subscriber = EventSubscriber(  origin=notification_request.origin,
            origin_type = notification_request.origin_type,
            event_type=notification_request.event_type,
            sub_type=notification_request.event_subtype,
            callback=callback)
        self.notification_subscription_id = None

    def set_notification_id(self, id_=None):
        """
        Set the notification id of the notification object
        @param notification id
        """
        self.notification_subscription_id = id_

    def activate(self):
        """
        Start subscribing
        """
        self.subscriber.start()

    def deactivate(self):
        """
        Stop subscribing
        """
        self.subscriber.stop()
예제 #3
0
    def test_instrument_device_metadata_notification_l4_ci_sa_rq_145_323(self):
        """
        Instrument management shall update physical resource metadata when change occurs

        For example, when there is a change of state.

        note from maurice 2012-05-18: consider this to mean a change of stored RR data
        """

        inst_obj = any_old(RT.InstrumentDevice)
        instrument_device_id, _ = self.RR.create(inst_obj)
        self.received_event = AsyncResult()

        #Create subscribers for agent and driver events.

        def consume_event(*args, **kwargs):
            self.received_event.set(True)
            log.info("L4-CI-SA-RQ-323")
            log.info("L4-CI-SA-RQ-145")

        event_sub = EventSubscriber(event_type="ResourceModifiedEvent",
                                    callback=consume_event)
        event_sub.start()

        inst_obj = self.RR.read(instrument_device_id)
        inst_obj.description = "brand new description"
        self.RR.update(inst_obj)

        #wait for event
        result = self.received_event.get(timeout=10)
        event_sub.stop()

        self.assertTrue(result)
예제 #4
0
파일: test_event.py 프로젝트: daf/pyon
    def test_pub_on_different_subtypes(self):
        ar = event.AsyncResult()
        gq = queue.Queue()
        self.count = 0

        def cb(event, *args, **kwargs):
            self.count += 1
            gq.put(event)
            if event.description == "end":
                ar.set()

        sub = EventSubscriber(event_type="ResourceModifiedEvent", sub_type="st1", callback=cb)
        sub.start()

        pub1 = EventPublisher(event_type="ResourceModifiedEvent")
        pub2 = EventPublisher(event_type="ContainerLifecycleEvent")

        pub1.publish_event(origin="two", sub_type="st2", description="2")
        pub2.publish_event(origin="three", sub_type="st1", description="3")
        pub1.publish_event(origin="one", sub_type="st1", description="1")
        pub1.publish_event(origin="four", sub_type="st1", description="end")

        ar.get(timeout=5)
        sub.stop()

        res = []
        for x in xrange(self.count):
            res.append(gq.get(timeout=5))

        self.assertEquals(len(res), 2)
        self.assertEquals(res[0].description, "1")
예제 #5
0
    def test_ingestion_failover(self):
        stream_id, route, stream_def_id, dataset_id = self.make_simple_dataset(
        )
        self.start_ingestion(stream_id, dataset_id)

        event = Event()

        def cb(*args, **kwargs):
            event.set()

        sub = EventSubscriber(event_type="ExceptionEvent",
                              callback=cb,
                              origin="stream_exception")
        sub.start()

        self.publish_fake_data(stream_id, route)
        self.wait_until_we_have_enough_granules(dataset_id, 40)

        file_path = DatasetManagementService._get_coverage_path(dataset_id)
        master_file = os.path.join(file_path, '%s_master.hdf5' % dataset_id)

        with open(master_file, 'w') as f:
            f.write('this will crash HDF')

        self.publish_hifi(stream_id, route, 5)

        self.assertTrue(event.wait(10))

        sub.stop()
    def test_ingestion_failover(self):
        stream_id, route, stream_def_id, dataset_id = self.make_simple_dataset()
        self.start_ingestion(stream_id, dataset_id)
        
        event = Event()

        def cb(*args, **kwargs):
            event.set()

        sub = EventSubscriber(event_type="ExceptionEvent", callback=cb, origin="stream_exception")
        sub.start()

        self.publish_fake_data(stream_id, route)
        self.wait_until_we_have_enough_granules(dataset_id, 40)
        
        file_path = DatasetManagementService._get_coverage_path(dataset_id)
        master_file = os.path.join(file_path, '%s_master.hdf5' % dataset_id)

        with open(master_file, 'w') as f:
            f.write('this will crash HDF')

        self.publish_hifi(stream_id, route, 5)


        self.assertTrue(event.wait(10))

        sub.stop()
예제 #7
0
    def test_instrument_device_metadata_notification_l4_ci_sa_rq_145_323(self):
        """
        Instrument management shall update physical resource metadata when change occurs

        For example, when there is a change of state.

        note from maurice 2012-05-18: consider this to mean a change of stored RR data
        """

        inst_obj = any_old(RT.InstrumentDevice)
        instrument_device_id, _ = self.RR.create(inst_obj)
        self.received_event = AsyncResult()

        #Create subscribers for agent and driver events.

        def consume_event(*args, **kwargs):
            self.received_event.set(True)
            log.info("L4-CI-SA-RQ-323")
            log.info("L4-CI-SA-RQ-145")

        event_sub = EventSubscriber(event_type="ResourceModifiedEvent", callback=consume_event)
        event_sub.start()


        inst_obj = self.RR.read(instrument_device_id)
        inst_obj.description = "brand new description"
        self.RR.update(inst_obj)

        #wait for event
        result = self.received_event.get(timeout=10)
        event_sub.stop()

        self.assertTrue(result)
예제 #8
0
class EventPersister(StandaloneProcess):

    def on_init(self):
        # Time in between event persists
        self.persist_interval = 1.0

        # Holds received events FIFO
        self.event_queue = Queue()

        # Temporarily holds list of events to persist while datastore operation not yet completed
        self.events_to_persist = None

        # bookkeeping for timeout greenlet
        self._persist_greenlet = None
        self._terminate_persist = Event() # when set, exits the timeout greenlet

        # The event subscriber
        self.event_sub = None

    def on_start(self):
        # Persister thread
        self._persist_greenlet = spawn(self._trigger_func, self.persist_interval)
        log.debug('Publisher Greenlet started in "%s"' % self.__class__.__name__)

        # Event subscription
        self.event_sub = EventSubscriber(pattern=EventSubscriber.ALL_EVENTS, callback=self._on_event)
        self.event_sub.start()

    def on_quit(self):
        # Stop event subscriber
        self.event_sub.stop()

        # tell the trigger greenlet we're done
        self._terminate_persist.set()

        # wait on the greenlet to finish cleanly
        self._persist_greenlet.join(timeout=10)

    def _on_event(self, event, *args, **kwargs):
        self.event_queue.put(event)

    def _trigger_func(self, persist_interval):
        log.debug('Starting event persister thread with persist_interval=%s', persist_interval)

        # Event.wait returns False on timeout (and True when set in on_quit), so we use this to both exit cleanly and do our timeout in a loop
        while not self._terminate_persist.wait(timeout=persist_interval):
            try:
                self.events_to_persist = [self.event_queue.get() for x in xrange(self.event_queue.qsize())]

                self._persist_events(self.events_to_persist)
                self.events_to_persist = None
            except Exception as ex:
                log.exception("Failed to persist received events")
                return False

    def _persist_events(self, event_list):
        if event_list:
            bootstrap.container_instance.event_repository.put_events(event_list)
class DatasetMonitor(object):
    def __init__(self, dataset_id):
        self.dataset_id = dataset_id
        self.event = Event()

        self.es = EventSubscriber(event_type=OT.DatasetModiied, callback=self.cb, origin=self.dataset_id, auto_delete=True)
        self.es.start()
    
    def cb(self, *args, **kwargs):
        self.event.set()

    def stop(self):
        self.es.stop()
예제 #10
0
class TransformEventListener(TransformEventProcess):

    def on_start(self):
        event_type = self.CFG.get_safe('process.event_type', '')

        self.listener = EventSubscriber(event_type=event_type, callback=self.process_event)
        self.listener.start()

    def process_event(self, msg, headers):
        raise NotImplementedError('Method process_event not implemented')

    def on_quit(self):
        self.listener.stop()
class InstrumentAgentEventSubscribers(object):
    """
    Create subscribers for agent and driver events.
    """

    log.info("Start event subscribers")

    def __init__(self, instrument_agent_resource_id=None):
        # Start event subscribers, add stop to cleanup.
        self.no_events = None
        self.events_received = []
        self.async_event_result = AsyncResult()
        self.event_subscribers = []

        def consume_event(*args, **kwargs):
            log.debug(
                "#**#**# Event subscriber (consume_event) recieved ION event: args=%s, kwargs=%s, event=%s.",
                str(args),
                str(kwargs),
                str(args[0]),
            )

            log.debug("self.no_events = " + str(self.no_events))
            log.debug("self.event_received = " + str(self.events_received))

            self.events_received.append(args[0])
            if self.no_events and self.no_events == len(self.events_received):
                log.debug("CALLING self.async_event_result.set()")
                self.async_event_result.set()

        self.event_subscribers = EventSubscriber(
            event_type="ResourceAgentEvent", callback=consume_event, origin=instrument_agent_resource_id
        )
        self.event_subscribers.start()
        self.event_subscribers._ready_event.wait(timeout=5)

    def clear_events(self):
        """
        Reset event counter
        """
        self._events_received = []

    def stop(self):
        try:
            self.event_subscribers.stop()
        except Exception as ex:
            log.warn("Failed to stop event subscriber gracefully (%s)" % ex)

        self.event_subscribers = []
예제 #12
0
class DatasetMonitor(object):
    def __init__(self, dataset_id):
        self.dataset_id = dataset_id
        self.event = Event()

        self.es = EventSubscriber(event_type=OT.DatasetModified,
                                  callback=self.cb,
                                  origin=self.dataset_id,
                                  auto_delete=True)
        self.es.start()

    def cb(self, *args, **kwargs):
        self.event.set()

    def stop(self):
        self.es.stop()
class InstrumentAgentEventSubscribers(object):
    """
    Create subscribers for agent and driver events.
    """
    log.info("Start event subscribers")

    def __init__(self, instrument_agent_resource_id=None):
        # Start event subscribers, add stop to cleanup.
        self.no_events = None
        self.events_received = []
        self.async_event_result = AsyncResult()
        self.event_subscribers = []

        def consume_event(*args, **kwargs):
            log.debug(
                '#**#**# Event subscriber (consume_event) recieved ION event: args=%s, kwargs=%s, event=%s.',
                str(args), str(kwargs), str(args[0]))

            log.debug("self.no_events = " + str(self.no_events))
            log.debug("self.event_received = " + str(self.events_received))

            self.events_received.append(args[0])
            if self.no_events and self.no_events == len(self.events_received):
                log.debug("CALLING self.async_event_result.set()")
                self.async_event_result.set()

        self.event_subscribers = EventSubscriber(
            event_type='ResourceAgentEvent',
            callback=consume_event,
            origin=instrument_agent_resource_id)
        self.event_subscribers.start()
        self.event_subscribers._ready_event.wait(timeout=5)

    def clear_events(self):
        """
        Reset event counter
        """

        self.events_received = []

    def stop(self):
        try:
            self.event_subscribers.stop()
        except Exception as ex:
            log.warn("Failed to stop event subscriber gracefully (%s)" % ex)

        self.event_subscribers = []
예제 #14
0
class DatasetMonitor(object):
    def __init__(self, dataset_id):
        self.dataset_id = dataset_id
        self.event = Event()

        self.es = EventSubscriber(event_type=OT.DatasetModified, callback=self.cb, origin=self.dataset_id, auto_delete=True)
        self.es.start()
    
    def cb(self, *args, **kwargs):
        self.event.set()

    def stop(self):
        self.es.stop()

    def wait(self, timeout=None):
        if timeout is None:
            timeout = CFG.get_safe('endpoint.receive.timeout', 10)
        return self.event.wait(timeout)
예제 #15
0
class TransformEventListener(TransformEventProcess):

    def __init__(self):
        super(TransformEventListener,self).__init__()

    def on_start(self):
        super(TransformEventListener,self).on_start()
        event_type = self.CFG.get_safe('process.event_type', '')
        queue_name = self.CFG.get_safe('process.queue_name', None)

        self.listener = EventSubscriber(event_type=event_type, queue_name=queue_name, callback=self.process_event)
        self.listener.start()

    def process_event(self, msg, headers):
        raise NotImplementedError('Method process_event not implemented')

    def on_quit(self):
        self.listener.stop()
        super(TransformEventListener,self).on_quit()
예제 #16
0
class TransformEventListener(TransformEventProcess):
    def __init__(self):
        super(TransformEventListener, self).__init__()

    def on_start(self):
        super(TransformEventListener, self).on_start()
        event_type = self.CFG.get_safe('process.event_type', '')
        queue_name = self.CFG.get_safe('process.queue_name', None)

        self.listener = EventSubscriber(event_type=event_type,
                                        queue_name=queue_name,
                                        callback=self.process_event)
        self.listener.start()

    def process_event(self, msg, headers):
        raise NotImplementedError('Method process_event not implemented')

    def on_quit(self):
        self.listener.stop()
        super(TransformEventListener, self).on_quit()
class DatasetMonitor(object):
    def __init__(self, dataset_id=None, data_product_id=None):
        if data_product_id and not dataset_id:
            dataset_id = Container.instance.resource_registry.find_objects(data_product_id, PRED.hasDataset, id_only=True)[0][0]
        self.dataset_id = dataset_id
        self.event = Event()

        self.es = EventSubscriber(event_type=OT.DatasetModified, callback=self.cb, origin=self.dataset_id, auto_delete=True)
        self.es.start()
    
    def cb(self, *args, **kwargs):
        self.event.set()

    def stop(self):
        self.es.stop()

    def wait(self, timeout=None):
        if timeout is None:
            timeout = CFG.get_safe('endpoint.receive.timeout', 10)
        return self.event.wait(timeout)
예제 #18
0
class DatasetMonitor(object):
    def __init__(self, dataset_id=None, data_product_id=None):
        if data_product_id and not dataset_id:
            dataset_id = Container.instance.resource_registry.find_objects(data_product_id, PRED.hasDataset, id_only=True)[0][0]
        self.dataset_id = dataset_id
        self.event = Event()

        self.es = EventSubscriber(event_type=OT.DatasetModified, callback=self.cb, origin=self.dataset_id, auto_delete=True)
        self.es.start()
    
    def cb(self, *args, **kwargs):
        self.event.set()

    def stop(self):
        self.es.stop()

    def wait(self, timeout=None):
        if timeout is None:
            timeout = CFG.get_safe('endpoint.receive.timeout', 10)
        return self.event.wait(timeout)

    def reset(self):
        self.event.clear()
예제 #19
0
    def test_cei_launch_mode(self):
        
        pdc = ProcessDispatcherServiceClient(node=self.container.node)
        p_def = ProcessDefinition(name='Agent007')
        p_def.executable = {
            'module' : 'ion.agents.instrument.instrument_agent',
            'class' : 'InstrumentAgent'
        }
        p_def_id = pdc.create_process_definition(p_def)
        
        pid = pdc.create_process(p_def_id)
        
        def event_callback(event, *args, **kwargs):
            print '######### proc %s in state %s' % (event.origin, ProcessStateEnum._str_map[event.state])
 
        sub = EventSubscriber(event_type='ProcessLifecycleEvent',
                              callback=event_callback,
                              origin=pid,
                              origin_type='DispatchedProcess')
         
        sub.start()

        agent_config = deepcopy(self._agent_config)
        agent_config['bootmode'] = 'restart'
        pdc.schedule_process(p_def_id, process_id=pid,
                             configuration=agent_config)
        
        gevent.sleep(5)
        
        pdc.cancel_process(pid)
        
        gevent.sleep(15)

        sub.stop()
        
        
예제 #20
0
    def test_cei_launch_mode(self):
        
        pdc = ProcessDispatcherServiceClient(node=self.container.node)
        p_def = ProcessDefinition(name='Agent007')
        p_def.executable = {
            'module' : 'ion.agents.instrument.instrument_agent',
            'class' : 'InstrumentAgent'
        }
        p_def_id = pdc.create_process_definition(p_def)
        
        pid = pdc.create_process(p_def_id)
        
        def event_callback(event, *args, **kwargs):
            print '######### proc %s in state %s' % (event.origin, ProcessStateEnum._str_map[event.state])
 
        sub = EventSubscriber(event_type='ProcessLifecycleEvent',
                              callback=event_callback,
                              origin=pid,
                              origin_type='DispatchedProcess')
         
        sub.start()

        agent_config = deepcopy(self._agent_config)
        agent_config['bootmode'] = 'restart'
        pdc.schedule_process(p_def_id, process_id=pid,
                             configuration=agent_config)
        
        gevent.sleep(5)
        
        pdc.cancel_process(pid)
        
        gevent.sleep(15)

        sub.stop()
        
        
예제 #21
0
    def forward(self, *args, **kwargs):
        """
        Forward a service method to the terrestrial endpoint
        through the service interface.
        """
        func_name = kwargs.pop('func_name')
        try:
            link = kwargs.pop('link')
        except KeyError:
            link = True
        cid = ''
        try:
            remote_timeout = kwargs.pop('remote_timeout')
            if not isinstance(remote_timeout, int):
                remote_timeout = 0
            elif remote_timeout < 0:
                remote_timeout = 0
            elif remote_timeout == 0:
                pass
            else:
                cid = str(uuid.uuid4())

        except KeyError:
            remote_timeout = 0

        cmd = IonObject('RemoteCommand',
                        resource_id=self._resource_id,
                        svc_name=self._svc_name,
                        command=func_name,
                        command_id=cid,
                        args=args,
                        kwargs=kwargs)

        if remote_timeout == 0:
            return self._te_client.enqueue_command(cmd, link)

        else:

            if self._resource_id:
                origin = self._resource_id
            elif self._svc_name:
                origin = self._svc_name + self._xs_name

            pending_cmd = cmd
            async_result_evt = AsyncResult()

            def result_callback(evt, *args, **kwargs):
                """
                Callback for subscriber retrive blocking results.
                """
                #global async_result_evt
                if evt.type_ == 'RemoteCommandResult':
                    cmd = evt.command
                    if cmd.command_id == pending_cmd.command_id:
                        async_result_evt.set(cmd)

            sub = EventSubscriber(event_type='RemoteCommandResult',
                                  origin=origin,
                                  callback=result_callback)

            sub.start()
            #self._pending_cmd = cmd
            cmd = self._te_client.enqueue_command(cmd, link)
            try:
                result = async_result_evt.get(timeout=remote_timeout)
                #self._pending_cmd = None
                sub.stop()
            except gevent.Timeout:
                #self._pending_cmd = None
                sub.stop()
                raise Timeout('Timed out waiting for remote result.')

            return result
class HAProcessControl(object):

    def __init__(self, pd_name, resource_registry, service_id, callback=None, logprefix=""):
        self.pd_name = pd_name
        self.resource_registry = resource_registry
        self.service_id = service_id
        self.callback = callback
        if callback and not callable(callback):
            raise ValueError("callback is not callable")
        self.logprefix = logprefix

        self.client = ProcessDispatcherServiceClient(to_name=pd_name)
        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
            callback=self._event_callback, origin_type="DispatchedProcess",
            auto_delete=True)

        self.processes = {}

    def start(self):
        service = self.resource_registry.read(self.service_id)
        process_assocs = self.resource_registry.find_associations(service, "hasProcess")

        for process_assoc in process_assocs:
            process_id = process_assoc.o
            if process_id:
                try:
                    process = self.client.read_process(process_id)
                except NotFound:
                    log.debug("%sService was associated with process %s, which is unknown to PD. ignoring.",
                        self.logprefix, process_id)
                    continue

                state = process.process_state
                state_str = ProcessStateEnum._str_map.get(state, str(state))

                self.processes[process.process_id] = _process_dict_from_object(process)
                log.info("%srecovered process %s state=%s", self.logprefix, process_id, state_str)
        self.event_sub.start()

    def stop(self):
        self.event_sub.stop()

    def get_managed_upids(self):
        return self.processes.keys()

    def _event_callback(self, event, *args, **kwargs):
        if not event:
            return

        try:
            self._inner_event_callback(event)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            log.exception("%sException in event handler. This is a bug!", self.logprefix)

    def _inner_event_callback(self, event):
        process_id = event.origin
        state = event.state
        state_str = ProcessStateEnum._str_map.get(state, str(state))
        if not (process_id and process_id in self.processes):
            # we receive events for all processes but ignore most
            return

        process = None
        for _ in range(3):
            try:
                process = self.client.read_process(process_id)
                break
            except Timeout:
                log.warn("Timeout trying to read process from Process Dispatcher!", exc_info=True)
                pass  # retry
            except NotFound:
                break

        if process:
            log.info("%sreceived process %s state=%s", self.logprefix, process_id, state_str)

            # replace the cached data about this process
            self.processes[process_id] = _process_dict_from_object(process)

        else:
            log.warn("%sReceived process %s event but failed to read from Process Dispatcher",
                self.logprefix, process_id)

            #XXX better approach here? we at least have the state from the event,
            # so sticking that on cached process. We could miss other important
            # data like hostname however.
            self.processes[process_id]['state'] = process_state_to_pd_core(state)

        if self.callback:
            try:
                self.callback()
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                e = sys.exc_info()[0]
                log.warn("%sError in HAAgent callback: %s", self.logprefix, e, exc_info=True)

    def _associate_process(self, process):
        try:
            self.resource_registry.create_association(self.service_id,
                "hasProcess", process.process_id)
        except Exception:
            log.exception("Couldn't associate service %s to process %s" % (self.service_id, process.process_id))

    def schedule_process(self, pd_name, process_definition_id, **kwargs):

        if pd_name != self.pd_name:
            raise Exception("schedule_process request received for unknown PD: %s" % pd_name)

        # figure out if there is an existing PID which can be reused
        found_upid = None
        for process in self.processes.values():
            upid = process.get('upid')
            state = process.get('state')
            if not (upid and state):
                continue

            if state in CoreProcessState.TERMINAL_STATES:
                found_upid = upid

        if found_upid:
            upid = found_upid
            proc = self.client.read_process(upid)

        else:
            # otherwise create a new process and associate
            upid = self.client.create_process(process_definition_id)

            # note: if the HAAgent fails between the create call above and the
            # associate call below, there may be orphaned Process objects. These
            # processes will not however be running, so are largely harmless.
            proc = self.client.read_process(upid)
            self._associate_process(proc)

        process_schedule = _get_process_schedule(**kwargs)
        configuration = kwargs.get('configuration')

        # cheat and roll the process state to REQUESTED before we actually
        # schedule it. this is in-memory only, so should be harmless. This
        # avoids a race between this scheduling process and the event
        # subscriber.
        proc.process_state = ProcessStateEnum.REQUESTED
        self.processes[upid] = _process_dict_from_object(proc)

        self.client.schedule_process(process_definition_id, process_schedule,
            configuration=configuration, process_id=upid)
        return upid

    def terminate_process(self, pid):
        return self.client.cancel_process(pid)

    def get_all_processes(self):
        processes = deepcopy(self.processes.values())
        return {self.pd_name: processes}

    def reload_processes(self):
        for process_id, process_dict in self.processes.items():
            try:
                process = self.client.read_process(process_id)
            except Exception:
                log.warn("%sFailed to read process %s from PD. Will retry later.",
                    self.logprefix, process_id, exc_info=True)
                continue
            new_process_dict = _process_dict_from_object(process)
            if new_process_dict['state'] != process_dict['state']:
                log.warn("%sUpdating process %s record manually. we may have missed an event?",
                    self.logprefix, process_id)
                self.processes[process_id] = new_process_dict
예제 #23
0
class GovernanceController(object):
    """
    This is a singleton object which handles governance functionality in the container.
    """

    def __init__(self,container):
        log.debug('GovernanceController.__init__()')
        self.container = container
        self.enabled = False
        self.interceptor_by_name_dict = dict()
        self.interceptor_order = []
        self.policy_decision_point_manager = None
        self.governance_dispatcher = None

        # Holds a list per service operation of policy methods to check before the op in a process is allowed to be called
        self._service_op_preconditions = dict()

        self._is_container_org_boundary = False
        self._container_org_name = None
        self._container_org_id = None

    def start(self):

        log.debug("GovernanceController starting ...")

        self._CFG = CFG

        self.enabled = CFG.get_safe('interceptor.interceptors.governance.config.enabled', False)

        log.info("GovernanceInterceptor enabled: %s" % str(self.enabled))

        self.policy_event_subscriber = None

        #containers default to not Org Boundary and ION Root Org
        self._is_container_org_boundary = CFG.get_safe('container.org_boundary',False)
        self._container_org_name = CFG.get_safe('container.org_name', CFG.get_safe('system.root_org', 'ION'))
        self._container_org_id = None
        self._system_root_org_name = CFG.get_safe('system.root_org', 'ION')

        self._is_root_org_container = (self._container_org_name == self._system_root_org_name)

        if self.enabled:

            config = CFG.get_safe('interceptor.interceptors.governance.config')

            self.initialize_from_config(config)

            self.policy_event_subscriber = EventSubscriber(event_type=OT.PolicyEvent, callback=self.policy_event_callback)
            self.policy_event_subscriber.start()

            self.rr_client = ResourceRegistryServiceProcessClient(node=self.container.node, process=self.container)
            self.policy_client = PolicyManagementServiceProcessClient(node=self.container.node, process=self.container)

    def initialize_from_config(self, config):

        self.governance_dispatcher = GovernanceDispatcher()

        self.policy_decision_point_manager = PolicyDecisionPointManager(self)

        if 'interceptor_order' in config:
            self.interceptor_order = config['interceptor_order']

        if 'governance_interceptors' in config:
            gov_ints = config['governance_interceptors']

            for name in gov_ints:
                interceptor_def = gov_ints[name]

                # Instantiate and put in by_name array
                parts = interceptor_def["class"].split('.')
                modpath = ".".join(parts[:-1])
                classname = parts[-1]
                module = __import__(modpath, fromlist=[classname])
                classobj = getattr(module, classname)
                classinst = classobj()

                # Put in by_name_dict for possible re-use
                self.interceptor_by_name_dict[name] = classinst

    def stop(self):
        log.debug("GovernanceController stopping ...")

        if self.policy_event_subscriber is not None:
            self.policy_event_subscriber.stop()


    @property
    def is_container_org_boundary(self):
        return self._is_container_org_boundary

    @property
    def container_org_name(self):
        return self._container_org_name

    @property
    def system_root_org_name(self):
        return self._system_root_org_name

    @property
    def is_root_org_container(self):
        return self._is_root_org_container

    @property
    def CFG(self):
        return self._CFG


    @property
    def rr(self):
        """
        Returns the active resource registry instance or client.

        Used to directly contact the resource registry via the container if available,
        otherwise the messaging client to the RR service is returned.
        """
        if self.container.has_capability('RESOURCE_REGISTRY'):
            return self.container.resource_registry

        return self.rr_client


    def get_container_org_boundary_id(self):
        """
        Returns the permanent org identifier configured for this container
        @return:
        """

        if not self._is_container_org_boundary:
            return None

        if self._container_org_id is None:
            org, _ = self.rr.find_resources(restype=RT.Org,name=self._container_org_name)

            if org:
                self._container_org_id = org[0]._id

        return self._container_org_id

    def process_incoming_message(self,invocation):
        """
        The GovernanceController hook into the incoming message interceptor stack
        @param invocation:
        @return:
        """
        self.process_message(invocation, self.interceptor_order,'incoming' )
        return self.governance_dispatcher.handle_incoming_message(invocation)

    def process_outgoing_message(self,invocation):
        """
        The GovernanceController hook into the outgoing message interceptor stack
        @param invocation:
        @return:
        """
        self.process_message(invocation, reversed(self.interceptor_order),'outgoing')
        return self.governance_dispatcher.handle_outgoing_message(invocation)

    def process_message(self,invocation,interceptor_list, method):
        """
        The GovernanceController hook to iterate over the interceptors to call each one and evaluate the annotations
        to see what actions should be done.
        @TODO - may want to make this more dynamic instead of hard coded for the moment.
        @param invocation:
        @param interceptor_list:
        @param method:
        @return:
        """
        for int_name in interceptor_list:
            class_inst = self.interceptor_by_name_dict[int_name]
            getattr(class_inst, method)(invocation)

            #Stop processing message if an issue with the message was found by an interceptor.
            if ( invocation.message_annotations.has_key(GovernanceDispatcher.CONVERSATION__STATUS_ANNOTATION) and invocation.message_annotations[GovernanceDispatcher.CONVERSATION__STATUS_ANNOTATION] == GovernanceDispatcher.STATUS_REJECT) or\
               ( invocation.message_annotations.has_key(GovernanceDispatcher.POLICY__STATUS_ANNOTATION) and invocation.message_annotations[GovernanceDispatcher.POLICY__STATUS_ANNOTATION] == GovernanceDispatcher.STATUS_REJECT) :
                break

        return invocation



    #Manage all of the policies in the container

    def policy_event_callback(self, *args, **kwargs):
        """
        The generic policy event call back for dispatching policy related events

        @param args:
        @param kwargs:
        @return:
        """
        policy_event = args[0]
        if policy_event.type_ == OT.ResourcePolicyEvent:
            self.resource_policy_event_callback(*args, **kwargs)
        elif policy_event.type_ == OT.ServicePolicyEvent:
            self.service_policy_event_callback(*args, **kwargs)
        elif policy_event.type_ == OT.PolicyCacheResetEvent:
            self.policy_cache_reset_event_callback(*args, **kwargs)


    def resource_policy_event_callback(self, *args, **kwargs):
        """
        The ResourcePolicyEvent handler

        @param args:
        @param kwargs:
        @return:
        """
        resource_policy_event = args[0]
        log.debug('Resource related policy event received: %s', str(resource_policy_event.__dict__))

        policy_id = resource_policy_event.origin
        resource_id = resource_policy_event.resource_id
        resource_type = resource_policy_event.resource_type
        resource_name = resource_policy_event.resource_name
        delete_policy = True if resource_policy_event.sub_type == 'DeletePolicy' else False

        self.update_resource_access_policy(resource_id, delete_policy)

    def service_policy_event_callback(self, *args, **kwargs):
        """
        The ServicePolicyEvent handler

        @param args:
        @param kwargs:
        @return:
        """
        service_policy_event = args[0]
        log.debug('Service related policy event received: %s', str(service_policy_event.__dict__))

        policy_id = service_policy_event.origin
        service_name = service_policy_event.service_name
        service_op = service_policy_event.op
        delete_policy = True if service_policy_event.sub_type == 'DeletePolicy' else False

        if service_name:
            if self.container.proc_manager.is_local_service_process(service_name):
                self.update_service_access_policy(service_name, service_op, delete_policy)
            elif self.container.proc_manager.is_local_agent_process(service_name):
                self.update_service_access_policy(service_name, service_op, delete_policy)

        else:
            self.update_common_service_access_policy()



    def policy_cache_reset_event_callback(self, *args, **kwargs):
        """
        The PolicyCacheResetEvent handler

        @return:
        """
        policy_reset_event = args[0]
        log.info('Policy cache reset event received: %s', str(policy_reset_event.__dict__))

        #First remove all cached polices and precondition functions that are not hard-wired
        self.policy_decision_point_manager.clear_policy_cache()
        self.unregister_all_process_policy_preconditions()

        #Then load the common service access policies since they are shared across services
        self.update_common_service_access_policy()

        #Now iterate over the processes running in the container and reload their policies
        proc_list = self.container.proc_manager.list_local_processes()
        for proc in proc_list:
            self.update_container_policies(proc)



    def update_container_policies(self, process_instance, safe_mode=False):
        """
        This must be called after registering a new process to load any applicable policies

        @param process_instance:
        @return:
        """

        #This method can be called before policy management service is available during system startup
        if safe_mode and not self._is_policy_management_service_available():
            return

        if process_instance._proc_type == SERVICE_PROCESS_TYPE:

            # look to load any existing policies for this service

            self.update_service_access_policy(process_instance._proc_listen_name)

        elif process_instance._proc_type == AGENT_PROCESS_TYPE:

            # look to load any existing policies for this agent service
            if process_instance.resource_type is None:
                self.update_service_access_policy(process_instance.name)
            else:
                self.update_service_access_policy(process_instance.resource_type)

            if process_instance.resource_id:
                # look to load any existing policies for this resource
                self.update_resource_access_policy(process_instance.resource_id)


    def update_resource_access_policy(self, resource_id, delete_policy=False):

        if self.policy_decision_point_manager is not None:

            try:
                policy_rules = self.policy_client.get_active_resource_access_policy_rules(resource_id)
                self.policy_decision_point_manager.load_resource_policy_rules(resource_id, policy_rules)

            except Exception, e:
                #If the resource does not exist, just ignore it - but log a warning.
                log.warn("The resource %s is not found or there was an error applying access policy: %s" % ( resource_id, e.message))
예제 #24
0
class TestAlerts(IonIntegrationTestCase):
    """
    """
    
    ############################################################################
    # Setup, teardown.
    ############################################################################
        
    def setUp(self):
        """
        Set up subscribers for alarm events.
        """
        
        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        self._event_count = 0
        self._events_received = []
        self._async_event_result = AsyncResult()
        self._resource_id = 'abc123'
        self._origin_type = "InstrumentDevice"

        def consume_event(*args, **kwargs):
            log.debug('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()
            
            
        self._event_subscriber = EventSubscriber(
            event_type='DeviceStatusAlertEvent', callback=consume_event,
            origin=self._resource_id)
        
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

        def stop_subscriber():
            self._event_subscriber.stop()
            self._event_subscriber = None
            
        self.addCleanup(stop_subscriber)

    ###############################################################################
    # Tests.
    ###############################################################################

    def test_greater_than_interval(self):
        """
        """
        alert_def = {
            'name' : 'current_warning_interval',
            'description' : 'Current is above normal range.',
            'aggregate_type' : AggregateStatusType.AGGREGATE_DATA,
            'alert_type' : StreamAlertType.WARNING,
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,
            'stream_name' : 'fakestreamname',
            'value_id' : 'port_current',
            'lower_bound' : 10.5,
            'lower_rel_op' : '<',
            'upper_bound' : None,
            'upper_rel_op' : None,
            'alert_class' : 'IntervalAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()
        """
        {'status': None, 'alert_type': 1, 'lower_bound': 10.5, 'upper_rel_op': '<',
        'alert_class': 'IntervalAlert', 'message': 'Current is above normal range.',
        'stream_name': 'fakestreamname', 'name': 'current_warning_interval',
        'upper_bound': None, 'value': None, 'value_id': 'port_current',
        'lower_rel_op': None}
        """
        # This sequence will produce 5 alerts:
        # All clear on 30,
        # Warning on 5.5
        # All clear on 15.1
        # Warning on 3.3
        # All clear on 15.0
        self._event_count = 5
        test_vals = [30, 30.4, 5.5, 5.6, 15.1, 15.2,
                     15.3, 3.3, 3.4, 15.0, 15.5]

        for x in test_vals:
            alert.eval_alert(stream_name='fakestreamname',
                             value=x, value_id='port_current')

        self._async_event_result.get(timeout=30)        
        """
        {'origin': 'abc123', 'status': 1, '_id': '04ccd20d67574b2ea3df869f2b6d4123', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [30], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152082', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '050d2c66eb47435888ecab9d58399922', 'description': 'Current is above normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [5.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152089', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '3294c5f7e2be413c806604e93b69e973', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [15.1], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152095', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '99a98e19a1454740a8464dab8de4dc0e', 'description': 'Current is above normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [3.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152101', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '93a214ee727e424e8b6a7e024a4d89be', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [15.0], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152108', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        """

    def test_less_than_interval(self):
        """
        """
        alert_def = {
            'name' : 'current_warning_interval',
            'stream_name' : 'fakestreamname',
            'description' : 'Current is below normal range.',
            'alert_type' : StreamAlertType.WARNING,
            'value_id' : 'port_current',
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,
            'lower_bound' : None,
            'lower_rel_op' : None,
            'upper_bound' : 4.0,
            'upper_rel_op' : '<',
            'alert_class' : 'IntervalAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # Warning on the 5.5
        # All clear on 3.3,
        # Warning on 4.5
        # All clear on 3.3
        # Warning on 4.8
        self._event_count = 5
        test_vals = [5.5, 5.5, 5.4, 4.6, 4.5, 3.3, 3.3, 4.5, 4.5, 3.3, 3.3, 4.8]

        for x in test_vals:
            alert.eval_alert(stream_name='fakestreamname',
                             value=x, value_id='port_current')

        self._async_event_result.get(timeout=30)        
        """
        {'origin': 'abc123', 'status': 1, '_id': '43c83af591a84fa3adc3a77a5d97ed2b', 'description': 'Current is below normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [5.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238728', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '36c45d09286d4ff38f5003ff97bef6a1', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [3.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238735', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'd004b9c2d50f4b9899d6fbdd3c8c50d2', 'description': 'Current is below normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [4.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238741', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'cb0cbaf1be0b4aa387e1a5ba4f1adb2c', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [3.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238747', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'd4f6e4b4105e494083a0f8e34362f275', 'description': 'Current is below normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [4.8], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238754', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        """
        
    def test_two_sided_interval(self):
        """
        """
        alert_def = {
            'name' : 'current_warning_interval',
            'stream_name' : 'fakestreamname',
            'description' : 'Current is outside normal range.',
            'alert_type' : StreamAlertType.WARNING,
            'value_id' : 'port_current',
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,            
            'lower_bound' : 10,
            'lower_rel_op' : '<',
            'upper_bound' : 20,
            'upper_rel_op' : '<',
            'alert_class' : 'IntervalAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # Warning on the 5.5
        # All clear on 10.2,
        # Warning on 23.3
        # All clear on 17.5
        # Warning on 8.8
        self._event_count = 5
        test_vals = [5.5, 5.5, 5.4, 4.6, 4.5, 10.2, 10.3, 10.5, 15.5,
                     23.3, 23.3, 24.8, 17.5, 16.5, 12.5, 8.8, 7.7]

        for x in test_vals:
            event_data = alert.eval_alert(stream_name='fakestreamname',
                                          value=x, value_id='port_current')

        self._async_event_result.get(timeout=30)        
        """
        {'origin': 'abc123', 'status': 1, '_id': '45296cc01f3d42c59e1aeded4fafb33d', 'description': 'Current is outside normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [5.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411921', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'd1a87a248f6640ceafdf8d09b66f4c6f', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [10.2], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411927', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'e9945ff47b79436096f67ba9d373a889', 'description': 'Current is outside normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [23.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411934', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '649f5297997740dc83b39d23b6fbc5b9', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [17.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411940', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'c766e52fda05497f9e4a18024e4eb0d6', 'description': 'Current is outside normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [8.8], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411947', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        """
        
    def test_late_data(self):
        """
        """

        def get_state():
            return ResourceAgentState.STREAMING

        alert_def = {
            'name' : 'late_data_warning',
            'stream_name' : 'fakestreamname',
            'description' : 'Expected data has not arrived.',
            'alert_type' : StreamAlertType.WARNING,
            'value_id' : None,
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,            
            'time_delta' : 3,
            'get_state' : get_state,
            'alert_class' : 'LateDataAlert'
        }
                
        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()        
        
        # This sequence will produce 6 events:
        # All clear on the first check data.
        # Warning during the first 10s delay.
        # All clear during the 1s samples following the first 10s delay.
        # Warning during the 2nd 10s delay.
        # All clear during the 1s samples following the second 10s delay.
        # Warning during the final 10s delay.
        self._event_count = 6
        #sleep_vals = [0.5, 0.7, 1.0, 1.1, 2.5, 2.3, 0.75, 0.5, 2.25, 0.5, 0.5, 2.5, 2.5]
        sleep_vals = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1,
                      1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                      1, 1, 1, 1, 1, 1, 1, 10]
        for x in sleep_vals:
            event_data = alert.eval_alert(stream_name='fakestreamname')
            gevent.sleep(x)

        self._async_event_result.get(timeout=30)
        """
        {'origin': 'abc123', 'status': 1, '_id': '455f5916fbf845acb0f71da229fa46a4', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659773.60301], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659773603', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '01b74790959d40c99da09fd1e52b447f', 'description': 'Expected data has not arrived.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659785.624983], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659791612', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'db73d083bddc4ebbb4dd4e6541d70bf3', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659795.625946], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659795626', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'ddd501a1599f47a49886ff89cda2f980', 'description': 'Expected data has not arrived.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659806.649514], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659812631', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '0c1dda72a53c47908c2755dfdcf87d15', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659816.650299], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659816651', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '6b0f2fb6eedd4cc39ab3e6dc6c2c2d69', 'description': 'Expected data has not arrived.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659832.673774], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659836651', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        """        
        alert.stop()

    @unittest.skip('Waiting to be finished and verified.')
    def test_rsn_event_alert(self):
        """
        """
        alert_def = {
            'name' : 'input_voltage',
            'description' : 'input_voltage is not in range range.',
            'alert_type' : StreamAlertType.WARNING,
            'value_id' : 'input_voltage',
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,
            'alert_class' : 'RSNEventAlert',
            'aggregate_type' : AggregateStatusType.AGGREGATE_POWER

        }


        # Example from CI-OMS interface spec
        # need: tag for the value, current value and warning/error
#        {
#          "group": "power",
#          "url": "http://localhost:8000",
#          "timestamp": 3573569514.295556,
#          "ref_id": "44.78",
#          "platform_id": "TODO_some_platform_id_of_type_UPS",
#          "message": "low battery (synthetic event generated from simulator)"
#        }


        #proposed structure:
#        {
#        "group": "power",
#        "name" : "low_voltage_warning",
#        "value_id" : "input_voltage",
#        "value" : "1.2",
#        "alert_type" : "warning",
#        "url": "http://localhost:8000",
#        "timestamp": 3573569514.295556,
#        "ref_id": "44.78",
#        "platform_id": "TODO_some_platform_id_of_type_UPS",
#        "message": "low battery (synthetic event generated from simulator)"
#        }



        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        test_val = \
        {
        "group": "power",
        "name" : "low_voltage_warning",
        "value_id" : "input_voltage",
        "value" : "1.2",
        "alert_type" : "warning",
        "url": "http://localhost:8000",
        "ref_id": "44.78",
        "platform_id": "e88f8b325b274dafabcc7d7d1e85bc5d",
        "description": "low battery (synthetic event generated from simulator)"
        }

        alert.eval_alert(rsn_alert=test_val)

        status = alert.get_status()
        log.debug('test_rsn_event_alert status: %s', alert)

        #self._async_event_result.get(timeout=30)

    def test_state_alert(self):
        """
        """
        alert_def = {
            'name' : 'comms_warning',
            'description' : 'Detected comms failure.',
            'alert_type' : StreamAlertType.WARNING,
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,
            'alert_states' : [
                ResourceAgentState.LOST_CONNECTION,
                ResourceAgentState.ACTIVE_UNKNOWN
                              ],
            'clear_states' : [
                ResourceAgentState.IDLE,
                ResourceAgentState.COMMAND,
                ResourceAgentState.STREAMING
                ],
            'alert_class' : 'StateAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # All clear on uninitialized (prev value None)
        # Warning on lost connection,
        # All clear on streaming,
        # Warning on lost connection,
        # All clear on idle.
        self._event_count = 5
        test_vals = [
            ResourceAgentState.UNINITIALIZED,
            ResourceAgentState.INACTIVE,
            ResourceAgentState.IDLE,
            ResourceAgentState.COMMAND,
            ResourceAgentState.STREAMING,
            ResourceAgentState.LOST_CONNECTION,
            ResourceAgentState.STREAMING,
            ResourceAgentState.LOST_CONNECTION,
            ResourceAgentState.UNINITIALIZED,
            ResourceAgentState.INACTIVE,
            ResourceAgentState.IDLE,
            ResourceAgentState.COMMAND,
            ResourceAgentState.STREAMING
            ]


        for x in test_vals:
            alert.eval_alert(state=x)

        self._async_event_result.get(timeout=30)        
        """
        {'origin': 'abc123', 'status': 1, '_id': '36e733662e674388ba2cfb8165315b86', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_UNINITIALIZED'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466203', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'ee9578bd37ed45c088479131f4b71509', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_LOST_CONNECTION'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466210', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '237db9d3bb8e455a98e58429df6c08ea', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_STREAMING'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466216', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'bef0987444254adb9db8fa752b2e1c81', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_LOST_CONNECTION'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466222', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '4d78cede6e01419a881eac288cf1dbd3', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_IDLE'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466229', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        """
        
    def test_command_error_alert(self):
        """
        """
        alert_def = {
            'name' : 'comms_warning',
            'description' : 'Detected comms failure.',
            'alert_type' : StreamAlertType.WARNING,
            'resource_id' : self._resource_id,
            'origin_type' : self._origin_type,
            'command' : ResourceAgentEvent.GO_ACTIVE,
            'clear_states' : [
                ResourceAgentState.IDLE,
                ResourceAgentState.COMMAND,
                ResourceAgentState.STREAMING
                ],
            'alert_class' : 'CommandErrorAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # All clear on initialize success (prev value None)
        # Warning on go active failure,
        # All clear on go active success,
        # Warning on go active failure
        # All clear on transition to idle (reconnect)
        self._event_count = 5
        test_vals = [
            {'state': ResourceAgentState.UNINITIALIZED },
            {'command': ResourceAgentEvent.INITIALIZE, 'command_success': True},
            {'state': ResourceAgentState.INACTIVE },
            {'command': ResourceAgentEvent.GO_ACTIVE, 'command_success': False},
            {'state': ResourceAgentState.INACTIVE },
            {'command': ResourceAgentEvent.RESET, 'command_success': True},
            {'state': ResourceAgentState.UNINITIALIZED },
            {'command': ResourceAgentEvent.INITIALIZE, 'command_success': True},
            {'state': ResourceAgentState.INACTIVE },
            {'command': ResourceAgentEvent.GO_ACTIVE, 'command_success': True},
            {'state': ResourceAgentState.IDLE },
            {'command': ResourceAgentEvent.RESET, 'command_success': True},
            {'state': ResourceAgentState.UNINITIALIZED },
            {'command': ResourceAgentEvent.INITIALIZE, 'command_success': True},
            {'state': ResourceAgentState.INACTIVE },
            {'command': ResourceAgentEvent.GO_ACTIVE, 'command_success': False},
            {'state': ResourceAgentState.INACTIVE },
            {'state': ResourceAgentState.IDLE }
        ]

        for x in test_vals:
            alert.eval_alert(**x)

        self._async_event_result.get(timeout=30)            
        """
        {'origin': 'abc123', 'status': 1, '_id': '44ec7f5452004cceb3d8dbaa941f08ef', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366740538561', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'ae33b508a3d845feacd05d9d25992924', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366740538571', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'f9eb2f3c477c46f1af0076fd4eab58f1', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366740538579', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '23153a1c48de4f25bce6f84cfab8444a', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366740538586', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '18b85d52ac1a438a9f5f8e69e5f4f6e8', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366740538592', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        """
        
class HAProcessControl(object):

    def __init__(self, pd_name, resource_registry, service_id, callback=None, logprefix=""):
        self.pd_name = pd_name
        self.resource_registry = resource_registry
        self.service_id = service_id
        self.callback = callback
        if callback and not callable(callback):
            raise ValueError("callback is not callable")
        self.logprefix = logprefix

        self.client = ProcessDispatcherServiceClient(to_name=pd_name)
        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
            callback=self._event_callback, origin_type="DispatchedProcess",
            auto_delete=True)

        self.processes = {}

    def start(self):
        service = self.resource_registry.read(self.service_id)
        process_assocs = self.resource_registry.find_associations(service, "hasProcess")

        for process_assoc in process_assocs:
            process_id = process_assoc.o
            if process_id:
                try:
                    process = self.client.read_process(process_id)
                except NotFound:
                    log.debug("%sService was associated with process %s, which is unknown to PD. ignoring.",
                        self.logprefix, process_id)
                    continue

                state = process.process_state
                state_str = ProcessStateEnum._str_map.get(state, str(state))

                self.processes[process.process_id] = _process_dict_from_object(process)
                log.info("%srecovered process %s state=%s", self.logprefix, process_id, state_str)
        self.event_sub.start()

    def stop(self):
        self.event_sub.stop()

    def get_managed_upids(self):
        return self.processes.keys()

    def _event_callback(self, event, *args, **kwargs):
        if not event:
            return

        try:
            self._inner_event_callback(event)
        except Exception:
            log.exception("%sException in event handler. This is a bug!", self.logprefix)

    def _inner_event_callback(self, event):
        process_id = event.origin
        state = event.state
        state_str = ProcessStateEnum._str_map.get(state, str(state))
        if not (process_id and process_id in self.processes):
            # we receive events for all processes but ignore most
            return

        process = None
        for _ in range(3):
            try:
                process = self.client.read_process(process_id)
                break
            except Timeout:
                log.warn("Timeout trying to read process from Process Dispatcher!", exc_info=True)
                pass  # retry
            except NotFound:
                break

        if process:
            log.info("%sreceived process %s state=%s", self.logprefix, process_id, state_str)

            # replace the cached data about this process
            self.processes[process_id] = _process_dict_from_object(process)

        else:
            log.warn("%sReceived process %s event but failed to read from Process Dispatcher",
                self.logprefix, process_id)

            #XXX better approach here? we at least have the state from the event,
            # so sticking that on cached process. We could miss other important
            # data like hostname however.
            self.processes[process_id]['state'] = process_state_to_pd_core(state)

        if self.callback:
            try:
                self.callback()
            except Exception, e:
                log.warn("%sError in HAAgent callback: %s", self.logprefix, e, exc_info=True)
예제 #26
0
class EventPersister(StandaloneProcess):

    def on_init(self):
        # Time in between event persists
        self.persist_interval = float(self.CFG.get_safe("event_persist_interval", 1.0))

        # Holds received events FIFO in syncronized queue
        self.event_queue = Queue()

        # Temporarily holds list of events to persist while datastore operation are not yet completed
        # This is where events to persist will remain if datastore operation fails occasionally.
        self.events_to_persist = None

        # bookkeeping for timeout greenlet
        self._persist_greenlet = None
        self._terminate_persist = Event() # when set, exits the timeout greenlet

        # The event subscriber
        self.event_sub = None

    def on_start(self):
        # Persister thread
        self._persist_greenlet = spawn(self._trigger_func, self.persist_interval)
        log.debug('EventPersister timer greenlet started in "%s" (interval %s)', self.__class__.__name__, self.persist_interval)

        # Event subscription
        self.event_sub = EventSubscriber(pattern=EventSubscriber.ALL_EVENTS,
                                         callback=self._on_event,
                                         queue_name="event_persister")

        self.event_sub.start()

    def on_quit(self):
        # Stop event subscriber
        self.event_sub.stop()

        # tell the trigger greenlet we're done
        self._terminate_persist.set()

        # wait on the greenlet to finish cleanly
        self._persist_greenlet.join(timeout=10)

    def _on_event(self, event, *args, **kwargs):
        self.event_queue.put(event)

    def _trigger_func(self, persist_interval):
        log.debug('Starting event persister thread with persist_interval=%s', persist_interval)

        # Event.wait returns False on timeout (and True when set in on_quit), so we use this to both exit cleanly and do our timeout in a loop
        while not self._terminate_persist.wait(timeout=persist_interval):
            try:
                if self.events_to_persist:
                    # There was an error last time and we need to retry
                    log.info("Retry persisting %s events" % len(self.events_to_persist))
                    self._persist_events(self.events_to_persist)
                    self.events_to_persist = None

                self.events_to_persist = [self.event_queue.get() for x in xrange(self.event_queue.qsize())]

                self._persist_events(self.events_to_persist)
                self.events_to_persist = None
            except Exception as ex:
                # Note: Persisting events may fail occasionally during test runs (when the "events" datastore is force
                # deleted and recreated). We'll log and keep retrying forever.
                log.exception("Failed to persist %s received events. Will retry next cycle" % len(self.events_to_persist))
                return False

    def _persist_events(self, event_list):
        if event_list:
            bootstrap.container_instance.event_repository.put_events(event_list)
class NotificationWorker(TransformEventListener):
    """
    Instances of this class acts as a Notification Worker.
    """
    def on_init(self):
        self.user_info = {}
        self.resource_registry = ResourceRegistryServiceClient()
        self.q = gevent.queue.Queue()

        super(NotificationWorker, self).on_init()

    def test_hook(self, user_info, reverse_user_info ):
        '''
        This method exists only to facilitate the testing of the reload of the user_info dictionary
        '''
        self.q.put((user_info, reverse_user_info))

    def on_start(self):
        super(NotificationWorker,self).on_start()

        self.reverse_user_info = None
        self.user_info = None

        #------------------------------------------------------------------------------------
        # Start by loading the user info and reverse user info dictionaries
        #------------------------------------------------------------------------------------

        try:
            self.user_info = self.load_user_info()
            self.reverse_user_info =  calculate_reverse_user_info(self.user_info)

            log.debug("On start up, notification workers loaded the following user_info dictionary: %s" % self.user_info)
            log.debug("The calculated reverse user info: %s" % self.reverse_user_info )

        except NotFound as exc:
            if exc.message.find('users_index') > -1:
                log.warning("Notification workers found on start up that users_index have not been loaded yet.")
            else:
                raise NotFound(exc.message)

        #------------------------------------------------------------------------------------
        # Create an event subscriber for Reload User Info events
        #------------------------------------------------------------------------------------

        def reload_user_info(event_msg, headers):
            '''
            Callback method for the subscriber to ReloadUserInfoEvent
            '''

            notification_id =  event_msg.notification_id
            log.debug("(Notification worker received a ReloadNotificationEvent. The relevant notification_id is %s" % notification_id)

            try:
                self.user_info = self.load_user_info()
            except NotFound:
                log.warning("ElasticSearch has not yet loaded the user_index.")

            self.reverse_user_info =  calculate_reverse_user_info(self.user_info)
            self.test_hook(self.user_info, self.reverse_user_info)

            log.debug("After a reload, the user_info: %s" % self.user_info)
            log.debug("The recalculated reverse_user_info: %s" % self.reverse_user_info)

        # the subscriber for the ReloadUSerInfoEvent
        self.reload_user_info_subscriber = EventSubscriber(
            event_type="ReloadUserInfoEvent",
            origin='UserNotificationService',
            callback=reload_user_info
        )
        self.reload_user_info_subscriber.start()


    def process_event(self, msg, headers):
        """
        Callback method for the subscriber listening for all events
        """
        #------------------------------------------------------------------------------------
        # From the reverse user info dict find out which users have subscribed to that event
        #------------------------------------------------------------------------------------

        user_ids = []
        if self.reverse_user_info:
            log.debug("Notification worker checking for users interested in %s" % msg.type_)
            user_ids = check_user_notification_interest(event = msg, reverse_user_info = self.reverse_user_info)

        log.debug("Notification worker deduced the following users were interested in the event: %s, event_type: %s, origin: %s" % (user_ids, msg.type_, msg.origin ))
        #------------------------------------------------------------------------------------
        # Send email to the users
        #------------------------------------------------------------------------------------

        for user_id in user_ids:
            msg_recipient = self.user_info[user_id]['user_contact'].email
            self.smtp_client = setting_up_smtp_client()
            send_email(message = msg, msg_recipient = msg_recipient, smtp_client = self.smtp_client )
            self.smtp_client.quit()

    def on_stop(self):
        # close subscribers safely
        self.reload_user_info_subscriber.stop()

        super(NotificationWorker, self).on_stop()

    def on_quit(self):
        # close subscribers safely
        self.reload_user_info_subscriber.stop()

        super(NotificationWorker, self).on_quit()

    def load_user_info(self):
        '''
        Method to load the user info dictionary used by the notification workers and the UNS

        @retval user_info dict
        '''

        users, _ = self.resource_registry.find_resources(restype= RT.UserInfo)

        user_info = {}

        if not users:
            return {}

        for user in users:
            notifications = []
            notification_preferences = None
            for variable in user.variables:
                if variable['name'] == 'notifications':
                    notifications = variable['value']

                if variable['name'] == 'notification_preferences':
                    notification_preferences = variable['value']

            user_info[user._id] = { 'user_contact' : user.contact, 'notifications' : notifications, 'notification_preferences' : notification_preferences}

        return user_info
class HAProcessControl(object):
    def __init__(self,
                 pd_name,
                 resource_registry,
                 service_id,
                 callback=None,
                 logprefix=""):
        self.pd_name = pd_name
        self.resource_registry = resource_registry
        self.service_id = service_id
        self.callback = callback
        if callback and not callable(callback):
            raise ValueError("callback is not callable")
        self.logprefix = logprefix

        self.client = ProcessDispatcherServiceClient(to_name=pd_name)
        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
                                         callback=self._event_callback,
                                         origin_type="DispatchedProcess",
                                         auto_delete=True)

        self.processes = {}

    def start(self):
        service = self.resource_registry.read(self.service_id)
        process_assocs = self.resource_registry.find_associations(
            service, "hasProcess")

        for process_assoc in process_assocs:
            process_id = process_assoc.o
            if process_id:
                try:
                    process = self.client.read_process(process_id)
                except NotFound:
                    log.debug(
                        "%sService was associated with process %s, which is unknown to PD. ignoring.",
                        self.logprefix, process_id)
                    continue

                state = process.process_state
                state_str = ProcessStateEnum._str_map.get(state, str(state))

                self.processes[process.process_id] = _process_dict_from_object(
                    process)
                log.info("%srecovered process %s state=%s", self.logprefix,
                         process_id, state_str)
        self.event_sub.start()

    def stop(self):
        self.event_sub.stop()

    def get_managed_upids(self):
        return self.processes.keys()

    def _event_callback(self, event, *args, **kwargs):
        if not event:
            return

        try:
            self._inner_event_callback(event)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            log.exception("%sException in event handler. This is a bug!",
                          self.logprefix)

    def _inner_event_callback(self, event):
        process_id = event.origin
        state = event.state
        state_str = ProcessStateEnum._str_map.get(state, str(state))
        if not (process_id and process_id in self.processes):
            # we receive events for all processes but ignore most
            return

        process = None
        for _ in range(3):
            try:
                process = self.client.read_process(process_id)
                break
            except Timeout:
                log.warn(
                    "Timeout trying to read process from Process Dispatcher!",
                    exc_info=True)
                pass  # retry
            except NotFound:
                break

        if process:
            log.info("%sreceived process %s state=%s", self.logprefix,
                     process_id, state_str)

            # replace the cached data about this process
            self.processes[process_id] = _process_dict_from_object(process)

        else:
            log.warn(
                "%sReceived process %s event but failed to read from Process Dispatcher",
                self.logprefix, process_id)

            #XXX better approach here? we at least have the state from the event,
            # so sticking that on cached process. We could miss other important
            # data like hostname however.
            self.processes[process_id]['state'] = process_state_to_pd_core(
                state)

        if self.callback:
            try:
                self.callback()
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                e = sys.exc_info()[0]
                log.warn("%sError in HAAgent callback: %s",
                         self.logprefix,
                         e,
                         exc_info=True)

    def _associate_process(self, process):
        try:
            self.resource_registry.create_association(self.service_id,
                                                      "hasProcess",
                                                      process.process_id)
        except Exception:
            log.exception("Couldn't associate service %s to process %s" %
                          (self.service_id, process.process_id))

    def schedule_process(self, pd_name, process_definition_id, **kwargs):

        if pd_name != self.pd_name:
            raise Exception(
                "schedule_process request received for unknown PD: %s" %
                pd_name)

        # figure out if there is an existing PID which can be reused
        found_upid = None
        for process in self.processes.values():
            upid = process.get('upid')
            state = process.get('state')
            if not (upid and state):
                continue

            if state in CoreProcessState.TERMINAL_STATES:
                found_upid = upid

        if found_upid:
            upid = found_upid
            proc = self.client.read_process(upid)

        else:
            # otherwise create a new process and associate
            upid = self.client.create_process(process_definition_id)

            # note: if the HAAgent fails between the create call above and the
            # associate call below, there may be orphaned Process objects. These
            # processes will not however be running, so are largely harmless.
            proc = self.client.read_process(upid)
            self._associate_process(proc)

        process_schedule = _get_process_schedule(**kwargs)
        configuration = kwargs.get('configuration')

        # cheat and roll the process state to REQUESTED before we actually
        # schedule it. this is in-memory only, so should be harmless. This
        # avoids a race between this scheduling process and the event
        # subscriber.
        proc.process_state = ProcessStateEnum.REQUESTED
        self.processes[upid] = _process_dict_from_object(proc)

        self.client.schedule_process(process_definition_id,
                                     process_schedule,
                                     configuration=configuration,
                                     process_id=upid)
        return upid

    def terminate_process(self, pid):
        return self.client.cancel_process(pid)

    def get_all_processes(self):
        processes = deepcopy(self.processes.values())
        return {self.pd_name: processes}

    def reload_processes(self):
        for process_id, process_dict in self.processes.items():
            try:
                process = self.client.read_process(process_id)
            except Exception:
                log.warn(
                    "%sFailed to read process %s from PD. Will retry later.",
                    self.logprefix,
                    process_id,
                    exc_info=True)
                continue
            new_process_dict = _process_dict_from_object(process)
            if new_process_dict['state'] != process_dict['state']:
                log.warn(
                    "%sUpdating process %s record manually. we may have missed an event?",
                    self.logprefix, process_id)
                self.processes[process_id] = new_process_dict
class TestActivateInstrumentIntegration(IonIntegrationTestCase):

    def setUp(self):
        # Start container
        super(TestActivateInstrumentIntegration, self).setUp()
        config = DotDict()
        config.bootstrap.use_es = True

        self._start_container()
        self.addCleanup(TestActivateInstrumentIntegration.es_cleanup)

        self.container.start_rel_from_url('res/deploy/r2deploy.yml', config)

        # Now create client to DataProductManagementService
        self.rrclient = ResourceRegistryServiceClient(node=self.container.node)
        self.damsclient = DataAcquisitionManagementServiceClient(node=self.container.node)
        self.pubsubcli =  PubsubManagementServiceClient(node=self.container.node)
        self.imsclient = InstrumentManagementServiceClient(node=self.container.node)
        self.dpclient = DataProductManagementServiceClient(node=self.container.node)
        self.datasetclient =  DatasetManagementServiceClient(node=self.container.node)
        self.processdispatchclient = ProcessDispatcherServiceClient(node=self.container.node)
        self.dataprocessclient = DataProcessManagementServiceClient(node=self.container.node)
        self.dataproductclient = DataProductManagementServiceClient(node=self.container.node)
        self.dataretrieverclient = DataRetrieverServiceClient(node=self.container.node)
        self.dataset_management = DatasetManagementServiceClient()
        self.usernotificationclient = UserNotificationServiceClient()

        #setup listerner vars
        self._data_greenlets = []
        self._no_samples = None
        self._samples_received = []

        self.event_publisher = EventPublisher()

    @staticmethod
    def es_cleanup():
        es_host = CFG.get_safe('server.elasticsearch.host', 'localhost')
        es_port = CFG.get_safe('server.elasticsearch.port', '9200')
        es = ep.ElasticSearch(
            host=es_host,
            port=es_port,
            timeout=10
        )
        indexes = STD_INDEXES.keys()
        indexes.append('%s_resources_index' % get_sys_name().lower())
        indexes.append('%s_events_index' % get_sys_name().lower())

        for index in indexes:
            IndexManagementService._es_call(es.river_couchdb_delete,index)
            IndexManagementService._es_call(es.index_delete,index)

    def create_logger(self, name, stream_id=''):

        # logger process
        producer_definition = ProcessDefinition(name=name+'_logger')
        producer_definition.executable = {
            'module':'ion.processes.data.stream_granule_logger',
            'class':'StreamGranuleLogger'
        }

        logger_procdef_id = self.processdispatchclient.create_process_definition(process_definition=producer_definition)
        configuration = {
            'process':{
                'stream_id':stream_id,
                }
        }
        pid = self.processdispatchclient.schedule_process(process_definition_id=logger_procdef_id,
                                                            configuration=configuration)

        return pid

    def _create_notification(self, user_name = '', instrument_id='', product_id=''):
        #--------------------------------------------------------------------------------------
        # Make notification request objects
        #--------------------------------------------------------------------------------------

        notification_request_1 = NotificationRequest(   name= 'notification_1',
            origin=instrument_id,
            origin_type="instrument",
            event_type='ResourceLifecycleEvent')

        notification_request_2 = NotificationRequest(   name='notification_2',
            origin=product_id,
            origin_type="data product",
            event_type='DetectionEvent')

        #--------------------------------------------------------------------------------------
        # Create a user and get the user_id
        #--------------------------------------------------------------------------------------

        user = UserInfo()
        user.name = user_name
        user.contact.email = '*****@*****.**' % user_name

        user_id, _ = self.rrclient.create(user)

        #--------------------------------------------------------------------------------------
        # Create notification
        #--------------------------------------------------------------------------------------

        self.usernotificationclient.create_notification(notification=notification_request_1, user_id=user_id)
        self.usernotificationclient.create_notification(notification=notification_request_2, user_id=user_id)
        log.debug( "test_activateInstrumentSample: create_user_notifications user_id %s", str(user_id) )

        return user_id

    def get_datastore(self, dataset_id):
        dataset = self.datasetclient.read_dataset(dataset_id)
        datastore_name = dataset.datastore_name
        datastore = self.container.datastore_manager.get_datastore(datastore_name, DataStore.DS_PROFILE.SCIDATA)
        return datastore

    def _check_computed_attributes_of_extended_instrument(self, expected_instrument_device_id = '',extended_instrument = None):

        # Verify that computed attributes exist for the extended instrument
        self.assertIsInstance(extended_instrument.computed.firmware_version, ComputedFloatValue)
        self.assertIsInstance(extended_instrument.computed.last_data_received_datetime, ComputedFloatValue)
        self.assertIsInstance(extended_instrument.computed.last_calibration_datetime, ComputedFloatValue)
        self.assertIsInstance(extended_instrument.computed.uptime, ComputedStringValue)

        self.assertIsInstance(extended_instrument.computed.power_status_roll_up, ComputedIntValue)
        self.assertIsInstance(extended_instrument.computed.communications_status_roll_up, ComputedIntValue)
        self.assertIsInstance(extended_instrument.computed.data_status_roll_up, ComputedIntValue)
        self.assertIsInstance(extended_instrument.computed.location_status_roll_up, ComputedIntValue)

        # the following assert will not work without elasticsearch.
        #self.assertEqual( 1, len(extended_instrument.computed.user_notification_requests.value) )
        self.assertEqual(extended_instrument.computed.communications_status_roll_up.value, StatusType.STATUS_WARNING)
        self.assertEqual(extended_instrument.computed.data_status_roll_up.value, StatusType.STATUS_OK)
        self.assertEqual(extended_instrument.computed.power_status_roll_up.value, StatusType.STATUS_WARNING)

        # Verify the computed attribute for user notification requests
        self.assertEqual( 1, len(extended_instrument.computed.user_notification_requests.value) )
        notifications = extended_instrument.computed.user_notification_requests.value
        notification = notifications[0]
        self.assertEqual(notification.origin, expected_instrument_device_id)
        self.assertEqual(notification.origin_type, "instrument")
        self.assertEqual(notification.event_type, 'ResourceLifecycleEvent')


    def _check_computed_attributes_of_extended_product(self, expected_data_product_id = '', extended_data_product = None):

        self.assertEqual(expected_data_product_id, extended_data_product._id)
        log.debug("extended_data_product.computed: %s", extended_data_product.computed)

        # Verify that computed attributes exist for the extended instrument
        self.assertIsInstance(extended_data_product.computed.product_download_size_estimated, ComputedIntValue)
        self.assertIsInstance(extended_data_product.computed.number_active_subscriptions, ComputedIntValue)
        self.assertIsInstance(extended_data_product.computed.data_url, ComputedStringValue)
        self.assertIsInstance(extended_data_product.computed.stored_data_size, ComputedIntValue)
        self.assertIsInstance(extended_data_product.computed.recent_granules, ComputedDictValue)
        self.assertIsInstance(extended_data_product.computed.parameters, ComputedListValue)
        self.assertIsInstance(extended_data_product.computed.recent_events, ComputedEventListValue)

        self.assertIsInstance(extended_data_product.computed.provenance, ComputedDictValue)
        self.assertIsInstance(extended_data_product.computed.user_notification_requests, ComputedListValue)
        self.assertIsInstance(extended_data_product.computed.active_user_subscriptions, ComputedListValue)
        self.assertIsInstance(extended_data_product.computed.past_user_subscriptions, ComputedListValue)
        self.assertIsInstance(extended_data_product.computed.last_granule, ComputedDictValue)
        self.assertIsInstance(extended_data_product.computed.is_persisted, ComputedIntValue)
        self.assertIsInstance(extended_data_product.computed.data_contents_updated, ComputedStringValue)
        self.assertIsInstance(extended_data_product.computed.data_datetime, ComputedListValue)

        # exact text here keeps changing to fit UI capabilities.  keep assertion general...
        self.assertTrue( 'ok' in extended_data_product.computed.last_granule.value['quality_flag'] )
        self.assertEqual( 2, len(extended_data_product.computed.data_datetime.value) )

        notifications = extended_data_product.computed.user_notification_requests.value

        notification = notifications[0]
        self.assertEqual(notification.origin, expected_data_product_id)
        self.assertEqual(notification.origin_type, "data product")
        self.assertEqual(notification.event_type, 'DetectionEvent')


    @attr('LOCOINT')
    @unittest.skipIf(not use_es, 'No ElasticSearch')
    @unittest.skipIf(os.getenv('CEI_LAUNCH_TEST', False), 'Skip test while in CEI LAUNCH mode')
    @patch.dict(CFG, {'endpoint':{'receive':{'timeout': 60}}})
    def test_activateInstrumentSample(self):

        self.loggerpids = []

        # Create InstrumentModel
        instModel_obj = IonObject(RT.InstrumentModel,
                                  name='SBE37IMModel',
                                  description="SBE37IMModel")
        instModel_id = self.imsclient.create_instrument_model(instModel_obj)
        log.debug( 'new InstrumentModel id = %s ', instModel_id)



        #Create stream alarms
        """
        test_two_sided_interval
        Test interval alarm and alarm event publishing for a closed
        inteval.
        """

        #        kwargs = {
        #            'name' : 'test_sim_warning',
        #            'stream_name' : 'parsed',
        #            'value_id' : 'temp',
        #            'message' : 'Temperature is above test range of 5.0.',
        #            'type' : StreamAlarmType.WARNING,
        #            'upper_bound' : 5.0,
        #            'upper_rel_op' : '<'
        #        }


        kwargs = {
            'name' : 'temperature_warning_interval',
            'stream_name' : 'parsed',
            'value_id' : 'temp',
            'message' : 'Temperature is below the normal range of 50.0 and above.',
            'type' : StreamAlarmType.WARNING,
            'lower_bound' : 50.0,
            'lower_rel_op' : '<'
        }

        # Create alarm object.
        alarm = {}
        alarm['type'] = 'IntervalAlarmDef'
        alarm['kwargs'] = kwargs

        raw_config = StreamConfiguration(stream_name='raw', parameter_dictionary_name='ctd_raw_param_dict', records_per_granule=2, granule_publish_rate=5 )
        parsed_config = StreamConfiguration(stream_name='parsed', parameter_dictionary_name='ctd_parsed_param_dict', records_per_granule=2, granule_publish_rate=5, alarms=[alarm] )


        # Create InstrumentAgent
        instAgent_obj = IonObject(RT.InstrumentAgent,
                                  name='agent007',
                                  description="SBE37IMAgent",
                                  driver_uri="http://sddevrepo.oceanobservatories.org/releases/seabird_sbe37smb_ooicore-0.0.1a-py2.7.egg",
                                  stream_configurations = [raw_config, parsed_config])
        instAgent_id = self.imsclient.create_instrument_agent(instAgent_obj)
        log.debug('new InstrumentAgent id = %s', instAgent_id)

        self.imsclient.assign_instrument_model_to_instrument_agent(instModel_id, instAgent_id)

        # Create InstrumentDevice
        log.debug('test_activateInstrumentSample: Create instrument resource to represent the SBE37 (SA Req: L4-CI-SA-RQ-241) ')
        instDevice_obj = IonObject(RT.InstrumentDevice,
                                   name='SBE37IMDevice',
                                   description="SBE37IMDevice",
                                   serial_number="12345" )
        instDevice_id = self.imsclient.create_instrument_device(instrument_device=instDevice_obj)
        self.imsclient.assign_instrument_model_to_instrument_device(instModel_id, instDevice_id)

        log.debug("test_activateInstrumentSample: new InstrumentDevice id = %s (SA Req: L4-CI-SA-RQ-241) " , instDevice_id)


        port_agent_config = {
            'device_addr':  CFG.device.sbe37.host,
            'device_port':  CFG.device.sbe37.port,
            'process_type': PortAgentProcessType.UNIX,
            'binary_path': "port_agent",
            'port_agent_addr': 'localhost',
            'command_port': CFG.device.sbe37.port_agent_cmd_port,
            'data_port': CFG.device.sbe37.port_agent_data_port,
            'log_level': 5,
            'type': PortAgentType.ETHERNET
        }

        instAgentInstance_obj = IonObject(RT.InstrumentAgentInstance, name='SBE37IMAgentInstance',
                                          description="SBE37IMAgentInstance",
                                          port_agent_config = port_agent_config)


        instAgentInstance_id = self.imsclient.create_instrument_agent_instance(instAgentInstance_obj,
                                                                               instAgent_id,
                                                                               instDevice_id)

        tdom, sdom = time_series_domain()
        sdom = sdom.dump()
        tdom = tdom.dump()


        parsed_pdict_id = self.dataset_management.read_parameter_dictionary_by_name('ctd_parsed_param_dict', id_only=True)
        parsed_stream_def_id = self.pubsubcli.create_stream_definition(name='parsed', parameter_dictionary_id=parsed_pdict_id)

        raw_pdict_id = self.dataset_management.read_parameter_dictionary_by_name('ctd_raw_param_dict', id_only=True)
        raw_stream_def_id = self.pubsubcli.create_stream_definition(name='raw', parameter_dictionary_id=raw_pdict_id)


        #-------------------------------
        # Create Raw and Parsed Data Products for the device
        #-------------------------------

        dp_obj = IonObject(RT.DataProduct,
            name='the parsed data',
            description='ctd stream test',
            temporal_domain = tdom,
            spatial_domain = sdom)

        data_product_id1 = self.dpclient.create_data_product(data_product=dp_obj, stream_definition_id=parsed_stream_def_id)
        log.debug( 'new dp_id = %s' , data_product_id1)
        self.dpclient.activate_data_product_persistence(data_product_id=data_product_id1)

        self.damsclient.assign_data_product(input_resource_id=instDevice_id, data_product_id=data_product_id1)



        # Retrieve the id of the OUTPUT stream from the out Data Product
        stream_ids, _ = self.rrclient.find_objects(data_product_id1, PRED.hasStream, None, True)
        log.debug('Data product streams1 = %s', stream_ids)

        # Retrieve the id of the OUTPUT stream from the out Data Product
        dataset_ids, _ = self.rrclient.find_objects(data_product_id1, PRED.hasDataset, RT.Dataset, True)
        log.debug('Data set for data_product_id1 = %s' , dataset_ids[0])
        self.parsed_dataset = dataset_ids[0]


        pid = self.create_logger('ctd_parsed', stream_ids[0] )
        self.loggerpids.append(pid)


        dp_obj = IonObject(RT.DataProduct,
            name='the raw data',
            description='raw stream test',
            temporal_domain = tdom,
            spatial_domain = sdom)

        data_product_id2 = self.dpclient.create_data_product(data_product=dp_obj, stream_definition_id=raw_stream_def_id)
        log.debug('new dp_id = %s', data_product_id2)

        self.damsclient.assign_data_product(input_resource_id=instDevice_id, data_product_id=data_product_id2)

        self.dpclient.activate_data_product_persistence(data_product_id=data_product_id2)

        # setup notifications for the device and parsed data product
        user_id_1 = self._create_notification( user_name='user_1', instrument_id=instDevice_id, product_id=data_product_id1)
        #---------- Create notifications for another user and verify that we see different computed subscriptions for the two users ---------
        user_id_2 = self._create_notification( user_name='user_2', instrument_id=instDevice_id, product_id=data_product_id2)

        # Retrieve the id of the OUTPUT stream from the out Data Product
        stream_ids, _ = self.rrclient.find_objects(data_product_id2, PRED.hasStream, None, True)
        log.debug('Data product streams2 = %s' , str(stream_ids))

        # Retrieve the id of the OUTPUT stream from the out Data Product
        dataset_ids, _ = self.rrclient.find_objects(data_product_id2, PRED.hasDataset, RT.Dataset, True)
        log.debug('Data set for data_product_id2 = %s' , dataset_ids[0])
        self.raw_dataset = dataset_ids[0]

        #elastic search debug
        es_indexes, _ = self.container.resource_registry.find_resources(restype='ElasticSearchIndex')
        log.debug('ElasticSearch indexes: %s', [i.name for i in es_indexes])
        log.debug('Bootstrap %s', CFG.bootstrap.use_es)


        def start_instrument_agent():
            self.imsclient.start_instrument_agent_instance(instrument_agent_instance_id=instAgentInstance_id)

        gevent.joinall([gevent.spawn(start_instrument_agent)])


        #setup a subscriber to alarm events from the device
        self._events_received= []
        self._event_count = 0
        self._samples_out_of_range = 0
        self._samples_complete = False
        self._async_sample_result = AsyncResult()

        def consume_event(*args, **kwargs):
            log.debug('TestActivateInstrument recieved ION event: args=%s, kwargs=%s, event=%s.',
                str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            self._event_count = len(self._events_received)
            self._async_sample_result.set()

        self._event_subscriber = EventSubscriber(
            event_type= 'StreamWarningAlarmEvent',   #'StreamWarningAlarmEvent', #  StreamAlarmEvent
            callback=consume_event,
            origin=instDevice_id)
        self._event_subscriber.start()


        #cleanup
        self.addCleanup(self.imsclient.stop_instrument_agent_instance,
                        instrument_agent_instance_id=instAgentInstance_id)

        def stop_subscriber():
            self._event_subscriber.stop()
            self._event_subscriber = None

        self.addCleanup(stop_subscriber)


        #wait for start
        inst_agent_instance_obj = self.imsclient.read_instrument_agent_instance(instAgentInstance_id)
        gate = ProcessStateGate(self.processdispatchclient.read_process,
                                inst_agent_instance_obj.agent_process_id,
                                ProcessStateEnum.RUNNING)
        self.assertTrue(gate.await(30), "The instrument agent instance (%s) did not spawn in 30 seconds" %
                                        inst_agent_instance_obj.agent_process_id)

        log.debug('Instrument agent instance obj: = %s' , str(inst_agent_instance_obj))

        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = ResourceAgentClient(instDevice_id,
                                              to_name=inst_agent_instance_obj.agent_process_id,
                                              process=FakeProcess())

        log.debug("test_activateInstrumentSample: got ia client %s" , str(self._ia_client))

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrumentSample: initialize %s" , str(retval))
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        log.debug("(L4-CI-SA-RQ-334): Sending go_active command ")
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        reply = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrument: return value from go_active %s" , str(reply))
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.GET_RESOURCE_STATE)
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        log.debug("(L4-CI-SA-RQ-334): current state after sending go_active command %s" , str(state))

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        reply = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrumentSample: run %s" , str(reply))
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.PAUSE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STOPPED)

        cmd = AgentCommand(command=ResourceAgentEvent.RESUME)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.CLEAR)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)
        for i in xrange(10):
            retval = self._ia_client.execute_resource(cmd)
            log.debug("test_activateInstrumentSample: return from sample %s" , str(retval))

        log.debug( "test_activateInstrumentSample: calling reset ")
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        reply = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrumentSample: return from reset %s" , str(reply))

        self._samples_complete = True

        #--------------------------------------------------------------------------------
        # Now get the data in one chunk using an RPC Call to start_retreive
        #--------------------------------------------------------------------------------

        replay_data = self.dataretrieverclient.retrieve(self.parsed_dataset)
        self.assertIsInstance(replay_data, Granule)
        rdt = RecordDictionaryTool.load_from_granule(replay_data)
        log.debug("test_activateInstrumentSample: RDT parsed: %s", str(rdt.pretty_print()) )
        temp_vals = rdt['temp']
        self.assertEquals(len(temp_vals) , 10)
        log.debug("test_activateInstrumentSample: all temp_vals: %s", temp_vals )

        #out_of_range_temp_vals = [i for i in temp_vals if i > 5]
        out_of_range_temp_vals = [i for i in temp_vals if i < 50.0]
        log.debug("test_activateInstrumentSample: Out_of_range_temp_vals: %s", out_of_range_temp_vals )
        self._samples_out_of_range = len(out_of_range_temp_vals)

        # if no bad values were produced, then do not wait for an event
        if self._samples_out_of_range == 0:
            self._async_sample_result.set()


        log.debug("test_activateInstrumentSample: _events_received: %s", self._events_received )
        log.debug("test_activateInstrumentSample: _event_count: %s", self._event_count )

        self._async_sample_result.get(timeout=CFG.endpoint.receive.timeout)

        replay_data = self.dataretrieverclient.retrieve(self.raw_dataset)
        self.assertIsInstance(replay_data, Granule)
        rdt = RecordDictionaryTool.load_from_granule(replay_data)
        log.debug("RDT raw: %s", str(rdt.pretty_print()) )

        raw_vals = rdt['raw']
        self.assertEquals(len(raw_vals) , 10)


        log.debug("l4-ci-sa-rq-138")
        """
        Physical resource control shall be subject to policy

        Instrument management control capabilities shall be subject to policy

        The actor accessing the control capabilities must be authorized to send commands.

        note from maurice 2012-05-18: Talk to tim M to verify that this is policy.  If it is then talk with Stephen to
                                      get an example of a policy test and use that to create a test stub that will be
                                      completed when we have instrument policies.

        Tim M: The "actor", aka observatory operator, will access the instrument through ION.

        """


        #--------------------------------------------------------------------------------
        # Get the extended data product to see if it contains the granules
        #--------------------------------------------------------------------------------
        extended_product = self.dpclient.get_data_product_extension(data_product_id=data_product_id1, user_id=user_id_1)
        def poller(extended_product):
            return len(extended_product.computed.user_notification_requests.value) == 1

        poll(poller, extended_product, timeout=30)

        self._check_computed_attributes_of_extended_product( expected_data_product_id = data_product_id1, extended_data_product = extended_product)

        #--------------------------------------------------------------------------------
        #put some events into the eventsdb to test - this should set the comms and data status to WARNING
        #--------------------------------------------------------------------------------

        t = get_ion_ts()
        self.event_publisher.publish_event(  ts_created= t,  event_type = 'DeviceStatusEvent',
            origin = instDevice_id, state=DeviceStatusType.OUT_OF_RANGE, values = [200] )
        self.event_publisher.publish_event( ts_created= t,   event_type = 'DeviceCommsEvent',
            origin = instDevice_id, state=DeviceCommsType.DATA_DELIVERY_INTERRUPTION, lapse_interval_seconds = 20 )

        #--------------------------------------------------------------------------------
        # Get the extended instrument
        #--------------------------------------------------------------------------------

        extended_instrument = self.imsclient.get_instrument_device_extension(instrument_device_id=instDevice_id, user_id=user_id_1)
        self._check_computed_attributes_of_extended_instrument(expected_instrument_device_id = instDevice_id, extended_instrument = extended_instrument)

        #--------------------------------------------------------------------------------
        # For the second user, check the extended data product and the extended intrument
        #--------------------------------------------------------------------------------
        extended_product = self.dpclient.get_data_product_extension(data_product_id=data_product_id2, user_id=user_id_2)
        self._check_computed_attributes_of_extended_product(expected_data_product_id = data_product_id2, extended_data_product = extended_product)

        #---------- Put some events into the eventsdb to test - this should set the comms and data status to WARNING  ---------

        t = get_ion_ts()
        self.event_publisher.publish_event(  ts_created= t,  event_type = 'DeviceStatusEvent',
            origin = instDevice_id, state=DeviceStatusType.OUT_OF_RANGE, values = [200] )
        self.event_publisher.publish_event( ts_created= t,   event_type = 'DeviceCommsEvent',
            origin = instDevice_id, state=DeviceCommsType.DATA_DELIVERY_INTERRUPTION, lapse_interval_seconds = 20 )

        #--------------------------------------------------------------------------------
        # Get the extended instrument
        #--------------------------------------------------------------------------------

        extended_instrument = self.imsclient.get_instrument_device_extension(instrument_device_id=instDevice_id, user_id=user_id_2)
        self._check_computed_attributes_of_extended_instrument(expected_instrument_device_id = instDevice_id, extended_instrument = extended_instrument)

        #--------------------------------------------------------------------------------
        # Deactivate loggers
        #--------------------------------------------------------------------------------

        for pid in self.loggerpids:
            self.processdispatchclient.cancel_process(pid)

        self.dpclient.delete_data_product(data_product_id1)
        self.dpclient.delete_data_product(data_product_id2)
예제 #30
0
class EventAlertTransform(TransformEventListener):

    def on_start(self):
        log.debug('EventAlertTransform.on_start()')
        super(EventAlertTransform, self).on_start()

        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # get the algorithm to use
        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        self.timer_origin = self.CFG.get_safe('process.timer_origin', 'Interval Timer')
        self.instrument_origin = self.CFG.get_safe('process.instrument_origin', '')

        self.counter = 0
        self.event_times = []

        #-------------------------------------------------------------------------------------
        # Set up a listener for instrument events
        #-------------------------------------------------------------------------------------

        self.instrument_event_queue = gevent.queue.Queue()

        def instrument_event_received(message, headers):
            log.debug("EventAlertTransform received an instrument event here::: %s" % message)
            self.instrument_event_queue.put(message)

        self.instrument_event_subscriber = EventSubscriber(origin = self.instrument_origin,
            callback=instrument_event_received)

        self.instrument_event_subscriber.start()

        #-------------------------------------------------------------------------------------
        # Create the publisher that will publish the Alert message
        #-------------------------------------------------------------------------------------

        self.event_publisher = EventPublisher()

    def on_quit(self):
        self.instrument_event_subscriber.stop()
        super(EventAlertTransform, self).on_quit()

    def process_event(self, msg, headers):
        '''
        The callback method.
        If the events satisfy the criteria, publish an alert event.
        '''

        if msg.origin == self.timer_origin:
            if self.instrument_event_queue.empty():
                log.debug("no event received from the instrument. Publishing an alarm event!")
                self.publish()
            else:
                log.debug("Events were received from the instrument in between timer events. Instrument working normally.")
                self.instrument_event_queue.queue.clear()


    def publish(self):

        #-------------------------------------------------------------------------------------
        # publish an alert event
        #-------------------------------------------------------------------------------------
        self.event_publisher.publish_event( event_type= "DeviceEvent",
            origin="EventAlertTransform",
            description= "An alert event being published.")
예제 #31
0
class InteractionObserver(object):
    """
    Observes ongoing interactions in the Exchange. Logs them to disk and makes them available
    in the local container (for development purposes) and on request.
    """

    def start(self):
        self.msg_log = []

        self.event_sub = None
        self.conv_sub = None

        #Conv subscription
        self.conv_sub = ConvSubscriber(callback=self._msg_received)
        self.conv_sub.start()

        # Event subscription
        self.event_sub = EventSubscriber(pattern=EventSubscriber.ALL_EVENTS,
                                         callback=self._event_received,
                                         queue_name="event_persister")
        self.event_sub.start()

        self.started = True

    def stop(self):
        # Stop event subscriber
        self.event_sub.stop()
        # Stop conv subscriber
        self.conv_sub.stop()
        self.started = False

    def _msg_received(self, msg, *args, **kwargs):
        self.log_message(args[0])

    def _event_received(self, event, *args, **kwargs):
        if 'origin' in event:
            args[0]['origin'] = event.origin
        if 'origin_type' in event:
            args[0]['origin_type'] = event.origin_type
        if 'sub_type' in event:
            args[0]['sub_type'] = event.sub_type
        self.log_message(args[0], True)

    def log_message(self, mhdrs, evmsg=False):
        """
        @param evmsg    This message is an event, render it as such!
        """
        mhdrs['_content_type'] = mhdrs.get('format', None)

        # TUPLE: timestamp (MS), type, boolean if its an event
        msg_rec = (get_ion_ts(), mhdrs, evmsg)
        self.msg_log.append(msg_rec)

        # Truncate if too long in increments of slice
        if len(self.msg_log) > MAX_MSGLOG + SLICE:
            self.msg_log = self.msg_log[SLICE:]

    def _get_data(self, msglog, response_msgs):
        """
        Provides msc data in python format, to be converted either to msc text or to json
        for use with msc web monitor.
        Returns a list of hashes in the form:
        { to, from, content, type, ts, error (boolean), to_raw, from_raw, topline }
        """
        msgdata = []

        for msgtup in msglog:

            datatemp = {"to": None, "from": None, "content": None, "type": None,
                        "ts": None, "error": False}
            msg = msgtup[1]

            convid = msg.get('conv-id', None)

            if (convid in response_msgs):
                response = response_msgs.pop(convid)
                sname = response.get('sender')
                rname = response.get('receiver')

            else:
                if (msg.get('sender-type', 'unknown') == 'service'):
                    sname = msg.get('sender-service', msg.get('sender-name',
                                    msg.get('sender', 'unknown')))
                else:
                    sname = msg.get('sender-name', msg.get('sender', 'unknown'))
                rname = msg.get('receiver', 'unknown')

                if (convid is not None):
                    response_msgs[convid] = {'sender': rname, 'receiver': sname}

            # from_raw is displayed as the header on the webpage
            datatemp["from_raw"] = sname
            sname = self._sanitize(sname)
            datatemp["from"] = sname
            datatemp["ts"] = msg.get("ts", "Unknown")
            datatemp["to_raw"] = rname
            rname = self._sanitize(rname)
            datatemp["to"] = rname

            if msgtup[2]:
                # this is an EVENT, show it as a box!
                datatemp["type"] = "events"

                #todo: not sure if we can hard code the splitting mechanism like done below !!
                datatemp["from"] = "events,"+ (msg.get('routing_key').split('._._.')[0]).split('.')[1]
                datatemp["from_raw"] = "events,"+(msg.get('routing_key').split('._._.')[0]).split('.')[1]
                datatemp["to"] = "events,"+ (msg.get('routing_key').split('._._.')[0]).split('.')[1]
                datatemp["to_raw"] = "events,"+ (msg.get('routing_key').split('._._.')[0]).split('.')[1]

                evlabel = "%s \nOrigin: %s" % (msg.get('routing_key'), msg.get('origin'))
                datatemp["content"] = evlabel
                datatemp["topline"] = msg.get('sub_type', '') + " " + msg.get('origin_type', '')
                if (datatemp['topline'] == " "):
                    datatemp['topline'] = msg.get('origin')

            else:

                mlabel = "%s\n(%s->%s)\n<%s>" % (msg.get('op', None), sname.rsplit(",", 1)[-1],
                         rname.rsplit(",", 1)[-1], msg.get('_content_type', '?content-type?'))
                datatemp["content"] = mlabel
                datatemp["topline"] = mlabel.split("\n")[0]

                if msg.get('protocol', None) == 'rpc':

                    datatemp["type"] = "rpcres"

                    performative = msg.get('performative', None)
                    if performative == 'request':
                        datatemp["type"] = "rpcreq"
                    elif performative == 'timeout':
                        pass  # timeout, unfortunately you don't see this @TODO

                    if performative == 'failure' or performative == 'error':
                        datatemp["error"] = True

                else:
                    # non rpc -> perhaps a data message?
                    datatemp["type"] = "data"

            msgdata.append(datatemp)

        return msgdata, response_msgs

    @classmethod
    def _sanitize(cls, input):
        return string.replace(string.replace(input, ".", "_"), "-", "_")
예제 #32
0
    def forward(self, *args, **kwargs):
        """
        Forward a service method to the terrestrial endpoint
        through the service interface.
        """
        func_name = kwargs.pop('func_name')
        try:
            link = kwargs.pop('link')
        except KeyError:
            link = True
        cid = ''
        try:
            remote_timeout = kwargs.pop('remote_timeout')
            if not isinstance(remote_timeout, int):
                remote_timeout = 0
            elif remote_timeout < 0:
                remote_timeout = 0
            elif remote_timeout == 0:
                pass
            else:
                cid = str(uuid.uuid4())
                
        except KeyError:
            remote_timeout = 0
                
        cmd = IonObject('RemoteCommand',
                             resource_id=self._resource_id,
                             svc_name=self._svc_name,
                             command=func_name,
                             command_id=cid,
                             args= args,
                             kwargs= kwargs)
        
        if remote_timeout == 0 :
            return self._te_client.enqueue_command(cmd, link)
            
        else:

            if self._resource_id:
                origin = self._resource_id
            elif self._svc_name:
                origin = self._svc_name + self._xs_name

            pending_cmd = cmd
            async_result_evt = AsyncResult()
            
            def result_callback(evt, *args, **kwargs):
                """
                Callback for subscriber retrive blocking results.
                """
                #global async_result_evt
                if evt.type_ == 'RemoteCommandResult':
                    cmd = evt.command
                    if cmd.command_id == pending_cmd.command_id:
                        async_result_evt.set(cmd)                

            sub = EventSubscriber(
                event_type='RemoteCommandResult',
                origin=origin,
                callback=result_callback)

            sub.start()
            #self._pending_cmd = cmd
            cmd = self._te_client.enqueue_command(cmd, link)
            try:
                result = async_result_evt.get(timeout=remote_timeout)
                #self._pending_cmd = None
                sub.stop()                
            except gevent.Timeout:
                #self._pending_cmd = None
                sub.stop()
                raise Timeout('Timed out waiting for remote result.')

            return result
예제 #33
0
class TestAgentConnectionFailures(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """

    ############################################################################
    # Setup, teardown.
    ############################################################################

    def setUp(self):
        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        self._ia_client = None

        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        log.info('Creating driver integration test support:')
        log.info('driver uri: %s', DRV_URI)
        log.info('device address: %s', DEV_ADDR)
        log.info('device port: %s', DEV_PORT)
        log.info('log delimiter: %s', DELIM)
        log.info('work dir: %s', WORK_DIR)
        self._support = DriverIntegrationTestSupport(None, None, DEV_ADDR,
                                                     DEV_PORT, DATA_PORT,
                                                     CMD_PORT, PA_BINARY,
                                                     DELIM, WORK_DIR)

        # Start port agent, add stop to cleanup.
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)

        log.info('building stream configuration')
        # Setup stream config.
        self._build_stream_config()

        # Start a resource agent client to talk with the instrument agent.
        log.info('starting IA process')
        self._ia_client = start_instrument_agent_process(
            self.container, self._stream_config)
        self.addCleanup(self._verify_agent_reset)
        log.info('test setup complete')

    ###############################################################################
    # Port agent helpers.
    ###############################################################################

    def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        port = self._support.start_pagent()
        log.info('Port agent started at port %i', port)

        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr': 'localhost',
            'port': port,
            'cmd_port': CMD_PORT
        }

    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state(timeout=120.1)
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd, timeout=300)

    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()

        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()

        self._event_subscriber = EventSubscriber(event_type=type,
                                                 callback=consume_event,
                                                 origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient()

        encoder = IonObjectSerializer()

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        stream_name = 'parsed'
        param_dict_name = 'ctd_parsed_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(
            param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(
            name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(
            name=stream_name,
            exchange_point='science_data',
            stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                             exchange_point=stream_route.exchange_point,
                             stream_id=stream_id,
                             parameter_dictionary=pd,
                             stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

        stream_name = 'raw'
        param_dict_name = 'ctd_raw_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(
            param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(
            name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(
            name=stream_name,
            exchange_point='science_data',
            stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                             exchange_point=stream_route.exchange_point,
                             stream_id=stream_id,
                             parameter_dictionary=pd,
                             stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

    def _start_data_subscribers(self, count, raw_count):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        self._raw_samples_received = []
        self._async_sample_result = AsyncResult()
        self._async_raw_sample_result = AsyncResult()

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            log.info('Received parsed data on %s (%s,%s)', stream_id,
                     stream_route.exchange_point, stream_route.routing_key)
            self._samples_received.append(message)
            if len(self._samples_received) == count:
                self._async_sample_result.set()

        def recv_raw_data(message, stream_route, stream_id):
            log.info('Received raw data on %s (%s,%s)', stream_id,
                     stream_route.exchange_point, stream_route.routing_key)
            self._raw_samples_received.append(message)
            if len(self._raw_samples_received) == raw_count:
                self._async_raw_sample_result.set()

        from pyon.util.containers import create_unique_identifier

        stream_name = 'parsed'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" % stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name,
                                                   stream_ids=[stream_id],
                                                   timeout=120.2)
        pubsub_client.activate_subscription(sub_id, timeout=120.3)
        sub.subscription_id = sub_id  # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

        stream_name = 'raw'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" % stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_raw_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name,
                                                   stream_ids=[stream_id],
                                                   timeout=120.4)
        pubsub_client.activate_subscription(sub_id, timeout=120.5)
        sub.subscription_id = sub_id  # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()

    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber, 'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(
                        subscriber.subscription_id, timeout=120.6)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id,
                                                  timeout=120.7)
            subscriber.stop()

    ###############################################################################
    # Socket listen.
    ###############################################################################

    def _socket_listen(self, s, prompt, timeout):

        buf = ''
        starttime = time.time()
        while True:
            try:
                buf += s.recv(1024)
                print '##### Listening, got: %s' % buf
                if prompt and buf.find(prompt) != -1:
                    break
            except:
                gevent.sleep(1)

            finally:
                delta = time.time() - starttime
                if delta > timeout:
                    break
        return buf

    ###############################################################################
    # Assert helpers.
    ###############################################################################

    def assertSampleDict(self, val):
        """
        Verify the value is a sample dictionary for the sbe37.
        """
        # AgentCommandResult.result['parsed']
        """
        {'quality_flag': 'ok', 'preferred_timestamp': 'driver_timestamp',
        'stream_name': 'parsed', 'pkt_format_id': 'JSON_Data',
        'pkt_version': 1, 'values':
        [{'value_id': 'temp', 'value': 21.4894},
        {'value_id': 'conductivity', 'value': 13.22157},
        {'value_id': 'pressure', 'value': 146.186}],
        'driver_timestamp': 3556901018.170206}
        """

        self.assertIsInstance(val, dict)
        self.assertTrue(val.has_key('values'))
        values_list = val['values']
        self.assertTrue(isinstance(values_list, list))
        self.assertTrue(len(values_list) == 3)

        ids = ['temp', 'conductivity', 'pressure']
        ids_found = []

        for x in values_list:
            self.assertTrue(x.has_key('value_id'))
            self.assertTrue(x.has_key('value'))
            ids_found.append(x['value_id'])
            self.assertTrue(isinstance(x['value'], float))

        self.assertItemsEqual(ids, ids_found)

        self.assertTrue(val.has_key('driver_timestamp'))
        time = val['driver_timestamp']
        self.assertTrue(isinstance(time, float))

    def assertParamDict(self, pd, all_params=False):
        """
        Verify all device parameters exist and are correct type.
        """
        if all_params:
            self.assertEqual(set(pd.keys()), set(PARAMS.keys()))
            for (key, type_val) in PARAMS.iteritems():
                if type_val == list or type_val == tuple:
                    self.assertTrue(isinstance(pd[key], (list, tuple)))
                else:
                    self.assertTrue(isinstance(pd[key], type_val))

        else:
            for (key, val) in pd.iteritems():
                self.assertTrue(PARAMS.has_key(key))
                self.assertTrue(isinstance(val, PARAMS[key]))

    def assertParamVals(self, params, correct_params):
        """
        Verify parameters take the correct values.
        """
        self.assertEqual(set(params.keys()), set(correct_params.keys()))
        for (key, val) in params.iteritems():
            correct_val = correct_params[key]
            if isinstance(val, float):
                # Verify to 5% of the larger value.
                max_val = max(abs(val), abs(correct_val))
                self.assertAlmostEqual(val, correct_val, delta=max_val * .01)

            elif isinstance(val, (list, tuple)):
                # list of tuple.
                self.assertEqual(list(val), list(correct_val))

            else:
                # int, bool, str.
                self.assertEqual(val, correct_val)

    ###############################################################################
    # Tests.
    ###############################################################################

    def test_lost_connection(self):
        """
        test_lost_connection
        """

        # Set up a subscriber to collect command events.
        self._start_event_subscriber('ResourceAgentConnectionLostErrorEvent',
                                     1)
        self.addCleanup(self._stop_event_subscriber)

        # Start in uninitialized.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Initialize the agent.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Activate.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        # Go into command mode.
        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        # Start streaming.
        cmd = AgentCommand(command=SBE37ProtocolEvent.START_AUTOSAMPLE)
        retval = self._ia_client.execute_resource(cmd)

        # Wait for a while, collect some samples.
        gevent.sleep(10)

        # Blow the port agent out from under the agent.
        self._support.stop_pagent()

        # Loop until we resyncronize to LOST_CONNECTION/DISCONNECTED.
        # Test will timeout if this dosn't occur.
        while True:
            state = self._ia_client.get_agent_state()
            if state == ResourceAgentState.LOST_CONNECTION:
                break
            else:
                gevent.sleep(1)

        # Verify the driver has transitioned to disconnected
        while True:
            state = self._ia_client.get_resource_state()
            if state == DriverConnectionState.DISCONNECTED:
                break
            else:
                gevent.sleep(1)

        # Make sure the lost connection error event arrives.
        self._async_event_result.get(timeout=CFG.endpoint.receive.timeout)
        self.assertEqual(len(self._events_received), 1)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    #@unittest.skip('Fails on buildbot for some god unknown reason.')
    def test_autoreconnect(self):
        """
        test_autoreconnect
        """
        # Set up a subscriber to collect command events.
        self._start_event_subscriber('ResourceAgentConnectionLostErrorEvent',
                                     1)
        self.addCleanup(self._stop_event_subscriber)

        # Start in uninitialized.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Initialize the agent.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Activate.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        # Go into command mode.
        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        def poll_func(test):
            cmd = AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)
            while True:
                try:
                    gevent.sleep(.5)
                    test._ia_client.execute_resource(cmd)
                except IonException as ex:
                    # This exception could be ResourceException (broken pipe)
                    # Timeout or Conflict
                    log.info('#### pre shutdown exception: %s, %s',
                             str(type(ex)), str(ex))
                    break

            while True:
                try:
                    gevent.sleep(.5)
                    test._ia_client.execute_resource(cmd)
                    log.info('#### post shutdown got new sample.')
                    break
                except IonException as ex:
                    # This should be conflict.
                    log.info('#### post shutdown exception: %s, %s',
                             str(type(ex)), str(ex))

        timeout = gevent.Timeout(600)
        timeout.start()
        try:

            # Start the command greenlet and let poll for a bit.
            gl = gevent.spawn(poll_func, self)
            gevent.sleep(20)

            # Blow the port agent out from under the agent.
            self._support.stop_pagent()

            # Wait for a while, the supervisor is restarting the port agent.
            gevent.sleep(10)
            self._support.start_pagent()

            # Wait for the device to connect and start sampling again.
            gl.join()
            gl = None
            timeout.cancel()

        except (Exception, gevent.Timeout) as ex:
            if gl:
                gl.kill()
                gl = None
            self.fail(('Could not reconnect to device: %s,  %s', str(type(ex)),
                       str(ex)))

    def test_connect_failed(self):
        """
        test_connect_failed
        """
        # Stop the port agent.
        self._support.stop_pagent()

        # Sleep a bit.
        gevent.sleep(3)

        # Start in uninitialized.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Initialize the agent.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Activate. This should fail because there is no port agent to connect to.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        with self.assertRaises(ResourceError):
            retval = self._ia_client.execute_agent(cmd)

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

    def test_get_set_alerts(self):
        """
        test_get_set_alerts
        Test specific of get/set alerts, including using result of get to
        set later.
        """
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertItemsEqual(retval, [])

        alert_def1 = {
            'name': 'temp_warning_interval',
            'stream_name': 'parsed',
            'description': 'Temperature is above normal range.',
            'alert_type': StreamAlertType.WARNING,
            'aggregate_type': AggregateStatusType.AGGREGATE_DATA,
            'value_id': 'temp',
            'lower_bound': None,
            'lower_rel_op': None,
            'upper_bound': 10.5,
            'upper_rel_op': '<',
            'alert_class': 'IntervalAlert'
        }

        alert_def2 = {
            'name': 'temp_alarm_interval',
            'stream_name': 'parsed',
            'description': 'Temperature is way above normal range.',
            'alert_type': StreamAlertType.WARNING,
            'aggregate_type': AggregateStatusType.AGGREGATE_DATA,
            'value_id': 'temp',
            'lower_bound': None,
            'lower_rel_op': None,
            'upper_bound': 15.5,
            'upper_rel_op': '<',
            'alert_class': 'IntervalAlert'
        }
        """
        Interval alerts are returned from get like this:
        (value and status fields describe state of the alert)
        {
        'name': 'temp_warning_interval',
        'stream_name': 'parsed',
        'description': 'Temperature is above normal range.',
        'alert_type': 1,
        'aggregate_type': 2,
        'value_id': 'temp',
        'lower_bound': None,
        'lower_rel_op': None,
        'upper_bound': 10.5,
        'upper_rel_op': '<',
        'alert_class': 'IntervalAlert',

        'status': None,
        'value': None
        }
        """

        alert_def3 = {
            'name': 'late_data_warning',
            'stream_name': 'parsed',
            'description': 'Expected data has not arrived.',
            'alert_type': StreamAlertType.WARNING,
            'aggregate_type': AggregateStatusType.AGGREGATE_COMMS,
            'time_delta': 180,
            'alert_class': 'LateDataAlert'
        }
        """
        Late data alerts are returned from get like this:
        (value and status fields describe state of the alert)
        {
        'name': 'late_data_warning',
        'stream_name': 'parsed',
        'description': 'Expected data has not arrived.',
        'alert_type': 1,
        'aggregate_type': 1,
        'value_id': None,
        'time_delta': 180,
        'alert_class': 'LateDataAlert',
        
        'status': None,
        'value': None
        }
        """
        """
        [
            {'status': None,
            'alert_type': 1,
            'name': 'temp_warning_interval',
            'upper_bound': 10.5,
            'lower_bound': None,
            'aggregate_type': 2,
            'alert_class': 'IntervalAlert',
            'value': None,
            'value_id': 'temp',
            'lower_rel_op': None,
            'upper_rel_op': '<',
            'description': 'Temperature is above normal range.'},
            {'status': None,
            'alert_type': 1,
            'name': 'temp_alarm_interval',
            'upper_bound': 15.5,
            'lower_bound': None,
            'aggregate_type': 2,
            'alert_class': 'IntervalAlert',
            'value': None,
            'value_id': 'temp',
            'lower_rel_op': None,
            'upper_rel_op': '<',
            'description': 'Temperature is way above normal range.'},
            {'status': None,
             'stream_name': 'parsed',
             'alert_type': 1,
             'name': 'late_data_warning',
             'aggregate_type': 1,
             'alert_class': 'LateDataAlert',
             'value': None,
             'time_delta': 180,
             'description': 'Expected data has not arrived.'}
        ]
        """

        orig_alerts = [alert_def1, alert_def2, alert_def3]
        self._ia_client.set_agent({'alerts': orig_alerts})

        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertTrue(len(retval) == 3)
        alerts = retval

        self._ia_client.set_agent({'alerts': ['clear']})
        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertItemsEqual(retval, [])

        self._ia_client.set_agent({'alerts': alerts})
        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertTrue(len(retval) == 3)

        count = 0
        for x in retval:
            x.pop('status')
            x.pop('value')
            for y in orig_alerts:
                if x['name'] == y['name']:
                    count += 1
                    self.assertItemsEqual(x.keys(), y.keys())
        self.assertEquals(count, 3)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
class ProcessStateGateIntTest(IonIntegrationTestCase):
    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url('res/deploy/r2cei.yml')

        self.pd_cli = ProcessDispatcherServiceClient(node=self.container.node)

        self.process_definition = IonObject(OT.ProcessDefinition,
                                            name='test_process')
        self.process_definition.executable = {
            'module': 'ion.services.cei.test.test_process_state_gate',
            'class': 'TestProcess'
        }
        self.process_definition_id = self.pd_cli.create_process_definition(
            self.process_definition)
        self.event_queue = queue.Queue()

        self.process_schedule = IonObject(OT.ProcessSchedule)
        self.process_schedule.queueing_mode = ProcessQueueingMode.ALWAYS

        self.pid = self.pd_cli.create_process(self.process_definition_id)

        self.event_queue = queue.Queue()

        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
                                         callback=self._event_callback,
                                         origin=self.pid,
                                         origin_type="DispatchedProcess")

    def tearDown(self):
        #stop subscriber if its running
        if self.event_sub and self.event_sub._cbthread:
            self.event_sub.stop()
        self._stop_container()

    def _event_callback(self, event, *args, **kwargs):
        self.event_queue.put(event)

    def latest_event(self, timeout=10):
        # get latest event from our local event subscriber
        try:
            event = self.event_queue.get(timeout=timeout)
        except Empty:
            event = None
        return event

    def await_state(self, state, timeout=10):

        print "Emptying event queue"
        while True:
            event = self.latest_event(0)
            if event:
                print "State %s from event %s" % (event.state, event)
            else:
                break
        self.event_sub.start()

        #wait for process state
        print "Setting up %s gate" % ProcessStateEnum._str_map[state]
        gate = ProcessStateGate(self.pd_cli.read_process, self.pid, state)
        print "Waiting"
        ret = gate. await (timeout)

        print "Await got %s" % ret
        event = self.latest_event(timeout=1)

        # check false positives/negatives
        if ret and gate._get_first_chance() is None and event is None:
            self.fail(
                "ProcessStateGate got an event that EventSubscriber didnt....")

        self.event_sub.stop()

        if (not ret) or gate._get_last_chance():
            if event and event.state == state:
                self.fail(
                    "EventSubscriber got state event %s for process %s, ProcessStateGate missed it"
                    % (ProcessStateEnum._str_map[event.state], self.pid))

        return ret

    def process_start(self):
        print "Scheduling process...",
        self.pd_cli.schedule_process(self.process_definition_id,
                                     self.process_schedule,
                                     configuration={},
                                     process_id=self.pid)
        print "Done scheduling process."

    def process_stop(self):
        print "STOPPING process...",
        self.pd_cli.cancel_process(self.pid)
        print "Done stopping process"

    def test_process_state_gate(self):

        self.assertFalse(
            self.await_state(ProcessStateEnum.RUNNING, 1),
            "The process was reported as spawned, but we didn't yet")

        print "GOING TO ACTUALLY START PROCESS NOW"
        spawn_later(1, self.process_start)

        self.assertTrue(self.await_state(ProcessStateEnum.RUNNING),
                        "The process did not spawn")

        self.assertFalse(
            self.await_state(ProcessStateEnum.TERMINATED, 1),
            "The process claims to have terminated, but we didn't kill it")

        print "communicating with the process to make sure it is really running"
        test_client = TestClient()
        for i in range(5):
            self.assertEqual(i + 1, test_client.count(timeout=10))

        spawn_later(1, self.process_stop)

        self.assertTrue(
            self.await_state(ProcessStateEnum.TERMINATED),
            "The process failed to be reported as terminated when it was terminated"
        )

        self.assertFalse(
            self.await_state(ProcessStateEnum.RUNNING, 1),
            "The process was reported as spawned, but we killed it")
예제 #35
0
class GovernanceController(object):
    def __init__(self, container):
        log.debug('GovernanceController.__init__()')
        self.container = container
        self.enabled = False
        self.interceptor_by_name_dict = dict()
        self.interceptor_order = []
        self.policy_decision_point_manager = None
        self.governance_dispatcher = None

        # Holds a list per service operation of policy methods to check before the op in a process is allowed to be called
        self._service_op_preconditions = dict()

        self._is_container_org_boundary = False
        self._container_org_name = None
        self._container_org_id = None

    def start(self):

        log.debug("GovernanceController starting ...")

        self.enabled = CFG.get_safe(
            'interceptor.interceptors.governance.config.enabled', False)

        log.info("GovernanceInterceptor enabled: %s" % str(self.enabled))

        self.resource_policy_event_subscriber = None
        self.service_policy_event_subscriber = None

        #containers default to not Org Boundary and ION Root Org
        self._is_container_org_boundary = CFG.get_safe(
            'container.org_boundary', False)
        self._container_org_name = CFG.get_safe(
            'container.org_name', CFG.get_safe('system.root_org', 'ION'))
        self._container_org_id = None
        self._system_root_org_name = CFG.get_safe('system.root_org', 'ION')

        self._is_root_org_container = (
            self._container_org_name == self._system_root_org_name)

        if self.enabled:

            config = CFG.get_safe('interceptor.interceptors.governance.config')

            self.initialize_from_config(config)

            self.resource_policy_event_subscriber = EventSubscriber(
                event_type="ResourcePolicyEvent",
                callback=self.resource_policy_event_callback)
            self.resource_policy_event_subscriber.start()

            self.service_policy_event_subscriber = EventSubscriber(
                event_type="ServicePolicyEvent",
                callback=self.service_policy_event_callback)
            self.service_policy_event_subscriber.start()

            self.rr_client = ResourceRegistryServiceProcessClient(
                node=self.container.node, process=self.container)
            self.policy_client = PolicyManagementServiceProcessClient(
                node=self.container.node, process=self.container)

    def initialize_from_config(self, config):

        self.governance_dispatcher = GovernanceDispatcher()

        self.policy_decision_point_manager = PolicyDecisionPointManager(self)

        if 'interceptor_order' in config:
            self.interceptor_order = config['interceptor_order']

        if 'governance_interceptors' in config:
            gov_ints = config['governance_interceptors']

            for name in gov_ints:
                interceptor_def = gov_ints[name]

                # Instantiate and put in by_name array
                parts = interceptor_def["class"].split('.')
                modpath = ".".join(parts[:-1])
                classname = parts[-1]
                module = __import__(modpath, fromlist=[classname])
                classobj = getattr(module, classname)
                classinst = classobj()

                # Put in by_name_dict for possible re-use
                self.interceptor_by_name_dict[name] = classinst

    def stop(self):
        log.debug("GovernanceController stopping ...")

        if self.resource_policy_event_subscriber is not None:
            self.resource_policy_event_subscriber.stop()

        if self.service_policy_event_subscriber is not None:
            self.service_policy_event_subscriber.stop()

    @property
    def _rr(self):
        """
        Returns the active resource registry instance or client.

        Used to directly contact the resource registry via the container if available,
        otherwise the messaging client to the RR service is returned.
        """
        if self.container.has_capability('RESOURCE_REGISTRY'):
            return self.container.resource_registry

        return self.rr_client

    def is_container_org_boundary(self):
        return self._is_container_org_boundary

    def is_root_org_container(self):
        return self._is_root_org_container

    def get_container_org_boundary_name(self):
        return self._container_org_name

    def get_container_org_boundary_id(self):

        if not self._is_container_org_boundary:
            return None

        if self._container_org_id is None:
            org, _ = self._rr.find_resources(restype=RT.Org,
                                             name=self._container_org_name)

            if org:
                self._container_org_id = org[0]._id

        return self._container_org_id

    def process_incoming_message(self, invocation):

        self.process_message(invocation, self.interceptor_order, 'incoming')
        return self.governance_dispatcher.handle_incoming_message(invocation)

    def process_outgoing_message(self, invocation):
        self.process_message(invocation, reversed(self.interceptor_order),
                             'outgoing')
        return self.governance_dispatcher.handle_outgoing_message(invocation)

    def process_message(self, invocation, interceptor_list, method):

        for int_name in interceptor_list:
            class_inst = self.interceptor_by_name_dict[int_name]
            getattr(class_inst, method)(invocation)

            #Stop processing message if an issue with the message was found by an interceptor.
            if ( invocation.message_annotations.has_key(GovernanceDispatcher.CONVERSATION__STATUS_ANNOTATION) and invocation.message_annotations[GovernanceDispatcher.CONVERSATION__STATUS_ANNOTATION] == GovernanceDispatcher.STATUS_REJECT) or\
               ( invocation.message_annotations.has_key(GovernanceDispatcher.POLICY__STATUS_ANNOTATION) and invocation.message_annotations[GovernanceDispatcher.POLICY__STATUS_ANNOTATION] == GovernanceDispatcher.STATUS_REJECT) :
                break

        return invocation

    #Helper methods

    #Iterate the Org(s) that the user belongs to and create a header that lists only the role names per Org assigned
    #to the user; i.e. {'ION': ['Member', 'Operator'], 'Org2': ['Member']}
    def get_role_message_headers(self, org_roles):

        role_header = dict()
        try:
            for org in org_roles:
                role_header[org] = []
                for role in org_roles[org]:
                    role_header[org].append(role.name)
            return role_header

        except Exception, e:
            log.error(e)
            return role_header
예제 #36
0
class TestBulkIngest(IonIntegrationTestCase):

    EDA_MOD = 'ion.agents.data.external_dataset_agent'
    EDA_CLS = 'ExternalDatasetAgent'


    def setUp(self):
        # Start container
        self._start_container()
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        # Now create client to DataAcquisitionManagementService
        self.client = DataAcquisitionManagementServiceClient(node=self.container.node)
        self.rrclient = ResourceRegistryServiceClient(node=self.container.node)
        self.dataproductclient = DataProductManagementServiceClient(node=self.container.node)
        self.dams_client = DataAcquisitionManagementServiceClient(node=self.container.node)
        self.pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        self.processdispatchclient = ProcessDispatcherServiceClient(node=self.container.node)
        self.data_retriever    = DataRetrieverServiceClient(node=self.container.node)

        self._container_client = ContainerAgentClient(node=self.container.node, name=self.container.name)

        # Data async and subscription  TODO: Replace with new subscriber
        self._finished_count = None
        #TODO: Switch to gevent.queue.Queue
        self._async_finished_result = AsyncResult()
        self._finished_events_received = []
        self._finished_event_subscriber = None
        self._start_finished_event_subscriber()
        self.addCleanup(self._stop_finished_event_subscriber)


        self.DVR_CONFIG = {}
        self.DVR_CONFIG = {
            'dvr_mod' : 'ion.agents.data.handlers.slocum_data_handler',
            'dvr_cls' : 'SlocumDataHandler',
            }

        self._setup_resources()

        self.agent_config = {
            'driver_config' : self.DVR_CONFIG,
            'stream_config' : {},
            'agent'         : {'resource_id': self.EDA_RESOURCE_ID},
            'test_mode' : True
        }

        datasetagent_instance_obj = IonObject(RT.ExternalDatasetAgentInstance,  name='ExternalDatasetAgentInstance1', description='external data agent instance',
                                              handler_module=self.EDA_MOD, handler_class=self.EDA_CLS,
                                              dataset_driver_config=self.DVR_CONFIG, dataset_agent_config=self.agent_config )
        self.dataset_agent_instance_id = self.dams_client.create_external_dataset_agent_instance(external_dataset_agent_instance=datasetagent_instance_obj,
                                                                                                 external_dataset_agent_id=self.datasetagent_id, external_dataset_id=self.EDA_RESOURCE_ID)


        #TG: Setup/configure the granule logger to log granules as they're published
        pid = self.dams_client.start_external_dataset_agent_instance(self.dataset_agent_instance_id)

        dataset_agent_instance_obj= self.dams_client.read_external_dataset_agent_instance(self.dataset_agent_instance_id)
        print 'TestBulkIngest: Dataset agent instance obj: = ', dataset_agent_instance_obj


        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = ResourceAgentClient('datasetagentclient', name=pid,  process=FakeProcess())
        log.debug(" test_createTransformsThenActivateInstrument:: got ia client %s", str(self._ia_client))



    def create_logger(self, name, stream_id=''):

        # logger process
        producer_definition = ProcessDefinition(name=name+'_logger')
        producer_definition.executable = {
            'module':'ion.processes.data.stream_granule_logger',
            'class':'StreamGranuleLogger'
        }

        logger_procdef_id = self.processdispatchclient.create_process_definition(process_definition=producer_definition)
        configuration = {
            'process':{
                'stream_id':stream_id,
                }
        }
        pid = self.processdispatchclient.schedule_process(process_definition_id= logger_procdef_id, configuration=configuration)

        return pid

    def _start_finished_event_subscriber(self):

        def consume_event(*args,**kwargs):
            log.debug('EventSubscriber event received: %s', str(args[0]) )
            if args[0].description == 'TestingFinished':
                log.debug('TestingFinished event received')
                self._finished_events_received.append(args[0])
                if self._finished_count and self._finished_count == len(self._finished_events_received):
                    log.debug('Finishing test...')
                    self._async_finished_result.set(len(self._finished_events_received))
                    log.debug('Called self._async_finished_result.set({0})'.format(len(self._finished_events_received)))

        self._finished_event_subscriber = EventSubscriber(event_type='DeviceEvent', callback=consume_event)
        self._finished_event_subscriber.start()

    def _stop_finished_event_subscriber(self):
        if self._finished_event_subscriber:
            self._finished_event_subscriber.stop()
            self._finished_event_subscriber = None


    def tearDown(self):
        pass


    @unittest.skip('Update to agent refactor.')
    def test_slocum_data_ingest(self):

        HIST_CONSTRAINTS_1 = {}
        # Test instrument driver execute interface to start and stop streaming mode.
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)




        # Make sure the polling interval is appropriate for a test
        params = {
            'POLLING_INTERVAL': 3
        }
        self._ia_client.set_param(params)

        self._finished_count = 1

        cmd = AgentCommand(command='acquire_data')
        self._ia_client.execute(cmd)

        # Assert that data was received
        self._async_finished_result.get(timeout=15)

        self.assertTrue(len(self._finished_events_received) >= 1)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)


        #todo enable after Luke's mor to retrieve, right now must have the Time axis called 'time'
        #        replay_granule = self.data_retriever.retrieve_last_data_points(self.dataset_id, 10)

        #        rdt = RecordDictionaryTool.load_from_granule(replay_granule)
        #
        #        comp = rdt['date_pattern'] == numpy.arange(10) + 10
        #
        #        log.debug("TestBulkIngest: comp: %s", comp)
        #
        #        self.assertTrue(comp.all())

        for pid in self.loggerpids:
            self.processdispatchclient.cancel_process(pid)


    def _setup_resources(self):

        self.loggerpids = []

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(), contact=ContactInformation())
        dprov.contact.name = 'Christopher Mueller'
        dprov.contact.email = '*****@*****.**'

        # Create DataSetModel
        dataset_model = ExternalDatasetModel(name='slocum_model')
        dataset_model.datset_type = 'SLOCUM'
        dataset_model_id = self.dams_client.create_external_dataset_model(dataset_model)

        # Create ExternalDataset
        ds_name = 'slocum_test_dataset'
        dset = ExternalDataset(name=ds_name, dataset_description=DatasetDescription(), update_description=UpdateDescription(), contact=ContactInformation())


        dset.dataset_description.parameters['base_url'] = 'test_data/slocum/'
        dset.dataset_description.parameters['list_pattern'] = 'ru05-2012-021-0-0-sbd.dat'
        dset.dataset_description.parameters['date_pattern'] = '%Y %j'
        dset.dataset_description.parameters['date_extraction_pattern'] = 'ru05-([\d]{4})-([\d]{3})-\d-\d-sbd.dat'
        dset.dataset_description.parameters['temporal_dimension'] = None
        dset.dataset_description.parameters['zonal_dimension'] = None
        dset.dataset_description.parameters['meridional_dimension'] = None
        dset.dataset_description.parameters['vertical_dimension'] = None
        dset.dataset_description.parameters['variables'] = [
            'c_wpt_y_lmc',
            'sci_water_cond',
            'm_y_lmc',
            'u_hd_fin_ap_inflection_holdoff',
            'sci_m_present_time',
            'm_leakdetect_voltage_forward',
            'sci_bb3slo_b660_scaled',
            'c_science_send_all',
            'm_gps_status',
            'm_water_vx',
            'm_water_vy',
            'c_heading',
            'sci_fl3slo_chlor_units',
            'u_hd_fin_ap_gain',
            'm_vacuum',
            'u_min_water_depth',
            'm_gps_lat',
            'm_veh_temp',
            'f_fin_offset',
            'u_hd_fin_ap_hardover_holdoff',
            'c_alt_time',
            'm_present_time',
            'm_heading',
            'sci_bb3slo_b532_scaled',
            'sci_fl3slo_cdom_units',
            'm_fin',
            'x_cycle_overrun_in_ms',
            'sci_water_pressure',
            'u_hd_fin_ap_igain',
            'sci_fl3slo_phyco_units',
            'm_battpos',
            'sci_bb3slo_b470_scaled',
            'm_lat',
            'm_gps_lon',
            'sci_ctd41cp_timestamp',
            'm_pressure',
            'c_wpt_x_lmc',
            'c_ballast_pumped',
            'x_lmc_xy_source',
            'm_lon',
            'm_avg_speed',
            'sci_water_temp',
            'u_pitch_ap_gain',
            'm_roll',
            'm_tot_num_inflections',
            'm_x_lmc',
            'u_pitch_ap_deadband',
            'm_final_water_vy',
            'm_final_water_vx',
            'm_water_depth',
            'm_leakdetect_voltage',
            'u_pitch_max_delta_battpos',
            'm_coulomb_amphr',
            'm_pitch',
            ]



        ## Create the external dataset
        ds_id = self.dams_client.create_external_dataset(external_dataset=dset, external_dataset_model_id=dataset_model_id)
        ext_dprov_id = self.dams_client.create_external_data_provider(external_data_provider=dprov)

        # Register the ExternalDataset
        dproducer_id = self.dams_client.register_external_data_set(external_dataset_id=ds_id)

        ## Create the dataset agent
        datasetagent_obj = IonObject(RT.ExternalDatasetAgent,  name='ExternalDatasetAgent1', description='external data agent', handler_module=self.EDA_MOD, handler_class=self.EDA_CLS )
        self.datasetagent_id = self.dams_client.create_external_dataset_agent(external_dataset_agent=datasetagent_obj, external_dataset_model_id=dataset_model_id)

        # Generate the data product and associate it to the ExternalDataset
        pdict = DatasetManagementService.get_parameter_dictionary_by_name('ctd_parsed_param_dict')
        streamdef_id = self.pubsub_client.create_stream_definition(name="temp", parameter_dictionary_id=pdict.identifier)

        tdom, sdom = time_series_domain()
        tdom = tdom.dump()
        sdom = sdom.dump()


        dprod = IonObject(RT.DataProduct,
                          name='slocum_parsed_product',
                          description='parsed slocum product',
                          temporal_domain = tdom,
                          spatial_domain = sdom)

        self.dproduct_id = self.dataproductclient.create_data_product(data_product=dprod,
                                                                      stream_definition_id=streamdef_id)

        self.dams_client.assign_data_product(input_resource_id=ds_id, data_product_id=self.dproduct_id)

        #save the incoming slocum data
        self.dataproductclient.activate_data_product_persistence(self.dproduct_id)
        self.addCleanup(self.dataproductclient.suspend_data_product_persistence, self.dproduct_id)
        
        stream_ids, assn = self.rrclient.find_objects(subject=self.dproduct_id, predicate=PRED.hasStream, object_type=RT.Stream, id_only=True)
        stream_id = stream_ids[0]

        dataset_id, assn = self.rrclient.find_objects(subject=self.dproduct_id, predicate=PRED.hasDataset, object_type=RT.Dataset, id_only=True)
        self.dataset_id = dataset_id[0]

        pid = self.create_logger('slocum_parsed_product', stream_id )
        self.loggerpids.append(pid)

        self.DVR_CONFIG['dh_cfg'] = {
            'TESTING':True,
            'stream_id':stream_id,
            'param_dictionary':pdict.dump(),
            'data_producer_id':dproducer_id, #CBM: Should this be put in the main body of the config - with mod & cls?
            'max_records':20,
            }

        # Create the logger for receiving publications
        #self.create_stream_and_logger(name='slocum',stream_id=stream_id)
        # Create agent config.
        self.EDA_RESOURCE_ID = ds_id
        self.EDA_NAME = ds_name
예제 #37
0
class ProcessStateWaiter(object):
    def __init__(self):
        self.event_queue = queue.Queue()
        self.event_sub = None

    def start(self, process_id=None):
        assert self.event_sub is None
        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
            callback=self._event_callback, origin=process_id, origin_type="DispatchedProcess")
        self.event_sub.start()

    def stop(self):
        if self.event_sub:
            self.event_sub.stop()
            self.event_sub = None

    def _event_callback(self, event, *args, **kwargs):
        self.event_queue.put(event)

    def await_state_event(self, pid=None, state=None, timeout=30, strict=False):
        """Wait for a state event for a process.

        if strict is False, allow intermediary events
        """

        start_time = datetime.now()

        assert state in ProcessStateEnum._str_map, "process state %s unknown!" % state
        state_str = ProcessStateEnum._str_map.get(state)

        # stick the pid into a container if it is only one
        if pid is not None and not isinstance(pid, (list, tuple)):
            pid = (pid,)

        while 1:
            if datetime.now() - start_time > timedelta(seconds=timeout):
                raise AssertionError("Waiter timeout! Waited %s seconds for process %s state %s" % (timeout, pid, state_str))
            try:
                event = self.event_queue.get(timeout=timeout)
            except queue.Empty:
                raise AssertionError("Event timeout! Waited %s seconds for process %s state %s" % (timeout, pid, state_str))
            log.debug("Got event: %s", event)

            if (pid is None or event.origin in pid) and (state is None or event.state == state):
                return event

            elif strict:
                raise AssertionError("Got unexpected event %s. Expected state %s for process %s" % (event, state_str, pid))

    def await_many_state_events(self, pids, state=None, timeout=30, strict=False):
        pid_set = set(pids)
        while pid_set:
            event = self.await_state_event(tuple(pid_set), state, timeout=timeout, strict=strict)
            pid_set.remove(event.origin)

    def await_nothing(self, pid=None, timeout=10):
        start_time = datetime.now()

        # stick the pid into a container if it is only one
        if pid is not None and not isinstance(pid, (list, tuple)):
            pid = (pid,)

        while 1:
            timeleft = timedelta(seconds=timeout) - (datetime.now() - start_time)
            timeleft_seconds = timeleft.total_seconds()
            if timeleft_seconds <= 0:
                return
            try:
                event = self.event_queue.get(timeout=timeleft_seconds)
                if pid is None or event.origin in pid:
                    state_str = ProcessStateEnum._str_map.get(event.state, str(event.state))
                    raise AssertionError("Expected no event, but got state %s for process %s" % (state_str, event.origin))

            except queue.Empty:
                return
예제 #38
0
class EndpointMixin(object):
    """
    """
    
    def __init__(self):
        """
        """
        pass
    
    def mixin_on_init(self):
        """
        """
        self._server = None
        self._client = None
        self._other_host = self.CFG.other_host
        self._other_port = self.CFG.other_port
        self._this_port = self.CFG.this_port
        self._platform_resource_id = self.CFG.platform_resource_id
        self._link_status = TelemetryStatusType.UNAVAILABLE
        self._event_subscriber = None
        self._server_greenlet = None
        self._publisher = EventPublisher()
    
    def mixin_on_start(self):
        """
        """
        self._server = R3PCServer(self._req_callback,
                                self._server_close_callback)
        self._client = R3PCClient(self._ack_callback,
                                self._client_close_callback)
        if self._this_port == 0:            
            self._this_port = self._server.start('*', self._this_port)
        else:
            self._server.start('*', self._this_port)
        log.debug('%s server binding to *:%i', self.__class__.__name__,
                  self._this_port)
    
        # Start the event subscriber.
        self._event_subscriber = EventSubscriber(
            event_type='PlatformTelemetryEvent',
            callback=self._consume_telemetry_event,
            origin=self._platform_resource_id)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)
    
    def mixin_on_quit(self):
        """
        """
        self._stop()
    
    def mixin_on_stop(self):
        """
        """
        self._stop()
    
    ######################################################################    
    # Helpers.
    ######################################################################    

        
    def _stop(self):
        """
        Stop sockets and subscriber.
        """
        if self._event_subscriber:
            self._event_subscriber.stop()
            self._event_subscriber = None
        if self._server:
            self._server.stop()
            self._server = None
        if self._client:
            self._client.stop()
            self._client = None
class TestBulkIngest(IonIntegrationTestCase):

    EDA_MOD = 'ion.agents.data.external_dataset_agent'
    EDA_CLS = 'ExternalDatasetAgent'


    def setUp(self):
        # Start container
        self._start_container()
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        # Now create client to DataAcquisitionManagementService
        self.client = DataAcquisitionManagementServiceClient(node=self.container.node)
        self.rrclient = ResourceRegistryServiceClient(node=self.container.node)
        self.dataproductclient = DataProductManagementServiceClient(node=self.container.node)
        self.dams_client = DataAcquisitionManagementServiceClient(node=self.container.node)
        self.pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        self.processdispatchclient = ProcessDispatcherServiceClient(node=self.container.node)
        self.data_retriever    = DataRetrieverServiceClient(node=self.container.node)

        self._container_client = ContainerAgentClient(node=self.container.node, name=self.container.name)

        # Data async and subscription  TODO: Replace with new subscriber
        self._finished_count = None
        #TODO: Switch to gevent.queue.Queue
        self._async_finished_result = AsyncResult()
        self._finished_events_received = []
        self._finished_event_subscriber = None
        self._start_finished_event_subscriber()
        self.addCleanup(self._stop_finished_event_subscriber)


        self.DVR_CONFIG = {}
        self.DVR_CONFIG = {
            'dvr_mod' : 'ion.agents.data.handlers.slocum_data_handler',
            'dvr_cls' : 'SlocumDataHandler',
            }

        self._setup_resources()

        self.agent_config = {
            'driver_config' : self.DVR_CONFIG,
            'stream_config' : {},
            'agent'         : {'resource_id': self.EDA_RESOURCE_ID},
            'test_mode' : True
        }

        datasetagent_instance_obj = IonObject(RT.ExternalDatasetAgentInstance,  name='ExternalDatasetAgentInstance1', description='external data agent instance',
                                              handler_module=self.EDA_MOD, handler_class=self.EDA_CLS,
                                              dataset_driver_config=self.DVR_CONFIG, dataset_agent_config=self.agent_config )
        self.dataset_agent_instance_id = self.dams_client.create_external_dataset_agent_instance(external_dataset_agent_instance=datasetagent_instance_obj,
                                                                                                 external_dataset_agent_id=self.datasetagent_id, external_dataset_id=self.EDA_RESOURCE_ID)


        #TG: Setup/configure the granule logger to log granules as they're published
        pid = self.dams_client.start_external_dataset_agent_instance(self.dataset_agent_instance_id)

        dataset_agent_instance_obj= self.dams_client.read_external_dataset_agent_instance(self.dataset_agent_instance_id)
        print 'TestBulkIngest: Dataset agent instance obj: = ', dataset_agent_instance_obj


        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = ResourceAgentClient('datasetagentclient', name=pid,  process=FakeProcess())
        log.debug(" test_createTransformsThenActivateInstrument:: got ia client %s", str(self._ia_client))



    def create_logger(self, name, stream_id=''):

        # logger process
        producer_definition = ProcessDefinition(name=name+'_logger')
        producer_definition.executable = {
            'module':'ion.processes.data.stream_granule_logger',
            'class':'StreamGranuleLogger'
        }

        logger_procdef_id = self.processdispatchclient.create_process_definition(process_definition=producer_definition)
        configuration = {
            'process':{
                'stream_id':stream_id,
                }
        }
        pid = self.processdispatchclient.schedule_process(process_definition_id= logger_procdef_id, configuration=configuration)

        return pid

    def _start_finished_event_subscriber(self):

        def consume_event(*args,**kwargs):
            log.debug('EventSubscriber event received: %s', str(args[0]) )
            if args[0].description == 'TestingFinished':
                log.debug('TestingFinished event received')
                self._finished_events_received.append(args[0])
                if self._finished_count and self._finished_count == len(self._finished_events_received):
                    log.debug('Finishing test...')
                    self._async_finished_result.set(len(self._finished_events_received))
                    log.debug('Called self._async_finished_result.set({0})'.format(len(self._finished_events_received)))

        self._finished_event_subscriber = EventSubscriber(event_type='DeviceEvent', callback=consume_event)
        self._finished_event_subscriber.start()

    def _stop_finished_event_subscriber(self):
        if self._finished_event_subscriber:
            self._finished_event_subscriber.stop()
            self._finished_event_subscriber = None


    def tearDown(self):
        pass


    @unittest.skip('Update to agent refactor.')
    def test_slocum_data_ingest(self):

        HIST_CONSTRAINTS_1 = {}
        # Test instrument driver execute interface to start and stop streaming mode.
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)




        # Make sure the polling interval is appropriate for a test
        params = {
            'POLLING_INTERVAL': 3
        }
        self._ia_client.set_param(params)

        self._finished_count = 1

        cmd = AgentCommand(command='acquire_data')
        self._ia_client.execute(cmd)

        # Assert that data was received
        self._async_finished_result.get(timeout=15)

        self.assertTrue(len(self._finished_events_received) >= 1)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)


        #todo enable after Luke's mor to retrieve, right now must have the Time axis called 'time'
        #        replay_granule = self.data_retriever.retrieve_last_data_points(self.dataset_id, 10)

        #        rdt = RecordDictionaryTool.load_from_granule(replay_granule)
        #
        #        comp = rdt['date_pattern'] == numpy.arange(10) + 10
        #
        #        log.debug("TestBulkIngest: comp: %s", comp)
        #
        #        self.assertTrue(comp.all())

        for pid in self.loggerpids:
            self.processdispatchclient.cancel_process(pid)


    def _setup_resources(self):

        self.loggerpids = []

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(), contact=ContactInformation())
        dprov.contact.name = 'Christopher Mueller'
        dprov.contact.email = '*****@*****.**'

        # Create DataSetModel
        dataset_model = ExternalDatasetModel(name='slocum_model')
        dataset_model.datset_type = 'SLOCUM'
        dataset_model_id = self.dams_client.create_external_dataset_model(dataset_model)

        # Create ExternalDataset
        ds_name = 'slocum_test_dataset'
        dset = ExternalDataset(name=ds_name, dataset_description=DatasetDescription(), update_description=UpdateDescription(), contact=ContactInformation())


        dset.dataset_description.parameters['base_url'] = 'test_data/slocum/'
        dset.dataset_description.parameters['list_pattern'] = 'ru05-2012-021-0-0-sbd.dat'
        dset.dataset_description.parameters['date_pattern'] = '%Y %j'
        dset.dataset_description.parameters['date_extraction_pattern'] = 'ru05-([\d]{4})-([\d]{3})-\d-\d-sbd.dat'
        dset.dataset_description.parameters['temporal_dimension'] = None
        dset.dataset_description.parameters['zonal_dimension'] = None
        dset.dataset_description.parameters['meridional_dimension'] = None
        dset.dataset_description.parameters['vertical_dimension'] = None
        dset.dataset_description.parameters['variables'] = [
            'c_wpt_y_lmc',
            'sci_water_cond',
            'm_y_lmc',
            'u_hd_fin_ap_inflection_holdoff',
            'sci_m_present_time',
            'm_leakdetect_voltage_forward',
            'sci_bb3slo_b660_scaled',
            'c_science_send_all',
            'm_gps_status',
            'm_water_vx',
            'm_water_vy',
            'c_heading',
            'sci_fl3slo_chlor_units',
            'u_hd_fin_ap_gain',
            'm_vacuum',
            'u_min_water_depth',
            'm_gps_lat',
            'm_veh_temp',
            'f_fin_offset',
            'u_hd_fin_ap_hardover_holdoff',
            'c_alt_time',
            'm_present_time',
            'm_heading',
            'sci_bb3slo_b532_scaled',
            'sci_fl3slo_cdom_units',
            'm_fin',
            'x_cycle_overrun_in_ms',
            'sci_water_pressure',
            'u_hd_fin_ap_igain',
            'sci_fl3slo_phyco_units',
            'm_battpos',
            'sci_bb3slo_b470_scaled',
            'm_lat',
            'm_gps_lon',
            'sci_ctd41cp_timestamp',
            'm_pressure',
            'c_wpt_x_lmc',
            'c_ballast_pumped',
            'x_lmc_xy_source',
            'm_lon',
            'm_avg_speed',
            'sci_water_temp',
            'u_pitch_ap_gain',
            'm_roll',
            'm_tot_num_inflections',
            'm_x_lmc',
            'u_pitch_ap_deadband',
            'm_final_water_vy',
            'm_final_water_vx',
            'm_water_depth',
            'm_leakdetect_voltage',
            'u_pitch_max_delta_battpos',
            'm_coulomb_amphr',
            'm_pitch',
            ]



        ## Create the external dataset
        ds_id = self.dams_client.create_external_dataset(external_dataset=dset, external_dataset_model_id=dataset_model_id)
        ext_dprov_id = self.dams_client.create_external_data_provider(external_data_provider=dprov)

        # Register the ExternalDataset
        dproducer_id = self.dams_client.register_external_data_set(external_dataset_id=ds_id)

        ## Create the dataset agent
        datasetagent_obj = IonObject(RT.ExternalDatasetAgent,  name='ExternalDatasetAgent1', description='external data agent', handler_module=self.EDA_MOD, handler_class=self.EDA_CLS )
        self.datasetagent_id = self.dams_client.create_external_dataset_agent(external_dataset_agent=datasetagent_obj, external_dataset_model_id=dataset_model_id)

        # Generate the data product and associate it to the ExternalDataset
        pdict = DatasetManagementService.get_parameter_dictionary_by_name('ctd_parsed_param_dict')
        streamdef_id = self.pubsub_client.create_stream_definition(name="temp", parameter_dictionary_id=pdict.identifier)

        tdom, sdom = time_series_domain()
        tdom = tdom.dump()
        sdom = sdom.dump()


        dprod = IonObject(RT.DataProduct,
                          name='slocum_parsed_product',
                          description='parsed slocum product',
                          temporal_domain = tdom,
                          spatial_domain = sdom)

        self.dproduct_id = self.dataproductclient.create_data_product(data_product=dprod,
                                                                      stream_definition_id=streamdef_id)

        self.dams_client.assign_data_product(input_resource_id=ds_id, data_product_id=self.dproduct_id)

        #save the incoming slocum data
        self.dataproductclient.activate_data_product_persistence(self.dproduct_id)

        stream_ids, assn = self.rrclient.find_objects(subject=self.dproduct_id, predicate=PRED.hasStream, object_type=RT.Stream, id_only=True)
        stream_id = stream_ids[0]

        dataset_id, assn = self.rrclient.find_objects(subject=self.dproduct_id, predicate=PRED.hasDataset, object_type=RT.Dataset, id_only=True)
        self.dataset_id = dataset_id[0]

        pid = self.create_logger('slocum_parsed_product', stream_id )
        self.loggerpids.append(pid)

        self.DVR_CONFIG['dh_cfg'] = {
            'TESTING':True,
            'stream_id':stream_id,
            'param_dictionary':pdict.dump(),
            'data_producer_id':dproducer_id, #CBM: Should this be put in the main body of the config - with mod & cls?
            'max_records':20,
            }

        # Create the logger for receiving publications
        #self.create_stream_and_logger(name='slocum',stream_id=stream_id)
        # Create agent config.
        self.EDA_RESOURCE_ID = ds_id
        self.EDA_NAME = ds_name
class InstrumentAgentTestDA():
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests for Direct Access mode and provide 
    a tutorial on use of the agent setup and interface.
    """
    
    def _setup(self):

        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        self._ia_client = None

        log.info('Creating driver integration test support:')
        log.info('driver uri: %s', DRV_URI)
        log.info('device address: %s', DEV_ADDR)
        log.info('device port: %s', DEV_PORT)
        log.info('log delimiter: %s', DELIM)
        log.info('work dir: %s', WORK_DIR)
        log.info('LAUNCH_FROM_EGG: %s', LAUNCH_FROM_EGG)
        self._support = DriverIntegrationTestSupport(None,
            None,
            DEV_ADDR,
            DEV_PORT,
            DATA_PORT,
            CMD_PORT,
            PA_BINARY,
            DELIM,
            WORK_DIR)

        # Start port agent, add stop to cleanup.
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)

        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        # Start a resource agent client to talk with the instrument agent.
        log.info('starting IA process')
        self._ia_client = start_instrument_agent_process(self.container)
        self.addCleanup(self._verify_agent_reset)
        log.info('test setup complete')


    ###############################################################################
    # Port agent helpers.
    ###############################################################################
        
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        port = self._support.start_pagent()
        log.info('Port agent started at port %i',port)
        
        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr' : 'localhost',
            'port' : port,
            'cmd_port' : CMD_PORT
        }
                        
    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state()
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd)
            
    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.', 
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()
            
        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()
            
        self._event_subscriber = EventSubscriber(
            event_type=type, callback=consume_event,
            origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # tcp helpers.
    ###############################################################################        

    def _start_tcp_client(self, retval):
        host = retval.result['ip_address']
        port = retval.result['port']
        tcp_client = TcpClient(host, port)
        return tcp_client
        

    ###############################################################################
    # Assert helpers.
    ###############################################################################        

    def assertInstrumentAgentState(self, expected_state, timeout=0):
        end_time = time.time() + timeout
        
        while (True):
            state = self._ia_client.get_agent_state()
            log.debug("assertInstrumentAgentState: IA state = %s, expected state = %s" %(state, expected_state))
            if state == expected_state:
                return True
            if time.time() >= end_time:
                self.fail("assertInstrumentAgentState: IA failed to transition to %s state" %expected_state)
            gevent.sleep(1)
                
    
    def assertSetInstrumentState(self, command, new_state):
        if type(command) == str:
            log.debug("assertSetInstrumentState: building command for %s" %command)
            cmd = AgentCommand(command=command)
        else:
            cmd = command
        retval = self._ia_client.execute_agent(cmd)
        self.assertInstrumentAgentState(new_state)
        return retval
        

    ###############################################################################
    # Tests.
    #
    # response from IA for start DA looks similar to the following:
    #
    # {'status': 0, 'type_': 'AgentCommandResult', 'command': 'RESOURCE_AGENT_EVENT_GO_DIRECT_ACCESS',
    # 'result': {'token': 'F2B6EED3-F926-4B3B-AE80-4F8DE79276F3', 'ip_address': 'Edwards-MacBook-Pro.local', 'port': 8000},
    # 'ts_execute': '1344889063861', 'command_id': ''}
    ###############################################################################
    
    
    def test_direct_access_vsp_IA_shutdown(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown from IA. 
        """
        def start_and_stop_DA():
            retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
            tcp_client = self._start_tcp_client(retval)
            self.assertTrue(tcp_client.send_data('ts\r\n'))
            self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                           'session_timeout':600,
                           'inactivity_timeout':600})
        
        # test for starting DA, making connection with TCP client, sending/recving commands to/from instrument, and DA shut down
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)
        
        self.assertTrue(tcp_client.send_data('ts\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ts
        
        -0.1964,85.12299, 697.168,   39.5241, 1506.965, 01 Feb 2001, 01:01:00
        """
        
        sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'
        sample_regex = re.compile(sample_pattern)
        lines = response.split('\r\n')
        
        sample_count = 0
        for x in lines:
            if sample_regex.match(x):
                sample_count += 1
        #self.assertEqual(sample_count, 1)

        self.assertTrue(tcp_client.send_data('ds\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ds
        SBE37-SMP V 2.6 SERIAL NO. 2165   01 Feb 2001  01:01:00
        not logging: received stop command
        sample interval = 23195 seconds
        samplenumber = 0, free = 200000
        do not transmit real-time data
        do not output salinity with each sample
        do not output sound velocity with each sample
        do not store time with each sample
        number of samples to average = 0
        reference pressure = 0.0 db
        serial sync mode disabled
        wait time after serial sync sampling = 0 seconds
        internal pump is installed
        temperature = 7.54 deg C
        WARNING: LOW BATTERY VOLTAGE!!
        """

        self.assertNotEqual(response.find('SBE37-SMP'), -1)
        self.assertNotEqual(response.find('sample interval'), -1)
        self.assertNotEqual(response.find('samplenumber'), -1)
        self.assertNotEqual(response.find('number of samples to average'), -1)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)
        
        # test for starting and shutting down DA w/o ever connecting TCP client 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)
        
        # test for race condition in DA when shut down by IA after sending something from TCP client
        for i in range(0, 10):
            start_and_stop_DA()
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_telnet_IA_shutdown(self):
        """
        Test agent direct_access mode for telnet when shutdown from IA. 
        """

        def start_and_stop_DA():
            retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
            tcp_client = self._start_tcp_client(retval)
            self.assertTrue(tcp_client.start_telnet(token=retval.result['token']))
            self.assertTrue(tcp_client.send_data('ts\r\n'))
            self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':600,
                           'inactivity_timeout':600})

        # test for starting DA, making connection with TCP/telnet client, sending/recving commands to/from instrument, and DA shut down
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)
        
        self.assertTrue(tcp_client.start_telnet(token=retval.result['token']))
        
        self.assertTrue(tcp_client.send_data('ts\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ts
        
        -0.1964,85.12299, 697.168,   39.5241, 1506.965, 01 Feb 2001, 01:01:00
        """
        
        sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'
        sample_regex = re.compile(sample_pattern)
        lines = response.split('\r\n')
        
        sample_count = 0
        for x in lines:
            if sample_regex.match(x):
                sample_count += 1
        #self.assertEqual(sample_count, 1)

        self.assertTrue(tcp_client.send_data('ds\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ds
        SBE37-SMP V 2.6 SERIAL NO. 2165   01 Feb 2001  01:01:00
        not logging: received stop command
        sample interval = 23195 seconds
        samplenumber = 0, free = 200000
        do not transmit real-time data
        do not output salinity with each sample
        do not output sound velocity with each sample
        do not store time with each sample
        number of samples to average = 0
        reference pressure = 0.0 db
        serial sync mode disabled
        wait time after serial sync sampling = 0 seconds
        internal pump is installed
        temperature = 7.54 deg C
        WARNING: LOW BATTERY VOLTAGE!!
        """

        self.assertNotEqual(response.find('SBE37-SMP'), -1)
        self.assertNotEqual(response.find('sample interval'), -1)
        self.assertNotEqual(response.find('samplenumber'), -1)
        self.assertNotEqual(response.find('number of samples to average'), -1)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)
        
        # test for starting and shutting down DA w/o ever connecting TCP client 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)
        
        # test for starting and shutting down DA w/o ever entering a username 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)
        
        # test for starting and shutting down DA w/o ever entering a token 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND, ResourceAgentState.COMMAND)
        
        # test for race condition in DA when shut down by IA after sending something from TCP client
        for i in range(0, 10):
            start_and_stop_DA()
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_telnet_login_failure(self):
        """
        Test agent direct_access mode for telnet when login fails. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':600,
                           'inactivity_timeout':600})

        # test that DA session quits when bad token is sent
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)
        
        self.assertFalse(tcp_client.start_telnet(token='some junk'))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        

    def test_direct_access_telnet_session_setup_failure(self):
        """
        Test agent direct_access mode for telnet when session setup fails. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':600,
                           'inactivity_timeout':600})

        # test that DA session quits when bad token is sent
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)
        
        self.assertTrue(tcp_client.start_telnet(token=retval.result['token'], handshake=False))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        

    def test_direct_access_vsp_client_disconnect(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown by tcp client disconnect. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                           'session_timeout':600,
                           'inactivity_timeout':600})
        
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)
        
        self.assertTrue(tcp_client.send_data('ts\r\n'))
        
        tcp_client.disconnect()

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
                
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_telnet_client_disconnect(self):
        """
        Test agent direct_access mode for telnet when shutdown by tcp client disconnect. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':600,
                           'inactivity_timeout':600})
        
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(retval.result['token']))

        self.assertTrue(tcp_client.send_data('ts\r\n'))
        
        tcp_client.disconnect()

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
                
        # test for starting and disconnecting client w/o ever entering a username 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        tcp_client.disconnect()
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        # test for starting and disconnecting client w/o ever entering a token 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        tcp_client.disconnect()
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        # test for starting and disconnecting client w/o ever negotiating the telnet session 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet(token=retval.result['token'], handshake=False))
        tcp_client.disconnect()
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_vsp_session_timeout(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown by session timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                           'session_timeout':10,
                           'inactivity_timeout':600})
        
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        self._start_tcp_client(retval)

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)        
        
        # test for starting and session timeout w/o ever connecting TCP client 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)        
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_telnet_session_timeout(self):
        """
        Test agent direct_access mode for telnet when shutdown by session timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':10,
                           'inactivity_timeout':600})
        
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(retval.result['token']))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)        
        
        # test for starting and session timeout w/o ever entering a username 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        # test for starting and session timeout w/o ever entering a token 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        # test for starting and session timeout w/o ever negotiating the telnet session 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet(token=retval.result['token'], handshake=False))
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_vsp_inactivity_timeout(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown by inactivity timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                           'session_timeout':600,
                           'inactivity_timeout':10})
        
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        self._start_tcp_client(retval)

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)        
        
        # test for starting and inactivity timeout w/o ever connecting TCP client 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)        
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    def test_direct_access_telnet_inactivity_timeout(self):
        """
        Test agent direct_access mode for telnet when shutdown by inactivity timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)
    
        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE, ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE, ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':600,
                           'inactivity_timeout':10})
        
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(retval.result['token']))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)        
        
        # test for starting and inactivity timeout w/o ever entering a username 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        # test for starting and inactivity timeout w/o ever entering a token 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        # test for starting and inactivity timeout w/o ever negotiating the telnet session 
        retval = self.assertSetInstrumentState(cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet(token=retval.result['token'], handshake=False))
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)        
        
        self.assertSetInstrumentState(ResourceAgentEvent.RESET, ResourceAgentState.UNINITIALIZED)
        
    @unittest.skip('A manual test for timing purposes.')        
    def test_exit_da_timing(self):
        """
        test_exit_da_timing
        Test time it takes to leave direct access and return to command mode.
        """

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                           'session_timeout':600,
                           'inactivity_timeout':600})
        
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.DIRECT_ACCESS)
                
        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        starttime = time.time()
        
        cmd = AgentCommand(command=ResourceAgentEvent.GO_COMMAND)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)
        
        delta = time.time() - starttime
        
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        print '######## exiting direct access takes: %f seconds' % delta
예제 #41
0
class EventPersister(StandaloneProcess):
    def on_init(self):
        # Time in between event persists
        self.persist_interval = float(
            self.CFG.get_safe("process.event_persister.persist_interval", 1.0))

        self.persist_blacklist = self.CFG.get_safe(
            "process.event_persister.persist_blacklist", {})

        self._event_type_blacklist = [
            entry['event_type'] for entry in self.persist_blacklist
            if entry.get('event_type', None) and len(entry) == 1
        ]
        self._complex_blacklist = [
            entry for entry in self.persist_blacklist
            if not (entry.get('event_type', None) and len(entry) == 1)
        ]
        if self._complex_blacklist:
            log.warn(
                "EventPersister does not yet support complex blacklist expressions: %s",
                self._complex_blacklist)

        # Time in between view refreshs
        self.refresh_interval = float(
            self.CFG.get_safe("process.event_persister.refresh_interval",
                              60.0))

        # Holds received events FIFO in syncronized queue
        self.event_queue = Queue()

        # Temporarily holds list of events to persist while datastore operation are not yet completed
        # This is where events to persist will remain if datastore operation fails occasionally.
        self.events_to_persist = None

        # Number of unsuccessful attempts to persist in a row
        self.failure_count = 0

        # bookkeeping for greenlet
        self._persist_greenlet = None
        self._terminate_persist = Event(
        )  # when set, exits the persister greenlet
        self._refresh_greenlet = None
        self._terminate_refresh = Event(
        )  # when set, exits the refresher greenlet

        # The event subscriber
        self.event_sub = None

    def on_start(self):
        # Persister thread
        self._persist_greenlet = spawn(self._persister_loop,
                                       self.persist_interval)
        log.debug(
            'EventPersister persist greenlet started in "%s" (interval %s)',
            self.__class__.__name__, self.persist_interval)

        # View trigger thread
        self._refresh_greenlet = spawn(self._refresher_loop,
                                       self.refresh_interval)
        log.debug(
            'EventPersister view refresher greenlet started in "%s" (interval %s)',
            self.__class__.__name__, self.refresh_interval)

        # Event subscription
        self.event_sub = EventSubscriber(pattern=EventSubscriber.ALL_EVENTS,
                                         callback=self._on_event,
                                         queue_name="event_persister")

        self.event_sub.start()

    def on_quit(self):
        # Stop event subscriber
        self.event_sub.stop()

        # tell the trigger greenlet we're done
        self._terminate_persist.set()
        self._terminate_refresh.set()

        # wait on the greenlets to finish cleanly
        self._persist_greenlet.join(timeout=5)
        self._refresh_greenlet.join(timeout=5)

    def _on_event(self, event, *args, **kwargs):
        if not self._in_blacklist(event):
            self.event_queue.put(event)

    def _in_blacklist(self, event):
        if event.type_ in self._event_type_blacklist:
            return True
        if event.base_types:
            for base_type in event.base_types:
                if base_type in self._event_type_blacklist:
                    return True
            # TODO: Complex event blacklist
        return False

    def _persister_loop(self, persist_interval):
        log.debug('Starting event persister thread with persist_interval=%s',
                  persist_interval)

        # Event.wait returns False on timeout (and True when set in on_quit), so we use this to both exit cleanly and do our timeout in a loop
        while not self._terminate_persist.wait(timeout=persist_interval):
            try:
                if self.events_to_persist and self.failure_count > 2:
                    bad_events = []
                    log.warn("Attempting to persist %s events individually" %
                             (len(self.events_to_persist)))
                    for event in self.events_to_persist:
                        try:
                            self.container.event_repository.put_event(event)
                        except Exception:
                            bad_events.append(event)

                    if len(self.events_to_persist) != len(bad_events):
                        log.warn(
                            "Succeeded to persist some of the events - rest must be bad"
                        )
                        self._log_events(bad_events)
                    elif bad_events:
                        log.error("Discarding %s events after %s attempts!!" %
                                  (len(bad_events), self.failure_count))
                        self._log_events(bad_events)

                    self.events_to_persist = None
                    self.failure_count = 0

                elif self.events_to_persist:
                    # There was an error last time and we need to retry
                    log.info("Retry persisting %s events" %
                             len(self.events_to_persist))
                    self._persist_events(self.events_to_persist)
                    self.events_to_persist = None

                self.events_to_persist = [
                    self.event_queue.get()
                    for x in xrange(self.event_queue.qsize())
                ]

                self._persist_events(self.events_to_persist)
                self.events_to_persist = None
                self.failure_count = 0
            except Exception as ex:
                # Note: Persisting events may fail occasionally during test runs (when the "events" datastore is force
                # deleted and recreated). We'll log and keep retrying forever.
                log.exception(
                    "Failed to persist %s received events. Will retry next cycle"
                    % len(self.events_to_persist))
                self.failure_count += 1
                self._log_events(self.events_to_persist)

    def _persist_events(self, event_list):
        if event_list:
            self.container.event_repository.put_events(event_list)

    def _log_events(self, events):
        events_str = pprint.pformat([event.__dict__
                                     for event in events]) if events else ""
        log.warn("EVENTS:\n%s", events_str)

    def _refresher_loop(self, refresh_interval):
        log.debug(
            'Starting event view refresher thread with refresh_interval=%s',
            refresh_interval)

        # Event.wait returns False on timeout (and True when set in on_quit), so we use this to both exit cleanly and do our timeout in a loop
        while not self._terminate_persist.wait(timeout=refresh_interval):
            try:
                self.container.event_repository.find_events(limit=1)
            except Exception as ex:
                log.exception("Failed to refresh events views")
예제 #42
0
class TestIAAlarms(IonIntegrationTestCase):
    """
    """
    
    ############################################################################
    # Setup, teardown.
    ############################################################################
        
    def setUp(self):
        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        self._ia_client = None

        log.info('Creating driver integration test support:')
        log.info('driver uri: %s', DRV_URI)
        log.info('device address: %s', DEV_ADDR)
        log.info('device port: %s', DEV_PORT)
        log.info('log delimiter: %s', DELIM)
        log.info('work dir: %s', WORK_DIR)
        self._support = DriverIntegrationTestSupport(None,
                                                     None,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
        
        # Start port agent, add stop to cleanup.
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)    
        
        # Start container.
        log.info('Staring capability container.')
        self._start_container()
        
        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        log.info('building stream configuration')
        # Setup stream config.
        self._build_stream_config()

        # Start a resource agent client to talk with the instrument agent.
        log.info('starting IA process')
        self._ia_client = start_instrument_agent_process(self.container, self._stream_config)
        self.addCleanup(self._verify_agent_reset)
        log.info('test setup complete')


    ###############################################################################
    # Port agent helpers.
    ###############################################################################
        
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        port = self._support.start_pagent()
        log.info('Port agent started at port %i',port)
        
        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr' : 'localhost',
            'port' : port,
            'cmd_port' : CMD_PORT
        }
                        
    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state()
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd)
            
    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='StreamAlarmEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            print '#################'
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.', 
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()
            
        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()
            
        self._event_subscriber = EventSubscriber(
            event_type=type, callback=consume_event,
            origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None


    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient() 
        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        streams = {
            'parsed' : 'ctd_parsed_param_dict',
            'raw'    : 'ctd_raw_param_dict'
        }

        for (stream_name, param_dict_name) in streams.iteritems():
            pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)

            stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
            pd            = pubsub_client.read_stream_definition(stream_def_id).parameter_dictionary

            stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)

            stream_config = dict(stream_route=stream_route,
                                 routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 stream_definition_ref=stream_def_id,
                                 parameter_dictionary=pd)
            
            if stream_name == 'parsed':
                
                type = 'IntervalAlarmDef'
                kwargs = {
                    'name' : 'test_sim_warning',
                    'stream_name' : 'parsed',
                    'value_id' : 'temp',
                    'message' : 'Temperature is above test range of 5.0.',
                    'type' : StreamAlarmType.WARNING,
                    'upper_bound' : 5.0,
                    'upper_rel_op' : '<'
                }
                alarm = {}
                alarm['type'] = type
                alarm['kwargs'] = kwargs
                alarms = [alarm]
                stream_config['alarms'] = alarms
            
            self._stream_config[stream_name] = stream_config

    def _start_data_subscribers(self, count):
        """
        """        
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
                
        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        self._async_sample_result = AsyncResult()

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            log.info('Received message on %s (%s,%s)', stream_id, stream_route.exchange_point, stream_route.routing_key)
            #print '######################## stream message:'
            #print str(message)
            self._samples_received.append(message)
            if len(self._samples_received) == count:
                self._async_sample_result.set()

        for (stream_name, stream_config) in self._stream_config.iteritems():
            
            if stream_name == 'parsed':
                
                # Create subscription for parsed stream only.

                stream_id = stream_config['stream_id']
                
                from pyon.util.containers import create_unique_identifier
                # exchange_name = '%s_queue' % stream_name
                exchange_name = create_unique_identifier("%s_queue" %
                        stream_name)
                self._purge_queue(exchange_name)
                sub = StandaloneStreamSubscriber(exchange_name, recv_data)
                sub.start()
                self._data_subscribers.append(sub)
                print 'stream_id: %s' % stream_id
                sub_id = pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id])
                pubsub_client.activate_subscription(sub_id)
                sub.subscription_id = sub_id # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()
 
    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber,'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(subscriber.subscription_id)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id)
            subscriber.stop()

    ###############################################################################
    # Tests.
    ###############################################################################
    
    def test_config(self):
        """
        test_initialize
        Test agent initialize command. This causes creation of
        driver process and transition to inactive.
        """

        # We start in uninitialized state.
        # In this state there is no driver process.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
        
        # Ping the agent.
        retval = self._ia_client.ping_agent()
        log.info(retval)

        # Initialize the agent.
        # The agent is spawned with a driver config, but you can pass one in
        # optinally with the initialize command. This validates the driver
        # config, launches a driver process and connects to it via messaging.
        # If successful, we switch to the inactive state.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Ping the driver proc.
        retval = self._ia_client.ping_resource()
        log.info(retval)

        decoder = IonObjectDeserializer(obj_registry=get_obj_registry())

        # Grab the alarms defined in the config.
        retval = decoder.deserialize(self._ia_client.get_agent(['alarms'])['alarms'])

        """
        {'status': None, 'stream_name': 'parsed', 'name': 'test_sim_warning',
        'upper_bound': 5.0, 'expr': 'x<5.0', 'upper_rel_op': '<',
        'lower_rel_op': None, 'type_': 'IntervalAlarmDef', 'value_id': 'temp',
        'lower_bound': None, 'message': 'Temperature is above test range of 5.0.',
        'current_val': None, 'type': 1}
        """
        self.assertEqual(retval[0].type_, 'IntervalAlarmDef')
        self.assertEqual(retval[0].upper_bound, 5.0)
        self.assertEqual(retval[0].expr, 'x<5.0')
        
        # Reset the agent. This causes the driver messaging to be stopped,
        # the driver process to end and switches us back to uninitialized.
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
        
        
    def test_autosample(self):
        """
        test_autosample
        Test instrument driver execute interface to start and stop streaming
        mode. Verify ResourceAgentResourceStateEvents are publsihed.
        """
        
        # Start data subscribers.
        self._start_data_subscribers(5)
        self.addCleanup(self._stop_data_subscribers)    
        
        # Set up a subscriber to collect error events.
        self._start_event_subscriber()
        self.addCleanup(self._stop_event_subscriber)            
        
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=SBE37ProtocolEvent.START_AUTOSAMPLE)
        retval = self._ia_client.execute_resource(cmd)
        
        gevent.sleep(20)
        
        cmd = AgentCommand(command=SBE37ProtocolEvent.STOP_AUTOSAMPLE)
        retval = self._ia_client.execute_resource(cmd)
 
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        #self._async_event_result.get(timeout=CFG.endpoint.receive.timeout)
        #self.assertGreaterEqual(len(self._events_received), 6)

        self._async_sample_result.get(timeout=CFG.endpoint.receive.timeout)
        self.assertGreaterEqual(len(self._samples_received), 5)

        #for x in self._samples_received:
            

        gevent.sleep(5)
        
        """
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142753e+09]), 2: array([  3.57142753e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 83.66190338], dtype=float32), 8: array([ 27.09519958], dtype=float32), 9: array([ 495.5369873], dtype=float32), 10: None, 11: array([  3.57142753e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438731.584241, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142753e+09]), 2: array([  3.57142753e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 50.97378922], dtype=float32), 8: array([ 17.82060051], dtype=float32), 9: array([ 280.375], dtype=float32), 10: None, 11: array([  3.57142753e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438734.120577, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142754e+09]), 2: array([  3.57142754e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 79.61238098], dtype=float32), 8: array([ 99.90670013], dtype=float32), 9: array([ 830.60198975], dtype=float32), 10: None, 11: array([  3.57142754e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438737.154774, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142754e+09]), 2: array([  3.57142754e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 81.31384277], dtype=float32), 8: array([ 89.92569733], dtype=float32), 9: array([ 575.98901367], dtype=float32), 10: None, 11: array([  3.57142754e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438739.68893, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142754e+09]), 2: array([  3.57142754e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 80.85481262], dtype=float32), 8: array([ 86.46209717], dtype=float32), 9: array([ 563.9329834], dtype=float32), 10: None, 11: array([  3.57142754e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438742.722411, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142755e+09]), 2: array([  3.57142754e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 42.43975067], dtype=float32), 8: array([ 13.87370014], dtype=float32), 9: array([ 910.49298096], dtype=float32), 10: None, 11: array([  3.57142755e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438745.256714, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142755e+09]), 2: array([  3.57142755e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 76.90184021], dtype=float32), 8: array([ 84.85610199], dtype=float32), 9: array([ 742.34002686], dtype=float32), 10: None, 11: array([  3.57142755e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438748.29093, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142755e+09]), 2: array([  3.57142755e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 92.28489685], dtype=float32), 8: array([ 19.77809906], dtype=float32), 9: array([ 272.94500732], dtype=float32), 10: None, 11: array([  3.57142755e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438750.841668, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142755e+09]), 2: array([  3.57142755e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 60.02323151], dtype=float32), 8: array([-7.79680014], dtype=float32), 9: array([ 418.51400757], dtype=float32), 10: None, 11: array([  3.57142755e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438753.424074, 'provider_metadata_update': {}}
        {'domain': [1], 'data_producer_id': '123xyz', 'record_dictionary': {1: array([  3.57142756e+09]), 2: array([  3.57142756e+09]), 3: array([port_timestamp], dtype=object), 4: 'ok', 5: None, 6: None, 7: array([ 13.54555035], dtype=float32), 8: array([ 90.14240265], dtype=float32), 9: array([ 105.24099731], dtype=float32), 10: None, 11: array([  3.57142756e+09]), 12: None, 13: None}, 'locator': None, 'type_': 'Granule', 'param_dictionary': '529b995c1aee4d149bca755aba7de687', 'creation_timestamp': 1362438756.511647, 'provider_metadata_update': {}}
        #######################
        {'origin': '123xyz', 'stream_name': 'parsed', 'type': 'StreamAlaramType.WARNING', 'description': '', 'expr': 'x<5.0', 'value': 27.0952, 'type_': 'StreamWarningAlaramEvent', 'value_id': 'temp', 'base_types': ['StreamAlarmEvent', 'ResourceEvent', 'Event'], 'message': 'Temperature is above test range of 5.0.', '_id': '838337bd75ad4852afa15aac29c82a4c', 'ts_created': '1362438731572', 'sub_type': '', 'origin_type': 'InstrumentDevice', 'name': 'test_sim_warning'}
        {'origin': '123xyz', 'stream_name': 'parsed', 'type': 'StreamAlaramType.ALL_CLEAR', 'description': '', 'expr': 'x<5.0', 'value': -7.7968, 'type_': 'StreamAllClearAlarmEvent', 'value_id': 'temp', 'base_types': ['StreamAlarmEvent', 'ResourceEvent', 'Event'], 'message': 'Alarm is cleared.', '_id': '6a3dc641a3fd4092a203857cf6a70e0c', 'ts_created': '1362438753412', 'sub_type': '', 'origin_type': 'InstrumentDevice', 'name': 'test_sim_warning'}
        {'origin': '123xyz', 'stream_name': 'parsed', 'type': 'StreamAlaramType.WARNING', 'description': '', 'expr': 'x<5.0', 'value': 90.1424, 'type_': 'StreamWarningAlaramEvent', 'value_id': 'temp', 'base_types': ['StreamAlarmEvent', 'ResourceEvent', 'Event'], 'message': 'Temperature is above test range of 5.0.', '_id': 'd45d95a3d94345faa1cf82cb18398f3b', 'ts_created': '1362438756499', 'sub_type': '', 'origin_type': 'InstrumentDevice', 'name': 'test_sim_warning'}
        2013-03-04 15:13:00,746 INFO Dummy-1 pyon.event.event:259 EventSubscriber stopped. Event pattern=#.StreamAlarmEvent.#.*.#.*.123xyz
        """
        
        """
        {'domain': [1],
        'data_producer_id': '123xyz',
        'record_dictionary':
            {1: array([  3.57142756e+09]),
             2: array([  3.57142756e+09]),
             3: array([port_timestamp], dtype=object),
             4: 'ok',
             5: None,
             6: None,
             7: array([ 13.54555035], dtype=float32),
             8: array([ 90.14240265], dtype=float32),
             9: array([ 105.24099731], dtype=float32),
             10: None,
             11: array([  3.57142756e+09]),
             12: None, 13: None},
        'locator': None,
        'type_': 'Granule',
        'param_dictionary': '529b995c1aee4d149bca755aba7de687',
        'creation_timestamp': 1362438756.511647,
        'provider_metadata_update': {}}
        """
        
        print '#######################'
        print '#######################'
        print '#######################'
        for x in self._samples_received:
            #print str(x)
            print str(x.record_dictionary)
            #print str(type(x.record_dictionary))
            print str(x.param_dictionary)
            #print str(x['record_dictionary'][8])
            
        print '#######################'
        for x in self._events_received:
            print str(x)
            
예제 #43
0
class TestAlerts(IonIntegrationTestCase):
    """
    """

    ############################################################################
    # Setup, teardown.
    ############################################################################

    def setUp(self):
        """
        Set up subscribers for alarm events.
        """

        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        self._event_count = 0
        self._events_received = []
        self._async_event_result = AsyncResult()
        self._resource_id = 'abc123'
        self._origin_type = "InstrumentDevice"

        def consume_event(*args, **kwargs):
            log.debug('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                      str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()

        self._event_subscriber = EventSubscriber(
            event_type='DeviceStatusAlertEvent',
            callback=consume_event,
            origin=self._resource_id)

        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

        def stop_subscriber():
            self._event_subscriber.stop()
            self._event_subscriber = None

        self.addCleanup(stop_subscriber)

    ###############################################################################
    # Tests.
    ###############################################################################

    def test_greater_than_interval(self):
        """
        """
        alert_def = {
            'name': 'current_warning_interval',
            'description': 'Current is above normal range.',
            'aggregate_type': AggregateStatusType.AGGREGATE_DATA,
            'alert_type': StreamAlertType.WARNING,
            'resource_id': self._resource_id,
            'origin_type': self._origin_type,
            'stream_name': 'fakestreamname',
            'value_id': 'port_current',
            'lower_bound': 10.5,
            'lower_rel_op': '<',
            'upper_bound': None,
            'upper_rel_op': None,
            'alert_class': 'IntervalAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()
        """
        {'status': None, 'alert_type': 1, 'lower_bound': 10.5, 'upper_rel_op': '<',
        'alert_class': 'IntervalAlert', 'message': 'Current is above normal range.',
        'stream_name': 'fakestreamname', 'name': 'current_warning_interval',
        'upper_bound': None, 'value': None, 'value_id': 'port_current',
        'lower_rel_op': None}
        """
        # This sequence will produce 5 alerts:
        # All clear on 30,
        # Warning on 5.5
        # All clear on 15.1
        # Warning on 3.3
        # All clear on 15.0
        self._event_count = 5
        test_vals = [
            30, 30.4, 5.5, 5.6, 15.1, 15.2, 15.3, 3.3, 3.4, 15.0, 15.5
        ]

        for x in test_vals:
            alert.eval_alert(stream_name='fakestreamname',
                             value=x,
                             value_id='port_current')

        self._async_event_result.get(timeout=30)
        """
        {'origin': 'abc123', 'status': 1, '_id': '04ccd20d67574b2ea3df869f2b6d4123', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [30], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152082', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '050d2c66eb47435888ecab9d58399922', 'description': 'Current is above normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [5.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152089', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '3294c5f7e2be413c806604e93b69e973', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [15.1], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152095', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '99a98e19a1454740a8464dab8de4dc0e', 'description': 'Current is above normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [3.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152101', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '93a214ee727e424e8b6a7e024a4d89be', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [15.0], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659152108', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        """

    def test_less_than_interval(self):
        """
        """
        alert_def = {
            'name': 'current_warning_interval',
            'stream_name': 'fakestreamname',
            'description': 'Current is below normal range.',
            'alert_type': StreamAlertType.WARNING,
            'value_id': 'port_current',
            'resource_id': self._resource_id,
            'origin_type': self._origin_type,
            'lower_bound': None,
            'lower_rel_op': None,
            'upper_bound': 4.0,
            'upper_rel_op': '<',
            'alert_class': 'IntervalAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # Warning on the 5.5
        # All clear on 3.3,
        # Warning on 4.5
        # All clear on 3.3
        # Warning on 4.8
        self._event_count = 5
        test_vals = [
            5.5, 5.5, 5.4, 4.6, 4.5, 3.3, 3.3, 4.5, 4.5, 3.3, 3.3, 4.8
        ]

        for x in test_vals:
            alert.eval_alert(stream_name='fakestreamname',
                             value=x,
                             value_id='port_current')

        self._async_event_result.get(timeout=30)
        """
        {'origin': 'abc123', 'status': 1, '_id': '43c83af591a84fa3adc3a77a5d97ed2b', 'description': 'Current is below normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [5.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238728', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '36c45d09286d4ff38f5003ff97bef6a1', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [3.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238735', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'd004b9c2d50f4b9899d6fbdd3c8c50d2', 'description': 'Current is below normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [4.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238741', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'cb0cbaf1be0b4aa387e1a5ba4f1adb2c', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [3.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238747', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'd4f6e4b4105e494083a0f8e34362f275', 'description': 'Current is below normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [4.8], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659238754', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        """

    def test_two_sided_interval(self):
        """
        """
        alert_def = {
            'name': 'current_warning_interval',
            'stream_name': 'fakestreamname',
            'description': 'Current is outside normal range.',
            'alert_type': StreamAlertType.WARNING,
            'value_id': 'port_current',
            'resource_id': self._resource_id,
            'origin_type': self._origin_type,
            'lower_bound': 10,
            'lower_rel_op': '<',
            'upper_bound': 20,
            'upper_rel_op': '<',
            'alert_class': 'IntervalAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # Warning on the 5.5
        # All clear on 10.2,
        # Warning on 23.3
        # All clear on 17.5
        # Warning on 8.8
        self._event_count = 5
        test_vals = [
            5.5, 5.5, 5.4, 4.6, 4.5, 10.2, 10.3, 10.5, 15.5, 23.3, 23.3, 24.8,
            17.5, 16.5, 12.5, 8.8, 7.7
        ]

        for x in test_vals:
            event_data = alert.eval_alert(stream_name='fakestreamname',
                                          value=x,
                                          value_id='port_current')

        self._async_event_result.get(timeout=30)
        """
        {'origin': 'abc123', 'status': 1, '_id': '45296cc01f3d42c59e1aeded4fafb33d', 'description': 'Current is outside normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [5.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411921', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'd1a87a248f6640ceafdf8d09b66f4c6f', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [10.2], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411927', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'e9945ff47b79436096f67ba9d373a889', 'description': 'Current is outside normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [23.3], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411934', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': '649f5297997740dc83b39d23b6fbc5b9', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [17.5], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411940', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        {'origin': 'abc123', 'status': 1, '_id': 'c766e52fda05497f9e4a18024e4eb0d6', 'description': 'Current is outside normal range.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [8.8], 'value_id': 'port_current', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659411947', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'current_warning_interval'}
        """

    def test_late_data(self):
        """
        """
        def get_state():
            return ResourceAgentState.STREAMING

        alert_def = {
            'name': 'late_data_warning',
            'stream_name': 'fakestreamname',
            'description': 'Expected data has not arrived.',
            'alert_type': StreamAlertType.WARNING,
            'value_id': None,
            'resource_id': self._resource_id,
            'origin_type': self._origin_type,
            'time_delta': 3,
            'get_state': get_state,
            'alert_class': 'LateDataAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 6 events:
        # All clear on the first check data.
        # Warning during the first 10s delay.
        # All clear during the 1s samples following the first 10s delay.
        # Warning during the 2nd 10s delay.
        # All clear during the 1s samples following the second 10s delay.
        # Warning during the final 10s delay.
        self._event_count = 6
        #sleep_vals = [0.5, 0.7, 1.0, 1.1, 2.5, 2.3, 0.75, 0.5, 2.25, 0.5, 0.5, 2.5, 2.5]
        sleep_vals = [
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10
        ]
        for x in sleep_vals:
            event_data = alert.eval_alert(stream_name='fakestreamname')
            gevent.sleep(x)

        self._async_event_result.get(timeout=30)
        """
        {'origin': 'abc123', 'status': 1, '_id': '455f5916fbf845acb0f71da229fa46a4', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659773.60301], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659773603', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '01b74790959d40c99da09fd1e52b447f', 'description': 'Expected data has not arrived.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659785.624983], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659791612', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'db73d083bddc4ebbb4dd4e6541d70bf3', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659795.625946], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659795626', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'ddd501a1599f47a49886ff89cda2f980', 'description': 'Expected data has not arrived.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659806.649514], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659812631', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '0c1dda72a53c47908c2755dfdcf87d15', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659816.650299], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659816651', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '6b0f2fb6eedd4cc39ab3e6dc6c2c2d69', 'description': 'Expected data has not arrived.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [1366659832.673774], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': 'fakestreamname', 'ts_created': '1366659836651', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'late_data_warning'}
        """
        alert.stop()

    @unittest.skip('Waiting to be finished and verified.')
    def test_rsn_event_alert(self):
        """
        """
        alert_def = {
            'name': 'input_voltage',
            'description': 'input_voltage is not in range range.',
            'alert_type': StreamAlertType.WARNING,
            'value_id': 'input_voltage',
            'resource_id': self._resource_id,
            'origin_type': self._origin_type,
            'alert_class': 'RSNEventAlert',
            'aggregate_type': AggregateStatusType.AGGREGATE_POWER
        }

        # Example from CI-OMS interface spec
        # need: tag for the value, current value and warning/error
        #        {
        #          "group": "power",
        #          "url": "http://localhost:8000",
        #          "timestamp": 3573569514.295556,
        #          "ref_id": "44.78",
        #          "platform_id": "TODO_some_platform_id_of_type_UPS",
        #          "message": "low battery (synthetic event generated from simulator)"
        #        }

        #proposed structure:
        #        {
        #        "group": "power",
        #        "name" : "low_voltage_warning",
        #        "value_id" : "input_voltage",
        #        "value" : "1.2",
        #        "alert_type" : "warning",
        #        "url": "http://localhost:8000",
        #        "timestamp": 3573569514.295556,
        #        "ref_id": "44.78",
        #        "platform_id": "TODO_some_platform_id_of_type_UPS",
        #        "message": "low battery (synthetic event generated from simulator)"
        #        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        test_val = \
        {
        "group": "power",
        "name" : "low_voltage_warning",
        "value_id" : "input_voltage",
        "value" : "1.2",
        "alert_type" : "warning",
        "url": "http://localhost:8000",
        "ref_id": "44.78",
        "platform_id": "e88f8b325b274dafabcc7d7d1e85bc5d",
        "description": "low battery (synthetic event generated from simulator)"
        }

        alert.eval_alert(rsn_alert=test_val)

        status = alert.get_status()
        log.debug('test_rsn_event_alert status: %s', alert)

        #self._async_event_result.get(timeout=30)

    def test_state_alert(self):
        """
        """
        alert_def = {
            'name':
            'comms_warning',
            'description':
            'Detected comms failure.',
            'alert_type':
            StreamAlertType.WARNING,
            'resource_id':
            self._resource_id,
            'origin_type':
            self._origin_type,
            'alert_states': [
                ResourceAgentState.LOST_CONNECTION,
                ResourceAgentState.ACTIVE_UNKNOWN
            ],
            'clear_states': [
                ResourceAgentState.IDLE, ResourceAgentState.COMMAND,
                ResourceAgentState.STREAMING
            ],
            'alert_class':
            'StateAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # All clear on uninitialized (prev value None)
        # Warning on lost connection,
        # All clear on streaming,
        # Warning on lost connection,
        # All clear on idle.
        self._event_count = 5
        test_vals = [
            ResourceAgentState.UNINITIALIZED, ResourceAgentState.INACTIVE,
            ResourceAgentState.IDLE, ResourceAgentState.COMMAND,
            ResourceAgentState.STREAMING, ResourceAgentState.LOST_CONNECTION,
            ResourceAgentState.STREAMING, ResourceAgentState.LOST_CONNECTION,
            ResourceAgentState.UNINITIALIZED, ResourceAgentState.INACTIVE,
            ResourceAgentState.IDLE, ResourceAgentState.COMMAND,
            ResourceAgentState.STREAMING
        ]

        for x in test_vals:
            alert.eval_alert(state=x)

        self._async_event_result.get(timeout=30)
        """
        {'origin': 'abc123', 'status': 1, '_id': '36e733662e674388ba2cfb8165315b86', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_UNINITIALIZED'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466203', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'ee9578bd37ed45c088479131f4b71509', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_LOST_CONNECTION'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466210', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '237db9d3bb8e455a98e58429df6c08ea', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_STREAMING'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466216', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': 'bef0987444254adb9db8fa752b2e1c81', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_LOST_CONNECTION'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466222', 'sub_type': 1, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        {'origin': 'abc123', 'status': 1, '_id': '4d78cede6e01419a881eac288cf1dbd3', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_IDLE'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1366659466229', 'sub_type': 3, 'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        """

    def test_command_error_alert(self):
        """
        """
        alert_def = {
            'name':
            'comms_warning',
            'description':
            'Detected comms failure.',
            'alert_type':
            StreamAlertType.WARNING,
            'resource_id':
            self._resource_id,
            'origin_type':
            self._origin_type,
            'command':
            ResourceAgentEvent.GO_ACTIVE,
            'clear_states': [
                ResourceAgentState.IDLE, ResourceAgentState.COMMAND,
                ResourceAgentState.STREAMING
            ],
            'alert_class':
            'CommandErrorAlert'
        }

        cls = alert_def.pop('alert_class')
        alert = eval('%s(**alert_def)' % cls)

        status = alert.get_status()

        # This sequence will produce 5 alerts:
        # All clear on initialize success (prev value None)
        # Warning on go active failure,
        # All clear on go active success,
        # Warning on go active failure
        # All clear on transition to idle (reconnect)
        self._event_count = 5
        test_vals = [{
            'state': ResourceAgentState.UNINITIALIZED
        }, {
            'command': ResourceAgentEvent.INITIALIZE,
            'command_success': True
        }, {
            'state': ResourceAgentState.INACTIVE
        }, {
            'command': ResourceAgentEvent.GO_ACTIVE,
            'command_success': False
        }, {
            'state': ResourceAgentState.INACTIVE
        }, {
            'command': ResourceAgentEvent.RESET,
            'command_success': True
        }, {
            'state': ResourceAgentState.UNINITIALIZED
        }, {
            'command': ResourceAgentEvent.INITIALIZE,
            'command_success': True
        }, {
            'state': ResourceAgentState.INACTIVE
        }, {
            'command': ResourceAgentEvent.GO_ACTIVE,
            'command_success': True
        }, {
            'state': ResourceAgentState.IDLE
        }, {
            'command': ResourceAgentEvent.RESET,
            'command_success': True
        }, {
            'state': ResourceAgentState.UNINITIALIZED
        }, {
            'command': ResourceAgentEvent.INITIALIZE,
            'command_success': True
        }, {
            'state': ResourceAgentState.INACTIVE
        }, {
            'command': ResourceAgentEvent.GO_ACTIVE,
            'command_success': False
        }, {
            'state': ResourceAgentState.INACTIVE
        }, {
            'state': ResourceAgentState.IDLE
        }]

        for x in test_vals:
            alert.eval_alert(**x)

        self._async_event_result.get(timeout=30)
        """
예제 #44
0
class TestActivateInstrumentIntegration(IonIntegrationTestCase):
    def setUp(self):
        # Start container
        super(TestActivateInstrumentIntegration, self).setUp()
        config = DotDict()
        config.bootstrap.use_es = True

        self._start_container()
        self.addCleanup(TestActivateInstrumentIntegration.es_cleanup)

        self.container.start_rel_from_url('res/deploy/r2deploy.yml', config)

        # Now create client to DataProductManagementService
        self.rrclient = ResourceRegistryServiceClient(node=self.container.node)
        self.damsclient = DataAcquisitionManagementServiceClient(
            node=self.container.node)
        self.pubsubcli = PubsubManagementServiceClient(
            node=self.container.node)
        self.imsclient = InstrumentManagementServiceClient(
            node=self.container.node)
        self.dpclient = DataProductManagementServiceClient(
            node=self.container.node)
        self.datasetclient = DatasetManagementServiceClient(
            node=self.container.node)
        self.processdispatchclient = ProcessDispatcherServiceClient(
            node=self.container.node)
        self.dataprocessclient = DataProcessManagementServiceClient(
            node=self.container.node)
        self.dataproductclient = DataProductManagementServiceClient(
            node=self.container.node)
        self.dataretrieverclient = DataRetrieverServiceClient(
            node=self.container.node)
        self.dataset_management = DatasetManagementServiceClient()
        self.usernotificationclient = UserNotificationServiceClient()

        #setup listerner vars
        self._data_greenlets = []
        self._no_samples = None
        self._samples_received = []

        self.event_publisher = EventPublisher()

    @staticmethod
    def es_cleanup():
        es_host = CFG.get_safe('server.elasticsearch.host', 'localhost')
        es_port = CFG.get_safe('server.elasticsearch.port', '9200')
        es = ep.ElasticSearch(host=es_host, port=es_port, timeout=10)
        indexes = STD_INDEXES.keys()
        indexes.append('%s_resources_index' % get_sys_name().lower())
        indexes.append('%s_events_index' % get_sys_name().lower())

        for index in indexes:
            IndexManagementService._es_call(es.river_couchdb_delete, index)
            IndexManagementService._es_call(es.index_delete, index)

    def create_logger(self, name, stream_id=''):

        # logger process
        producer_definition = ProcessDefinition(name=name + '_logger')
        producer_definition.executable = {
            'module': 'ion.processes.data.stream_granule_logger',
            'class': 'StreamGranuleLogger'
        }

        logger_procdef_id = self.processdispatchclient.create_process_definition(
            process_definition=producer_definition)
        configuration = {
            'process': {
                'stream_id': stream_id,
            }
        }
        pid = self.processdispatchclient.schedule_process(
            process_definition_id=logger_procdef_id,
            configuration=configuration)

        return pid

    def _create_notification(self,
                             user_name='',
                             instrument_id='',
                             product_id=''):
        #--------------------------------------------------------------------------------------
        # Make notification request objects
        #--------------------------------------------------------------------------------------

        notification_request_1 = NotificationRequest(
            name='notification_1',
            origin=instrument_id,
            origin_type="instrument",
            event_type='ResourceLifecycleEvent')

        notification_request_2 = NotificationRequest(
            name='notification_2',
            origin=product_id,
            origin_type="data product",
            event_type='DetectionEvent')

        #--------------------------------------------------------------------------------------
        # Create a user and get the user_id
        #--------------------------------------------------------------------------------------

        user = UserInfo()
        user.name = user_name
        user.contact.email = '*****@*****.**' % user_name

        user_id, _ = self.rrclient.create(user)

        #--------------------------------------------------------------------------------------
        # Create notification
        #--------------------------------------------------------------------------------------

        self.usernotificationclient.create_notification(
            notification=notification_request_1, user_id=user_id)
        self.usernotificationclient.create_notification(
            notification=notification_request_2, user_id=user_id)
        log.debug(
            "test_activateInstrumentSample: create_user_notifications user_id %s",
            str(user_id))

        return user_id

    def get_datastore(self, dataset_id):
        dataset = self.datasetclient.read_dataset(dataset_id)
        datastore_name = dataset.datastore_name
        datastore = self.container.datastore_manager.get_datastore(
            datastore_name, DataStore.DS_PROFILE.SCIDATA)
        return datastore

    def _check_computed_attributes_of_extended_instrument(
            self, expected_instrument_device_id='', extended_instrument=None):

        # Verify that computed attributes exist for the extended instrument
        self.assertIsInstance(extended_instrument.computed.firmware_version,
                              ComputedFloatValue)
        self.assertIsInstance(
            extended_instrument.computed.last_data_received_datetime,
            ComputedFloatValue)
        self.assertIsInstance(
            extended_instrument.computed.last_calibration_datetime,
            ComputedFloatValue)
        self.assertIsInstance(extended_instrument.computed.uptime,
                              ComputedStringValue)

        self.assertIsInstance(
            extended_instrument.computed.power_status_roll_up,
            ComputedIntValue)
        self.assertIsInstance(
            extended_instrument.computed.communications_status_roll_up,
            ComputedIntValue)
        self.assertIsInstance(extended_instrument.computed.data_status_roll_up,
                              ComputedIntValue)
        self.assertIsInstance(
            extended_instrument.computed.location_status_roll_up,
            ComputedIntValue)

        # the following assert will not work without elasticsearch.
        #self.assertEqual( 1, len(extended_instrument.computed.user_notification_requests.value) )
        self.assertEqual(
            extended_instrument.computed.communications_status_roll_up.value,
            StatusType.STATUS_WARNING)
        self.assertEqual(
            extended_instrument.computed.data_status_roll_up.value,
            StatusType.STATUS_OK)
        self.assertEqual(
            extended_instrument.computed.power_status_roll_up.value,
            StatusType.STATUS_WARNING)

        # Verify the computed attribute for user notification requests
        self.assertEqual(
            1,
            len(extended_instrument.computed.user_notification_requests.value))
        notifications = extended_instrument.computed.user_notification_requests.value
        notification = notifications[0]
        self.assertEqual(notification.origin, expected_instrument_device_id)
        self.assertEqual(notification.origin_type, "instrument")
        self.assertEqual(notification.event_type, 'ResourceLifecycleEvent')

    def _check_computed_attributes_of_extended_product(
            self, expected_data_product_id='', extended_data_product=None):

        self.assertEqual(expected_data_product_id, extended_data_product._id)
        log.debug("extended_data_product.computed: %s",
                  extended_data_product.computed)

        # Verify that computed attributes exist for the extended instrument
        self.assertIsInstance(
            extended_data_product.computed.product_download_size_estimated,
            ComputedIntValue)
        self.assertIsInstance(
            extended_data_product.computed.number_active_subscriptions,
            ComputedIntValue)
        self.assertIsInstance(extended_data_product.computed.data_url,
                              ComputedStringValue)
        self.assertIsInstance(extended_data_product.computed.stored_data_size,
                              ComputedIntValue)
        self.assertIsInstance(extended_data_product.computed.recent_granules,
                              ComputedDictValue)
        self.assertIsInstance(extended_data_product.computed.parameters,
                              ComputedListValue)
        self.assertIsInstance(extended_data_product.computed.recent_events,
                              ComputedEventListValue)

        self.assertIsInstance(extended_data_product.computed.provenance,
                              ComputedDictValue)
        self.assertIsInstance(
            extended_data_product.computed.user_notification_requests,
            ComputedListValue)
        self.assertIsInstance(
            extended_data_product.computed.active_user_subscriptions,
            ComputedListValue)
        self.assertIsInstance(
            extended_data_product.computed.past_user_subscriptions,
            ComputedListValue)
        self.assertIsInstance(extended_data_product.computed.last_granule,
                              ComputedDictValue)
        self.assertIsInstance(extended_data_product.computed.is_persisted,
                              ComputedIntValue)
        self.assertIsInstance(
            extended_data_product.computed.data_contents_updated,
            ComputedStringValue)
        self.assertIsInstance(extended_data_product.computed.data_datetime,
                              ComputedListValue)

        # exact text here keeps changing to fit UI capabilities.  keep assertion general...
        self.assertTrue('ok' in extended_data_product.computed.last_granule.
                        value['quality_flag'])
        self.assertEqual(
            2, len(extended_data_product.computed.data_datetime.value))

        notifications = extended_data_product.computed.user_notification_requests.value

        notification = notifications[0]
        self.assertEqual(notification.origin, expected_data_product_id)
        self.assertEqual(notification.origin_type, "data product")
        self.assertEqual(notification.event_type, 'DetectionEvent')

    @attr('LOCOINT')
    @unittest.skipIf(not use_es, 'No ElasticSearch')
    @unittest.skipIf(os.getenv('CEI_LAUNCH_TEST', False),
                     'Skip test while in CEI LAUNCH mode')
    @patch.dict(CFG, {'endpoint': {'receive': {'timeout': 60}}})
    def test_activateInstrumentSample(self):

        self.loggerpids = []

        # Create InstrumentModel
        instModel_obj = IonObject(RT.InstrumentModel,
                                  name='SBE37IMModel',
                                  description="SBE37IMModel")
        instModel_id = self.imsclient.create_instrument_model(instModel_obj)
        log.debug('new InstrumentModel id = %s ', instModel_id)

        #Create stream alarms
        """
        test_two_sided_interval
        Test interval alarm and alarm event publishing for a closed
        inteval.
        """

        #        kwargs = {
        #            'name' : 'test_sim_warning',
        #            'stream_name' : 'parsed',
        #            'value_id' : 'temp',
        #            'message' : 'Temperature is above test range of 5.0.',
        #            'type' : StreamAlarmType.WARNING,
        #            'upper_bound' : 5.0,
        #            'upper_rel_op' : '<'
        #        }

        kwargs = {
            'name': 'temperature_warning_interval',
            'stream_name': 'parsed',
            'value_id': 'temp',
            'message':
            'Temperature is below the normal range of 50.0 and above.',
            'type': StreamAlarmType.WARNING,
            'lower_bound': 50.0,
            'lower_rel_op': '<'
        }

        # Create alarm object.
        alarm = {}
        alarm['type'] = 'IntervalAlarmDef'
        alarm['kwargs'] = kwargs

        raw_config = StreamConfiguration(
            stream_name='raw',
            parameter_dictionary_name='ctd_raw_param_dict',
            records_per_granule=2,
            granule_publish_rate=5)
        parsed_config = StreamConfiguration(
            stream_name='parsed',
            parameter_dictionary_name='ctd_parsed_param_dict',
            records_per_granule=2,
            granule_publish_rate=5,
            alarms=[alarm])

        # Create InstrumentAgent
        instAgent_obj = IonObject(
            RT.InstrumentAgent,
            name='agent007',
            description="SBE37IMAgent",
            driver_uri=
            "http://sddevrepo.oceanobservatories.org/releases/seabird_sbe37smb_ooicore-0.0.1a-py2.7.egg",
            stream_configurations=[raw_config, parsed_config])
        instAgent_id = self.imsclient.create_instrument_agent(instAgent_obj)
        log.debug('new InstrumentAgent id = %s', instAgent_id)

        self.imsclient.assign_instrument_model_to_instrument_agent(
            instModel_id, instAgent_id)

        # Create InstrumentDevice
        log.debug(
            'test_activateInstrumentSample: Create instrument resource to represent the SBE37 (SA Req: L4-CI-SA-RQ-241) '
        )
        instDevice_obj = IonObject(RT.InstrumentDevice,
                                   name='SBE37IMDevice',
                                   description="SBE37IMDevice",
                                   serial_number="12345")
        instDevice_id = self.imsclient.create_instrument_device(
            instrument_device=instDevice_obj)
        self.imsclient.assign_instrument_model_to_instrument_device(
            instModel_id, instDevice_id)

        log.debug(
            "test_activateInstrumentSample: new InstrumentDevice id = %s (SA Req: L4-CI-SA-RQ-241) ",
            instDevice_id)

        port_agent_config = {
            'device_addr': CFG.device.sbe37.host,
            'device_port': CFG.device.sbe37.port,
            'process_type': PortAgentProcessType.UNIX,
            'binary_path': "port_agent",
            'port_agent_addr': 'localhost',
            'command_port': CFG.device.sbe37.port_agent_cmd_port,
            'data_port': CFG.device.sbe37.port_agent_data_port,
            'log_level': 5,
            'type': PortAgentType.ETHERNET
        }

        instAgentInstance_obj = IonObject(RT.InstrumentAgentInstance,
                                          name='SBE37IMAgentInstance',
                                          description="SBE37IMAgentInstance",
                                          port_agent_config=port_agent_config)

        instAgentInstance_id = self.imsclient.create_instrument_agent_instance(
            instAgentInstance_obj, instAgent_id, instDevice_id)

        tdom, sdom = time_series_domain()
        sdom = sdom.dump()
        tdom = tdom.dump()

        parsed_pdict_id = self.dataset_management.read_parameter_dictionary_by_name(
            'ctd_parsed_param_dict', id_only=True)
        parsed_stream_def_id = self.pubsubcli.create_stream_definition(
            name='parsed', parameter_dictionary_id=parsed_pdict_id)

        raw_pdict_id = self.dataset_management.read_parameter_dictionary_by_name(
            'ctd_raw_param_dict', id_only=True)
        raw_stream_def_id = self.pubsubcli.create_stream_definition(
            name='raw', parameter_dictionary_id=raw_pdict_id)

        #-------------------------------
        # Create Raw and Parsed Data Products for the device
        #-------------------------------

        dp_obj = IonObject(RT.DataProduct,
                           name='the parsed data',
                           description='ctd stream test',
                           temporal_domain=tdom,
                           spatial_domain=sdom)

        data_product_id1 = self.dpclient.create_data_product(
            data_product=dp_obj, stream_definition_id=parsed_stream_def_id)
        log.debug('new dp_id = %s', data_product_id1)
        self.dpclient.activate_data_product_persistence(
            data_product_id=data_product_id1)

        self.damsclient.assign_data_product(input_resource_id=instDevice_id,
                                            data_product_id=data_product_id1)

        # Retrieve the id of the OUTPUT stream from the out Data Product
        stream_ids, _ = self.rrclient.find_objects(data_product_id1,
                                                   PRED.hasStream, None, True)
        log.debug('Data product streams1 = %s', stream_ids)

        # Retrieve the id of the OUTPUT stream from the out Data Product
        dataset_ids, _ = self.rrclient.find_objects(data_product_id1,
                                                    PRED.hasDataset,
                                                    RT.Dataset, True)
        log.debug('Data set for data_product_id1 = %s', dataset_ids[0])
        self.parsed_dataset = dataset_ids[0]

        pid = self.create_logger('ctd_parsed', stream_ids[0])
        self.loggerpids.append(pid)

        dp_obj = IonObject(RT.DataProduct,
                           name='the raw data',
                           description='raw stream test',
                           temporal_domain=tdom,
                           spatial_domain=sdom)

        data_product_id2 = self.dpclient.create_data_product(
            data_product=dp_obj, stream_definition_id=raw_stream_def_id)
        log.debug('new dp_id = %s', data_product_id2)

        self.damsclient.assign_data_product(input_resource_id=instDevice_id,
                                            data_product_id=data_product_id2)

        self.dpclient.activate_data_product_persistence(
            data_product_id=data_product_id2)

        # setup notifications for the device and parsed data product
        user_id_1 = self._create_notification(user_name='user_1',
                                              instrument_id=instDevice_id,
                                              product_id=data_product_id1)
        #---------- Create notifications for another user and verify that we see different computed subscriptions for the two users ---------
        user_id_2 = self._create_notification(user_name='user_2',
                                              instrument_id=instDevice_id,
                                              product_id=data_product_id2)

        # Retrieve the id of the OUTPUT stream from the out Data Product
        stream_ids, _ = self.rrclient.find_objects(data_product_id2,
                                                   PRED.hasStream, None, True)
        log.debug('Data product streams2 = %s', str(stream_ids))

        # Retrieve the id of the OUTPUT stream from the out Data Product
        dataset_ids, _ = self.rrclient.find_objects(data_product_id2,
                                                    PRED.hasDataset,
                                                    RT.Dataset, True)
        log.debug('Data set for data_product_id2 = %s', dataset_ids[0])
        self.raw_dataset = dataset_ids[0]

        #elastic search debug
        es_indexes, _ = self.container.resource_registry.find_resources(
            restype='ElasticSearchIndex')
        log.debug('ElasticSearch indexes: %s', [i.name for i in es_indexes])
        log.debug('Bootstrap %s', CFG.bootstrap.use_es)

        def start_instrument_agent():
            self.imsclient.start_instrument_agent_instance(
                instrument_agent_instance_id=instAgentInstance_id)

        gevent.joinall([gevent.spawn(start_instrument_agent)])

        #setup a subscriber to alarm events from the device
        self._events_received = []
        self._event_count = 0
        self._samples_out_of_range = 0
        self._samples_complete = False
        self._async_sample_result = AsyncResult()

        def consume_event(*args, **kwargs):
            log.debug(
                'TestActivateInstrument recieved ION event: args=%s, kwargs=%s, event=%s.',
                str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            self._event_count = len(self._events_received)
            self._async_sample_result.set()

        self._event_subscriber = EventSubscriber(
            event_type=
            'StreamWarningAlarmEvent',  #'StreamWarningAlarmEvent', #  StreamAlarmEvent
            callback=consume_event,
            origin=instDevice_id)
        self._event_subscriber.start()

        #cleanup
        self.addCleanup(self.imsclient.stop_instrument_agent_instance,
                        instrument_agent_instance_id=instAgentInstance_id)

        def stop_subscriber():
            self._event_subscriber.stop()
            self._event_subscriber = None

        self.addCleanup(stop_subscriber)

        #wait for start
        inst_agent_instance_obj = self.imsclient.read_instrument_agent_instance(
            instAgentInstance_id)
        gate = ProcessStateGate(self.processdispatchclient.read_process,
                                inst_agent_instance_obj.agent_process_id,
                                ProcessStateEnum.RUNNING)
        self.assertTrue(
            gate. await (30),
            "The instrument agent instance (%s) did not spawn in 30 seconds" %
            inst_agent_instance_obj.agent_process_id)

        log.debug('Instrument agent instance obj: = %s',
                  str(inst_agent_instance_obj))

        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = ResourceAgentClient(
            instDevice_id,
            to_name=inst_agent_instance_obj.agent_process_id,
            process=FakeProcess())

        log.debug("test_activateInstrumentSample: got ia client %s",
                  str(self._ia_client))

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrumentSample: initialize %s", str(retval))
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        log.debug("(L4-CI-SA-RQ-334): Sending go_active command ")
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        reply = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrument: return value from go_active %s",
                  str(reply))
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.GET_RESOURCE_STATE)
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        log.debug(
            "(L4-CI-SA-RQ-334): current state after sending go_active command %s",
            str(state))

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        reply = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrumentSample: run %s", str(reply))
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.PAUSE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STOPPED)

        cmd = AgentCommand(command=ResourceAgentEvent.RESUME)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.CLEAR)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)
        for i in xrange(10):
            retval = self._ia_client.execute_resource(cmd)
            log.debug("test_activateInstrumentSample: return from sample %s",
                      str(retval))

        log.debug("test_activateInstrumentSample: calling reset ")
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        reply = self._ia_client.execute_agent(cmd)
        log.debug("test_activateInstrumentSample: return from reset %s",
                  str(reply))

        self._samples_complete = True

        #--------------------------------------------------------------------------------
        # Now get the data in one chunk using an RPC Call to start_retreive
        #--------------------------------------------------------------------------------

        replay_data = self.dataretrieverclient.retrieve(self.parsed_dataset)
        self.assertIsInstance(replay_data, Granule)
        rdt = RecordDictionaryTool.load_from_granule(replay_data)
        log.debug("test_activateInstrumentSample: RDT parsed: %s",
                  str(rdt.pretty_print()))
        temp_vals = rdt['temp']
        self.assertEquals(len(temp_vals), 10)
        log.debug("test_activateInstrumentSample: all temp_vals: %s",
                  temp_vals)

        #out_of_range_temp_vals = [i for i in temp_vals if i > 5]
        out_of_range_temp_vals = [i for i in temp_vals if i < 50.0]
        log.debug("test_activateInstrumentSample: Out_of_range_temp_vals: %s",
                  out_of_range_temp_vals)
        self._samples_out_of_range = len(out_of_range_temp_vals)

        # if no bad values were produced, then do not wait for an event
        if self._samples_out_of_range == 0:
            self._async_sample_result.set()

        log.debug("test_activateInstrumentSample: _events_received: %s",
                  self._events_received)
        log.debug("test_activateInstrumentSample: _event_count: %s",
                  self._event_count)

        self._async_sample_result.get(timeout=CFG.endpoint.receive.timeout)

        replay_data = self.dataretrieverclient.retrieve(self.raw_dataset)
        self.assertIsInstance(replay_data, Granule)
        rdt = RecordDictionaryTool.load_from_granule(replay_data)
        log.debug("RDT raw: %s", str(rdt.pretty_print()))

        raw_vals = rdt['raw']
        self.assertEquals(len(raw_vals), 10)

        log.debug("l4-ci-sa-rq-138")
        """
        Physical resource control shall be subject to policy

        Instrument management control capabilities shall be subject to policy

        The actor accessing the control capabilities must be authorized to send commands.

        note from maurice 2012-05-18: Talk to tim M to verify that this is policy.  If it is then talk with Stephen to
                                      get an example of a policy test and use that to create a test stub that will be
                                      completed when we have instrument policies.

        Tim M: The "actor", aka observatory operator, will access the instrument through ION.

        """

        #--------------------------------------------------------------------------------
        # Get the extended data product to see if it contains the granules
        #--------------------------------------------------------------------------------
        extended_product = self.dpclient.get_data_product_extension(
            data_product_id=data_product_id1, user_id=user_id_1)

        def poller(extended_product):
            return len(extended_product.computed.user_notification_requests.
                       value) == 1

        poll(poller, extended_product, timeout=30)

        self._check_computed_attributes_of_extended_product(
            expected_data_product_id=data_product_id1,
            extended_data_product=extended_product)

        #--------------------------------------------------------------------------------
        #put some events into the eventsdb to test - this should set the comms and data status to WARNING
        #--------------------------------------------------------------------------------

        t = get_ion_ts()
        self.event_publisher.publish_event(ts_created=t,
                                           event_type='DeviceStatusEvent',
                                           origin=instDevice_id,
                                           state=DeviceStatusType.OUT_OF_RANGE,
                                           values=[200])
        self.event_publisher.publish_event(
            ts_created=t,
            event_type='DeviceCommsEvent',
            origin=instDevice_id,
            state=DeviceCommsType.DATA_DELIVERY_INTERRUPTION,
            lapse_interval_seconds=20)

        #--------------------------------------------------------------------------------
        # Get the extended instrument
        #--------------------------------------------------------------------------------

        extended_instrument = self.imsclient.get_instrument_device_extension(
            instrument_device_id=instDevice_id, user_id=user_id_1)
        self._check_computed_attributes_of_extended_instrument(
            expected_instrument_device_id=instDevice_id,
            extended_instrument=extended_instrument)

        #--------------------------------------------------------------------------------
        # For the second user, check the extended data product and the extended intrument
        #--------------------------------------------------------------------------------
        extended_product = self.dpclient.get_data_product_extension(
            data_product_id=data_product_id2, user_id=user_id_2)
        self._check_computed_attributes_of_extended_product(
            expected_data_product_id=data_product_id2,
            extended_data_product=extended_product)

        #---------- Put some events into the eventsdb to test - this should set the comms and data status to WARNING  ---------

        t = get_ion_ts()
        self.event_publisher.publish_event(ts_created=t,
                                           event_type='DeviceStatusEvent',
                                           origin=instDevice_id,
                                           state=DeviceStatusType.OUT_OF_RANGE,
                                           values=[200])
        self.event_publisher.publish_event(
            ts_created=t,
            event_type='DeviceCommsEvent',
            origin=instDevice_id,
            state=DeviceCommsType.DATA_DELIVERY_INTERRUPTION,
            lapse_interval_seconds=20)

        #--------------------------------------------------------------------------------
        # Get the extended instrument
        #--------------------------------------------------------------------------------

        extended_instrument = self.imsclient.get_instrument_device_extension(
            instrument_device_id=instDevice_id, user_id=user_id_2)
        self._check_computed_attributes_of_extended_instrument(
            expected_instrument_device_id=instDevice_id,
            extended_instrument=extended_instrument)

        #--------------------------------------------------------------------------------
        # Deactivate loggers
        #--------------------------------------------------------------------------------

        for pid in self.loggerpids:
            self.processdispatchclient.cancel_process(pid)

        self.dpclient.delete_data_product(data_product_id1)
        self.dpclient.delete_data_product(data_product_id2)
class TestAgentConnectionFailures(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """
    
    ############################################################################
    # Setup, teardown.
    ############################################################################
        
    def setUp(self):
        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        self._ia_client = None

        # Start container.
        log.info('Staring capability container.')
        self._start_container()
        
        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        log.info('Creating driver integration test support:')
        log.info('driver uri: %s', DRV_URI)
        log.info('device address: %s', DEV_ADDR)
        log.info('device port: %s', DEV_PORT)
        log.info('log delimiter: %s', DELIM)
        log.info('work dir: %s', WORK_DIR)
        self._support = DriverIntegrationTestSupport(None,
                                                     None,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
        
        # Start port agent, add stop to cleanup.
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)    
        
        log.info('building stream configuration')
        # Setup stream config.
        self._build_stream_config()

        # Start a resource agent client to talk with the instrument agent.
        log.info('starting IA process')
        self._ia_client = start_instrument_agent_process(self.container, self._stream_config)
        self.addCleanup(self._verify_agent_reset)
        log.info('test setup complete')


    ###############################################################################
    # Port agent helpers.
    ###############################################################################
        
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        port = self._support.start_pagent()
        log.info('Port agent started at port %i',port)
        
        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr' : 'localhost',
            'port' : port,
            'cmd_port' : CMD_PORT
        }
                        
    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state(timeout=120.1)
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd,timeout=300)
            
    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.', 
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()
            
        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()
            
        self._event_subscriber = EventSubscriber(
            event_type=type, callback=consume_event,
            origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient()
        
        encoder = IonObjectSerializer()
        
        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        stream_name = 'parsed'
        param_dict_name = 'ctd_parsed_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)        
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 parameter_dictionary=pd,
                                 stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

        stream_name = 'raw'
        param_dict_name = 'ctd_raw_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 parameter_dictionary=pd,
                                 stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

    def _start_data_subscribers(self, count, raw_count):
        """
        """        
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
                
        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        self._raw_samples_received = []
        self._async_sample_result = AsyncResult()
        self._async_raw_sample_result = AsyncResult()

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            log.info('Received parsed data on %s (%s,%s)', stream_id, stream_route.exchange_point, stream_route.routing_key)
            self._samples_received.append(message)
            if len(self._samples_received) == count:
                self._async_sample_result.set()

        def recv_raw_data(message, stream_route, stream_id):
            log.info('Received raw data on %s (%s,%s)', stream_id, stream_route.exchange_point, stream_route.routing_key)
            self._raw_samples_received.append(message)
            if len(self._raw_samples_received) == raw_count:
                self._async_raw_sample_result.set()

        from pyon.util.containers import create_unique_identifier

        stream_name = 'parsed'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" %
                    stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id],timeout=120.2)
        pubsub_client.activate_subscription(sub_id,timeout=120.3)
        sub.subscription_id = sub_id # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)
        
        stream_name = 'raw'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" %
                    stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_raw_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id],timeout=120.4)
        pubsub_client.activate_subscription(sub_id,timeout=120.5)
        sub.subscription_id = sub_id # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()
 
    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber,'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(subscriber.subscription_id,timeout=120.6)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id,timeout=120.7)
            subscriber.stop()

    ###############################################################################
    # Socket listen.
    ###############################################################################

    def _socket_listen(self, s, prompt, timeout):

        buf = ''
        starttime = time.time()
        while True:
            try:
                buf += s.recv(1024)
                print '##### Listening, got: %s' % buf
                if prompt and buf.find(prompt) != -1:
                    break
            except:
                gevent.sleep(1)
            
            finally:
                delta = time.time() - starttime
                if delta > timeout:
                    break
        return buf            
                
    ###############################################################################
    # Assert helpers.
    ###############################################################################
        
    def assertSampleDict(self, val):
        """
        Verify the value is a sample dictionary for the sbe37.
        """
        # AgentCommandResult.result['parsed']
        """
        {'quality_flag': 'ok', 'preferred_timestamp': 'driver_timestamp',
        'stream_name': 'parsed', 'pkt_format_id': 'JSON_Data',
        'pkt_version': 1, 'values':
        [{'value_id': 'temp', 'value': 21.4894},
        {'value_id': 'conductivity', 'value': 13.22157},
        {'value_id': 'pressure', 'value': 146.186}],
        'driver_timestamp': 3556901018.170206}
        """
        
        self.assertIsInstance(val, dict)
        self.assertTrue(val.has_key('values'))
        values_list = val['values']
        self.assertTrue(isinstance(values_list, list))
        self.assertTrue(len(values_list)==3)
        
        ids = ['temp', 'conductivity', 'pressure']
        ids_found = []

        for x in values_list:
            self.assertTrue(x.has_key('value_id'))
            self.assertTrue(x.has_key('value'))
            ids_found.append(x['value_id'])
            self.assertTrue(isinstance(x['value'], float))

        self.assertItemsEqual(ids, ids_found)

        self.assertTrue(val.has_key('driver_timestamp'))
        time = val['driver_timestamp']
        self.assertTrue(isinstance(time, float))
        
    def assertParamDict(self, pd, all_params=False):
        """
        Verify all device parameters exist and are correct type.
        """
        if all_params:
            self.assertEqual(set(pd.keys()), set(PARAMS.keys()))
            for (key, type_val) in PARAMS.iteritems():
                if type_val == list or type_val == tuple:
                    self.assertTrue(isinstance(pd[key], (list, tuple)))
                else:
                    self.assertTrue(isinstance(pd[key], type_val))

        else:
            for (key, val) in pd.iteritems():
                self.assertTrue(PARAMS.has_key(key))
                self.assertTrue(isinstance(val, PARAMS[key]))
        
    def assertParamVals(self, params, correct_params):
        """
        Verify parameters take the correct values.
        """
        self.assertEqual(set(params.keys()), set(correct_params.keys()))
        for (key, val) in params.iteritems():
            correct_val = correct_params[key]
            if isinstance(val, float):
                # Verify to 5% of the larger value.
                max_val = max(abs(val), abs(correct_val))
                self.assertAlmostEqual(val, correct_val, delta=max_val*.01)

            elif isinstance(val, (list, tuple)):
                # list of tuple.
                self.assertEqual(list(val), list(correct_val))
            
            else:
                # int, bool, str.
                self.assertEqual(val, correct_val)

    ###############################################################################
    # Tests.
    ###############################################################################
                
    def test_lost_connection(self):
        """
        test_lost_connection
        """
        
        # Set up a subscriber to collect command events.
        self._start_event_subscriber('ResourceAgentConnectionLostErrorEvent', 1)
        self.addCleanup(self._stop_event_subscriber)    
        
        # Start in uninitialized.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
    
        # Initialize the agent.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Activate.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        # Go into command mode.
        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        # Start streaming.
        cmd = AgentCommand(command=SBE37ProtocolEvent.START_AUTOSAMPLE)
        retval = self._ia_client.execute_resource(cmd)
        
        # Wait for a while, collect some samples.
        gevent.sleep(10)
        
        # Blow the port agent out from under the agent.
        self._support.stop_pagent()
        
        # Loop until we resyncronize to LOST_CONNECTION/DISCONNECTED.
        # Test will timeout if this dosn't occur.
        while True:
            state = self._ia_client.get_agent_state()
            if state == ResourceAgentState.LOST_CONNECTION:
                break
            else:
                gevent.sleep(1)
        
        # Verify the driver has transitioned to disconnected
        while True:
            state = self._ia_client.get_resource_state()
            if state == DriverConnectionState.DISCONNECTED:
                break
            else:
                gevent.sleep(1)

        # Make sure the lost connection error event arrives.
        self._async_event_result.get(timeout=CFG.endpoint.receive.timeout)                        
        self.assertEqual(len(self._events_received), 1)        

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    #@unittest.skip('Fails on buildbot for some god unknown reason.')
    def test_autoreconnect(self):
        """
        test_autoreconnect
        """
        # Set up a subscriber to collect command events.
        self._start_event_subscriber('ResourceAgentConnectionLostErrorEvent', 1)
        self.addCleanup(self._stop_event_subscriber)    
        
        # Start in uninitialized.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
    
        # Initialize the agent.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Activate.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        # Go into command mode.
        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)


        def poll_func(test):
            cmd = AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)
            while True:
                try:
                    gevent.sleep(.5)
                    test._ia_client.execute_resource(cmd)
                except IonException as ex:
                    # This exception could be ResourceException (broken pipe)
                    # Timeout or Conflict
                    log.info('#### pre shutdown exception: %s, %s', str(type(ex)), str(ex))
                    break
                    
            while True:
                try:
                    gevent.sleep(.5)
                    test._ia_client.execute_resource(cmd)
                    log.info('#### post shutdown got new sample.')
                    break
                except IonException as ex:
                    # This should be conflict.
                    log.info('#### post shutdown exception: %s, %s', str(type(ex)), str(ex))
                
        timeout = gevent.Timeout(600)
        timeout.start()
        try:

            # Start the command greenlet and let poll for a bit.
            gl = gevent.spawn(poll_func, self)        
            gevent.sleep(20)
        
            # Blow the port agent out from under the agent.
            self._support.stop_pagent()

            # Wait for a while, the supervisor is restarting the port agent.
            gevent.sleep(10)
            self._support.start_pagent()
            
            # Wait for the device to connect and start sampling again.
            gl.join()
            gl = None
            timeout.cancel()
            
        except (Exception, gevent.Timeout) as ex:
            if gl:
                gl.kill()
                gl = None
            self.fail(('Could not reconnect to device: %s,  %s',
                      str(type(ex)), str(ex)))

    def test_connect_failed(self):
        """
        test_connect_failed
        """
        # Stop the port agent.
        self._support.stop_pagent()
        
        # Sleep a bit.
        gevent.sleep(3)
        
        # Start in uninitialized.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
    
        # Initialize the agent.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Activate. This should fail because there is no port agent to connect to.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        with self.assertRaises(ResourceError):
            retval = self._ia_client.execute_agent(cmd)
            
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

    def test_get_set_alerts(self):
        """
        test_get_set_alerts
        Test specific of get/set alerts, including using result of get to
        set later.
        """
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertItemsEqual(retval, [])

        alert_def1 = {
            'name' : 'temp_warning_interval',
            'stream_name' : 'parsed',
            'description' : 'Temperature is above normal range.',
            'alert_type' : StreamAlertType.WARNING,
            'aggregate_type' : AggregateStatusType.AGGREGATE_DATA,
            'value_id' : 'temp',
            'lower_bound' : None,
            'lower_rel_op' : None,
            'upper_bound' : 10.5,
            'upper_rel_op' : '<',
            'alert_class' : 'IntervalAlert'
        }
        
        alert_def2 = {
            'name' : 'temp_alarm_interval',
            'stream_name' : 'parsed',
            'description' : 'Temperature is way above normal range.',
            'alert_type' : StreamAlertType.WARNING,
            'aggregate_type' : AggregateStatusType.AGGREGATE_DATA,
            'value_id' : 'temp',
            'lower_bound' : None,
            'lower_rel_op' : None,
            'upper_bound' : 15.5,
            'upper_rel_op' : '<',
            'alert_class' : 'IntervalAlert'
        }


        """
        Interval alerts are returned from get like this:
        (value and status fields describe state of the alert)
        {
        'name': 'temp_warning_interval',
        'stream_name': 'parsed',
        'description': 'Temperature is above normal range.',
        'alert_type': 1,
        'aggregate_type': 2,
        'value_id': 'temp',
        'lower_bound': None,
        'lower_rel_op': None,
        'upper_bound': 10.5,
        'upper_rel_op': '<',
        'alert_class': 'IntervalAlert',

        'status': None,
        'value': None
        }
        """
        
        alert_def3 = {
            'name' : 'late_data_warning',
            'stream_name' : 'parsed',
            'description' : 'Expected data has not arrived.',
            'alert_type' : StreamAlertType.WARNING,
            'aggregate_type' : AggregateStatusType.AGGREGATE_COMMS,
            'time_delta' : 180,
            'alert_class' : 'LateDataAlert'
        }

        """
        Late data alerts are returned from get like this:
        (value and status fields describe state of the alert)
        {
        'name': 'late_data_warning',
        'stream_name': 'parsed',
        'description': 'Expected data has not arrived.',
        'alert_type': 1,
        'aggregate_type': 1,
        'value_id': None,
        'time_delta': 180,
        'alert_class': 'LateDataAlert',
        
        'status': None,
        'value': None
        }
        """
        
        """
        [
            {'status': None,
            'alert_type': 1,
            'name': 'temp_warning_interval',
            'upper_bound': 10.5,
            'lower_bound': None,
            'aggregate_type': 2,
            'alert_class': 'IntervalAlert',
            'value': None,
            'value_id': 'temp',
            'lower_rel_op': None,
            'upper_rel_op': '<',
            'description': 'Temperature is above normal range.'},
            {'status': None,
            'alert_type': 1,
            'name': 'temp_alarm_interval',
            'upper_bound': 15.5,
            'lower_bound': None,
            'aggregate_type': 2,
            'alert_class': 'IntervalAlert',
            'value': None,
            'value_id': 'temp',
            'lower_rel_op': None,
            'upper_rel_op': '<',
            'description': 'Temperature is way above normal range.'},
            {'status': None,
             'stream_name': 'parsed',
             'alert_type': 1,
             'name': 'late_data_warning',
             'aggregate_type': 1,
             'alert_class': 'LateDataAlert',
             'value': None,
             'time_delta': 180,
             'description': 'Expected data has not arrived.'}
        ]
        """
        
        orig_alerts = [alert_def1, alert_def2, alert_def3]
        self._ia_client.set_agent({'alerts' : orig_alerts})
    
        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertTrue(len(retval)==3)
        alerts = retval

        self._ia_client.set_agent({'alerts' : ['clear']})        
        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertItemsEqual(retval, [])

        self._ia_client.set_agent({'alerts' : alerts})
        retval = self._ia_client.get_agent(['alerts'])['alerts']
        self.assertTrue(len(retval)==3)
        
        count = 0
        for x in retval:
            x.pop('status')
            x.pop('value')
            for y in orig_alerts:
                if x['name'] == y['name']:
                    count += 1
                    self.assertItemsEqual(x.keys(), y.keys())
        self.assertEquals(count, 3)
        
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
예제 #46
0
class TestVel3d(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """
    
    def setUp(self):

        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        print '#####################'
        print 'IN SETUP'
        
        self._ia_client = None

        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        log.info('building stream configuration')
        # Setup stream config.
        self._build_stream_config()

        #log.info('driver uri: %s', DRV_URI)
        #log.info('device address: %s', DEV_ADDR)
        #log.info('device port: %s', DEV_PORT)
        #log.info('work dir: %s', WORK_DIR)
        
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : self._stream_config,
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True,
            'forget_past' : True,
            'enable_persistence' : False
        }
    
        #if org_governance_name is not None:
        #    agent_config['org_governance_name'] = org_governance_name
    
        # Start instrument agent.
    
        log.info("TestInstrumentAgent.setup(): starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
    
        log.info("Agent setup")
        ia_pid = container_client.spawn_process(name=IA_NAME,
            module=IA_MOD,
            cls=IA_CLS,
            config=agent_config)    
        log.info('Agent pid=%s.', str(ia_pid))
        #self.addCleanup(self._verify_agent_reset)
    
        # Start a resource agent client to talk with the instrument agent.
    
        self._ia_client = ResourceAgentClient(IA_RESOURCE_ID, process=FakeProcess())
        log.info('Got ia client %s.', str(self._ia_client))
    
        log.info('test setup complete')


    ###############################################################################
    # Port agent helpers.
    ###############################################################################
                                
    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state()
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd)
            
    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.', 
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()
            
        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()
            
        self._event_subscriber = EventSubscriber(
            event_type=type, callback=consume_event,
            origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient()
        
        encoder = IonObjectSerializer()
        
        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        stream_name = 'parsed'
        param_dict_name = 'ctd_parsed_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)        
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 parameter_dictionary=pd,
                                 stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

        stream_name = 'raw'
        param_dict_name = 'ctd_raw_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 parameter_dictionary=pd,
                                 stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

    def _start_data_subscribers(self, count, raw_count):
        """
        """        
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
                
        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        self._raw_samples_received = []
        self._async_sample_result = AsyncResult()
        self._async_raw_sample_result = AsyncResult()

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            log.info('Received parsed data on %s (%s,%s)', stream_id, stream_route.exchange_point, stream_route.routing_key)
            self._samples_received.append(message)
            if len(self._samples_received) == count:
                self._async_sample_result.set()

        def recv_raw_data(message, stream_route, stream_id):
            log.info('Received raw data on %s (%s,%s)', stream_id, stream_route.exchange_point, stream_route.routing_key)
            self._raw_samples_received.append(message)
            if len(self._raw_samples_received) == raw_count:
                self._async_raw_sample_result.set()

        from pyon.util.containers import create_unique_identifier

        stream_name = 'parsed'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" %
                    stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id])
        pubsub_client.activate_subscription(sub_id)
        sub.subscription_id = sub_id # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)
        
        stream_name = 'raw'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" %
                    stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_raw_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id])
        pubsub_client.activate_subscription(sub_id)
        sub.subscription_id = sub_id # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()
 
    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber,'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(subscriber.subscription_id)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id)
            subscriber.stop()

    ###############################################################################
    # tcp helpers.
    ###############################################################################        

    def _start_tcp_client(self, retval):
        host = retval.result['ip_address']
        port = retval.result['port']
        tcp_client = TcpClient(host, port)
        return tcp_client
        

    ###############################################################################
    # Tests.
    ###############################################################################

    @unittest.skip('Test should be run manually only.')        
    def test_initialize(self):
        """
        test_initialize
        Test agent initialize command. This causes creation of
        driver process and transition to inactive.
        """
        
        print '#### in test'

        # We start in uninitialized state.
        # In this state there is no driver process.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
        
        # Ping the agent.
        retval = self._ia_client.ping_agent()
        log.info(retval)

        # Initialize the agent.
        # The agent is spawned with a driver config, but you can pass one in
        # optinally with the initialize command. This validates the driver
        # config, launches a driver process and connects to it via messaging.
        # If successful, we switch to the inactive state.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Ping the driver proc.
        retval = self._ia_client.ping_resource()
        log.info(retval)

        # Reset the agent. This causes the driver messaging to be stopped,
        # the driver process to end and switches us back to uninitialized.
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
        
    @unittest.skip('Test should be run manually only.')        
    def test_xx(self):
        """
        """


        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
    
        with self.assertRaises(Conflict):
            res_state = self._ia_client.get_resource_state()
            
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)
        
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()    
        print '################################# mavs4 came up in state: ' + state
        
        if state == ResourceAgentState.IDLE:
            cmd = AgentCommand(command=ResourceAgentEvent.RUN)
            retval = self._ia_client.execute_agent(cmd)
            state = self._ia_client.get_agent_state()
            self.assertEqual(state, ResourceAgentState.COMMAND)        
        
        elif state == ResourceAgentState.STREAMING:
            cmd = AgentCommand(command='DRIVER_EVENT_STOP_AUTOSAMPLE')
            retval = self._ia_client.execute_resource(cmd)
            state = self._ia_client.get_agent_state()
            self.assertEqual(state, ResourceAgentState.COMMAND)        
        
        state = self._ia_client.get_agent_state()    
        print '################################# mavs4 now in state: ' + state
        
        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={'session_type': DirectAccessTypes.telnet,
                           'session_timeout':600,
                           'inactivity_timeout':600})
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.DIRECT_ACCESS)        
        
        state = self._ia_client.get_agent_state()    
        print '################################# mavs4 now in state: ' + state
        
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet(token=retval.result['token']))
        
        self.assertTrue(tcp_client.send_data('\r\r'))        
        
        gevent.sleep(180)
        
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.DIRECT_ACCESS)
        
        cmd = AgentCommand(command=ResourceAgentEvent.GO_COMMAND)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)
예제 #47
0
class EventAlertTransform(TransformEventListener):
    def on_start(self):
        log.debug('EventAlertTransform.on_start()')
        super(EventAlertTransform, self).on_start()

        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # get the algorithm to use
        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        self.timer_origin = self.CFG.get_safe('process.timer_origin',
                                              'Interval Timer')
        self.instrument_origin = self.CFG.get_safe('process.instrument_origin',
                                                   '')

        self.event_times = []

        #-------------------------------------------------------------------------------------
        # Set up a listener for instrument events
        #-------------------------------------------------------------------------------------

        self.instrument_event_queue = gevent.queue.Queue()

        def instrument_event_received(message, headers):
            log.debug(
                "EventAlertTransform received an instrument event here::: %s" %
                message)
            self.instrument_event_queue.put(message)

        self.instrument_event_subscriber = EventSubscriber(
            origin=self.instrument_origin, callback=instrument_event_received)

        self.instrument_event_subscriber.start()

        #-------------------------------------------------------------------------------------
        # Create the publisher that will publish the Alert message
        #-------------------------------------------------------------------------------------

        self.event_publisher = EventPublisher()

    def on_quit(self):
        self.instrument_event_subscriber.stop()
        super(EventAlertTransform, self).on_quit()

    def process_event(self, msg, headers):
        '''
        The callback method.
        If the events satisfy the criteria, publish an alert event.
        '''

        if msg.origin == self.timer_origin:
            if self.instrument_event_queue.empty():
                log.debug(
                    "no event received from the instrument. Publishing an alarm event!"
                )
                self.publish()
            else:
                log.debug(
                    "Events were received from the instrument in between timer events. Instrument working normally."
                )
                self.instrument_event_queue.queue.clear()

    def publish(self):

        #-------------------------------------------------------------------------------------
        # publish an alert event
        #-------------------------------------------------------------------------------------
        self.event_publisher.publish_event(
            event_type="DeviceEvent",
            origin="EventAlertTransform",
            description="An alert event being published.")
예제 #48
0
class TestPrest(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """
    def setUp(self):
        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        print '#####################'
        print 'IN SETUP'

        self._ia_client = None

        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        log.info('building stream configuration')
        # Setup stream config.
        self._build_stream_config()

        #log.info('driver uri: %s', DRV_URI)
        #log.info('device address: %s', DEV_ADDR)
        #log.info('device port: %s', DEV_PORT)
        #log.info('work dir: %s', WORK_DIR)

        # Create agent config.
        agent_config = {
            'driver_config': DVR_CONFIG,
            'stream_config': self._stream_config,
            'agent': {
                'resource_id': IA_RESOURCE_ID
            },
            'test_mode': True,
            'forget_past': True,
            'enable_persistence': False
        }

        #if org_governance_name is not None:
        #    agent_config['org_governance_name'] = org_governance_name

        # Start instrument agent.

        log.info("TestInstrumentAgent.setup(): starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
                                                name=self.container.name)

        log.info("Agent setup")
        ia_pid = container_client.spawn_process(name=IA_NAME,
                                                module=IA_MOD,
                                                cls=IA_CLS,
                                                config=agent_config)
        log.info('Agent pid=%s.', str(ia_pid))
        self.addCleanup(self._verify_agent_reset)

        # Start a resource agent client to talk with the instrument agent.

        self._ia_client = ResourceAgentClient(IA_RESOURCE_ID,
                                              process=FakeProcess())
        log.info('Got ia client %s.', str(self._ia_client))

        log.info('test setup complete')

    ###############################################################################
    # Port agent helpers.
    ###############################################################################

    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state()
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd)

    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()

        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()

        self._event_subscriber = EventSubscriber(event_type=type,
                                                 callback=consume_event,
                                                 origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient()

        encoder = IonObjectSerializer()

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        stream_name = 'parsed'
        param_dict_name = 'ctd_parsed_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(
            param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(
            name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(
            name=stream_name,
            exchange_point='science_data',
            stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                             exchange_point=stream_route.exchange_point,
                             stream_id=stream_id,
                             parameter_dictionary=pd,
                             stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

        stream_name = 'raw'
        param_dict_name = 'ctd_raw_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(
            param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(
            name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(
            name=stream_name,
            exchange_point='science_data',
            stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                             exchange_point=stream_route.exchange_point,
                             stream_id=stream_id,
                             parameter_dictionary=pd,
                             stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

    def _start_data_subscribers(self, count, raw_count):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        self._raw_samples_received = []
        self._async_sample_result = AsyncResult()
        self._async_raw_sample_result = AsyncResult()

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            log.info('Received parsed data on %s (%s,%s)', stream_id,
                     stream_route.exchange_point, stream_route.routing_key)
            self._samples_received.append(message)
            if len(self._samples_received) == count:
                self._async_sample_result.set()

        def recv_raw_data(message, stream_route, stream_id):
            log.info('Received raw data on %s (%s,%s)', stream_id,
                     stream_route.exchange_point, stream_route.routing_key)
            self._raw_samples_received.append(message)
            if len(self._raw_samples_received) == raw_count:
                self._async_raw_sample_result.set()

        from pyon.util.containers import create_unique_identifier

        stream_name = 'parsed'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" % stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name,
                                                   stream_ids=[stream_id])
        pubsub_client.activate_subscription(sub_id)
        sub.subscription_id = sub_id  # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

        stream_name = 'raw'
        parsed_config = self._stream_config[stream_name]
        stream_id = parsed_config['stream_id']
        exchange_name = create_unique_identifier("%s_queue" % stream_name)
        self._purge_queue(exchange_name)
        sub = StandaloneStreamSubscriber(exchange_name, recv_raw_data)
        sub.start()
        self._data_subscribers.append(sub)
        sub_id = pubsub_client.create_subscription(name=exchange_name,
                                                   stream_ids=[stream_id])
        pubsub_client.activate_subscription(sub_id)
        sub.subscription_id = sub_id  # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()

    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber, 'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(
                        subscriber.subscription_id)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id)
            subscriber.stop()

    ###############################################################################
    # Tests.
    ###############################################################################

    @unittest.skip('Test should be run manually only.')
    def test_initialize(self):
        """
        test_initialize
        Test agent initialize command. This causes creation of
        driver process and transition to inactive.
        """

        print '#### in test'

        # We start in uninitialized state.
        # In this state there is no driver process.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Ping the agent.
        retval = self._ia_client.ping_agent()
        log.info(retval)

        # Initialize the agent.
        # The agent is spawned with a driver config, but you can pass one in
        # optinally with the initialize command. This validates the driver
        # config, launches a driver process and connects to it via messaging.
        # If successful, we switch to the inactive state.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Ping the driver proc.
        retval = self._ia_client.ping_resource()
        log.info(retval)

        # Reset the agent. This causes the driver messaging to be stopped,
        # the driver process to end and switches us back to uninitialized.
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    @unittest.skip('Test should be run manually only.')
    def test_xx(self):
        """
        """

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        with self.assertRaises(Conflict):
            res_state = self._ia_client.get_resource_state()

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        print '################################# sbe54 came up in state: ' + state

        if state == ResourceAgentState.IDLE:
            cmd = AgentCommand(command=ResourceAgentEvent.RUN)
            retval = self._ia_client.execute_agent(cmd)
            state = self._ia_client.get_agent_state()
            self.assertEqual(state, ResourceAgentState.COMMAND)

        elif state == ResourceAgentState.STREAMING:
            cmd = AgentCommand(command='DRIVER_EVENT_STOP_AUTOSAMPLE')
            retval = self._ia_client.execute_resource(cmd)
            state = self._ia_client.get_agent_state()
            self.assertEqual(state, ResourceAgentState.COMMAND)

        state = self._ia_client.get_agent_state()
        print '################################# sbe54 now in state: ' + state

        gevent.sleep(60 * 2.25)

        state = self._ia_client.get_agent_state()
        print '################################# sbe54 now in state: ' + state
        """
        'DRIVER_EVENT_START_AUTOSAMPLE'
        self.assertEqual(state, ResourceAgentState.IDLE)

        res_state = self._ia_client.get_resource_state()
        self.assertEqual(res_state, DriverProtocolState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)        
        
        res_state = self._ia_client.get_resource_state()
        self.assertEqual(res_state, DriverProtocolState.COMMAND)

        cmd = AgentCommand(command=SBE37ProtocolEvent.STOP_AUTOSAMPLE)
        with self.assertRaises(Conflict):
            retval = self._ia_client.execute_resource(cmd)
        """

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
예제 #49
0
class EventPersister(StandaloneProcess):

    def on_init(self):
        # Time in between event persists
        self.persist_interval = float(self.CFG.get_safe("process.event_persister.persist_interval", 1.0))

        self.persist_blacklist = self.CFG.get_safe("process.event_persister.persist_blacklist", 1.0)

        self._event_type_blacklist = [entry['event_type'] for entry in self.persist_blacklist if entry.get('event_type', None) and len(entry) == 1]
        self._complex_blacklist = [entry for entry in self.persist_blacklist if not (entry.get('event_type', None) and len(entry) == 1)]
        if self._complex_blacklist:
            log.warn("EventPersister does not yet support complex blacklist expressions: %s", self._complex_blacklist)

        # Holds received events FIFO in syncronized queue
        self.event_queue = Queue()

        # Temporarily holds list of events to persist while datastore operation are not yet completed
        # This is where events to persist will remain if datastore operation fails occasionally.
        self.events_to_persist = None

        # Number of unsuccessful attempts to persist in a row
        self.failure_count = 0

        # bookkeeping for timeout greenlet
        self._persist_greenlet = None
        self._terminate_persist = Event() # when set, exits the timeout greenlet

        # The event subscriber
        self.event_sub = None

    def on_start(self):
        # Persister thread
        self._persist_greenlet = spawn(self._trigger_func, self.persist_interval)
        log.debug('EventPersister timer greenlet started in "%s" (interval %s)', self.__class__.__name__, self.persist_interval)

        # Event subscription
        self.event_sub = EventSubscriber(pattern=EventSubscriber.ALL_EVENTS,
                                         callback=self._on_event,
                                         queue_name="event_persister")

        self.event_sub.start()

    def on_quit(self):
        # Stop event subscriber
        self.event_sub.stop()

        # tell the trigger greenlet we're done
        self._terminate_persist.set()

        # wait on the greenlet to finish cleanly
        self._persist_greenlet.join(timeout=10)

    def _on_event(self, event, *args, **kwargs):
        if not self._in_blacklist(event):
            self.event_queue.put(event)

    def _in_blacklist(self, event):
        if event.type_ in self._event_type_blacklist:
            return True
        if event.base_types:
            for base_type in event.base_types:
                if base_type in self._event_type_blacklist:
                    return True
            # TODO: Complex event blacklist
        return False

    def _trigger_func(self, persist_interval):
        log.debug('Starting event persister thread with persist_interval=%s', persist_interval)

        # Event.wait returns False on timeout (and True when set in on_quit), so we use this to both exit cleanly and do our timeout in a loop
        while not self._terminate_persist.wait(timeout=persist_interval):
            try:
                if self.events_to_persist and self.failure_count > 2:
                    bad_events = []
                    log.warn("Attempting to persist %s events individually" % (len(self.events_to_persist)))
                    for event in self.events_to_persist:
                        try:
                            self.container.event_repository.put_event(event)
                        except Exception:
                            bad_events.append(event)

                    if len(self.events_to_persist) != len(bad_events):
                        log.warn("Succeeded to persist some of the events - rest must be bad")
                        self._log_events(bad_events)
                    elif bad_events:
                        log.error("Discarding %s events after %s attempts!!" % (len(bad_events), self.failure_count))
                        self._log_events(bad_events)

                    self.events_to_persist = None
                    self.failure_count = 0

                elif self.events_to_persist:
                    # There was an error last time and we need to retry
                    log.info("Retry persisting %s events" % len(self.events_to_persist))
                    self._persist_events(self.events_to_persist)
                    self.events_to_persist = None

                self.events_to_persist = [self.event_queue.get() for x in xrange(self.event_queue.qsize())]

                self._persist_events(self.events_to_persist)
                self.events_to_persist = None
                self.failure_count = 0
            except Exception as ex:
                # Note: Persisting events may fail occasionally during test runs (when the "events" datastore is force
                # deleted and recreated). We'll log and keep retrying forever.
                log.exception("Failed to persist %s received events. Will retry next cycle" % len(self.events_to_persist))
                self.failure_count += 1
                self._log_events(self.events_to_persist)

    def _persist_events(self, event_list):
        if event_list:
            self.container.event_repository.put_events(event_list)

    def _log_events(self, events):
        events_str = pprint.pformat([event.__dict__ for event in events]) if events else ""
        log.warn("EVENTS:\n%s", events_str)
class NotificationWorker(SimpleProcess):
    """
    Instances of this class acts as a Notification Worker.
    """

    def on_init(self):
        self.event_pub = EventPublisher()
        self.user_info = {}
        self.resource_registry = ResourceRegistryServiceClient()

    def test_hook(self, user_info, reverse_user_info):
        """
        This method exists only to facilitate the testing of the reload of the user_info dictionary
        """
        pass

    def on_start(self):
        super(NotificationWorker, self).on_start()

        self.smtp_client = setting_up_smtp_client()

        # ------------------------------------------------------------------------------------
        # Start by loading the user info and reverse user info dictionaries
        # ------------------------------------------------------------------------------------

        try:
            self.user_info = self.load_user_info()
            self.reverse_user_info = calculate_reverse_user_info(self.user_info)

            log.info("On start up, notification workers loaded the following user_info dictionary: %s" % self.user_info)
            log.info("The calculated reverse user info: %s" % self.reverse_user_info)

        except NotFound as exc:
            if exc.message.find("users_index") > -1:
                log.warning("Notification workers found on start up that users_index have not been loaded yet.")
            else:
                raise NotFound(exc.message)

        # ------------------------------------------------------------------------------------
        # Create an event subscriber for Reload User Info events
        # ------------------------------------------------------------------------------------

        def reload_user_info(event_msg, headers):
            """
            Callback method for the subscriber to ReloadUserInfoEvent
            """

            notification_id = event_msg.notification_id
            log.info(
                "(Notification worker received a ReloadNotificationEvent. The relevant notification_id is %s"
                % notification_id
            )

            try:
                self.user_info = self.load_user_info()
            except NotFound:
                log.warning("ElasticSearch has not yet loaded the user_index.")

            self.reverse_user_info = calculate_reverse_user_info(self.user_info)
            self.test_hook(self.user_info, self.reverse_user_info)

            log.debug("After a reload, the user_info: %s" % self.user_info)
            log.debug("The recalculated reverse_user_info: %s" % self.reverse_user_info)

        # the subscriber for the ReloadUSerInfoEvent
        self.reload_user_info_subscriber = EventSubscriber(event_type="ReloadUserInfoEvent", callback=reload_user_info)
        self.reload_user_info_subscriber.start()

        # ------------------------------------------------------------------------------------
        # Create an event subscriber for all events that are of interest for notifications
        # ------------------------------------------------------------------------------------

        self.event_subscriber = EventSubscriber(queue_name="uns_queue", callback=self.process_event)
        self.event_subscriber.start()

    def process_event(self, msg, headers):
        """
        Callback method for the subscriber listening for all events
        """
        # ------------------------------------------------------------------------------------
        # From the reverse user info dict find out which users have subscribed to that event
        # ------------------------------------------------------------------------------------

        users = []
        if self.reverse_user_info:
            users = check_user_notification_interest(event=msg, reverse_user_info=self.reverse_user_info)

        log.info("Type of event received by notification worker: %s" % msg.type_)
        log.info("Notification worker deduced the following users were interested in the event: %s" % users)

        # ------------------------------------------------------------------------------------
        # Send email to the users
        # ------------------------------------------------------------------------------------

        for user_name in users:
            msg_recipient = self.user_info[user_name]["user_contact"].email
            send_email(message=msg, msg_recipient=msg_recipient, smtp_client=self.smtp_client)

    def on_stop(self):
        # close subscribers safely
        self.event_subscriber.stop()
        self.reload_user_info_subscriber.stop()

    def on_quit(self):
        # close subscribers safely
        self.event_subscriber.stop()
        self.reload_user_info_subscriber.stop()

    def poll(self, tries, callback, *args, **kwargs):
        """
        Polling wrapper for queries
        Elasticsearch may not index and cache the changes right away so we may need
        a couple of tries and a little time to go by before the results show.
        """
        for i in xrange(tries):
            retval = callback(*args, **kwargs)
            if retval:
                return retval
            time.sleep(0.2)
        return None

    def load_user_info(self):
        """
        Method to load the user info dictionary used by the notification workers and the UNS

        @retval user_info dict
        """

        users, _ = self.resource_registry.find_resources(restype=RT.UserInfo)

        user_info = {}

        if not users:
            return {}

        for user in users:
            notifications = []
            for variable in user.variables:
                if variable["name"] == "notifications":
                    notifications = variable["value"]

            user_info[user.name] = {"user_contact": user.contact, "notifications": notifications}

        return user_info
예제 #51
0
class TestAgentCommsAlerts(IonIntegrationTestCase):
    """
    """
    
    ############################################################################
    # Setup, teardown.
    ############################################################################
        
    def setUp(self):
        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config.
        """
        self._ia_client = None

        log.info('Creating driver integration test support:')
        log.info('driver uri: %s', DRV_URI)
        log.info('device address: %s', DEV_ADDR)
        log.info('device port: %s', DEV_PORT)
        log.info('log delimiter: %s', DELIM)
        log.info('work dir: %s', WORK_DIR)
        self._support = DriverIntegrationTestSupport(None,
                                                     None,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
                
        # Start port agent, add stop to cleanup.
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)    
        
        # Start container.
        log.info('Staring capability container.')
        self._start_container()
        
        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        self._event_count = 0
        self._events_received = []
        self._async_event_result = AsyncResult()

        def consume_event(*args, **kwargs):
            log.debug('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                     str(args), str(kwargs), str(args[0]))
            
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()
                        
        self._event_subscriber = EventSubscriber(
            event_type='DeviceStatusAlertEvent', callback=consume_event,
            origin=IA_RESOURCE_ID)
        
        self._event_subscriber.start()

        def stop_subscriber():
            self._event_subscriber.stop()
            self._event_subscriber = None
        self.addCleanup(stop_subscriber)

        log.info('building stream configuration')
        # Setup stream config.
        self._build_stream_config()

        # Create agent config.
        self._agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : self._stream_config,
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True,
            'aparam_alerts_config' : [state_alert_def, command_alert_def]
        }

        self._ia_client = None
        self._ia_pid = None
        
        self.addCleanup(self._verify_agent_reset)

    ###############################################################################
    # Port agent helpers.
    ###############################################################################
        
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        port = self._support.start_pagent()
        log.info('Port agent started at port %i',port)
        
        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr' : 'localhost',
            'port' : port,
            'cmd_port' : CMD_PORT
        }
                                    
    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient()
        
        encoder = IonObjectSerializer()
        
        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        stream_name = 'parsed'
        param_dict_name = 'ctd_parsed_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)        
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 parameter_dictionary=pd,
                                 stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config

        stream_name = 'raw'
        param_dict_name = 'ctd_raw_param_dict'
        pd_id = dataset_management.read_parameter_dictionary_by_name(param_dict_name, id_only=True)
        stream_def_id = pubsub_client.create_stream_definition(name=stream_name, parameter_dictionary_id=pd_id)
        stream_def = pubsub_client.read_stream_definition(stream_def_id)
        stream_def_dict = encoder.serialize(stream_def)
        pd = stream_def.parameter_dictionary
        stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)
        stream_config = dict(routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 parameter_dictionary=pd,
                                 stream_def_dict=stream_def_dict)
        self._stream_config[stream_name] = stream_config
        
    ###############################################################################
    # Agent start stop helpers.
    ###############################################################################

    def _start_agent(self):
        """
        """
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
        
        self._ia_pid = container_client.spawn_process(name=IA_NAME,
            module=IA_MOD,
            cls=IA_CLS,
            config=self._agent_config)
        log.info('Started instrument agent pid=%s.', str(self._ia_pid))
        
        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = None
        self._ia_client = ResourceAgentClient(IA_RESOURCE_ID, process=FakeProcess())
        log.info('Got instrument agent client %s.', str(self._ia_client))

    def _stop_agent(self):
        """
        """
        if self._ia_pid:
            container_client = ContainerAgentClient(node=self.container.node,
                name=self.container.name)
            container_client.terminate_process(self._ia_pid)
            self._ia_pid = None
            
        if self._ia_client:
            self._ia_client = None

    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state()
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd)
            self._ia_client = None

    ###############################################################################
    # Tests.
    ###############################################################################

    def test_lost_connection_alert(self):
        """
        test_lost_connection_alert
        Verify that agents detect lost connection state and issue alert.
        """

        self._event_count = 3

        self._start_agent()

        # We start in uninitialized state.
        # In this state there is no driver process.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
        
        # Ping the agent.
        retval = self._ia_client.ping_agent()
        log.info(retval)

        # Initialize the agent.
        # The agent is spawned with a driver config, but you can pass one in
        # optinally with the initialize command. This validates the driver
        # config, launches a driver process and connects to it via messaging.
        # If successful, we switch to the inactive state.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Ping the driver proc.
        retval = self._ia_client.ping_resource()
        log.info(retval)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        # Confirm the persisted parameters.
        retval = self._ia_client.get_agent(['alerts'])['alerts']
        """
        {'origin': '123xyz', 'status': 1, '_id': 'da03b90d2e064b25bf51ed90b729e82e',
        'description': 'The alert is cleared.', 'time_stamps': [],
        'type_': 'DeviceStatusAlertEvent', 'valid_values': [],
        'values': ['RESOURCE_AGENT_STATE_INACTIVE'], 'value_id': '',
        'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'],
        'stream_name': '', 'ts_created': '1366749987069', 'sub_type': 3,
        'origin_type': 'InstrumentDevice', 'name': 'comms_warning'}
        """

        # Acquire sample returns a string, not a particle.  The particle
        # is created by the data handler though.
        cmd = AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)
        retval = self._ia_client.execute_resource(cmd)

        # Blow the port agent out from under the agent.
        self._support.stop_pagent()

        # Wait for a while, the supervisor is restarting the port agent.
        gevent.sleep(5)
        self._support.start_pagent()

        timeout = gevent.Timeout(120)
        timeout.start()
        try:
            while True:
                state = self._ia_client.get_agent_state()
                if state == ResourceAgentState.COMMAND:
                    timeout.cancel()
                    break
                else:
                    gevent.sleep(2)
            
        except Timeout as t:
            self.fail('Could not reconnect to device.')
        
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        # Reset the agent. This causes the driver messaging to be stopped,
        # the driver process to end and switches us back to uninitialized.
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        self._async_event_result.get(timeout=30) 
        """
        {'origin': '123xyz', 'status': 1, '_id': '9d7db919a0414741a2aa97f3dc310647', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_INACTIVE'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367008029709', 'sub_type': 'ALL_CLEAR', 'origin_type': 'InstrumentDevice', 'name': 'comms_state_warning'}
        {'origin': '123xyz', 'status': 1, '_id': 'f746cec5e486445e856ef29af8d8d49a', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367008029717', 'sub_type': 'ALL_CLEAR', 'origin_type': 'InstrumentDevice', 'name': 'comms_command_warning'}
        {'origin': '123xyz', 'status': 1, '_id': '126b1e20f54f4bd2b84a93584831ea8d', 'description': 'Detected comms failure.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_LOST_CONNECTION'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367008062061', 'sub_type': 'WARNING', 'origin_type': 'InstrumentDevice', 'name': 'comms_state_warning'}
        {'origin': '123xyz', 'status': 1, '_id': '90f5762112dd446b939c6675d34dd61d', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_COMMAND'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367008098639', 'sub_type': 'ALL_CLEAR', 'origin_type': 'InstrumentDevice', 'name': 'comms_state_warning'}
        """
        
    def test_connect_failed_alert(self):
        """
        test_connect_failed_alert
        Verify that agents detect failed connections and issue alert.
        """

        self._event_count = 4

        # Remove the port agent.
        self._support.stop_pagent()

        self._start_agent()

        # We start in uninitialized state.
        # In this state there is no driver process.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
        
        # Ping the agent.
        retval = self._ia_client.ping_agent()
        log.info(retval)

        # Initialize the agent.
        # The agent is spawned with a driver config, but you can pass one in
        # optinally with the initialize command. This validates the driver
        # config, launches a driver process and connects to it via messaging.
        # If successful, we switch to the inactive state.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Ping the driver proc.
        retval = self._ia_client.ping_resource()
        log.info(retval)

        with self.assertRaises(Exception):
            cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
            retval = self._ia_client.execute_agent(cmd)

        # Now start up the port agent.
        self._support.start_pagent()
        gevent.sleep(5)

        # This time it will work.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        self._async_event_result.get(timeout=30)
        """
        {'origin': '123xyz', 'status': 1, '_id': 'ac1ae3ec24e74f65bde24362d689346a', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': ['RESOURCE_AGENT_STATE_INACTIVE'], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367007751167', 'sub_type': 'ALL_CLEAR', 'origin_type': 'InstrumentDevice', 'name': 'comms_state_warning'}
        {'origin': '123xyz', 'status': 1, '_id': '6af6a6156172481ebc44baad2708ec5c', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367007751175', 'sub_type': 'ALL_CLEAR', 'origin_type': 'InstrumentDevice', 'name': 'comms_command_warning'}
        {'origin': '123xyz', 'status': 1, '_id': 'b493698b46fd4fc1b4c66c53b70ed043', 'description': 'Detected comms failure while connecting.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367007756771', 'sub_type': 'WARNING', 'origin_type': 'InstrumentDevice', 'name': 'comms_command_warning'}
        {'origin': '123xyz', 'status': 1, '_id': 'c01e911ecb1a47c5a04cbbcbfb348a42', 'description': 'The alert is cleared.', 'time_stamps': [], 'type_': 'DeviceStatusAlertEvent', 'valid_values': [], 'values': [None], 'value_id': '', 'base_types': ['DeviceStatusEvent', 'DeviceEvent', 'Event'], 'stream_name': '', 'ts_created': '1367007792905', 'sub_type': 'ALL_CLEAR', 'origin_type': 'InstrumentDevice', 'name': 'comms_command_warning'}
        """
        
class ServiceGatewayService(BaseServiceGatewayService):
    """
	The Service Gateway Service is the service that uses a gevent web server and Flask
	to bridge HTTP requests to AMQP RPC ION process service calls.
    """

    def on_init(self):
        #defaults
        self.http_server = None

        #retain a pointer to this object for use in ProcessRPC calls
        global service_gateway_instance

        ######
        # to prevent cascading failure, here's an attempted hack
        if service_gateway_instance is not None and service_gateway_instance.http_server is not None:
            service_gateway_instance.http_server.stop()
        # end hack
        ######

        service_gateway_instance = self

        self.server_hostname = self.CFG.get_safe('container.service_gateway.web_server.hostname', DEFAULT_WEB_SERVER_HOSTNAME)
        self.server_port = self.CFG.get_safe('container.service_gateway.web_server.port', DEFAULT_WEB_SERVER_PORT)
        self.web_server_enabled = self.CFG.get_safe('container.service_gateway.web_server.enabled', True)
        self.web_logging = self.CFG.get_safe('container.service_gateway.web_server.log')
        self.log_errors = self.CFG.get_safe('container.service_gateway.log_errors', False)

        #Optional list of trusted originators can be specified in config.
        self.trusted_originators = self.CFG.get_safe('container.service_gateway.trusted_originators')
        if not self.trusted_originators:
            self.trusted_originators = None
            log.info("Service Gateway will not check requests against trusted originators since none are configured.")

        #Get the user_cache_size
        self.user_cache_size = self.CFG.get_safe('container.service_gateway.user_cache_size', DEFAULT_USER_CACHE_SIZE)

        #Initialize an LRU Cache to keep user roles cached for performance reasons
        #maxSize = maximum number of elements to keep in cache
        #maxAgeMs = oldest entry to keep
        self.user_data_cache = LRUCache(self.user_cache_size,0,0)

        #Start the gevent web server unless disabled
        if self.web_server_enabled:
            log.info("Starting service gateway on %s:%s", self.server_hostname, self.server_port)
            self.start_service(self.server_hostname, self.server_port)

        #Configure  subscriptions for user_cache events
        self.user_role_event_subscriber = EventSubscriber(event_type=OT.UserRoleModifiedEvent, origin_type="Org",
            callback=self.user_role_event_callback)
        self.user_role_event_subscriber.start()

        self.user_role_reset_subscriber = EventSubscriber(event_type=OT.UserRoleCacheResetEvent,
            callback=self.user_role_reset_callback)
        self.user_role_reset_subscriber.start()

    def on_quit(self):
        self.stop_service()

        if self.user_role_event_subscriber is not None:
            self.user_role_event_subscriber.stop()

        if self.user_role_reset_subscriber is not None:
            self.user_role_reset_subscriber.stop()


    def start_service(self, hostname=DEFAULT_WEB_SERVER_HOSTNAME, port=DEFAULT_WEB_SERVER_PORT):
        """Responsible for starting the gevent based web server."""

        if self.http_server is not None:
            self.stop_service()

        self.http_server = WSGIServer((hostname, port), service_gateway_app, log=self.web_logging)
        self.http_server.start()

        return True

    def stop_service(self):
        """Responsible for stopping the gevent based web server."""
        if self.http_server is not None:
            self.http_server.stop()
        return True

    def is_trusted_address(self, requesting_address):

        if self.trusted_originators is None:
            return True

        for addr in self.trusted_originators:
            if requesting_address == addr:
                return True

        return False

    def user_role_event_callback(self, *args, **kwargs):
        """
        This method is a callback function for receiving Events when User Roles are modified.
        """
        user_role_event = args[0]
        org_id = user_role_event.origin
        actor_id = user_role_event.actor_id
        role_name = user_role_event.role_name
        log.debug("User Role modified: %s %s %s" % (org_id, actor_id, role_name))

        #Evict the user and their roles from the cache so that it gets updated with the next call.
        if service_gateway_instance.user_data_cache and service_gateway_instance.user_data_cache.has_key(actor_id):
            log.debug('Evicting user from the user_data_cache: %s' % actor_id)
            service_gateway_instance.user_data_cache.evict(actor_id)

    def user_role_reset_callback(self, *args, **kwargs):
        '''
        This method is a callback function for when an event is received to clear the user data cache
        '''
        self.user_data_cache.clear()
예제 #53
0
class InteractionObserver(object):
    """
    Observes ongoing interactions in the Exchange. Logs them to disk and makes them available
    in the local container (for development purposes) and on request.
    """
    def start(self):
        self.msg_log = []

        self.event_sub = None
        self.conv_sub = None

        #Conv subscription
        self.conv_sub = ConvSubscriber(callback=self._msg_received)
        self.conv_sub.start()

        # Event subscription
        self.event_sub = EventSubscriber(pattern=EventSubscriber.ALL_EVENTS,
                                         callback=self._event_received,
                                         queue_name="event_persister")
        self.event_sub.start()

        self.started = True

    def stop(self):
        # Stop event subscriber
        self.event_sub.stop()
        # Stop conv subscriber
        self.conv_sub.stop()
        self.started = False

    def _msg_received(self, msg, *args, **kwargs):
        self.log_message(args[0])

    def _event_received(self, event, *args, **kwargs):
        if 'origin' in event:
            args[0]['origin'] = event.origin
        if 'origin_type' in event:
            args[0]['origin_type'] = event.origin_type
        if 'sub_type' in event:
            args[0]['sub_type'] = event.sub_type
        self.log_message(args[0], True)

    def log_message(self, mhdrs, evmsg=False):
        """
        @param evmsg    This message is an event, render it as such!
        """
        mhdrs['_content_type'] = mhdrs.get('format', None)

        # TUPLE: timestamp (MS), type, boolean if its an event
        msg_rec = (get_ion_ts(), mhdrs, evmsg)
        self.msg_log.append(msg_rec)

        # Truncate if too long in increments of slice
        if len(self.msg_log) > MAX_MSGLOG + SLICE:
            self.msg_log = self.msg_log[SLICE:]

    def _get_data(self, msglog, response_msgs):
        """
        Provides msc data in python format, to be converted either to msc text or to json
        for use with msc web monitor.
        Returns a list of hashes in the form:
        { to, from, content, type, ts, error (boolean), to_raw, from_raw, topline }
        """
        msgdata = []

        for msgtup in msglog:

            datatemp = {
                "to": None,
                "from": None,
                "content": None,
                "type": None,
                "ts": None,
                "error": False
            }
            msg = msgtup[1]

            convid = msg.get('conv-id', None)

            if (convid in response_msgs):
                response = response_msgs.pop(convid)
                sname = response.get('sender')
                rname = response.get('receiver')

            else:
                if (msg.get('sender-type', 'unknown') == 'service'):
                    sname = msg.get(
                        'sender-service',
                        msg.get('sender-name', msg.get('sender', 'unknown')))
                else:
                    sname = msg.get('sender-name',
                                    msg.get('sender', 'unknown'))
                rname = msg.get('receiver', 'unknown')

                if (convid is not None):
                    response_msgs[convid] = {
                        'sender': rname,
                        'receiver': sname
                    }

            # from_raw is displayed as the header on the webpage
            datatemp["from_raw"] = sname
            sname = self._sanitize(sname)
            datatemp["from"] = sname
            datatemp["ts"] = msg.get("ts", "Unknown")
            datatemp["to_raw"] = rname
            rname = self._sanitize(rname)
            datatemp["to"] = rname

            if msgtup[2]:
                # this is an EVENT, show it as a box!
                datatemp["type"] = "events"

                #todo: not sure if we can hard code the splitting mechanism like done below !!
                datatemp["from"] = "events," + (
                    msg.get('routing_key').split('._._.')[0]).split('.')[1]
                datatemp["from_raw"] = "events," + (
                    msg.get('routing_key').split('._._.')[0]).split('.')[1]
                datatemp["to"] = "events," + (
                    msg.get('routing_key').split('._._.')[0]).split('.')[1]
                datatemp["to_raw"] = "events," + (
                    msg.get('routing_key').split('._._.')[0]).split('.')[1]

                evlabel = "%s \nOrigin: %s" % (msg.get('routing_key'),
                                               msg.get('origin'))
                datatemp["content"] = evlabel
                datatemp["topline"] = msg.get('sub_type', '') + " " + msg.get(
                    'origin_type', '')
                if (datatemp['topline'] == " "):
                    datatemp['topline'] = msg.get('origin')

            else:

                mlabel = "%s\n(%s->%s)\n<%s>" % (msg.get(
                    'op', None), sname.rsplit(",", 1)[-1], rname.rsplit(
                        ",", 1)[-1], msg.get('_content_type',
                                             '?content-type?'))
                datatemp["content"] = mlabel
                datatemp["topline"] = mlabel.split("\n")[0]

                if msg.get('protocol', None) == 'rpc':

                    datatemp["type"] = "rpcres"

                    performative = msg.get('performative', None)
                    if performative == 'request':
                        datatemp["type"] = "rpcreq"
                    elif performative == 'timeout':
                        pass  # timeout, unfortunately you don't see this @TODO

                    if performative == 'failure' or performative == 'error':
                        datatemp["error"] = True

                else:
                    # non rpc -> perhaps a data message?
                    datatemp["type"] = "data"

            msgdata.append(datatemp)

        return msgdata, response_msgs

    @classmethod
    def _sanitize(cls, input):
        return string.replace(string.replace(input, ".", "_"), "-", "_")
예제 #54
0
class DatasetAgentTestCase(IonIntegrationTestCase):
    """
    Base class for all dataset agent end to end tests
    """
    test_config = DatasetAgentTestConfig()

    def setUp(self, deploy_file=DEPLOY_FILE):
        """
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        self._dsa_client = None
        self.dams = DataAcquisitionManagementServiceClient()

        # Ensure we have a good test configuration
        self.test_config.verify()

        # Start container.
        log.info('Staring capability container.')
        self._start_container()
        self.rr = self.container.resource_registry

        # Bring up services in a deploy file (no need to message)
        log.info('Starting deploy services. %s', deploy_file)
        self.container.start_rel_from_url(DEPLOY_FILE)

        # Load instrument specific parameters
        log.info('Preload test scenarios')
        self._load_params()

        # Start a resource agent client to talk with the instrument agent.
        log.info('Starting DSA process')
        self._dsa_client = self._start_dataset_agent_process()
        log.debug("Client created: %s", type(self._dsa_client))
        self.addCleanup(self._stop_dataset_agent_process)
        log.info('test setup complete')

        # Start data subscribers
        self._start_data_subscribers()
        self.addCleanup(self._stop_data_subscribers)

    ###
    #   Test/Agent Startup Helpers
    ###
    def _load_params(self):
        """
        Do a second round of preload with instrument specific scenarios
        """
        scenario = None
        categories = None

        if PRELOAD_CATEGORIES:
            categories = ",".join(PRELOAD_CATEGORIES)

        # load_parameter_scenarios
        if PRELOAD_SCENARIO:
            scenario = PRELOAD_SCENARIO
        else:
            log.warn("No common preload defined.  Was this intentional?")

        if self.test_config.preload_scenario:
            scenario = "%s,%s" % (
                scenario, self.test_config.preload_scenario
            ) if scenario else self.test_config.preload_scenario
        else:
            log.warn("No DSA specific preload defined.  Was this intentional?")

        if scenario:
            preload_config = dict(
                op="load",
                scenario=scenario,
                #path="master",
                path=TESTED_DOC,
                categories=categories,
                clearcols="owner_id,org_ids",
                #assets="res/preload/r2_ioc/ooi_assets",
                #parseooi="True",
            )
            log.debug("Starting preload now: config=%s", preload_config)
            self.container.spawn_process("Loader",
                                         "ion.processes.bootstrap.ion_loader",
                                         "IONLoader", preload_config)

    def _start_dataset_agent_process(self):
        """
        Launch the agent process and store the configuration.  Tried
        to emulate the same process used by import_data.py
        """
        instrument_device, dsa_instance = self._get_dsa_instance()
        self._driver_config = dsa_instance.driver_config

        self._update_dsa_config(dsa_instance)
        self._update_harvester_config(dsa_instance)

        self._dsa_instance = dsa_instance
        self.clear_sample_data()

        # Return a resource agent client
        return self._get_dsa_client(instrument_device, dsa_instance)

    def _stop_dataset_agent_process(self):
        """
        Stop the dataset agent instance
        """
        self.assert_reset()
        self.dams.stop_external_dataset_agent_instance(self._dsa_instance._id)

    def _get_dsa_instance(self):
        """
        Find the dsa instance in preload and return an instance of that object
        """
        name = self.test_config.instrument_device_name

        log.debug("Start dataset agent process for instrument device: %s",
                  name)
        objects, _ = self.rr.find_resources(RT.InstrumentDevice, name=name)
        log.debug("Found Instrument Devices: %s", objects)
        if not objects:
            raise ConfigNotFound(
                "No appropriate InstrumentDevice objects loaded")

        instrument_device = objects[0]
        log.trace("Found instrument device: %s", instrument_device)

        dsa_instance = self.rr.read_object(
            subject=instrument_device._id,
            predicate=PRED.hasAgentInstance,
            object_type=RT.ExternalDatasetAgentInstance)

        log.info("dsa_instance found: %s", dsa_instance)

        return instrument_device, dsa_instance

    def _update_harvester_config(self, dsa_instance):
        """
        Update the harvester config such that we change the directory to something
        we have write permissions.
        """
        log.info("dsa agent instance: %s", dsa_instance)
        driver_config = dsa_instance.driver_config

        log.info("dsa agent driver config: %s", driver_config)
        driver_config['startup_config']['harvester'][
            'directory'] = self.test_config.data_dir

        log.info("updated driver config: %s", driver_config)
        dsa_instance.driver_config = driver_config

        self.rr.update(dsa_instance)

    def _update_dsa_config(self, dsa_instance):
        """
        Update the dsa configuration prior to loading the agent.  This is where we can
        alter production configurations for use in a controlled test environment.
        """
        dsa_obj = self.rr.read_object(object_type=RT.ExternalDatasetAgent,
                                      predicate=PRED.hasAgentDefinition,
                                      subject=dsa_instance._id,
                                      id_only=False)

        log.info("dsa agent definition found: %s", dsa_obj)

        # If we don't want to load from an egg then we need to
        # alter the driver config read from preload
        if self.test_config.mi_repo is not None:
            dsa_obj.driver_uri = None
            # Strip the custom namespace
            dsa_obj.driver_module = ".".join(
                dsa_obj.driver_module.split('.')[1:])

            log.info("saving new dsa agent config: %s", dsa_obj)
            self.rr.update(dsa_obj)

            if not self.test_config.mi_repo in sys.path:
                sys.path.insert(0, self.test_config.mi_repo)

            log.debug("Driver module: %s", dsa_obj.driver_module)
            log.debug("MI Repo: %s", self.test_config.mi_repo)
            log.trace("Sys Path: %s", sys.path)

    def _get_dsa_client(self, instrument_device, dsa_instance):
        """
        Launch the agent and return a client
        """
        fake_process = FakeProcess()
        fake_process.container = self.container

        clients = DataAcquisitionManagementServiceDependentClients(
            fake_process)
        config_builder = ExternalDatasetAgentConfigurationBuilder(clients)

        try:
            config_builder.set_agent_instance_object(dsa_instance)
            self.agent_config = config_builder.prepare()
        except Exception as e:
            log.error('failed to launch: %s', e, exc_info=True)
            raise ServerError('failed to launch')

        self._dsa_pid = self.dams.start_external_dataset_agent_instance(
            dsa_instance._id)
        log.debug("_get_dsa_client CFG")
        return ResourceAgentClient(instrument_device._id,
                                   process=FakeProcess())

        dispatcher = ProcessDispatcherServiceClient()
        launcher = AgentLauncher(dispatcher)

        log.debug("Launching agent process!")

        self._dsa_pid = launcher.launch(
            self.agent_config,
            config_builder._get_process_definition()._id)
        if not self._dsa_pid:
            raise ServerError(
                "Launched external dataset agent instance but no process_id")
        config_builder.record_launch_parameters(self.agent_config)

        launcher.await_launch(10.0)
        return ResourceAgentClient(instrument_device._id,
                                   process=FakeProcess())

    def _get_dsa_object_state(self):
        state, _id = self.container.state_repository.get_state(self._dsa_pid)
        log.debug("agent_state (%s): %s", self._dsa_pid, state)

        driver_state = state.get(DSA_STATE_KEY)
        log.debug("driver_state (%s): %s", self._dsa_pid, driver_state)

        return driver_state

    ###
    #   Data file helpers
    ###

    def _get_source_data_file(self, filename):
        """
        Search for a sample data file, first check the driver resource directory
        then just use the filename as a path.  If the file doesn't exists
        raise an exception
        @param filename name or path of the file to search for
        @return full path to the found data file
        @raise IonException if the file isn't found
        """
        resource_dir = self.test_config.test_resource_dir
        source_path = os.path.join(resource_dir, filename)

        log.debug("Search for resource file (%s) in %s", filename,
                  resource_dir)
        if os.path.isfile(source_path):
            log.debug("Found %s in resource directory", filename)
            return source_path

        log.debug("Search for resource file (%s) in current directory",
                  filename)
        if os.path.isfile(filename):
            log.debug("Found %s in the current directory", filename)
            return filename

        raise IonException("Data file %s does not exist", filename)

    def create_data_dir(self):
        """
        Verify the test data directory is created and exists.  Return the path to
        the directory.
        @return: path to data directory
        @raise: ConfigNotFound no harvester config
        @raise: IonException if data_dir exists, but not a directory
        """
        startup_config = self._driver_config.get('startup_config')
        if not startup_config:
            raise ConfigNotFound("Driver config missing 'startup_config'")

        harvester_config = startup_config.get('harvester')
        if not harvester_config:
            raise ConfigNotFound("Startup config missing 'harvester' config")

        data_dir = harvester_config.get("directory")
        if not data_dir:
            raise ConfigNotFound("Harvester config missing 'directory'")

        if not os.path.exists(data_dir):
            log.debug("Creating data dir: %s", data_dir)
            os.makedirs(data_dir)

        elif not os.path.isdir(data_dir):
            raise IonException("'data_dir' is not a directory")

        return data_dir

    def clear_sample_data(self):
        """
        Remove all files from the sample data directory
        """
        data_dir = self.create_data_dir()

        log.debug("Clean all data from %s", data_dir)
        self.remove_all_files(data_dir)

    def create_sample_data(self, filename, dest_filename=None):
        """
        Search for a data file in the driver resource directory and if the file
        is not found there then search using the filename directly.  Then copy
        the file to the test data directory.

        If a dest_filename is supplied it will be renamed in the destination
        directory.
        @param: filename - filename or path to a data file to copy
        @param: dest_filename - name of the file when copied. default to filename
        """
        data_dir = self.create_data_dir()
        source_path = self._get_source_data_file(filename)

        log.debug("DIR: %s", data_dir)
        if dest_filename is None:
            dest_path = os.path.join(data_dir, os.path.basename(source_path))
        else:
            dest_path = os.path.join(data_dir, dest_filename)

        log.debug("Creating data file src: %s, dest: %s", source_path,
                  dest_path)
        shutil.copy2(source_path, dest_path)

        return dest_path

    def remove_all_files(self, dir_name):
        """
        Remove all files from a directory.  Raise an exception if the directory contains something
        other than files.
        @param dir_name directory path to remove files.
        @raise RuntimeError if the directory contains anything except files.
        """
        for file_name in os.listdir(dir_name):
            file_path = os.path.join(dir_name, file_name)
            if not os.path.isfile(file_path):
                raise RuntimeError("%s is not a file", file_path)

        for file_name in os.listdir(dir_name):
            file_path = os.path.join(dir_name, file_name)
            os.unlink(file_path)

    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()

        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()

        self._event_subscriber = EventSubscriber(
            event_type=type, callback=consume_event,
            origin="IA_RESOURCE_ID")  #TODO
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # Data stream helpers.
    ###############################################################################
    def _start_data_subscribers(self):
        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            if self._samples_received.get(stream_id) is None:
                self._samples_received[stream_id] = []

            log.info('Received parsed data on %s (%s,%s)', stream_id,
                     stream_route.exchange_point, stream_route.routing_key)
            self._samples_received[stream_id].append(message)

        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = {}
        self._stream_id_map = {}
        stream_config = self.agent_config['stream_config']

        log.info("starting data subscribers")

        for stream_name in stream_config.keys():
            log.debug("Starting data subscriber for stream '%s'", stream_name)
            stream_id = stream_config[stream_name]['stream_id']
            self._stream_id_map[stream_name] = stream_id
            self._start_data_subscriber(stream_config[stream_name], recv_data)

    def _start_data_subscriber(self, config, callback):
        """
        Setup and start a data subscriber
        """
        exchange_point = config['exchange_point']
        stream_id = config['stream_id']

        sub = StandaloneStreamSubscriber(exchange_point, callback)
        sub.start()
        self._data_subscribers.append(sub)

        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        sub_id = pubsub_client.create_subscription(name=exchange_point,
                                                   stream_ids=[stream_id])
        pubsub_client.activate_subscription(sub_id)
        sub.subscription_id = sub_id  # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def make_data_product(self, pdict_name, dp_name, available_fields=None):
        self.pubsub_management = PubsubManagementServiceClient()
        if available_fields is None: available_fields = []
        pdict_id = self.dataset_management.read_parameter_dictionary_by_name(
            pdict_name, id_only=True)
        stream_def_id = self.pubsub_management.create_stream_definition(
            '%s stream_def' % dp_name,
            parameter_dictionary_id=pdict_id,
            available_fields=available_fields or None)
        self.addCleanup(self.pubsub_management.delete_stream_definition,
                        stream_def_id)
        dp_obj = DataProduct(name=dp_name)
        data_product_id = self.data_product_management.create_data_product(
            dp_obj, stream_definition_id=stream_def_id)
        self.addCleanup(self.data_product_management.delete_data_product,
                        data_product_id)
        return data_product_id

    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber, 'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(
                        subscriber.subscription_id)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id)
            subscriber.stop()

    def get_samples(self, stream_name, sample_count=1, timeout=30):
        """
        listen on a stream until 'sample_count' samples are read and return
        a list of all samples read.  If the required number of samples aren't
        read then throw an exception.

        Note that this method does not clear the sample queue for the stream.
        This should be done explicitly by the caller.  However, samples that
        are consumed by this method are removed.

        @raise SampleTimeout - if the required number of samples aren't read
        """
        to = gevent.Timeout(timeout)
        to.start()
        done = False
        result = []
        i = 0

        log.debug("Fetch %s sample(s) from stream '%s'" %
                  (sample_count, stream_name))

        stream_id = self._stream_id_map.get(stream_name)
        log.debug("Stream ID Map: %s ", self._stream_id_map)
        self.assertIsNotNone(stream_id,
                             msg="Unable to find stream name '%s'" %
                             stream_name)

        try:
            while (not done):
                if (self._samples_received.has_key(stream_id)
                        and len(self._samples_received.get(stream_id))):
                    log.trace("get_samples() received sample #%d!", i)
                    result.append(self._samples_received[stream_id].pop(0))
                    i += 1

                    if i >= sample_count:
                        done = True

                else:
                    log.debug(
                        "No samples in %s. Sleep a bit to wait for the data queue to fill up.",
                        stream_name)
                    gevent.sleep(1)

        except Timeout:
            log.error("Failed to get %d records from %s.  received: %d",
                      sample_count, stream_name, i)
            self.fail("Failed to read samples from stream %s", stream_name)
        finally:
            to.cancel()
            return result

    def remove_sample_dir(self):
        """
        Remove the sample dir and all files
        """
        data_dir = self.create_data_dir()
        self.clear_sample_data()
        os.rmdir(data_dir)

    ###
    #   Common assert methods
    ###

    def assertDictEqual(self, d1, d2, msg=None):  # assertEqual uses for dicts
        for k, v1 in d1.iteritems():
            self.assertIn(k, d2, msg)
            v2 = d2[k]
            if (isinstance(v1, collections.Iterable)
                    and not isinstance(v1, basestring)):
                self.assertItemsEqual(v1, v2, msg)
            else:
                self.assertEqual(v1, v2, msg)
        return True

    def assert_initialize(self, final_state=ResourceAgentState.STREAMING):
        '''
        Walk through DSA states to get to streaming mode from uninitialized
        '''
        state = self._dsa_client.get_agent_state()

        with self.assertRaises(Conflict):
            res_state = self._dsa_client.get_resource_state()

        log.debug("Initialize DataSet agent")
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._dsa_client.execute_agent(cmd)
        state = self._dsa_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)
        log.info("Sent INITIALIZE; DSA state = %s", state)

        log.debug("DataSet agent go active")
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._dsa_client.execute_agent(cmd)
        state = self._dsa_client.get_agent_state()
        log.info("Sent GO_ACTIVE; DSA state = %s", state)
        self.assertEqual(state, ResourceAgentState.IDLE)

        log.debug("DataSet agent run")
        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._dsa_client.execute_agent(cmd)
        state = self._dsa_client.get_agent_state()
        log.info("Sent RUN; DSA state = %s", state)
        self.assertEqual(state, ResourceAgentState.COMMAND)

        if final_state == ResourceAgentState.STREAMING:
            self.assert_start_sampling()

    def assert_stop_sampling(self):
        '''
        transition to command.  Must be called from streaming
        '''
        state = self._dsa_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STREAMING)

        log.debug("DataSet agent start sampling")
        cmd = AgentCommand(command='DRIVER_EVENT_STOP_AUTOSAMPLE')
        retval = self._dsa_client.execute_resource(cmd)
        state = self._dsa_client.get_agent_state()
        log.info("Sent START SAMPLING; DSA state = %s", state)
        self.assertEqual(state, ResourceAgentState.COMMAND)

    def assert_start_sampling(self):
        '''
        transition to sampling.  Must be called from command
        '''
        state = self._dsa_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        log.debug("DataSet agent start sampling")
        cmd = AgentCommand(command='DRIVER_EVENT_START_AUTOSAMPLE')
        retval = self._dsa_client.execute_resource(cmd)
        state = self._dsa_client.get_agent_state()
        log.info("Sent START SAMPLING; DSA state = %s", state)
        self.assertEqual(state, ResourceAgentState.STREAMING)

    def assert_reset(self):
        '''
        Put the instrument back in uninitialized
        '''
        if self._dsa_client is None:
            return

        state = self._dsa_client.get_agent_state()

        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._dsa_client.execute_agent(cmd)
            state = self._dsa_client.get_agent_state()

        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def assert_data_values(self, granules, dataset_definition_file):
        """
        Verify granules match the granules defined in the definition file
        """
        rs_file = self._get_source_data_file(dataset_definition_file)
        rs = ResultSet(rs_file)

        self.assertTrue(rs.verify(granules),
                        msg="Failed data validation.  See log for details")

    def assert_sample_queue_size(self, stream_name, size):
        """
        verify a sample queue is the size we expect it to be.
        """
        # Sleep a couple seconds to ensure the
        gevent.sleep(2)

        stream_id = self._stream_id_map.get(stream_name)
        length = 0
        if stream_id in self._samples_received:
            length = len(self._samples_received[stream_id])
        self.assertEqual(length,
                         size,
                         msg="Queue size != expected size (%d != %d)" %
                         (length, size))

    def assert_set_pubrate(self, rate):
        """
        Set the pubrate for the parsed data stream.  Set to 0 for
        no buffering
        """
        self.assertIsInstance(rate, (int, float))
        self.assertGreaterEqual(rate, 0)

        expected_pubrate = {self.test_config.stream_name: rate}

        retval = self._dsa_client.set_agent({'pubrate': expected_pubrate})

        retval = self._dsa_client.get_agent(['pubrate'])
        expected_pubrate_result = {'pubrate': expected_pubrate}
        self.assertEqual(retval, expected_pubrate_result)

    def assert_agent_command(self, command, args=None, timeout=None):
        """
        Verify an agent command
        @param command: driver command to execute
        @param args: kwargs to pass to the agent command object
        """
        cmd = AgentCommand(command=command, kwargs=args)
        retval = self._dsa_client.execute_agent(cmd, timeout=timeout)

    def assert_resource_command(self, command, args=None, timeout=None):
        """
        Verify a resource command
        @param command: driver command to execute
        @param args: kwargs to pass to the agent command object
        """
        cmd = AgentCommand(command=command, kwargs=args)
        retval = self._dsa_client.execute_resource(cmd)

    def assert_schema(self, caps_list):

        dd_list = ['display_name', 'description']
        ddt_list = ['display_name', 'description', 'type']
        ddvt_list = ['display_name', 'description', 'visibility', 'type']
        ddak_list = ['display_name', 'description', 'args', 'kwargs']

        for x in caps_list:
            if isinstance(x, dict):
                x.pop('type_')
                x = IonObject('AgentCapability', **x)

            if x.cap_type == CapabilityType.AGT_CMD:
                keys = x.schema.keys()
                for y in ddak_list:
                    self.assertIn(y, keys)

            elif x.cap_type == CapabilityType.AGT_PAR:
                if x.name != 'example':
                    keys = x.schema.keys()
                    for y in ddvt_list:
                        self.assertIn(y, keys)

            elif x.cap_type == CapabilityType.RES_CMD:
                keys = x.schema.keys()
                self.assertIn('return', keys)
                self.assertIn('display_name', keys)
                self.assertIn('arguments', keys)
                self.assertIn('timeout', keys)

            elif x.cap_type == CapabilityType.RES_IFACE:
                pass

            elif x.cap_type == CapabilityType.RES_PAR:
                keys = x.schema.keys()
                self.assertIn('get_timeout', keys)
                self.assertIn('set_timeout', keys)
                self.assertIn('direct_access', keys)
                self.assertIn('startup', keys)
                self.assertIn('visibility', keys)

            elif x.cap_type == CapabilityType.AGT_STATES:
                for (k, v) in x.schema.iteritems():
                    keys = v.keys()
                    for y in dd_list:
                        self.assertIn(y, keys)

            elif x.cap_type == CapabilityType.ALERT_DEFS:
                for (k, v) in x.schema.iteritems():
                    keys = v.keys()
                    for y in ddt_list:
                        self.assertIn(y, keys)

            elif x.cap_type == CapabilityType.AGT_CMD_ARGS:
                for (k, v) in x.schema.iteritems():
                    keys = v.keys()
                    for y in ddt_list:
                        self.assertIn(y, keys)

    def assert_agent_capabilities(self):
        """
        Verify capabilities throughout the agent lifecycle
        """
        capabilities = {
            AgentCapabilityType.AGENT_COMMAND:
            self._common_agent_commands(ResourceAgentState.UNINITIALIZED),
            AgentCapabilityType.AGENT_PARAMETER:
            self._common_agent_parameters(),
            AgentCapabilityType.RESOURCE_COMMAND:
            None,
            AgentCapabilityType.RESOURCE_INTERFACE:
            None,
            AgentCapabilityType.RESOURCE_PARAMETER:
            None,
        }

        ###
        # DSA State INACTIVE
        ###

        log.debug("Initialize DataSet agent")
        self.assert_agent_command(ResourceAgentEvent.INITIALIZE)
        self.assert_state_change(ResourceAgentState.INACTIVE)
        self.assert_capabilities(capabilities)

        ###
        # DSA State IDLE
        ###

        log.debug("DataSet agent go active")
        capabilities[
            AgentCapabilityType.AGENT_COMMAND] = self._common_agent_commands(
                ResourceAgentState.IDLE)
        self.assert_agent_command(ResourceAgentEvent.GO_ACTIVE)
        self.assert_state_change(ResourceAgentState.IDLE)
        self.assert_capabilities(capabilities)

        ###
        # DSA State COMMAND
        ###

        log.debug("DataSet agent run")
        capabilities[
            AgentCapabilityType.AGENT_COMMAND] = self._common_agent_commands(
                ResourceAgentState.COMMAND)
        capabilities[AgentCapabilityType.RESOURCE_COMMAND] = [
            'DRIVER_EVENT_START_AUTOSAMPLE'
        ]
        capabilities[AgentCapabilityType.
                     RESOURCE_PARAMETER] = self._common_resource_parameters()
        self.assert_agent_command(ResourceAgentEvent.RUN)
        self.assert_state_change(ResourceAgentState.COMMAND)
        self.assert_capabilities(capabilities)

        ###
        # DSA State STREAMING
        ###
        capabilities[
            AgentCapabilityType.AGENT_COMMAND] = self._common_agent_commands(
                ResourceAgentState.STREAMING)
        capabilities[AgentCapabilityType.RESOURCE_COMMAND] = [
            'DRIVER_EVENT_STOP_AUTOSAMPLE'
        ]
        capabilities[AgentCapabilityType.
                     RESOURCE_PARAMETER] = self._common_resource_parameters()
        self.assert_start_sampling()
        self.assert_capabilities(capabilities)

        ###
        # DSA State LOST_CONNECTION
        ###
        capabilities[
            AgentCapabilityType.AGENT_COMMAND] = self._common_agent_commands(
                ResourceAgentState.LOST_CONNECTION)
        capabilities[AgentCapabilityType.RESOURCE_COMMAND] = None
        capabilities[AgentCapabilityType.RESOURCE_PARAMETER] = None
        self.assert_agent_command(ResourceAgentEvent.RESET)
        self.assert_state_change(ResourceAgentState.UNINITIALIZED)

        self.remove_sample_dir()
        self.assert_initialize(final_state=ResourceAgentState.COMMAND)
        self.assert_resource_command('DRIVER_EVENT_START_AUTOSAMPLE')
        self.assert_state_change(ResourceAgentState.LOST_CONNECTION, 90)

    def assert_driver_state(self, expected_state=None):
        '''
        verify that expected persisted agent state matches was it actually stored
        @param expected_state dict expected
        '''
        state = self._get_dsa_object_state()

        if expected_state is None:
            self.assertIsNone(expected_state)
        else:
            self.assertEqual(expected_state, state)

    def assert_agent_state_after_restart(self):
        '''
        Restart the agent.  Verify that the agent PID changes. Then verify the new state
        matches the old state.
        '''
        old_pid = self._dsa_pid
        old_state = self._get_dsa_object_state()

        # Start a resource agent client to talk with the instrument agent.
        log.info('Restarting DSA process')
        self._stop_dataset_agent_process()
        self._dsa_client = self._start_dataset_agent_process()
        log.debug("Client created: %s", type(self._dsa_client))
        self.addCleanup(self.assert_reset)

        self.assert_initialize()

        self.assertNotEqual(old_pid, self._dsa_pid)
        self.assertEqual(old_state, self._get_dsa_object_state())

        # Kick it into autosample and give it time for samples to come in, there shouldn't be any
        gevent.sleep(5)

    def assert_capabilities(self, capabilities):
        '''
        Verify that all capabilities are available for a give state

        @todo: Currently resource interface not implemented because it requires
               a submodule update and some of the submodules are in release
               states.  So for now, no resource interfaces

        @param: dictionary of all the different capability types that are
        supposed to be there. i.e.
        {
          agent_command = ['DO_MY_COMMAND'],
          agent_parameter = ['foo'],
          resource_command = None,
          resource_interface = None,
          resource_parameter = None,
        }
        '''
        def sort_capabilities(caps_list):
            '''
            sort a return value into capability buckets.
            @retval agt_cmds, agt_pars, res_cmds, res_iface, res_pars
            '''
            agt_cmds = []
            agt_pars = []
            res_cmds = []
            res_iface = []
            res_pars = []

            if len(caps_list) > 0 and isinstance(caps_list[0],
                                                 AgentCapability):
                agt_cmds = [
                    x.name for x in caps_list
                    if x.cap_type == CapabilityType.AGT_CMD
                ]
                agt_pars = [
                    x.name for x in caps_list
                    if x.cap_type == CapabilityType.AGT_PAR
                ]
                res_cmds = [
                    x.name for x in caps_list
                    if x.cap_type == CapabilityType.RES_CMD
                ]
                #res_iface = [x.name for x in caps_list if x.cap_type==CapabilityType.RES_IFACE]
                res_pars = [
                    x.name for x in caps_list
                    if x.cap_type == CapabilityType.RES_PAR
                ]

            elif len(caps_list) > 0 and isinstance(caps_list[0], dict):
                agt_cmds = [
                    x['name'] for x in caps_list
                    if x['cap_type'] == CapabilityType.AGT_CMD
                ]
                agt_pars = [
                    x['name'] for x in caps_list
                    if x['cap_type'] == CapabilityType.AGT_PAR
                ]
                res_cmds = [
                    x['name'] for x in caps_list
                    if x['cap_type'] == CapabilityType.RES_CMD
                ]
                #res_iface = [x['name'] for x in caps_list if x['cap_type']==CapabilityType.RES_IFACE]
                res_pars = [
                    x['name'] for x in caps_list
                    if x['cap_type'] == CapabilityType.RES_PAR
                ]

            agt_cmds.sort()
            agt_pars.sort()
            res_cmds.sort()
            res_iface.sort()
            res_pars.sort()

            return agt_cmds, agt_pars, res_cmds, res_iface, res_pars

        if not capabilities.get(AgentCapabilityType.AGENT_COMMAND):
            capabilities[AgentCapabilityType.AGENT_COMMAND] = []
        if not capabilities.get(AgentCapabilityType.AGENT_PARAMETER):
            capabilities[AgentCapabilityType.AGENT_PARAMETER] = []
        if not capabilities.get(AgentCapabilityType.RESOURCE_COMMAND):
            capabilities[AgentCapabilityType.RESOURCE_COMMAND] = []
        if not capabilities.get(AgentCapabilityType.RESOURCE_INTERFACE):
            capabilities[AgentCapabilityType.RESOURCE_INTERFACE] = []
        if not capabilities.get(AgentCapabilityType.RESOURCE_PARAMETER):
            capabilities[AgentCapabilityType.RESOURCE_PARAMETER] = []

        expected_agent_cmd = capabilities.get(
            AgentCapabilityType.AGENT_COMMAND)
        expected_agent_cmd.sort()
        expected_agent_param = self._common_agent_parameters()
        expected_agent_param.sort()
        expected_res_cmd = capabilities.get(
            AgentCapabilityType.RESOURCE_COMMAND)
        expected_res_cmd.sort()
        expected_res_param = capabilities.get(
            AgentCapabilityType.RESOURCE_PARAMETER)
        expected_res_param.sort()
        expected_res_int = capabilities.get(
            AgentCapabilityType.RESOURCE_INTERFACE)
        expected_res_int.sort()

        # go get the active capabilities
        retval = self._dsa_client.get_capabilities()
        agt_cmds, agt_pars, res_cmds, res_iface, res_pars = sort_capabilities(
            retval)
        self.assert_schema(retval)

        log.debug("Agent Commands: %s ", str(agt_cmds))
        log.debug("Compared to: %s", expected_agent_cmd)
        log.debug("Agent Parameters: %s ", str(agt_pars))
        log.debug("Compared to: %s", expected_agent_param)
        log.debug("Resource Commands: %s ", str(res_cmds))
        log.debug("Compared to: %s", expected_res_cmd)
        log.debug("Resource Interface: %s ", str(res_iface))
        log.debug("Compared to: %s", expected_res_int)
        log.debug("Resource Parameter: %s ", str(res_pars))
        log.debug("Compared to: %s", expected_res_param)

        # Compare to what we are supposed to have
        self.assertEqual(expected_agent_cmd, agt_cmds)
        self.assertEqual(expected_agent_param, agt_pars)
        self.assertEqual(expected_res_cmd, res_cmds)
        self.assertEqual(expected_res_int, res_iface)
        self.assertEqual(expected_res_param, res_pars)

    def _common_resource_parameters(self):
        '''
        list of common resource parameters
        @return: list of resource parameters
        '''
        return [
            'batched_particle_count', 'publisher_polling_interval',
            'records_per_second'
        ]

    def _common_agent_parameters(self):
        '''
        list of common agent parameters
        @return: list of agent parameters
        '''
        return [
            'aggstatus', 'alerts', 'driver_name', 'driver_pid', 'example',
            'pubrate', 'streams'
        ]

    def _common_agent_commands(self, agent_state):
        '''
        list of common agent parameters for a agent state
        @return: list of agent parameters
        @raise: KeyError for undefined agent state
        '''
        capabilities = {
            ResourceAgentState.UNINITIALIZED: [
                ResourceAgentEvent.GO_ACTIVE,
                ResourceAgentEvent.RESET,
            ],
            ResourceAgentState.IDLE: [
                ResourceAgentEvent.GO_INACTIVE,
                ResourceAgentEvent.RESET,
                ResourceAgentEvent.RUN,
            ],
            ResourceAgentState.COMMAND: [
                ResourceAgentEvent.CLEAR, ResourceAgentEvent.RESET,
                ResourceAgentEvent.GO_INACTIVE, ResourceAgentEvent.PAUSE
            ],
            ResourceAgentState.STREAMING:
            [ResourceAgentEvent.RESET, ResourceAgentEvent.GO_INACTIVE],
            ResourceAgentState.LOST_CONNECTION:
            [ResourceAgentEvent.RESET, ResourceAgentEvent.GO_INACTIVE]
        }

        return capabilities[agent_state]

    def assert_state_change(self, target_agent_state, timeout=10):
        """
        Verify the agent and resource states change as expected within the timeout
        Fail if the state doesn't change to the expected state.
        @param target_agent_state: State we expect the agent to be in
        @param timeout: how long to wait for the driver to change states
        """
        to = gevent.Timeout(timeout)
        to.start()
        done = False
        agent_state = None

        try:
            while (not done):

                agent_state = self._dsa_client.get_agent_state()
                log.error("Current agent state: %s", agent_state)

                if (agent_state == target_agent_state):
                    log.debug("Current state match: %s", agent_state)
                    done = True

                if not done:
                    log.debug(
                        "state mismatch, waiting for state to transition.")
                    gevent.sleep(1)
        except Timeout:
            log.error(
                "Failed to transition agent state to %s, current state: %s",
                target_agent_state, agent_state)
            self.fail("Failed to transition state.")
        finally:
            to.cancel()
예제 #55
0
class EndpointMixin(object):
    """
    """
    def __init__(self):
        """
        """
        pass

    def mixin_on_init(self):
        """
        """
        self._server = None
        self._client = None
        self._other_host = self.CFG.other_host
        self._other_port = self.CFG.other_port
        self._this_port = self.CFG.this_port
        self._platform_resource_id = self.CFG.platform_resource_id
        self._link_status = TelemetryStatusType.UNAVAILABLE
        self._event_subscriber = None
        self._server_greenlet = None
        self._publisher = EventPublisher()

    def mixin_on_start(self):
        """
        """
        self._server = R3PCServer(self._req_callback,
                                  self._server_close_callback)
        self._client = R3PCClient(self._ack_callback,
                                  self._client_close_callback)
        if self._this_port == 0:
            self._this_port = self._server.start('*', self._this_port)
        else:
            self._server.start('*', self._this_port)
        log.debug('%s server binding to *:%i', self.__class__.__name__,
                  self._this_port)

        # Start the event subscriber.
        self._event_subscriber = EventSubscriber(
            event_type='PlatformTelemetryEvent',
            callback=self._consume_telemetry_event,
            origin=self._platform_resource_id)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def mixin_on_quit(self):
        """
        """
        self._stop()

    def mixin_on_stop(self):
        """
        """
        self._stop()

    ######################################################################
    # Helpers.
    ######################################################################

    def _stop(self):
        """
        Stop sockets and subscriber.
        """
        if self._event_subscriber:
            self._event_subscriber.stop()
            self._event_subscriber = None
        if self._server:
            self._server.stop()
            self._server = None
        if self._client:
            self._client.stop()
            self._client = None
예제 #56
0
class InstrumentAgentTestDA():
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests for Direct Access mode and provide 
    a tutorial on use of the agent setup and interface.
    """
    def _setup(self):
        """
        Set up driver integration support.
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        self._ia_client = None

        log.info('Creating driver integration test support:')
        log.info('driver uri: %s', DRV_URI)
        log.info('device address: %s', DEV_ADDR)
        log.info('device port: %s', DEV_PORT)
        log.info('log delimiter: %s', DELIM)
        log.info('work dir: %s', WORK_DIR)
        self._support = DriverIntegrationTestSupport(None, None, DEV_ADDR,
                                                     DEV_PORT, DATA_PORT,
                                                     CMD_PORT, PA_BINARY,
                                                     DELIM, WORK_DIR)

        # Start port agent, add stop to cleanup.
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)

        # Start container.
        log.info('Staring capability container.')
        self._start_container()

        # Bring up services in a deploy file (no need to message)
        log.info('Staring deploy services.')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        # Start a resource agent client to talk with the instrument agent.
        log.info('starting IA process')
        self._ia_client = start_instrument_agent_process(self.container)
        self.addCleanup(self._verify_agent_reset)
        log.info('test setup complete')

    ###############################################################################
    # Port agent helpers.
    ###############################################################################

    def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        port = self._support.start_pagent()
        log.info('Port agent started at port %i', port)

        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr': 'localhost',
            'port': port,
            'cmd_port': CMD_PORT
        }

    def _verify_agent_reset(self):
        """
        Check agent state and reset if necessary.
        This called if a test fails and reset hasn't occurred.
        """
        if self._ia_client is None:
            return

        state = self._ia_client.get_agent_state()
        if state != ResourceAgentState.UNINITIALIZED:
            cmd = AgentCommand(command=ResourceAgentEvent.RESET)
            retval = self._ia_client.execute_agent(cmd)

    ###############################################################################
    # Event helpers.
    ###############################################################################

    def _start_event_subscriber(self, type='ResourceAgentEvent', count=0):
        """
        Start a subscriber to the instrument agent events.
        @param type The type of event to catch.
        @count Trigger the async event result when events received reaches this.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.',
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._event_count > 0 and \
                self._event_count == len(self._events_received):
                self._async_event_result.set()

        # Event array and async event result.
        self._event_count = count
        self._events_received = []
        self._async_event_result = AsyncResult()

        self._event_subscriber = EventSubscriber(event_type=type,
                                                 callback=consume_event,
                                                 origin=IA_RESOURCE_ID)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=5)

    def _stop_event_subscriber(self):
        """
        Stop event subscribers on cleanup.
        """
        self._event_subscriber.stop()
        self._event_subscriber = None

    ###############################################################################
    # tcp helpers.
    ###############################################################################

    def _start_tcp_client(self, retval):
        host = retval.result['ip_address']
        port = retval.result['port']
        tcp_client = TcpClient(host, port)
        return tcp_client

    ###############################################################################
    # Assert helpers.
    ###############################################################################

    def assertInstrumentAgentState(self, expected_state, timeout=0):
        end_time = time.time() + timeout

        while (True):
            state = self._ia_client.get_agent_state()
            log.debug(
                "assertInstrumentAgentState: IA state = %s, expected state = %s"
                % (state, expected_state))
            if state == expected_state:
                return True
            if time.time() >= end_time:
                self.fail(
                    "assertInstrumentAgentState: IA failed to transition to %s state"
                    % expected_state)
            gevent.sleep(1)

    def assertSetInstrumentState(self, command, new_state):
        if type(command) == str:
            log.debug("assertSetInstrumentState: building command for %s" %
                      command)
            cmd = AgentCommand(command=command)
        else:
            cmd = command
        retval = self._ia_client.execute_agent(cmd)
        self.assertInstrumentAgentState(new_state)
        return retval

    ###############################################################################
    # Tests.
    #
    # response from IA for start DA looks similar to the following:
    #
    # {'status': 0, 'type_': 'AgentCommandResult', 'command': 'RESOURCE_AGENT_EVENT_GO_DIRECT_ACCESS',
    # 'result': {'token': 'F2B6EED3-F926-4B3B-AE80-4F8DE79276F3', 'ip_address': 'Edwards-MacBook-Pro.local', 'port': 8000},
    # 'ts_execute': '1344889063861', 'command_id': ''}
    ###############################################################################

    def test_direct_access_vsp_IA_shutdown(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown from IA. 
        """
        def start_and_stop_DA():
            retval = self.assertSetInstrumentState(
                cmd, ResourceAgentState.DIRECT_ACCESS)
            tcp_client = self._start_tcp_client(retval)
            self.assertTrue(tcp_client.send_data('ts\r\n'))
            self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                          ResourceAgentState.COMMAND)

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.vsp,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        # test for starting DA, making connection with TCP client, sending/recving commands to/from instrument, and DA shut down
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.send_data('ts\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ts
        
        -0.1964,85.12299, 697.168,   39.5241, 1506.965, 01 Feb 2001, 01:01:00
        """

        sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'
        sample_regex = re.compile(sample_pattern)
        lines = response.split('\r\n')

        sample_count = 0
        for x in lines:
            if sample_regex.match(x):
                sample_count += 1
        #self.assertEqual(sample_count, 1)

        self.assertTrue(tcp_client.send_data('ds\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ds
        SBE37-SMP V 2.6 SERIAL NO. 2165   01 Feb 2001  01:01:00
        not logging: received stop command
        sample interval = 23195 seconds
        samplenumber = 0, free = 200000
        do not transmit real-time data
        do not output salinity with each sample
        do not output sound velocity with each sample
        do not store time with each sample
        number of samples to average = 0
        reference pressure = 0.0 db
        serial sync mode disabled
        wait time after serial sync sampling = 0 seconds
        internal pump is installed
        temperature = 7.54 deg C
        WARNING: LOW BATTERY VOLTAGE!!
        """

        self.assertNotEqual(response.find('SBE37-SMP'), -1)
        self.assertNotEqual(response.find('sample interval'), -1)
        self.assertNotEqual(response.find('samplenumber'), -1)
        self.assertNotEqual(response.find('number of samples to average'), -1)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                      ResourceAgentState.COMMAND)

        # test for starting and shutting down DA w/o ever connecting TCP client
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                      ResourceAgentState.COMMAND)

        # test for race condition in DA when shut down by IA after sending something from TCP client
        for i in range(0, 10):
            start_and_stop_DA()

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_telnet_IA_shutdown(self):
        """
        Test agent direct_access mode for telnet when shutdown from IA. 
        """
        def start_and_stop_DA():
            retval = self.assertSetInstrumentState(
                cmd, ResourceAgentState.DIRECT_ACCESS)
            tcp_client = self._start_tcp_client(retval)
            self.assertTrue(
                tcp_client.start_telnet(token=retval.result['token']))
            self.assertTrue(tcp_client.send_data('ts\r\n'))
            self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                          ResourceAgentState.COMMAND)

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.telnet,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        # test for starting DA, making connection with TCP/telnet client, sending/recving commands to/from instrument, and DA shut down
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(token=retval.result['token']))

        self.assertTrue(tcp_client.send_data('ts\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ts
        
        -0.1964,85.12299, 697.168,   39.5241, 1506.965, 01 Feb 2001, 01:01:00
        """

        sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'
        sample_regex = re.compile(sample_pattern)
        lines = response.split('\r\n')

        sample_count = 0
        for x in lines:
            if sample_regex.match(x):
                sample_count += 1
        #self.assertEqual(sample_count, 1)

        self.assertTrue(tcp_client.send_data('ds\r\n'))
        _, response = tcp_client.expect(None, 3)

        # CORRECT PATTERN TO MATCH IN RESPONSE BUFFER:
        """
        ds
        SBE37-SMP V 2.6 SERIAL NO. 2165   01 Feb 2001  01:01:00
        not logging: received stop command
        sample interval = 23195 seconds
        samplenumber = 0, free = 200000
        do not transmit real-time data
        do not output salinity with each sample
        do not output sound velocity with each sample
        do not store time with each sample
        number of samples to average = 0
        reference pressure = 0.0 db
        serial sync mode disabled
        wait time after serial sync sampling = 0 seconds
        internal pump is installed
        temperature = 7.54 deg C
        WARNING: LOW BATTERY VOLTAGE!!
        """

        self.assertNotEqual(response.find('SBE37-SMP'), -1)
        self.assertNotEqual(response.find('sample interval'), -1)
        self.assertNotEqual(response.find('samplenumber'), -1)
        self.assertNotEqual(response.find('number of samples to average'), -1)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                      ResourceAgentState.COMMAND)

        # test for starting and shutting down DA w/o ever connecting TCP client
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                      ResourceAgentState.COMMAND)

        # test for starting and shutting down DA w/o ever entering a username
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                      ResourceAgentState.COMMAND)

        # test for starting and shutting down DA w/o ever entering a token
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        self.assertSetInstrumentState(ResourceAgentEvent.GO_COMMAND,
                                      ResourceAgentState.COMMAND)

        # test for race condition in DA when shut down by IA after sending something from TCP client
        for i in range(0, 10):
            start_and_stop_DA()

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_telnet_login_failure(self):
        """
        Test agent direct_access mode for telnet when login fails. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.telnet,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        # test that DA session quits when bad token is sent
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertFalse(tcp_client.start_telnet(token='some junk'))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

    def test_direct_access_telnet_session_setup_failure(self):
        """
        Test agent direct_access mode for telnet when session setup fails. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.telnet,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        # test that DA session quits when bad token is sent
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(
            tcp_client.start_telnet(token=retval.result['token'],
                                    handshake=False))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

    def test_direct_access_vsp_client_disconnect(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown by tcp client disconnect. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.vsp,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.send_data('ts\r\n'))

        tcp_client.disconnect()

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_telnet_client_disconnect(self):
        """
        Test agent direct_access mode for telnet when shutdown by tcp client disconnect. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.telnet,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(retval.result['token']))

        self.assertTrue(tcp_client.send_data('ts\r\n'))

        tcp_client.disconnect()

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and disconnecting client w/o ever entering a username
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        tcp_client.disconnect()
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and disconnecting client w/o ever entering a token
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        tcp_client.disconnect()
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and disconnecting client w/o ever negotiating the telnet session
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(
            tcp_client.start_telnet(token=retval.result['token'],
                                    handshake=False))
        tcp_client.disconnect()
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_vsp_session_timeout(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown by session timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.vsp,
                               'session_timeout': 10,
                               'inactivity_timeout': 600
                           })

        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        self._start_tcp_client(retval)

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)

        # test for starting and session timeout w/o ever connecting TCP client
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_telnet_session_timeout(self):
        """
        Test agent direct_access mode for telnet when shutdown by session timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.telnet,
                               'session_timeout': 10,
                               'inactivity_timeout': 600
                           })

        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(retval.result['token']))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)

        # test for starting and session timeout w/o ever entering a username
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and session timeout w/o ever entering a token
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and session timeout w/o ever negotiating the telnet session
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(
            tcp_client.start_telnet(token=retval.result['token'],
                                    handshake=False))
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_vsp_inactivity_timeout(self):
        """
        Test agent direct_access mode for virtual serial port when shutdown by inactivity timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.vsp,
                               'session_timeout': 600,
                               'inactivity_timeout': 10
                           })

        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        self._start_tcp_client(retval)

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)

        # test for starting and inactivity timeout w/o ever connecting TCP client
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    def test_direct_access_telnet_inactivity_timeout(self):
        """
        Test agent direct_access mode for telnet when shutdown by inactivity timeout. 
        """

        self.assertInstrumentAgentState(ResourceAgentState.UNINITIALIZED)

        self.assertSetInstrumentState(ResourceAgentEvent.INITIALIZE,
                                      ResourceAgentState.INACTIVE)

        self.assertSetInstrumentState(ResourceAgentEvent.GO_ACTIVE,
                                      ResourceAgentState.IDLE)

        self.assertSetInstrumentState(ResourceAgentEvent.RUN,
                                      ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.telnet,
                               'session_timeout': 600,
                               'inactivity_timeout': 10
                           })

        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        tcp_client = self._start_tcp_client(retval)

        self.assertTrue(tcp_client.start_telnet(retval.result['token']))

        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=30)

        # test for starting and inactivity timeout w/o ever entering a username
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and inactivity timeout w/o ever entering a token
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(tcp_client.start_telnet())
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        # test for starting and inactivity timeout w/o ever negotiating the telnet session
        retval = self.assertSetInstrumentState(
            cmd, ResourceAgentState.DIRECT_ACCESS)
        tcp_client = self._start_tcp_client(retval)
        self.assertTrue(
            tcp_client.start_telnet(token=retval.result['token'],
                                    handshake=False))
        self.assertInstrumentAgentState(ResourceAgentState.COMMAND, timeout=20)

        self.assertSetInstrumentState(ResourceAgentEvent.RESET,
                                      ResourceAgentState.UNINITIALIZED)

    @unittest.skip('A manual test for timing purposes.')
    def test_exit_da_timing(self):
        """
        test_exit_da_timing
        Test time it takes to leave direct access and return to command mode.
        """

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_DIRECT_ACCESS,
                           kwargs={
                               'session_type': DirectAccessTypes.vsp,
                               'session_timeout': 600,
                               'inactivity_timeout': 600
                           })

        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.DIRECT_ACCESS)

        log.info("GO_DIRECT_ACCESS retval=" + str(retval.result))

        starttime = time.time()

        cmd = AgentCommand(command=ResourceAgentEvent.GO_COMMAND)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        delta = time.time() - starttime

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        retval = self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        print '######## exiting direct access takes: %f seconds' % delta
예제 #57
0
    def test_pub_on_different_subsubtypes(self):
        res_list = [
            DotDict(ar=event.AsyncResult(), gq=queue.Queue(), count=0)
            for i in xrange(4)
        ]

        def cb_gen(num):
            def cb(event, *args, **kwargs):
                res_list[num].count += 1
                res_list[num].gq.put(event)
                if event.description == "end":
                    res_list[num].ar.set()

            return cb

        sub0 = EventSubscriber(event_type="ResourceModifiedEvent",
                               sub_type="st1.*",
                               callback=cb_gen(0))
        sub0.start()

        sub1 = EventSubscriber(event_type="ResourceModifiedEvent",
                               sub_type="st1.a",
                               callback=cb_gen(1))
        sub1.start()

        sub2 = EventSubscriber(event_type="ResourceModifiedEvent",
                               sub_type="*.a",
                               callback=cb_gen(2))
        sub2.start()

        sub3 = EventSubscriber(event_type="ResourceModifiedEvent",
                               sub_type="st1",
                               callback=cb_gen(3))
        sub3.start()

        pub1 = EventPublisher(event_type="ResourceModifiedEvent")

        pub1.publish_event(origin="one", sub_type="st1.a", description="1")
        pub1.publish_event(origin="two", sub_type="st1", description="2")
        pub1.publish_event(origin="three", sub_type="st1.b", description="3")

        pub1.publish_event(origin="four", sub_type="st2.a", description="4")
        pub1.publish_event(origin="five", sub_type="st2", description="5")

        pub1.publish_event(origin="six", sub_type="a", description="6")
        pub1.publish_event(origin="seven", sub_type="", description="7")

        pub1.publish_event(origin="end", sub_type="st1.a", description="end")
        pub1.publish_event(origin="end", sub_type="st1", description="end")

        [res_list[i].ar.get(timeout=5) for i in xrange(3)]

        sub0.stop()
        sub1.stop()
        sub2.stop()
        sub3.stop()

        for i in xrange(4):
            res_list[i].res = []
            for x in xrange(res_list[i].count):
                res_list[i].res.append(res_list[i].gq.get(timeout=5))

        self.assertEquals(len(res_list[0].res), 3)
        self.assertEquals(res_list[0].res[0].description, "1")

        self.assertEquals(len(res_list[1].res), 2)
        self.assertEquals(res_list[1].res[0].description, "1")

        self.assertEquals(len(res_list[2].res), 3)
        self.assertEquals(res_list[2].res[0].description, "1")

        self.assertEquals(len(res_list[3].res), 2)
        self.assertEquals(res_list[3].res[0].description, "2")