def prepare(self, will_launch=True):
        """
        Prepare (validate) an agent for launch, fetching all associated resources

        @param will_launch - whether the running status should be checked -- set false if just generating config
        """
        assert self.agent_instance_obj

        # fetch caches just in time
        if any([not self.RR2.has_cached_predicate(x) for x in self._predicates_to_cache()]):
            self._update_cached_predicates()

        if any([not self.RR2.has_cached_resource(x) for x in self._resources_to_cache()]):
            self._update_cached_resources()

        # validate the associations, then pick things up
        self._collect_agent_instance_associations()

        if will_launch:
            # if there is an agent pid then assume that a drive is already started
            agent_process_id = ResourceAgentClient._get_agent_process_id(self._get_device()._id)
            if agent_process_id:
                raise BadRequest("Agent Instance already running for this device pid: %s" %
                                 str(agent_process_id))

        self.will_launch = will_launch
        config = self.generate_config()
        return config
    def prepare(self, will_launch=True):
        """
        Prepare (validate) an agent for launch, fetching all associated resources

        @param will_launch - whether the running status should be checked -- set false if just generating config
        """
        assert self.agent_instance_obj

        # fetch caches just in time
        if any([
                not self.RR2.has_cached_predicate(x)
                for x in self._predicates_to_cache()
        ]):
            self._update_cached_predicates()

        if any([
                not self.RR2.has_cached_resource(x)
                for x in self._resources_to_cache()
        ]):
            self._update_cached_resources()

        # validate the associations, then pick things up
        self._collect_agent_instance_associations()

        if will_launch:
            # if there is an agent pid then assume that a drive is already started
            agent_process_id = ResourceAgentClient._get_agent_process_id(
                self._get_device()._id)
            if agent_process_id:
                raise BadRequest(
                    "Agent Instance already running for this device pid: %s" %
                    str(agent_process_id))

        self.will_launch = will_launch
        return self.generate_config()
예제 #3
0
    def __init__(self, read_process_fn=None, resource_id='', desired_state=None, *args, **kwargs):

        agent_process_id = ResourceAgentClient._get_agent_process_id(resource_id)

        super(AgentProcessStateGate, self).__init__(read_process_fn,
                                                    agent_process_id,
                                                    desired_state,
                                                    *args, **kwargs)
    def test_get_agent_client_noprocess(self):
        inst_device_id = self.RR2.create(any_old(RT.InstrumentDevice))

        iap = ResourceAgentClient._get_agent_process_id(inst_device_id)

        # should be no running agent
        self.assertIsNone(iap)

        # should raise NotFound
        self.assertRaises(NotFound, ResourceAgentClient, inst_device_id)
    def test_get_agent_client_noprocess(self):
        inst_device_id = self.RR2.create(any_old(RT.InstrumentDevice))

        iap = ResourceAgentClient._get_agent_process_id(inst_device_id)

        # should be no running agent
        self.assertIsNone(iap)

        # should raise NotFound
        self.assertRaises(NotFound, ResourceAgentClient, inst_device_id)
예제 #6
0
    def __init__(self,
                 read_process_fn=None,
                 resource_id='',
                 desired_state=None,
                 *args,
                 **kwargs):

        agent_process_id = ResourceAgentClient._get_agent_process_id(
            resource_id)

        super(AgentProcessStateGate,
              self).__init__(read_process_fn, agent_process_id, desired_state,
                             *args, **kwargs)
