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()
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)
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")
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()
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)
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()
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 = []
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 = []
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)
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 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)
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()
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()
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()
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
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))
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)
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)
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.")
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, ".", "_"), "-", "_")
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 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")
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
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
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
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
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")
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)
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) """
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)
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)
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.")
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)
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
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()
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, ".", "_"), "-", "_")
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()
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 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
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")