예제 #7
0
    def base_activateInstrumentSample(self, instAgent_obj, expect_launch=True, expect_command=True):
        """
        This method runs a test of launching a driver with a given agent configuration
        """

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



        # Create InstrumentAgent

        instAgent_id = self.imsclient.create_instrument_agent(instAgent_obj)
        print  'new InstrumentAgent id = %s' % instAgent_id

        self.imsclient.assign_instrument_model_to_instrument_agent(instModel_id, instAgent_id)

        # Create InstrumentDevice
        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)


        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)



        parsed_pdict_id = self.dataset_management.read_parameter_dictionary_by_name('ctd_parsed_param_dict',
                                                                                    id_only=True)
        raw_pdict_id    = self.dataset_management.read_parameter_dictionary_by_name('ctd_raw_param_dict',
                                                                                    id_only=True)

        parsed_stream_def_id = self.pubsubcli.create_stream_definition(name='parsed',
                                                                       parameter_dictionary_id=parsed_pdict_id)
        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')

        data_product_id1 = self.dpclient.create_data_product(data_product=dp_obj, stream_definition_id=parsed_stream_def_id)
        print  '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)
        print  '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)
        print  'Data set for data_product_id1 = %s' % dataset_ids[0]
        self.parsed_dataset = dataset_ids[0]


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

        data_product_id2 = self.dpclient.create_data_product(data_product=dp_obj,
                                                             stream_definition_id=raw_stream_def_id)
        print  'new dp_id = %s' % str(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)

        # 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)
        print  '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)
        print  'Data set for data_product_id2 = %s' % dataset_ids[0]
        self.raw_dataset = dataset_ids[0]

        # add start/stop for instrument agent
        gevent.joinall([gevent.spawn(lambda:
            self.imsclient.start_instrument_agent_instance(instrument_agent_instance_id=instAgentInstance_id))])
        self.addCleanup(self.imsclient.stop_instrument_agent_instance,
                        instrument_agent_instance_id=instAgentInstance_id)

        #wait for start
        inst_agent_instance_obj = self.imsclient.read_instrument_agent_instance(instAgentInstance_id)
        agent_process_id = ResourceAgentClient._get_agent_process_id(instDevice_id)

        print "Agent process id is '%s'" % str(agent_process_id)
        self.assertTrue(agent_process_id)
        gate = ProcessStateGate(self.processdispatchclient.read_process,
                                agent_process_id,
                                ProcessStateEnum.RUNNING)

        if not expect_launch:
            self.assertFalse(gate.await(30), "The instance (%s) of bogus instrument agent spawned in 30 seconds ?!?" %
                                             agent_process_id)
            return

        self.assertTrue(gate.await(30), "The instrument agent instance (%s) did not spawn in 30 seconds" %
                                        agent_process_id)


        print "Instrument Agent Instance successfully triggered ProcessStateGate as RUNNING"

        #print  '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=agent_process_id,
                                              process=FakeProcess())

        print "ResourceAgentClient created: %s" % str(self._ia_client)

        print "Sending command=ResourceAgentEvent.INITIALIZE"
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)

        if not expect_command:
            self.assertRaises(ServerError, self._ia_client.execute_agent, cmd)
            return

        retval = self._ia_client.execute_agent(cmd)
        print "Result of INITIALIZE: %s" % str(retval)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        reply = self._ia_client.execute_agent(cmd)
        self.assertTrue(reply.status == 0)

        cmd = AgentCommand(command=ResourceAgentEvent.GET_RESOURCE_STATE)
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertTrue(state, 'DRIVER_STATE_COMMAND')

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        reply = self._ia_client.execute_agent(cmd)
        self.assertTrue(reply.status == 0)

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

        # This gevent sleep is there to test the autosample time, which will show something different from default
        # only if the instrument runs for over a minute
        gevent.sleep(90)

        extended_instrument = self.imsclient.get_instrument_device_extension(instrument_device_id=instDevice_id)

        self.assertIsInstance(extended_instrument.computed.uptime, ComputedStringValue)

        autosample_string = extended_instrument.computed.uptime.value
        autosampling_time = int(autosample_string.split()[4])

        self.assertTrue(autosampling_time > 0)

        cmd = AgentCommand(command=SBE37ProtocolEvent.STOP_AUTOSAMPLE)
        retval = self._ia_client.execute_resource(cmd)

        print "Sending command=ResourceAgentEvent.RESET"
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        reply = self._ia_client.execute_agent(cmd)
        print "Result of RESET: %s" % str(reply)
예제 #8
0
    def _start_subscriber_process_lifecycle_event(self, origin):
        """
        @param origin    Child's resource_id. The associated PID retrieved via
                         ResourceAgentClient._get_agent_process_id is used for
                         the event subscriber itself, but we still index
                         _event_subscribers with the given origin.
        """
        def _got_process_lifecycle_event(evt, *args, **kwargs):
            with self._lock:
                if not self._active:
                    log.warn("%r: _got_process_lifecycle_event called but "
                             "manager has been destroyed",
                             self._platform_id)
                    return

                if evt.type_ != "ProcessLifecycleEvent":
                    log.trace("%r: ignoring event type %r. Only handle "
                              "ProcessLifecycleEvent directly.",
                              self._platform_id, evt.type_)
                    return

                # evt.origin is a PID
                pid = evt.origin

                if not pid in self._rids:
                    log.warn("%r: OOIION-1077 ignoring event from pid=%r. "
                             "Expecting one of %s",
                             self._platform_id, pid, self._rids.keys())
                    return

                origin = self._rids[pid]

                # # Before the _rids mapping, a preliminary mechanism to check
                # # whether the event came from the expected origin relied on
                # # the process ID having origin as a substring:
                #
                # if not origin in pid:
                #     log.warn("%r: OOIION-1077 ignoring event from origin %r. "
                #              "Expecting an origin containing %r",
                #              self._platform_id, pid, origin)
                #     return
                # # BUT this was definitely weak. Although the PID for an
                # # initial agent process seems to satisfy this assumption,
                # # this is not anymore the case upon a re-start of that agent.

                log.debug("%r: OOIION-1077  _got_process_lifecycle_event: "
                          "pid=%r origin=%r state=%r(%s)",
                          self._platform_id, pid, origin,
                          ProcessStateEnum._str_map[evt.state], evt.state)

                if evt.state is ProcessStateEnum.TERMINATED:
                    self._device_terminated_event(origin, pid)

        # use associated process ID for the subscription:
        pid = ResourceAgentClient._get_agent_process_id(origin)

        if pid is None:
            log.warn("%r: OOIION-1077 ResourceAgentClient._get_agent_process_id"
                     " returned None for origin=%r. Subscriber not created.",
                     self._platform_id, origin)
            return

        sub = self._agent._create_event_subscriber(event_type="ProcessLifecycleEvent",
                                                   origin_type='DispatchedProcess',
                                                   origin=pid,
                                                   callback=_got_process_lifecycle_event)

        with self._lock:
            # but note that we use the given origin as index in _event_subscribers:
            self._event_subscribers[origin] = sub

            # and capture the pid -> origin mapping:
            self._rids[pid] = origin

        log.debug("%r: OOIION-1077 registered ProcessLifecycleEvent subscriber "
                  "with pid=%r (origin=%r)",
                  self._platform_id, pid, origin)
예제 #9
0
    def base_activateInstrumentSample(self,
                                      instAgent_obj,
                                      expect_launch=True,
                                      expect_command=True):
        """
        This method runs a test of launching a driver with a given agent configuration
        """

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

        # Create InstrumentAgent

        instAgent_id = self.imsclient.create_instrument_agent(instAgent_obj)
        print 'new InstrumentAgent id = %s' % instAgent_id

        self.imsclient.assign_instrument_model_to_instrument_agent(
            instModel_id, instAgent_id)

        # Create InstrumentDevice
        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)

        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)
        raw_pdict_id = self.dataset_management.read_parameter_dictionary_by_name(
            'ctd_raw_param_dict', id_only=True)

        parsed_stream_def_id = self.pubsubcli.create_stream_definition(
            name='parsed', parameter_dictionary_id=parsed_pdict_id)
        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)
        print '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)
        print '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)
        print 'Data set for data_product_id1 = %s' % dataset_ids[0]
        self.parsed_dataset = dataset_ids[0]
        #create the datastore at the beginning of each int test that persists data
        self.get_datastore(self.parsed_dataset)

        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)
        print 'new dp_id = %s' % str(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)

        # 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)
        print '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)
        print 'Data set for data_product_id2 = %s' % dataset_ids[0]
        self.raw_dataset = dataset_ids[0]

        # add start/stop for instrument agent
        gevent.joinall([
            gevent.spawn(
                lambda: self.imsclient.start_instrument_agent_instance(
                    instrument_agent_instance_id=instAgentInstance_id))
        ])
        self.addCleanup(self.imsclient.stop_instrument_agent_instance,
                        instrument_agent_instance_id=instAgentInstance_id)

        #wait for start
        inst_agent_instance_obj = self.imsclient.read_instrument_agent_instance(
            instAgentInstance_id)
        agent_process_id = ResourceAgentClient._get_agent_process_id(
            instDevice_id)

        print "Agent process id is '%s'" % str(agent_process_id)
        self.assertTrue(agent_process_id)
        gate = ProcessStateGate(self.processdispatchclient.read_process,
                                agent_process_id, ProcessStateEnum.RUNNING)

        if not expect_launch:
            self.assertFalse(
                gate. await (30),
                "The instance (%s) of bogus instrument agent spawned in 30 seconds ?!?"
                % agent_process_id)
            return

        self.assertTrue(
            gate. await (30),
            "The instrument agent instance (%s) did not spawn in 30 seconds" %
            agent_process_id)

        print "Instrument Agent Instance successfully triggered ProcessStateGate as RUNNING"

        #print  '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=agent_process_id,
                                              process=FakeProcess())

        print "ResourceAgentClient created: %s" % str(self._ia_client)

        print "Sending command=ResourceAgentEvent.INITIALIZE"
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)

        if not expect_command:
            self.assertRaises(ServerError, self._ia_client.execute_agent, cmd)
            return

        retval = self._ia_client.execute_agent(cmd)
        print "Result of INITIALIZE: %s" % str(retval)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        reply = self._ia_client.execute_agent(cmd)
        self.assertTrue(reply.status == 0)

        cmd = AgentCommand(command=ResourceAgentEvent.GET_RESOURCE_STATE)
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertTrue(state, 'DRIVER_STATE_COMMAND')

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        reply = self._ia_client.execute_agent(cmd)
        self.assertTrue(reply.status == 0)

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

        # This gevent sleep is there to test the autosample time, which will show something different from default
        # only if the instrument runs for over a minute
        gevent.sleep(90)

        extended_instrument = self.imsclient.get_instrument_device_extension(
            instrument_device_id=instDevice_id)

        self.assertIsInstance(extended_instrument.computed.uptime,
                              ComputedStringValue)

        autosample_string = extended_instrument.computed.uptime.value
        autosampling_time = int(autosample_string.split()[4])

        self.assertTrue(autosampling_time > 0)

        cmd = AgentCommand(command=SBE37ProtocolEvent.STOP_AUTOSAMPLE)
        retval = self._ia_client.execute_resource(cmd)

        print "Sending command=ResourceAgentEvent.RESET"
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        reply = self._ia_client.execute_agent(cmd)
        print "Result of RESET: %s" % str(reply)
예제 #10
0
    def _start_subscriber_process_lifecycle_event(self, origin):
        """
        @param origin    Child's resource_id. The associated PID retrieved via
                         ResourceAgentClient._get_agent_process_id is used for
                         the event subscriber itself, but we still index
                         _event_subscribers with the given origin.
        """
        def _got_process_lifecycle_event(evt, *args, **kwargs):
            with self._lock:
                if not self._active:
                    log.warn(
                        "%r: _got_process_lifecycle_event called but "
                        "manager has been destroyed", self._platform_id)
                    return

                if evt.type_ != "ProcessLifecycleEvent":
                    log.trace(
                        "%r: ignoring event type %r. Only handle "
                        "ProcessLifecycleEvent directly.", self._platform_id,
                        evt.type_)
                    return

                # evt.origin is a PID
                pid = evt.origin

                if not pid in self._rids:
                    log.warn(
                        "%r: OOIION-1077 ignoring event from pid=%r. "
                        "Expecting one of %s", self._platform_id, pid,
                        self._rids.keys())
                    return

                origin = self._rids[pid]

                # # Before the _rids mapping, a preliminary mechanism to check
                # # whether the event came from the expected origin relied on
                # # the process ID having origin as a substring:
                #
                # if not origin in pid:
                #     log.warn("%r: OOIION-1077 ignoring event from origin %r. "
                #              "Expecting an origin containing %r",
                #              self._platform_id, pid, origin)
                #     return
                # # BUT this was definitely weak. Although the PID for an
                # # initial agent process seems to satisfy this assumption,
                # # this is not anymore the case upon a re-start of that agent.

                log.debug(
                    "%r: OOIION-1077  _got_process_lifecycle_event: "
                    "pid=%r origin=%r state=%r(%s)", self._platform_id, pid,
                    origin, ProcessStateEnum._str_map[evt.state], evt.state)

                if evt.state is ProcessStateEnum.TERMINATED:
                    self._device_terminated_event(origin, pid)

        # use associated process ID for the subscription:
        pid = ResourceAgentClient._get_agent_process_id(origin)

        if pid is None:
            log.warn(
                "%r: OOIION-1077 ResourceAgentClient._get_agent_process_id"
                " returned None for origin=%r. Subscriber not created.",
                self._platform_id, origin)
            return

        sub = self._agent._create_event_subscriber(
            event_type="ProcessLifecycleEvent",
            origin_type='DispatchedProcess',
            origin=pid,
            callback=_got_process_lifecycle_event)

        with self._lock:
            # but note that we use the given origin as index in _event_subscribers:
            self._event_subscribers[origin] = sub

            # and capture the pid -> origin mapping:
            self._rids[pid] = origin

        log.debug(
            "%r: OOIION-1077 registered ProcessLifecycleEvent subscriber "
            "with pid=%r (origin=%r)", self._platform_id, pid, origin)