Example #1
0
    def _start_remote(self):
        """
        Start up the remote endpoint.
        """
        # Create agent config.
        remote_endpoint_config = {
            'other_host': 'localhost',
            'other_port': self._terrestrial_port,
            'this_port': self._remote_port,
            'platform_resource_id': self._remote_platform_id,
            'xs_name': self._xs_name,
            'process': {
                'listen_name': self._remote_listen_name
            }
        }

        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=remote_endpoint_config)
        log.debug('Remote endpoint pid=%s.', str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(process=FakeProcess(),
                                              to_name=self._remote_listen_name)
        log.debug('Got re client %s.', str(self.re_client))

        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._remote_port)
Example #2
0
    def _start_remote(self):
        """
        """        
        # Create agent config.
        remote_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._terrestrial_port,
            'this_port' : self._remote_port,
            'platform_resource_id' : self._remote_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._remote_listen_name
            }
        }
        
        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=remote_endpoint_config)
        log.debug('Remote endpoint pid=%s.', str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(
            process=FakeProcess(),
            to_name=self._remote_listen_name)
        log.debug('Got re client %s.', str(self.re_client))
        
        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._remote_port)
Example #3
0
def launch_remote():
    """
    """
    listen_name = 'remote_endpoint' + tcaa_args['xs_name']

    config = {
        'other_host': tcaa_args['terrestrial_host'],
        'other_port': tcaa_args['terrestrial_port'],
        'this_port': tcaa_args['remote_port'],
        'platform_resource_id': tcaa_args['remote_platform_id'],
        'xs_name': tcaa_args['xs_name'],
        'process': {
            'listen_name': listen_name
        }
    }

    spargs = {
        'name': listen_name,
        'module': tcaa_args['remote_module'],
        'cls': tcaa_args['remote_class'],
        'config': config
    }

    pid = cc.spawn_process(**spargs)
    print 'remote pid = %s' % pid

    rc = RemoteEndpointClient(process=FakeProcess(), to_name=listen_name)

    return rc
Example #4
0
    def _start_remote(self):
        """
        """
        # Create agent config.
        remote_endpoint_config = {
            "other_host": "localhost",
            "other_port": self._terrestrial_port,
            "this_port": self._remote_port,
            "platform_resource_id": self._remote_platform_id,
            "xs_name": self._xs_name,
            "process": {"listen_name": self._remote_listen_name},
        }

        # Spawn the remote enpoint process.
        log.debug("Spawning remote endpoint process.")
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module="ion.services.sa.tcaa.remote_endpoint",
            cls="RemoteEndpoint",
            config=remote_endpoint_config,
        )
        log.debug("Remote endpoint pid=%s.", str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(process=FakeProcess(), to_name=self._remote_listen_name)
        log.debug("Got re client %s.", str(self.re_client))

        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug("The remote port is: %i.", self._remote_port)
    def setUp(self):
        """
        Start fake terrestrial components and add cleanup.
        Start terrestrial server and retrieve port.
        Set internal variables.
        Start container.
        Start deployment.
        Start container agent.
        Spawn remote endpoint process.
        Create remote endpoint client and retrieve remote server port.
        Create event publisher.
        """
        
        self._terrestrial_server = R3PCServer(self.consume_req, self.terrestrial_server_close)
        self._terrestrial_client = R3PCClient(self.consume_ack, self.terrestrial_client_close)
        self.addCleanup(self._terrestrial_server.stop)
        self.addCleanup(self._terrestrial_client.stop)
        self._other_port = self._terrestrial_server.start('*', 0)
        log.debug('Terrestrial server binding to *:%i', self._other_port)
        
        self._other_host = 'localhost'
        self._platform_resource_id = 'abc123'
        self._resource_id = 'fake_id'
        self._no_requests = 10
        self._requests_sent = {}
        self._results_recv = {}
        self._no_telem_events = 0
        self._done_evt = AsyncResult()
        self._done_telem_evts = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        
        # Start container.
        log.debug('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')

        # Create a container client.
        log.debug('Creating container client.')
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)

        # Create agent config.
        endpoint_config = {
            'other_host' : self._other_host,
            'other_port' : self._other_port,
            'this_port' : 0,
            'platform_resource_id' : self._platform_resource_id
        }
        
        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        re_pid = container_client.spawn_process(
            name='remote_endpoint_1',
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=endpoint_config)
        log.debug('Endpoint pid=%s.', str(re_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(
            process=FakeProcess(),
            to_name=re_pid)
        log.debug('Got re client %s.', str(self.re_client))
        
        # Remember the remote port.
        self._this_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._this_port)
        
        # Start the event publisher.
        self._event_publisher = EventPublisher()
class TestRemoteEndpoint(IonIntegrationTestCase):
    """
    Test cases for 2CAA terrestrial endpoint.
    """
    def setUp(self):
        """
        Start fake terrestrial components and add cleanup.
        Start terrestrial server and retrieve port.
        Set internal variables.
        Start container.
        Start deployment.
        Start container agent.
        Spawn remote endpoint process.
        Create remote endpoint client and retrieve remote server port.
        Create event publisher.
        """
        
        self._terrestrial_server = R3PCServer(self.consume_req, self.terrestrial_server_close)
        self._terrestrial_client = R3PCClient(self.consume_ack, self.terrestrial_client_close)
        self.addCleanup(self._terrestrial_server.stop)
        self.addCleanup(self._terrestrial_client.stop)
        self._other_port = self._terrestrial_server.start('*', 0)
        log.debug('Terrestrial server binding to *:%i', self._other_port)
        
        self._other_host = 'localhost'
        self._platform_resource_id = 'abc123'
        self._resource_id = 'fake_id'
        self._no_requests = 10
        self._requests_sent = {}
        self._results_recv = {}
        self._no_telem_events = 0
        self._done_evt = AsyncResult()
        self._done_telem_evts = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        
        # Start container.
        log.debug('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')

        # Create a container client.
        log.debug('Creating container client.')
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)

        # Create agent config.
        endpoint_config = {
            'other_host' : self._other_host,
            'other_port' : self._other_port,
            'this_port' : 0,
            'platform_resource_id' : self._platform_resource_id
        }
        
        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        re_pid = container_client.spawn_process(
            name='remote_endpoint_1',
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=endpoint_config)
        log.debug('Endpoint pid=%s.', str(re_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(
            process=FakeProcess(),
            to_name=re_pid)
        log.debug('Got re client %s.', str(self.re_client))
        
        # Remember the remote port.
        self._this_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._this_port)
        
        # Start the event publisher.
        self._event_publisher = EventPublisher()
      
    ######################################################################    
    # Helpers.
    ######################################################################    

    def on_link_up(self):
        """
        Called by a test to simulate turning the link on.
        """
        log.debug('Terrestrial client connecting to localhost:%i.',
                 self._this_port)
        self._terrestrial_client.start('localhost', self._this_port)
        # Publish a link up event to be caught by the endpoint.
        log.debug('Publishing telemetry event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._platform_resource_id,
                            status = TelemetryStatusType.AVAILABLE)
    
    def on_link_down(self):
        """
        Called by a test to simulate turning the link off.
        """
        self._terrestrial_client.stop()
        # Publish a link down event to be caught by the endpoint.
        log.debug('Publishing telemetry event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._platform_resource_id,
                            status = TelemetryStatusType.UNAVAILABLE)    
    
    def consume_req(self, res):
        """
        Consume a terrestrial request setting async event when necessary.
        """
        command_id = res['command_id']
        self._results_recv[command_id] = res
        if len(self._results_recv) == self._no_requests:
            self._done_evt.set()
    
    def consume_ack(self, cmd):
        """
        Consume terrestrial ack setting async event when necessary.
        """
        self._requests_sent[cmd.command_id] = cmd
        if len(self._requests_sent) == self._no_requests:
            self._cmd_tx_evt.set()
        
    def terrestrial_server_close(self):
        """
        Callback when terrestrial server closes.
        """
        pass
    
    def terrestrial_client_close(self):
        """
        Callback when terrestrial client closes.
        """
        pass
    
    def make_fake_command(self, no):
        """
        Build a fake command for use in tests.
        """
            
        cmdstr = 'fake_cmd_%i' % no
        cmd = IonObject('RemoteCommand',
                             resource_id=self._resource_id,
                             command=cmdstr,
                             args=['arg1', 23],
                             kwargs={'worktime':3},
                             command_id = str(uuid.uuid4()))
        return cmd

    def start_agent(self):
        """
        Start an instrument agent and client.
        """
        
        log.info('Creating driver integration test support:')
        log.info('driver module: %s', DRV_MOD)
        log.info('driver class: %s', DRV_CLS)
        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(DRV_MOD,
                                                     DRV_CLS,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
        
        # Start port agent, add stop to cleanup.
        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
        }
        self.addCleanup(self._support.stop_pagent)    
                        
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : {},
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True
        }
    
        # Start instrument agent.
        log.debug("Starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
    
        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))
    
        # 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))                
                
    ######################################################################    
    # Tests.
    ######################################################################    

    def test_process_queued(self):
        """
        test_process_queued
        Test that queued commands are forwarded to and handled by
        remote endpoint when link comes up.
        """        
        
        # Create and enqueue some requests.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            self._terrestrial_client.enqueue(cmd)

        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Wait for all the enqueued commands to be acked.
        # Wait for all the responses to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        # Confirm the results match the commands sent.
        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())
    
    def test_process_online(self):
        """
        test_process_online
        Test commands are forwarded and handled while link is up.
        """        
        
        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Wait for the link to be up.
        # The remote side does not publish public telemetry events
        # so we can't wait for that.
        gevent.sleep(1)

        # Create and enqueue some requests.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            self._terrestrial_client.enqueue(cmd)

        # Wait for all the enqueued commands to be acked.
        # Wait for all the responses to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        # Confirm the results match the commands sent.
        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    def test_terrestrial_late(self):
        """
        test_terrestrial_late
        Test queued commands are forwarded and handled by remote endpoint
        when terrestrial side is late to come up.
        """        
        
        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Wait for the link to be up.
        # The remote side does not publish public telemetry events
        # so we can't wait for that.
        gevent.sleep(1)

        # Manually stop the terrestrial endpoint.
        # This will cause it to be unavailable when commands are queued
        # to simulate stability during asynchronous wake ups.
        self._terrestrial_server.stop()
        self._terrestrial_client.stop()

        # Create and enqueue some requests.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            self._terrestrial_client.enqueue(cmd)

        # Remote side awaits the terrestrial waking up.
        gevent.sleep(3)

        # Terrestrail endpoint eventually wakes up and starts transmitting.        
        self._terrestrial_client.start('localhost', self._this_port)
        self._terrestrial_server.start('*', self._other_port)
    
        # Wait for all the enqueued commands to be acked.
        # Wait for all the responses to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        # Confirm the results match the commands sent.
        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    def test_service_commands(self):
        """
        test_service_commands
        Test that real service commands are handled by the remote endpoint.
        """
        
        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}
        
        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='create',
                             args=[obj],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Returns obj_id, obj_rev.
        obj_id, obj_rev = self._results_recv[cmd.command_id]['result']
        
        # Confirm the results are valid.
        """
        Result is a tuple of strings.
        {'result': ['ad183ff26bae4f329ddd85fd69d160a9',
        '1-00a308c45fff459c7cda1db9a7314de6'],
        'command_id': 'cc2ae00d-40b0-47d2-af61-8ffb87f1aca2'}
        """
        self.assertIsInstance(obj_id, str)
        self.assertNotEqual(obj_id, '')
        self.assertIsInstance(obj_rev, str)
        self.assertNotEqual(obj_rev, '')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Read user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='read',
                             args=[obj_id],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)

        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns read_obj.
        read_obj = self._results_recv[cmd.command_id]['result']
        
        # Confirm the results are valid.
        """
        Result is a user info object with the name set.
        {'lcstate': 'DEPLOYED_AVAILABLE',
        '_rev': '1-851f067bac3c34b2238c0188b3340d0f',
        'description': '',
        'ts_updated': '1349213207638',
        'type_': 'UserInfo',
        'contact': <interface.objects.ContactInformation object at 0x10d7df590>,
        '_id': '27832d93f4cd4535a75ac75c06e00a7e',
        'ts_created': '1349213207638',
        'variables': [{'name': '', 'value': ''}],
        'name': 'some_name'}
        """
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_name')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Update user object.
        read_obj.name = 'some_other_name'
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='update',
                             args=[read_obj],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)

        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns nothing.
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Read user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='read',
                             args=[obj_id],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)        

        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns read_obj.
        read_obj = self._results_recv[cmd.command_id]['result']
        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_other_name')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}
        
        # Delete user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='delete',
                             args=[obj_id],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)        

        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns nothing.
            
        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        gevent.sleep(1)
        
    def test_resource_commands(self):
        """
        test_resource_commands
        Test that real resource commands are handled by the remote endpoint.
        """
        
        # Start the IA and check it's out there and behaving.
        self.start_agent()
        
        state = self._ia_client.get_agent_state()
        log.debug('Agent state is: %s', state)
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        retval = self._ia_client.ping_agent()
        log.debug('Agent ping is: %s', str(retval))
        self.assertIn('ping from InstrumentAgent', retval)

        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Wait for the link to be up.
        # The remote side does not publish public telemetry events
        # so we can't wait for that.
        gevent.sleep(1)

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Get agent state via remote endpoint.        
        cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Returns agent state.
        state = self._results_recv[cmd.command_id]['result']
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Ping agent via remote endpoint. 
        cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='ping_agent',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Returns agent state.
        ping = self._results_recv[cmd.command_id]['result']
        self.assertIn('ping from InstrumentAgent', ping)
        
        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        gevent.sleep(1)

    def test_bad_service_name_resource_id(self):
        """
        test_bad_service_name_resource_id
        Test for proper exception behavior when a bad service name or
        resource id is used in a command forwarded to the remote endpoint.
        """
        
        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Wait for the link to be up.
        # The remote side does not publish public telemetry events
        # so we can't wait for that.
        gevent.sleep(1)

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}
        
        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='bogus_service',
                             command='create',
                             args=[obj],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Returns NotFound.
        result = self._results_recv[cmd.command_id]['result']
        self.assertIsInstance(result, NotFound)

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Get agent state via remote endpoint.        
        cmd = IonObject('RemoteCommand',
                             resource_id='bogus_resource_id',
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns NotFound.
        result = self._results_recv[cmd.command_id]['result']
        self.assertIsInstance(result, NotFound)

        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        gevent.sleep(1)

    def test_bad_commands(self):
        """
        test_bad_commands
        Test for correct exception behavior if a bad command name is forwarded
        to a remote service or resource.
        """
        
        # Start the IA and check it's out there and behaving.
        self.start_agent()
        
        state = self._ia_client.get_agent_state()
        log.debug('Agent state is: %s', state)
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        retval = self._ia_client.ping_agent()
        log.debug('Agent ping is: %s', str(retval))
        self.assertIn('ping from InstrumentAgent', retval)
        
        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()

        # Wait for the link to be up.
        # The remote side does not publish public telemetry events
        # so we can't wait for that.
        gevent.sleep(1)

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}
        
        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='what_the_flunk',
                             args=[obj],
                             kwargs='',
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Returns BadRequest.
        result = self._results_recv[cmd.command_id]['result']
        self.assertIsInstance(result, BadRequest)

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._done_evt = AsyncResult()
        self._cmd_tx_evt = AsyncResult()
        self._requests_sent = {}
        self._results_recv = {}

        # Get agent state via remote endpoint.        
        cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='what_the_flunk',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd)
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns NotFound.
        result = self._results_recv[cmd.command_id]['result']
        self.assertIsInstance(result, BadRequest)
        
        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        gevent.sleep(1)

    def test_resource_command_sequence(self):
        """
        test_resource_command_sequence
        Test for successful completion of a properly ordered sequence of
        resource commands queued for forwarding to the remote endpoint.
        """
        # Start the IA and check it's out there and behaving.
        self.start_agent()
        
        state = self._ia_client.get_agent_state()
        log.debug('Agent state is: %s', state)
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        retval = self._ia_client.ping_agent()
        log.debug('Agent ping is: %s', str(retval))
        self.assertIn('ping from InstrumentAgent', retval)

        # We execute a sequence of twelve consecutive events.
        self._no_requests = 12

        # Get agent state.
        cmd1 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd1)
        
        # Initialize agent.
        cmd2 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_agent',
                             args=[AgentCommand(command=ResourceAgentEvent.INITIALIZE)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd2)
        
        # Get agent state.
        cmd3 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd3)
        
        # Go active.
        cmd4 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_agent',
                             args=[AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd4)
        
        # Get agent state.
        cmd5 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd5)
        
        # Run.
        cmd6 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_agent',
                             args=[AgentCommand(command=ResourceAgentEvent.RUN)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd6)
        
        # Get agent state.
        cmd7 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd7)
        
        # Acquire sample.
        cmd8 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_resource',
                             args=[AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd8)
        
        # Acquire sample
        cmd9 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_resource',
                             args=[AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd9)
        
        # Acquire sample.
        cmd10 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_resource',
                             args=[AgentCommand(command=SBE37ProtocolEvent.ACQUIRE_SAMPLE)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd10)
        
        # Reset.
        cmd11 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='execute_agent',
                             args=[AgentCommand(command=ResourceAgentEvent.RESET)],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd11)
        
        # Get agent state.
        cmd12 = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={},
                             command_id = str(uuid.uuid4()))
        self._terrestrial_client.enqueue(cmd12)

        
        # Publish a telemetry available event.
        # This will cause the endpoint clients to wake up and connect.
        self.on_link_up()
        
        # Wait for command request to be acked.
        # Wait for response to arrive.
        self._cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Check results of command sequence.
        """
        0ccf1e10-eeca-400d-aefe-f9d6888ec963   {'result': 'RESOURCE_AGENT_STATE_INACTIVE', 'command_id': '0ccf1e10-eeca-400d-aefe-f9d6888ec963'}
        92531bdf-c2c8-4aa8-817d-5107c7311b37   {'result': <interface.objects.AgentCommandResult object at 0x10d7f11d0>, 'command_id': '92531bdf-c2c8-4aa8-817d-5107c7311b37'}
        509934a1-5038-40d8-8014-591e2d8042b6   {'result': 'RESOURCE_AGENT_STATE_COMMAND', 'command_id': '509934a1-5038-40d8-8014-591e2d8042b6'}
        88bacbb7-5366-4d27-9ecf-fff2bec34b2c   {'result': <interface.objects.AgentCommandResult object at 0x10d389190>, 'command_id': '88bacbb7-5366-4d27-9ecf-fff2bec34b2c'}
        f8b4d3fa-a249-439b-8bd4-ac212b6100aa   {'result': <interface.objects.AgentCommandResult object at 0x10d3893d0>, 'command_id': 'f8b4d3fa-a249-439b-8bd4-ac212b6100aa'}
        8ae98e39-fdb3-4218-ad8f-584620397d9f   {'result': <interface.objects.AgentCommandResult object at 0x10d739990>, 'command_id': '8ae98e39-fdb3-4218-ad8f-584620397d9f'}
        746364a1-c4c7-400f-96d4-ee36df5dc1a4   {'result': BadRequest('Execute argument "command" not set.',), 'command_id': '746364a1-c4c7-400f-96d4-ee36df5dc1a4'}
        d516d3d9-e4f9-4ea5-80e0-34639a6377b5   {'result': <interface.objects.AgentCommandResult object at 0x10d3b2350>, 'command_id': 'd516d3d9-e4f9-4ea5-80e0-34639a6377b5'}
        c7da03f5-59bc-420a-9e10-0a7794266599   {'result': 'RESOURCE_AGENT_STATE_IDLE', 'command_id': 'c7da03f5-59bc-420a-9e10-0a7794266599'}
        678d870a-bf18-424a-afb0-f80ecf3277e2   {'result': <interface.objects.AgentCommandResult object at 0x10d739590>, 'command_id': '678d870a-bf18-424a-afb0-f80ecf3277e2'}
        750c6a30-56eb-4535-99c2-a81fefab1b1f   {'result': 'RESOURCE_AGENT_STATE_COMMAND', 'command_id': '750c6a30-56eb-4535-99c2-a81fefab1b1f'}
        c17bd658-3775-4aa3-8844-02df70a0e3c0   {'result': 'RESOURCE_AGENT_STATE_UNINITIALIZED', 'command_id': 'c17bd658-3775-4aa3-8844-02df70a0e3c0'}
        """        
        
        # First result is a state string.
        result1 = self._results_recv[cmd1.command_id]['result']
        self.assertEqual(result1, ResourceAgentState.UNINITIALIZED)
        
        # Second result is an empty AgentCommandResult.
        result2 = self._results_recv[cmd2.command_id]['result']

        # Third result is a state string.
        result3 = self._results_recv[cmd3.command_id]['result']
        self.assertEqual(result3, ResourceAgentState.INACTIVE)
        
        # Fourth result is an empty AgentCommandResult.
        result4 = self._results_recv[cmd4.command_id]['result']

        # Fifth result is a state string.
        result5 = self._results_recv[cmd5.command_id]['result']
        self.assertEqual(result5, ResourceAgentState.IDLE)

        # Sixth result is an empty AgentCommandResult.
        result6 = self._results_recv[cmd6.command_id]['result']

        # Seventh result is a state string.
        result7 = self._results_recv[cmd7.command_id]['result']
        self.assertEqual(result7, ResourceAgentState.COMMAND)
        
        """
        {'raw': {'quality_flag': 'ok', 'preferred_timestamp': 'driver_timestamp',
        'stream_name': 'raw', 'pkt_format_id': 'JSON_Data',
        'pkt_version': 1, '
        values': [{'binary': True, 'value_id': 'raw',
        'value': 'NzkuNDM3MywxNy4yMDU2NCwgNzYxLjg4NSwgICA2LjIxOTgsIDE1MDYuMzk3LCAwMSBGZWIgMjAwMSwgMDE6MDE6MDA='}],
        'driver_timestamp': 3558286748.8039923},
        'parsed': {'quality_flag': 'ok', 'preferred_timestamp': 'driver_timestamp',
        'stream_name': 'parsed', 'pkt_format_id': 'JSON_Data', 'pkt_version': 1,
        'values': [{'value_id': 'temp', 'value': 79.4373},
        {'value_id': 'conductivity', 'value': 17.20564},
        {'value_id': 'pressure', 'value': 761.885}],
        'driver_timestamp': 3558286748.8039923}}
        """
        
        # Eigth result is an AgentCommandResult containing a sample.
        result8 = self._results_recv[cmd8.command_id]['result']
        self.assertTrue('parsed',result8.result )
        
        # Ninth result is an AgentCommandResult containing a sample.
        result9 = self._results_recv[cmd9.command_id]['result']
        self.assertTrue('parsed',result9.result )

        # Tenth result is an AgentCommandResult containing a sample.
        result10 = self._results_recv[cmd10.command_id]['result']
        self.assertTrue('parsed',result10.result )

        # Eleventh result is an empty AgentCommandResult.
        result11 = self._results_recv[cmd11.command_id]['result']

        # Twelth result is a state string.
        result12 = self._results_recv[cmd12.command_id]['result']
        self.assertEqual(result1, ResourceAgentState.UNINITIALIZED)
        
        # Publish a telemetry unavailable event.
        # This will cause the endpoint clients to disconnect and go to sleep.
        self.on_link_down()

        gevent.sleep(1)
Example #7
0
class Test2CAA(IonIntegrationTestCase):
    """
    Test cases for 2CAA terrestrial endpoint.
    """
    def setUp(self):
        """
        """
        
        ###################################################################
        # Internal parameters and container.
        ###################################################################
        
        # Internal parameters.        
        self._terrestrial_platform_id = 'terrestrial_id'
        self._remote_platform_id = 'remote_id'
        self._resource_id = 'fake_id'
        self._xs_name = 'remote1'
        self._terrestrial_svc_name = 'terrestrial_endpoint'
        self._terrestrial_listen_name = self._terrestrial_svc_name + self._xs_name
        self._remote_svc_name = 'remote_endpoint'
        self._remote_listen_name = self._remote_svc_name + self._xs_name
        self._remote_port = 0
        self._terrestrial_port = 0
        self._te_client = None
        self._re_client = None
        self._remote_pid = None
        self._terrestrial_pid = None

        # Async test results.
        self._no_requests = 10
        self._no_telem_evts = 2
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._telem_evts = []
        self._queue_mod_evts = []
        self._cmd_tx_evts = []
        self._results_recv = {}
        self._requests_sent = {}
        self._done_telem_evt = AsyncResult()
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()

        # Start container.
        log.debug('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')

        # Create a container client.
        log.debug('Creating container client.')
        self._container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
        
        ###################################################################
        # Start endpoints, agent.
        ###################################################################

        self._start_terrestrial()
        self._start_remote()
        self._start_agent()
            
        ###################################################################
        # Assign client ports.
        # This is primarily for test purposes as the IP config in
        # deployment will be fixed in advance.
        ###################################################################
        
        self.te_client.set_client_port(self._remote_port)
        check_port = self.te_client.get_client_port()
        log.debug('Terrestrial client port is: %i', check_port)
    
        self.re_client.set_client_port(self._terrestrial_port)
        check_port = self.re_client.get_client_port()
        log.debug('Remote client port is: %i', check_port)
        
        ###################################################################
        # Start the event publisher and subscribers.
        # Used to send fake agent telemetry publications to the endpoints,
        # and to receive endpoint publications.
        ###################################################################
        self._event_publisher = EventPublisher()

        # Start the event subscriber for remote namespace platform events.
        # This event could be changed to RemoteNamespaceEvent.
        self._event_subscriber = EventSubscriber(
            event_type='PlatformEvent',
            callback=self.consume_event,
            origin=self._xs_name)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._event_subscriber.stop)

        # Start the result subscriber for remote resource events.
        self._resource_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=IA_RESOURCE_ID,
            callback=self.consume_event)
        self._resource_result_subscriber.start()
        self._resource_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._resource_result_subscriber.stop)

        # Start the result subscriber for remote service events.
        self._service_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin='resource_registry' + 'remote1',
            callback=self.consume_event)
        self._service_result_subscriber.start()
        self._service_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._service_result_subscriber.stop)

        # Start the result subscriber for fake resource results.      
        self._fake_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=self._resource_id,
            callback=self.consume_event)
        self._fake_result_subscriber.start()
        self._fake_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._fake_result_subscriber.stop)

    ###################################################################
    # Start/stop helpers.
    ###################################################################

    def _start_agent(self):
        """
        Start an instrument agent and client.
        """
        
        log.info('Creating driver integration test support:')
        log.info('driver module: %s', DRV_MOD)
        log.info('driver class: %s', DRV_CLS)
        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(DRV_MOD,
                                                     DRV_CLS,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
        
        # Start port agent, add stop to cleanup.
        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
        }
        self.addCleanup(self._support.stop_pagent)    
                        
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : {},
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True
        }
    
        # Start instrument agent.
        log.debug("Starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
    
        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))
    
        # 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))
        
    def _start_terrestrial(self):
        """
        """
        # Create terrestrial config.
        terrestrial_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._remote_port,
            'this_port' : self._terrestrial_port,
            'platform_resource_id' : self._terrestrial_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._terrestrial_listen_name
            }
        }
        
        # Spawn the terrestrial enpoint process.
        log.debug('Spawning terrestrial endpoint process.')
        self._terrestrial_pid = self._container_client.spawn_process(
            name=self._terrestrial_listen_name,
            module='ion.services.sa.tcaa.terrestrial_endpoint',
            cls='TerrestrialEndpoint',
            config=terrestrial_endpoint_config)
        log.debug('Terrestrial endpoint pid=%s.', str(self._terrestrial_pid))

        # Create a terrestrial client.
        self.te_client = TerrestrialEndpointClient(
            process=FakeProcess(),
            to_name=self._terrestrial_listen_name)
        log.debug('Got te client %s.', str(self.te_client))
        self._terrestrial_port = self.te_client.get_port()
        log.debug('Terrestrial port is: %i', self._terrestrial_port)
        
    def _start_remote(self):
        """
        """        
        # Create agent config.
        remote_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._terrestrial_port,
            'this_port' : self._remote_port,
            'platform_resource_id' : self._remote_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._remote_listen_name
            }
        }
        
        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=remote_endpoint_config)
        log.debug('Remote endpoint pid=%s.', str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(
            process=FakeProcess(),
            to_name=self._remote_listen_name)
        log.debug('Got re client %s.', str(self.re_client))
        
        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._remote_port)
        
        
    ###################################################################
    # Telemetry publications to start/top endpoint.
    # (Normally be published by appropriate platform agents.)
    ###################################################################

    def terrestrial_link_up(self):
        """
        Publish telemetry available to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry available event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._terrestrial_platform_id,
                            status = TelemetryStatusType.AVAILABLE)
        
    def terrestrial_link_down(self):
        """
        Publish telemetry unavailable to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry unavailable event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._terrestrial_platform_id,
                            status = TelemetryStatusType.UNAVAILABLE)
    
    def remote_link_up(self):
        """
        Publish telemetry available to the remote endpoint.
        """
        # Publish a link up event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry available event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._remote_platform_id,
                            status = TelemetryStatusType.AVAILABLE)
    
    def remote_link_down(self):
        """
        Publish telemetry unavailable to the remote endpoint.
        """
        # Publish a link down event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry unavailable event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._remote_platform_id,
                            status = TelemetryStatusType.UNAVAILABLE)
    
    def consume_event(self, evt, *args, **kwargs):
        """
        Test callback for events.
        """
        log.debug('Test got event: %s, args: %s, kwargs: %s',
                  str(evt), str(args), str(kwargs))
        
        if evt.type_ == 'PublicPlatformTelemetryEvent':
            self._telem_evts.append(evt)
            if self._no_telem_evts > 0 and self._no_telem_evts == len(self._telem_evts):
                    self._done_telem_evt.set()
                    
        elif evt.type_ == 'RemoteQueueModifiedEvent':
            self._queue_mod_evts.append(evt)
            if self._no_queue_mod_evts > 0 and self._no_queue_mod_evts == len(self._queue_mod_evts):
                    self._done_queue_mod_evt.set()
            
        elif evt.type_ == 'RemoteCommandTransmittedEvent':
            self._cmd_tx_evts.append(evt)
            if self._no_cmd_tx_evts > 0 and self._no_cmd_tx_evts == len(self._cmd_tx_evts):
                    self._done_cmd_tx_evt.set()
        
        elif evt.type_ == 'RemoteCommandResult':
            cmd = evt.command
            self._results_recv[cmd.command_id] = cmd
            if len(self._results_recv) == self._no_requests:
                self._done_cmd_evt.set()
        
    ###################################################################
    # Misc helpers.
    ###################################################################
        
    def make_fake_command(self, no):
        """
        Build a fake command for use in tests.
        """
            
        cmdstr = 'fake_cmd_%i' % no
        cmd = IonObject('RemoteCommand',
                             resource_id='fake_id',
                             command=cmdstr,
                             args=['arg1', 23],
                             kwargs={'kwargs1':'someval'})
        return cmd
        
    ###################################################################
    # Tests.
    ###################################################################

    def test_queued_fake(self):
        """
        test_queued_fake
        Test fake resource commands queued prior to linkup.
        """
                
        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            cmd = self.te_client.enqueue_command(cmd)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()
        
        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    # The following error occurs when queue persistence is enabled.
    # Need to verify correct solution to enable persistent queues.
    """
    2012-11-06 14:27:13,517 INFO     pyon.container.procs ProcManager.terminate_process: org_management -> pid=Edwards-MacBook-Pro_local_8975.8
    Traceback (most recent call last):
      File "/Users/edward/Documents/Dev/code/coi-services/eggs/gevent-0.13.7-py2.7-macosx-10.5-intel.egg/gevent/greenlet.py", line 390, in run
        result = self._run(*self.args, **self.kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/ion/services/sa/tcaa/remote_endpoint.py", line 97, in command_loop
        self._callback(cmd_result)
      File "/Users/edward/Documents/Dev/code/coi-services/ion/services/sa/tcaa/remote_endpoint.py", line 284, in _result_complete
        self._client.enqueue(result)
    AttributeError: 'NoneType' object has no attribute 'enqueue'
    <Greenlet at 0x1059ca9b0: command_loop> failed with AttributeError
    
    2012-11-06 14:27:13,586 INFO     pyon.container.procs ProcManager.terminate_process: exchange_management -> pid=Edwards-MacBook-Pro_local_8975.7
    2012-11-06 14:27:13,665 INFO     pyon.container.procs ProcManager.terminate_process: policy_management -> pid=Edwards-MacBook-Pro_local_8975.6
    2012-11-06 14:27:13,739 INFO     pyon.container.procs ProcManager.terminate_process: identity_management -> pid=Edwards-MacBook-Pro_local_8975.5
    2012-11-06 14:27:13,807 INFO     pyon.container.procs ProcManager.terminate_process: directory -> pid=Edwards-MacBook-Pro_local_8975.4
    2012-11-06 14:27:13,874 INFO     pyon.container.procs ProcManager.terminate_process: resource_registry -> pid=Edwards-MacBook-Pro_local_8975.3
    2012-11-06 14:27:13,941 INFO     pyon.container.procs ProcManager.terminate_process: event_persister -> pid=Edwards-MacBook-Pro_local_8975.1
    2012-11-06 14:27:13,945 INFO     pyon.event.event EventSubscriber stopped. Event pattern=#
    2012-11-06 14:27:14,124 INFO     pyon.datastore.couchdb.couchdb_standalone Connecting to CouchDB server: http://localhost:5984
    2012-11-06 14:27:14,399 INFO     pyon.datastore.couchdb.couchdb_standalone Closing connection to CouchDB
    
    ======================================================================
    ERROR: test_process_online
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/Users/edward/Documents/Dev/code/coi-services/eggs/mock-0.8.0-py2.7.egg/mock.py", line 1605, in _inner
        return f(*args, **kw)
      File "/Users/edward/Documents/Dev/code/coi-services/ion/services/sa/tcaa/test/test_2caa.py", line 492, in test_process_online
        cmd = self.te_client.enqueue_command(cmd)
      File "/Users/edward/Documents/Dev/code/coi-services/interface/services/sa/iterrestrial_endpoint.py", line 188, in enqueue_command
        return self.request(IonObject('terrestrial_endpoint_enqueue_command_in', **{'command': command or None,'link': link}), op='enqueue_command', headers=headers, timeout=timeout)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 1012, in request
        return RequestResponseClient.request(self, msg, headers=headers, timeout=timeout)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 822, in request
        retval, headers = e.send(msg, headers=headers, timeout=timeout)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 310, in send
        result_data, result_headers = c.send(self.conv_type.server_role, msg, headers, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 126, in send
        return self._invite_and_send(to_role, msg, headers, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 145, in _invite_and_send
        return self._send(to_role, to_role_name, msg, header, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 169, in _send
        return self._end_point_unit._message_send(msg, headers, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 289, in _message_send
        return ProcessRPCRequestEndpointUnit.send(self, msg, headers,  **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 134, in send
        return self._send(_msg, _header, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 880, in _send
        raise ex
    Conflict: 409 - Object not based on most current version
    
    """

    def test_process_online(self):
        """
        test_process_online
        Test fake resource commands queued while link is up.
        """
                
        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            cmd = self.te_client.enqueue_command(cmd)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mods, command transmissions and results.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    def test_remote_late(self):
        """
        test_remote_late
        Test fake resource commands queued prior to linkup.
        Delay remote side linkup substantially to test terrestrial
        behavior when remote server not initially available.
        """
                
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            cmd = self.te_client.enqueue_command(cmd)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Actually stop the remote process, since the server is
        # available even if it hasn't linked up yet and the test is running
        # in the same container. The test will remember the appropriate
        # remote port numbers.
        self._container_client.terminate_process(self._remote_pid)
        self.terrestrial_link_up()
        gevent.sleep(10)
        
        # Restart remote side and publish remote link up.
        self._start_remote()
        self.remote_link_up()
        
        # Block for transmission and result events.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())
        
    def test_resource_commands(self):
        """
        test_resource_commands
        """

        # Set up to verify the two commands queued.        
        self._no_requests = 2
        self._no_telem_evts = 2
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests

        # Use IA client to verify IA.
        state = self._ia_client.get_agent_state()
        log.debug('Agent state is: %s', state)
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        retval = self._ia_client.ping_agent()
        log.debug('Agent ping is: %s', str(retval))
        self.assertIn('ping from InstrumentAgent', retval)

        # Create and enqueue commands.
        state_cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={})
        state_cmd = self.te_client.enqueue_command(state_cmd)
        self._requests_sent[state_cmd.command_id] = state_cmd

        ping_cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='ping_agent',
                             args=[],
                             kwargs={})
        ping_cmd = self.te_client.enqueue_command(ping_cmd)
        self._requests_sent[ping_cmd.command_id] = ping_cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()

        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())        
        
        self.assertEqual(self._results_recv[state_cmd.command_id].result,
                         ResourceAgentState.UNINITIALIZED)
        self.assertIn('ping from InstrumentAgent',
                      self._results_recv[ping_cmd.command_id].result)
        
    def test_service_command_sequence(self):
        """
        test_service_commands
        """
        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []        
        self._cmd_tx_evts = []
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='create',
                             args=[obj],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns obj_id, obj_rev.
        obj_id, obj_rev = self._results_recv[cmd.command_id].result
        
        # Confirm the results are valid.
        
        #Result is a tuple of strings.
        #{'result': ['ad183ff26bae4f329ddd85fd69d160a9',
        #'1-00a308c45fff459c7cda1db9a7314de6'],
        #'command_id': 'cc2ae00d-40b0-47d2-af61-8ffb87f1aca2'}
        
        self.assertIsInstance(obj_id, str)
        self.assertNotEqual(obj_id, '')
        self.assertIsInstance(obj_rev, str)
        self.assertNotEqual(obj_rev, '')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []        
        self._cmd_tx_evts = []
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='read',
                             args=[obj_id],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        
                
        # Returns read_obj.
        read_obj = self._results_recv[cmd.command_id].result
        
        # Confirm the results are valid.
        
        #Result is a user info object with the name set.
        #{'lcstate': 'DEPLOYED_AVAILABLE',
        #'_rev': '1-851f067bac3c34b2238c0188b3340d0f',
        #'description': '',
        #'ts_updated': '1349213207638',
        #'type_': 'UserInfo',
        #'contact': <interface.objects.ContactInformation object at 0x10d7df590>,
        #'_id': '27832d93f4cd4535a75ac75c06e00a7e',
        #'ts_created': '1349213207638',
        #'variables': [{'name': '', 'value': ''}],
        #'name': 'some_name'}
        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_name')

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []
        self._cmd_tx_evts = []        
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        read_obj.name = 'some_other_name'
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='update',
                             args=[read_obj],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        

        # Returns nothing.

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []
        self._cmd_tx_evts = []        
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='read',
                             args=[obj_id],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        
        
        # Returns read_obj.
        read_obj = self._results_recv[cmd.command_id].result
        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_other_name')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []
        self._cmd_tx_evts = []        
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='delete',
                             args=[obj_id],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        

        # Returns nothing.
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
class TestRemoteClient(IonIntegrationTestCase):
    """
    Test cases for 2CAA remote clients.
    """
    def setUp(self):
        """
        Setup the test parameters.
        Start the container and retrieve client.
        Start the endpoints and resource agent.
        Start publisher and subscribers.
        """
        ###################################################################
        # Internal parameters and container.
        ###################################################################
        
        # Internal parameters.        
        self._terrestrial_platform_id = 'terrestrial_id'
        self._remote_platform_id = 'remote_id'
        self._resource_id = 'fake_id'
        self._xs_name = 'remote1'
        self._terrestrial_svc_name = 'terrestrial_endpoint'
        self._terrestrial_listen_name = self._terrestrial_svc_name + self._xs_name
        self._remote_svc_name = 'remote_endpoint'
        self._remote_listen_name = self._remote_svc_name + self._xs_name
        self._remote_port = 0
        self._terrestrial_port = 0
        self._te_client = None
        self._re_client = None
        self._remote_pid = None
        self._terrestrial_pid = None

        # Async test results.
        self._no_requests = 10
        self._no_telem_evts = 2
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._telem_evts = []
        self._queue_mod_evts = []
        self._cmd_tx_evts = []
        self._results_recv = {}
        self._requests_sent = {}
        self._done_telem_evt = AsyncResult()
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()

        # Start container.
        log.debug('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')

        # Create a container client.
        log.debug('Creating container client.')
        self._container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
        
        ###################################################################
        # Start endpoints, agent.
        ###################################################################

        self._start_terrestrial()
        self._start_remote()
        self._start_agent()
                        
        ###################################################################
        # Assign client ports.
        # This is primarily for test purposes as the IP config in
        # deployment will be fixed in advance.
        ###################################################################
        
        self.te_client.set_client_port(self._remote_port)
        check_port = self.te_client.get_client_port()
        log.debug('Terrestrial client port is: %i', check_port)
    
        self.re_client.set_client_port(self._terrestrial_port)
        check_port = self.re_client.get_client_port()
        log.debug('Remote client port is: %i', check_port)
        
        ###################################################################
        # Start the event publisher and subscribers.
        # Used to send fake agent telemetry publications to the endpoints,
        # and to receive endpoint publications.
        ###################################################################
        self._event_publisher = EventPublisher()

        # Start the event subscriber for remote namespace platform events.
        # This event could be changed to RemoteNamespaceEvent.
        self._event_subscriber = EventSubscriber(
            event_type='PlatformEvent',
            callback=self.consume_event,
            origin=self._xs_name)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._event_subscriber.stop)

        # Start the result subscriber for remote resource events.
        self._resource_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=IA_RESOURCE_ID,
            callback=self.consume_event)
        self._resource_result_subscriber.start()
        self._resource_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._resource_result_subscriber.stop)

        # Start the result subscriber for remote service events.
        self._service_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin='resource_registry' + 'remote1',
            callback=self.consume_event)
        self._service_result_subscriber.start()
        self._service_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._service_result_subscriber.stop)

        # Start the result subscriber for fake resource results.      
        self._fake_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=self._resource_id,
            callback=self.consume_event)
        self._fake_result_subscriber.start()
        self._fake_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._fake_result_subscriber.stop)

    ###################################################################
    # Start/stop helpers.
    ###################################################################

    def _start_agent(self):
        """
        Start an instrument agent and client.
        """
        
        log.info('Creating driver integration test support:')
        log.info('driver module: %s', DRV_MOD)
        log.info('driver class: %s', DRV_CLS)
        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(DRV_MOD,
                                                     DRV_CLS,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
        
        # Start port agent, add stop to cleanup.
        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
        }
        self.addCleanup(self._support.stop_pagent)    
                        
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : {},
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True
        }
    
        # Start instrument agent.
        log.debug("Starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
    
        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))
    
        # 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))
        
    def _start_terrestrial(self):
        """
        Start up the terrestrial endpoint.
        """
        # Create terrestrial config.
        terrestrial_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._remote_port,
            'this_port' : self._terrestrial_port,
            'platform_resource_id' : self._terrestrial_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._terrestrial_listen_name
            }
        }
        
        # Spawn the terrestrial enpoint process.
        log.debug('Spawning terrestrial endpoint process.')
        self._terrestrial_pid = self._container_client.spawn_process(
            name=self._terrestrial_listen_name,
            module='ion.services.sa.tcaa.terrestrial_endpoint',
            cls='TerrestrialEndpoint',
            config=terrestrial_endpoint_config)
        log.debug('Terrestrial endpoint pid=%s.', str(self._terrestrial_pid))

        # Create a terrestrial client.
        self.te_client = TerrestrialEndpointClient(
            process=FakeProcess(),
            to_name=self._terrestrial_listen_name)
        log.debug('Got te client %s.', str(self.te_client))
        self._terrestrial_port = self.te_client.get_port()
        log.debug('Terrestrial port is: %i', self._terrestrial_port)
        
    def _start_remote(self):
        """
        Start up the remote endpoint.
        """        
        # Create agent config.
        remote_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._terrestrial_port,
            'this_port' : self._remote_port,
            'platform_resource_id' : self._remote_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._remote_listen_name
            }
        }
        
        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=remote_endpoint_config)
        log.debug('Remote endpoint pid=%s.', str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(
            process=FakeProcess(),
            to_name=self._remote_listen_name)
        log.debug('Got re client %s.', str(self.re_client))
        
        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._remote_port)
        
        
    ###################################################################
    # Telemetry publications to start/top endpoint.
    # (Normally be published by appropriate platform agents.)
    ###################################################################

    def terrestrial_link_up(self):
        """
        Publish telemetry available to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry available event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._terrestrial_platform_id,
                            status = TelemetryStatusType.AVAILABLE)
        
    def terrestrial_link_down(self):
        """
        Publish telemetry unavailable to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry unavailable event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._terrestrial_platform_id,
                            status = TelemetryStatusType.UNAVAILABLE)
    
    def remote_link_up(self):
        """
        Publish telemetry available to the remote endpoint.
        """
        # Publish a link up event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry available event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._remote_platform_id,
                            status = TelemetryStatusType.AVAILABLE)
    
    def remote_link_down(self):
        """
        Publish telemetry unavailable to the remote endpoint.
        """
        # Publish a link down event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry unavailable event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._remote_platform_id,
                            status = TelemetryStatusType.UNAVAILABLE)
    
    def consume_event(self, evt, *args, **kwargs):
        """
        Test callback for events.
        """
        log.debug('Test got event: %s, args: %s, kwargs: %s',
                  str(evt), str(args), str(kwargs))
        
        if evt.type_ == 'PublicPlatformTelemetryEvent':
            self._telem_evts.append(evt)
            if self._no_telem_evts > 0 and self._no_telem_evts == len(self._telem_evts):
                    self._done_telem_evt.set()
                    
        elif evt.type_ == 'RemoteQueueModifiedEvent':
            self._queue_mod_evts.append(evt)
            if self._no_queue_mod_evts > 0 and self._no_queue_mod_evts == len(self._queue_mod_evts):
                    self._done_queue_mod_evt.set()
            
        elif evt.type_ == 'RemoteCommandTransmittedEvent':
            self._cmd_tx_evts.append(evt)
            if self._no_cmd_tx_evts > 0 and self._no_cmd_tx_evts == len(self._cmd_tx_evts):
                    self._done_cmd_tx_evt.set()
        
        elif evt.type_ == 'RemoteCommandResult':
            cmd = evt.command
            self._results_recv[cmd.command_id] = cmd
            if len(self._results_recv) == self._no_requests:
                self._done_cmd_evt.set()
        
    ###################################################################
    # Misc helpers.
    ###################################################################
        
    def make_fake_command(self, no):
        """
        Build a fake command for use in tests.
        """
            
        cmdstr = 'fake_cmd_%i' % no
        cmd = IonObject('RemoteCommand',
                             resource_id='fake_id',
                             command=cmdstr,
                             args=['arg1', 23],
                             kwargs={'kwargs1':'someval'})
        return cmd

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

    def test_resource_client_online(self):
        """
        test_recourse_client_online
        Test the client transparently forwards commands to the remote
        resource while link is up.
        """

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()
        
        remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
            resource_id='fake_id', process=FakeProcess())
        
        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent()
            self._requests_sent[cmd.command_id] = cmd
            
        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())
    
    #@unittest.skip('For some reason this bastard wont run on the builder.')
    def test_resource_client_blocking(self):
        """
        test_resource_client_blocking
        Test the client can block on remote resource command results.
        """

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()
        
        remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
            resource_id=IA_RESOURCE_ID, process=FakeProcess())
        
        # Queue up a series of fake commands to be handled by the remote side.
        """
        {'time_completed': 1350421095.804607, 'resource_id': '123xyz',
        'time_queued': 1350421095.623531, 'args': [], 'type_': 'RemoteCommand',
        'command': 'ping_agent', 'result': 'ping from InstrumentAgent
        (name=Agent007,id=Edwards-MacBook-Pro_local_10126.35,type=agent),
        time: 1350421095757', 'kwargs': {}, 'svc_name': '',
        'command_id': '76be11b4-a22c-49de-89cd-4e019463d7c9'}
        """
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent(remote_timeout=CFG.endpoint.receive.timeout)
            self.assertEqual(cmd.resource_id, IA_RESOURCE_ID)
            self.assertEqual(cmd.command, 'ping_agent')
            self.assertIn('ping from InstrumentAgent', cmd.result)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

    #@unittest.skip('For some reason this bastard wont run on the builder.')
    def test_service_client_blocking(self):
        """
        test_service_client_blocking
        Test the client can command remote services and block on their
        results.
        """

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Create remote client for the resource registry.
        remote_client = RemoteClient(iface=IResourceRegistryService,
            xs_name=self._xs_name, svc_name='resource_registry',
            process=FakeProcess())

        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        result = remote_client.create(obj,
            remote_timeout=CFG.endpoint.receive.timeout)

        # Returns obj_id, obj_rev.
        obj_id, obj_rev = result.result
        
        # Confirm the results are valid.
        
        #Result is a tuple of strings.
        #{'result': ['ad183ff26bae4f329ddd85fd69d160a9',
        #'1-00a308c45fff459c7cda1db9a7314de6'],
        #'command_id': 'cc2ae00d-40b0-47d2-af61-8ffb87f1aca2'}
        
        self.assertIsInstance(obj_id, str)
        self.assertNotEqual(obj_id, '')
        self.assertIsInstance(obj_rev, str)
        self.assertNotEqual(obj_rev, '')

        # Read user object.
        result = remote_client.read(obj_id,
            remote_timeout=CFG.endpoint.receive.timeout)

        # Returns read_obj.
        read_obj = result.result
        
        # Confirm the results are valid.
        
        #Result is a user info object with the name set.
        #{'lcstate': 'DEPLOYED_AVAILABLE',
        #'_rev': '1-851f067bac3c34b2238c0188b3340d0f',
        #'description': '',
        #'ts_updated': '1349213207638',
        #'type_': 'UserInfo',
        #'contact': <interface.objects.ContactInformation object at 0x10d7df590>,
        #'_id': '27832d93f4cd4535a75ac75c06e00a7e',
        #'ts_created': '1349213207638',
        #'variables': [{'name': '', 'value': ''}],
        #'name': 'some_name'}
        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_name')

        # Update user object.
        read_obj.name = 'some_other_name'
        result = remote_client.update(read_obj,
            remote_timeout=CFG.endpoint.receive.timeout)
        
        # Returns nothing.

        # Read user object.
        result = remote_client.read(obj_id,
            remote_timeout=CFG.endpoint.receive.timeout)

        # Returns read_obj.
        read_obj = result.result
        
        # Confirm results are valid.        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_other_name')

        # Delete user object.
        result = remote_client.delete(obj_id,
            remote_timeout=CFG.endpoint.receive.timeout)
        
        # Returns nothing.

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

    def test_queue_manipulators(self):
        """
        test_queue_manipulators
        Test ability to instpect and manipulate the command queue corresponding
        to this resource or service.
        """

        remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
            resource_id='fake_id', process=FakeProcess())
        
        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent(link=False)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        queue = remote_client.get_queue()
        self.assertEqual(len(queue), self._no_requests)

        popped = remote_client.clear_queue()
        self.assertEqual(len(popped), self._no_requests)

        self._requests_sent = {}

        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent(link=False)
            self._requests_sent[cmd.command_id] = cmd

        # Pop the last three commands.
        cmd_ids = self._requests_sent.keys()[:3]
        poped = []
        for x in cmd_ids:
            poped.append(remote_client.pop_queue(x))
            self._requests_sent.pop(x)

        queue = remote_client.get_queue()
        self.assertEqual(len(queue), self._no_requests - 3)
        self.assertEqual(len(poped), 3)

        self._no_requests = self._no_requests - 3
        self._no_cmd_tx_evts = self._no_requests

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()            
        
        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        pending = remote_client.get_pending()
        for x in pending:
            self.assertIn(x.command_id, self._requests_sent.keys())
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    def test_errors(self):
        """
        test_errors
        Test various error conditions.
        """
        
        # Constructed without a xs name.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(iface=IResourceAgent,
                    resource_id=IA_RESOURCE_ID, process=FakeProcess())
        
        # Constructed without an interface.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(xs_name=self._xs_name,
                    resource_id=IA_RESOURCE_ID, process=FakeProcess())

        # Construct with invalid interface.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient('Bogus_interface', xs_name=self._xs_name,
            resource_id=IA_RESOURCE_ID, process=FakeProcess())
        
        # Construct with no resource or service specified.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
                    process=FakeProcess())

        # Construct with both resource and service specified.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
                    resource_id=IA_RESOURCE_ID, svc_name='resource_registry', process=FakeProcess())

        # Create a valid resource client.
        remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
            resource_id=IA_RESOURCE_ID, process=FakeProcess())
        
        # Send a command while link is down.
        with self.assertRaises(Conflict):
            result = remote_client.ping_agent()

        # Test port manipulators refused by remote proxy client.
        with self.assertRaises(BadRequest):
            remote_client.get_port()
        with self.assertRaises(BadRequest):
            remote_client.set_client_port()
        with self.assertRaises(BadRequest):
            remote_client.get_client_port()

    def test_interfaces(self):
        """
        test_interfaces
        Test that the client declare the correct interfaces.
        """
        remote_client = RemoteClient(iface=IResourceAgent, xs_name=self._xs_name,
            resource_id='fake_id', process=FakeProcess())

        interfaces = providedBy(remote_client)
        self.assertIn(IResourceAgent, interfaces)
        self.assertIn(ITerrestrialEndpoint, interfaces)
Example #9
0
class TestRemoteClient(IonIntegrationTestCase):
    """
    Test cases for 2CAA remote clients.
    """
    def setUp(self):
        """
        Setup the test parameters.
        Start the container and retrieve client.
        Start the endpoints and resource agent.
        Start publisher and subscribers.
        """
        ###################################################################
        # Internal parameters and container.
        ###################################################################

        # Internal parameters.
        self._terrestrial_platform_id = 'terrestrial_id'
        self._remote_platform_id = 'remote_id'
        self._resource_id = 'fake_id'
        self._xs_name = 'remote1'
        self._terrestrial_svc_name = 'terrestrial_endpoint'
        self._terrestrial_listen_name = self._terrestrial_svc_name + self._xs_name
        self._remote_svc_name = 'remote_endpoint'
        self._remote_listen_name = self._remote_svc_name + self._xs_name
        self._remote_port = 0
        self._terrestrial_port = 0
        self._te_client = None
        self._re_client = None
        self._remote_pid = None
        self._terrestrial_pid = None

        # Async test results.
        self._no_requests = 10
        self._no_telem_evts = 2
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._telem_evts = []
        self._queue_mod_evts = []
        self._cmd_tx_evts = []
        self._results_recv = {}
        self._requests_sent = {}
        self._done_telem_evt = AsyncResult()
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()

        # Start container.
        log.debug('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')

        # Create a container client.
        log.debug('Creating container client.')
        self._container_client = ContainerAgentClient(node=self.container.node,
                                                      name=self.container.name)

        ###################################################################
        # Start endpoints, agent.
        ###################################################################

        self._start_terrestrial()
        self._start_remote()
        self._start_agent()

        ###################################################################
        # Assign client ports.
        # This is primarily for test purposes as the IP config in
        # deployment will be fixed in advance.
        ###################################################################

        self.te_client.set_client_port(self._remote_port)
        check_port = self.te_client.get_client_port()
        log.debug('Terrestrial client port is: %i', check_port)

        self.re_client.set_client_port(self._terrestrial_port)
        check_port = self.re_client.get_client_port()
        log.debug('Remote client port is: %i', check_port)

        ###################################################################
        # Start the event publisher and subscribers.
        # Used to send fake agent telemetry publications to the endpoints,
        # and to receive endpoint publications.
        ###################################################################
        self._event_publisher = EventPublisher()

        # Start the event subscriber for remote namespace platform events.
        # This event could be changed to RemoteNamespaceEvent.
        self._event_subscriber = EventSubscriber(event_type='PlatformEvent',
                                                 callback=self.consume_event,
                                                 origin=self._xs_name)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(
            timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._event_subscriber.stop)

        # Start the result subscriber for remote resource events.
        self._resource_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=IA_RESOURCE_ID,
            callback=self.consume_event)
        self._resource_result_subscriber.start()
        self._resource_result_subscriber._ready_event.wait(
            timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._resource_result_subscriber.stop)

        # Start the result subscriber for remote service events.
        self._service_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin='resource_registry' + 'remote1',
            callback=self.consume_event)
        self._service_result_subscriber.start()
        self._service_result_subscriber._ready_event.wait(
            timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._service_result_subscriber.stop)

        # Start the result subscriber for fake resource results.
        self._fake_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=self._resource_id,
            callback=self.consume_event)
        self._fake_result_subscriber.start()
        self._fake_result_subscriber._ready_event.wait(
            timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._fake_result_subscriber.stop)

    ###################################################################
    # Start/stop helpers.
    ###################################################################

    def _start_agent(self):
        """
        Start an instrument agent and client.
        """

        log.info('Creating driver integration test support:')
        log.info('driver module: %s', DRV_MOD)
        log.info('driver class: %s', DRV_CLS)
        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(DRV_MOD, DRV_CLS,
                                                     DEV_ADDR, DEV_PORT,
                                                     DATA_PORT, CMD_PORT,
                                                     PA_BINARY, DELIM,
                                                     WORK_DIR)

        # Start port agent, add stop to cleanup.
        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}
        self.addCleanup(self._support.stop_pagent)

        # Create agent config.
        agent_config = {
            'driver_config': DVR_CONFIG,
            'stream_config': {},
            'agent': {
                'resource_id': IA_RESOURCE_ID
            },
            'test_mode': True
        }

        # Start instrument agent.
        log.debug("Starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
                                                name=self.container.name)

        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))

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

    def _start_terrestrial(self):
        """
        Start up the terrestrial endpoint.
        """
        # Create terrestrial config.
        terrestrial_endpoint_config = {
            'other_host': 'localhost',
            'other_port': self._remote_port,
            'this_port': self._terrestrial_port,
            'platform_resource_id': self._terrestrial_platform_id,
            'xs_name': self._xs_name,
            'process': {
                'listen_name': self._terrestrial_listen_name
            }
        }

        # Spawn the terrestrial enpoint process.
        log.debug('Spawning terrestrial endpoint process.')
        self._terrestrial_pid = self._container_client.spawn_process(
            name=self._terrestrial_listen_name,
            module='ion.services.sa.tcaa.terrestrial_endpoint',
            cls='TerrestrialEndpoint',
            config=terrestrial_endpoint_config)
        log.debug('Terrestrial endpoint pid=%s.', str(self._terrestrial_pid))

        # Create a terrestrial client.
        self.te_client = TerrestrialEndpointClient(
            process=FakeProcess(), to_name=self._terrestrial_listen_name)
        log.debug('Got te client %s.', str(self.te_client))
        self._terrestrial_port = self.te_client.get_port()
        log.debug('Terrestrial port is: %i', self._terrestrial_port)

    def _start_remote(self):
        """
        Start up the remote endpoint.
        """
        # Create agent config.
        remote_endpoint_config = {
            'other_host': 'localhost',
            'other_port': self._terrestrial_port,
            'this_port': self._remote_port,
            'platform_resource_id': self._remote_platform_id,
            'xs_name': self._xs_name,
            'process': {
                'listen_name': self._remote_listen_name
            }
        }

        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=remote_endpoint_config)
        log.debug('Remote endpoint pid=%s.', str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(process=FakeProcess(),
                                              to_name=self._remote_listen_name)
        log.debug('Got re client %s.', str(self.re_client))

        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._remote_port)

    ###################################################################
    # Telemetry publications to start/top endpoint.
    # (Normally be published by appropriate platform agents.)
    ###################################################################

    def terrestrial_link_up(self):
        """
        Publish telemetry available to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry available event.')
        self._event_publisher.publish_event(
            event_type='PlatformTelemetryEvent',
            origin=self._terrestrial_platform_id,
            status=TelemetryStatusType.AVAILABLE)

    def terrestrial_link_down(self):
        """
        Publish telemetry unavailable to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry unavailable event.')
        self._event_publisher.publish_event(
            event_type='PlatformTelemetryEvent',
            origin=self._terrestrial_platform_id,
            status=TelemetryStatusType.UNAVAILABLE)

    def remote_link_up(self):
        """
        Publish telemetry available to the remote endpoint.
        """
        # Publish a link up event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry available event.')
        self._event_publisher.publish_event(
            event_type='PlatformTelemetryEvent',
            origin=self._remote_platform_id,
            status=TelemetryStatusType.AVAILABLE)

    def remote_link_down(self):
        """
        Publish telemetry unavailable to the remote endpoint.
        """
        # Publish a link down event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry unavailable event.')
        self._event_publisher.publish_event(
            event_type='PlatformTelemetryEvent',
            origin=self._remote_platform_id,
            status=TelemetryStatusType.UNAVAILABLE)

    def consume_event(self, evt, *args, **kwargs):
        """
        Test callback for events.
        """
        log.debug('Test got event: %s, args: %s, kwargs: %s', str(evt),
                  str(args), str(kwargs))

        if evt.type_ == 'PublicPlatformTelemetryEvent':
            self._telem_evts.append(evt)
            if self._no_telem_evts > 0 and self._no_telem_evts == len(
                    self._telem_evts):
                self._done_telem_evt.set()

        elif evt.type_ == 'RemoteQueueModifiedEvent':
            self._queue_mod_evts.append(evt)
            if self._no_queue_mod_evts > 0 and self._no_queue_mod_evts == len(
                    self._queue_mod_evts):
                self._done_queue_mod_evt.set()

        elif evt.type_ == 'RemoteCommandTransmittedEvent':
            self._cmd_tx_evts.append(evt)
            if self._no_cmd_tx_evts > 0 and self._no_cmd_tx_evts == len(
                    self._cmd_tx_evts):
                self._done_cmd_tx_evt.set()

        elif evt.type_ == 'RemoteCommandResult':
            cmd = evt.command
            self._results_recv[cmd.command_id] = cmd
            if len(self._results_recv) == self._no_requests:
                self._done_cmd_evt.set()

    ###################################################################
    # Misc helpers.
    ###################################################################

    def make_fake_command(self, no):
        """
        Build a fake command for use in tests.
        """

        cmdstr = 'fake_cmd_%i' % no
        cmd = IonObject('RemoteCommand',
                        resource_id='fake_id',
                        command=cmdstr,
                        args=['arg1', 23],
                        kwargs={'kwargs1': 'someval'})
        return cmd

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

    def test_resource_client_online(self):
        """
        test_recourse_client_online
        Test the client transparently forwards commands to the remote
        resource while link is up.
        """

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        remote_client = RemoteClient(iface=IResourceAgent,
                                     xs_name=self._xs_name,
                                     resource_id='fake_id',
                                     process=FakeProcess())

        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent()
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()

        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                              self._results_recv.keys())

    #@unittest.skip('For some reason this bastard wont run on the builder.')
    def test_resource_client_blocking(self):
        """
        test_resource_client_blocking
        Test the client can block on remote resource command results.
        """

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        remote_client = RemoteClient(iface=IResourceAgent,
                                     xs_name=self._xs_name,
                                     resource_id=IA_RESOURCE_ID,
                                     process=FakeProcess())

        # Queue up a series of fake commands to be handled by the remote side.
        """
        {'time_completed': 1350421095.804607, 'resource_id': '123xyz',
        'time_queued': 1350421095.623531, 'args': [], 'type_': 'RemoteCommand',
        'command': 'ping_agent', 'result': 'ping from InstrumentAgent
        (name=Agent007,id=Edwards-MacBook-Pro_local_10126.35,type=agent),
        time: 1350421095757', 'kwargs': {}, 'svc_name': '',
        'command_id': '76be11b4-a22c-49de-89cd-4e019463d7c9'}
        """
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent(
                remote_timeout=CFG.endpoint.receive.timeout)
            self.assertEqual(cmd.resource_id, IA_RESOURCE_ID)
            self.assertEqual(cmd.command, 'ping_agent')
            self.assertIn('ping from InstrumentAgent', cmd.result)

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()

        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

    #@unittest.skip('For some reason this bastard wont run on the builder.')
    def test_service_client_blocking(self):
        """
        test_service_client_blocking
        Test the client can command remote services and block on their
        results.
        """

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Create remote client for the resource registry.
        remote_client = RemoteClient(iface=IResourceRegistryService,
                                     xs_name=self._xs_name,
                                     svc_name='resource_registry',
                                     process=FakeProcess())

        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        result = remote_client.create(
            obj, remote_timeout=CFG.endpoint.receive.timeout)

        # Returns obj_id, obj_rev.
        obj_id, obj_rev = result.result

        # Confirm the results are valid.

        #Result is a tuple of strings.
        #{'result': ['ad183ff26bae4f329ddd85fd69d160a9',
        #'1-00a308c45fff459c7cda1db9a7314de6'],
        #'command_id': 'cc2ae00d-40b0-47d2-af61-8ffb87f1aca2'}

        self.assertIsInstance(obj_id, str)
        self.assertNotEqual(obj_id, '')
        self.assertIsInstance(obj_rev, str)
        self.assertNotEqual(obj_rev, '')

        # Read user object.
        result = remote_client.read(
            obj_id, remote_timeout=CFG.endpoint.receive.timeout)

        # Returns read_obj.
        read_obj = result.result

        # Confirm the results are valid.

        #Result is a user info object with the name set.
        #{'lcstate': 'DEPLOYED_AVAILABLE',
        #'_rev': '1-851f067bac3c34b2238c0188b3340d0f',
        #'description': '',
        #'ts_updated': '1349213207638',
        #'type_': 'UserInfo',
        #'contact': <interface.objects.ContactInformation object at 0x10d7df590>,
        #'_id': '27832d93f4cd4535a75ac75c06e00a7e',
        #'ts_created': '1349213207638',
        #'variables': [{'name': '', 'value': ''}],
        #'name': 'some_name'}

        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_name')

        # Update user object.
        read_obj.name = 'some_other_name'
        result = remote_client.update(
            read_obj, remote_timeout=CFG.endpoint.receive.timeout)

        # Returns nothing.

        # Read user object.
        result = remote_client.read(
            obj_id, remote_timeout=CFG.endpoint.receive.timeout)

        # Returns read_obj.
        read_obj = result.result

        # Confirm results are valid.
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_other_name')

        # Delete user object.
        result = remote_client.delete(
            obj_id, remote_timeout=CFG.endpoint.receive.timeout)

        # Returns nothing.

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()

        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

    def test_queue_manipulators(self):
        """
        test_queue_manipulators
        Test ability to instpect and manipulate the command queue corresponding
        to this resource or service.
        """

        remote_client = RemoteClient(iface=IResourceAgent,
                                     xs_name=self._xs_name,
                                     resource_id='fake_id',
                                     process=FakeProcess())

        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent(link=False)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        queue = remote_client.get_queue()
        self.assertEqual(len(queue), self._no_requests)

        popped = remote_client.clear_queue()
        self.assertEqual(len(popped), self._no_requests)

        self._requests_sent = {}

        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = remote_client.ping_agent(link=False)
            self._requests_sent[cmd.command_id] = cmd

        # Pop the last three commands.
        cmd_ids = self._requests_sent.keys()[:3]
        poped = []
        for x in cmd_ids:
            poped.append(remote_client.pop_queue(x))
            self._requests_sent.pop(x)

        queue = remote_client.get_queue()
        self.assertEqual(len(queue), self._no_requests - 3)
        self.assertEqual(len(poped), 3)

        self._no_requests = self._no_requests - 3
        self._no_cmd_tx_evts = self._no_requests

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        pending = remote_client.get_pending()
        for x in pending:
            self.assertIn(x.command_id, self._requests_sent.keys())
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()

        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                              self._results_recv.keys())

    def test_errors(self):
        """
        test_errors
        Test various error conditions.
        """

        # Constructed without a xs name.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(iface=IResourceAgent,
                                         resource_id=IA_RESOURCE_ID,
                                         process=FakeProcess())

        # Constructed without an interface.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(xs_name=self._xs_name,
                                         resource_id=IA_RESOURCE_ID,
                                         process=FakeProcess())

        # Construct with invalid interface.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient('Bogus_interface',
                                         xs_name=self._xs_name,
                                         resource_id=IA_RESOURCE_ID,
                                         process=FakeProcess())

        # Construct with no resource or service specified.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(iface=IResourceAgent,
                                         xs_name=self._xs_name,
                                         process=FakeProcess())

        # Construct with both resource and service specified.
        with self.assertRaises(ConfigNotFound):
            remote_client = RemoteClient(iface=IResourceAgent,
                                         xs_name=self._xs_name,
                                         resource_id=IA_RESOURCE_ID,
                                         svc_name='resource_registry',
                                         process=FakeProcess())

        # Create a valid resource client.
        remote_client = RemoteClient(iface=IResourceAgent,
                                     xs_name=self._xs_name,
                                     resource_id=IA_RESOURCE_ID,
                                     process=FakeProcess())

        # Send a command while link is down.
        with self.assertRaises(Conflict):
            result = remote_client.ping_agent()

        # Test port manipulators refused by remote proxy client.
        with self.assertRaises(BadRequest):
            remote_client.get_port()
        with self.assertRaises(BadRequest):
            remote_client.set_client_port()
        with self.assertRaises(BadRequest):
            remote_client.get_client_port()

    def test_interfaces(self):
        """
        test_interfaces
        Test that the client declare the correct interfaces.
        """
        remote_client = RemoteClient(iface=IResourceAgent,
                                     xs_name=self._xs_name,
                                     resource_id='fake_id',
                                     process=FakeProcess())

        interfaces = providedBy(remote_client)
        self.assertIn(IResourceAgent, interfaces)
        self.assertIn(ITerrestrialEndpoint, interfaces)
Example #10
0
class Test2CAA(IonIntegrationTestCase):
    """
    Test cases for 2CAA terrestrial endpoint.
    """
    def setUp(self):
        """
        """
        
        ###################################################################
        # Internal parameters and container.
        ###################################################################
        
        # Internal parameters.        
        self._terrestrial_platform_id = 'terrestrial_id'
        self._remote_platform_id = 'remote_id'
        self._resource_id = 'fake_id'
        self._xs_name = 'remote1'
        self._terrestrial_svc_name = 'terrestrial_endpoint'
        self._terrestrial_listen_name = self._terrestrial_svc_name + self._xs_name
        self._remote_svc_name = 'remote_endpoint'
        self._remote_listen_name = self._remote_svc_name + self._xs_name
        self._remote_port = 0
        self._terrestrial_port = 0
        self._te_client = None
        self._re_client = None
        self._remote_pid = None
        self._terrestrial_pid = None

        # Async test results.
        self._no_requests = 10
        self._no_telem_evts = 2
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._telem_evts = []
        self._queue_mod_evts = []
        self._cmd_tx_evts = []
        self._results_recv = {}
        self._requests_sent = {}
        self._done_telem_evt = AsyncResult()
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()

        # Start container.
        log.debug('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')

        # Create a container client.
        log.debug('Creating container client.')
        self._container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
        
        ###################################################################
        # Start endpoints, agent.
        ###################################################################

        self._start_terrestrial()
        self._start_remote()
        self._start_agent()
            
        ###################################################################
        # Assign client ports.
        # This is primarily for test purposes as the IP config in
        # deployment will be fixed in advance.
        ###################################################################
        
        self.te_client.set_client_port(self._remote_port)
        check_port = self.te_client.get_client_port()
        log.debug('Terrestrial client port is: %i', check_port)
    
        self.re_client.set_client_port(self._terrestrial_port)
        check_port = self.re_client.get_client_port()
        log.debug('Remote client port is: %i', check_port)
        
        ###################################################################
        # Start the event publisher and subscribers.
        # Used to send fake agent telemetry publications to the endpoints,
        # and to receive endpoint publications.
        ###################################################################
        self._event_publisher = EventPublisher()

        # Start the event subscriber for remote namespace platform events.
        # This event could be changed to RemoteNamespaceEvent.
        self._event_subscriber = EventSubscriber(
            event_type='PlatformEvent',
            callback=self.consume_event,
            origin=self._xs_name)
        self._event_subscriber.start()
        self._event_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._event_subscriber.stop)

        # Start the result subscriber for remote resource events.
        self._resource_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=IA_RESOURCE_ID,
            callback=self.consume_event)
        self._resource_result_subscriber.start()
        self._resource_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._resource_result_subscriber.stop)

        # Start the result subscriber for remote service events.
        self._service_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin='resource_registry' + 'remote1',
            callback=self.consume_event)
        self._service_result_subscriber.start()
        self._service_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._service_result_subscriber.stop)

        # Start the result subscriber for fake resource results.      
        self._fake_result_subscriber = EventSubscriber(
            event_type='RemoteCommandResult',
            origin=self._resource_id,
            callback=self.consume_event)
        self._fake_result_subscriber.start()
        self._fake_result_subscriber._ready_event.wait(timeout=CFG.endpoint.receive.timeout)
        self.addCleanup(self._fake_result_subscriber.stop)

    ###################################################################
    # Start/stop helpers.
    ###################################################################

    def _start_agent(self):
        """
        Start an instrument agent and client.
        """
        
        log.info('Creating driver integration test support:')
        log.info('driver module: %s', DRV_MOD)
        log.info('driver class: %s', DRV_CLS)
        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(DRV_MOD,
                                                     DRV_CLS,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DATA_PORT,
                                                     CMD_PORT,
                                                     PA_BINARY,
                                                     DELIM,
                                                     WORK_DIR)
        
        # Start port agent, add stop to cleanup.
        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
        }
        self.addCleanup(self._support.stop_pagent)    
                        
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : {},
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True
        }
    
        # Start instrument agent.
        log.debug("Starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
            name=self.container.name)
    
        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))
    
        # 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))
        
    def _start_terrestrial(self):
        """
        """
        # Create terrestrial config.
        terrestrial_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._remote_port,
            'this_port' : self._terrestrial_port,
            'platform_resource_id' : self._terrestrial_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._terrestrial_listen_name
            }
        }
        
        # Spawn the terrestrial enpoint process.
        log.debug('Spawning terrestrial endpoint process.')
        self._terrestrial_pid = self._container_client.spawn_process(
            name=self._terrestrial_listen_name,
            module='ion.services.sa.tcaa.terrestrial_endpoint',
            cls='TerrestrialEndpoint',
            config=terrestrial_endpoint_config)
        log.debug('Terrestrial endpoint pid=%s.', str(self._terrestrial_pid))

        # Create a terrestrial client.
        self.te_client = TerrestrialEndpointClient(
            process=FakeProcess(),
            to_name=self._terrestrial_listen_name)
        log.debug('Got te client %s.', str(self.te_client))
        self._terrestrial_port = self.te_client.get_port()
        log.debug('Terrestrial port is: %i', self._terrestrial_port)
        
    def _start_remote(self):
        """
        """        
        # Create agent config.
        remote_endpoint_config = {
            'other_host' : 'localhost',
            'other_port' : self._terrestrial_port,
            'this_port' : self._remote_port,
            'platform_resource_id' : self._remote_platform_id,
            'xs_name' : self._xs_name,
            'process' : {
                'listen_name' : self._remote_listen_name
            }
        }
        
        # Spawn the remote enpoint process.
        log.debug('Spawning remote endpoint process.')
        self._remote_pid = self._container_client.spawn_process(
            name=self._remote_listen_name,
            module='ion.services.sa.tcaa.remote_endpoint',
            cls='RemoteEndpoint',
            config=remote_endpoint_config)
        log.debug('Remote endpoint pid=%s.', str(self._remote_pid))

        # Create an endpoint client.
        self.re_client = RemoteEndpointClient(
            process=FakeProcess(),
            to_name=self._remote_listen_name)
        log.debug('Got re client %s.', str(self.re_client))
        
        # Remember the remote port.
        self._remote_port = self.re_client.get_port()
        log.debug('The remote port is: %i.', self._remote_port)
        
        
    ###################################################################
    # Telemetry publications to start/top endpoint.
    # (Normally be published by appropriate platform agents.)
    ###################################################################

    def terrestrial_link_up(self):
        """
        Publish telemetry available to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry available event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._terrestrial_platform_id,
                            status = TelemetryStatusType.AVAILABLE)
        
    def terrestrial_link_down(self):
        """
        Publish telemetry unavailable to the terrestrial endpoint.
        """
        # Publish a link up event to be caught by the terrestrial endpoint.
        log.debug('Publishing terrestrial telemetry unavailable event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._terrestrial_platform_id,
                            status = TelemetryStatusType.UNAVAILABLE)
    
    def remote_link_up(self):
        """
        Publish telemetry available to the remote endpoint.
        """
        # Publish a link up event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry available event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._remote_platform_id,
                            status = TelemetryStatusType.AVAILABLE)
    
    def remote_link_down(self):
        """
        Publish telemetry unavailable to the remote endpoint.
        """
        # Publish a link down event to be caught by the remote endpoint.
        log.debug('Publishing remote telemetry unavailable event.')
        self._event_publisher.publish_event(
                            event_type='PlatformTelemetryEvent',
                            origin=self._remote_platform_id,
                            status = TelemetryStatusType.UNAVAILABLE)
    
    def consume_event(self, evt, *args, **kwargs):
        """
        Test callback for events.
        """
        log.debug('Test got event: %s, args: %s, kwargs: %s',
                  str(evt), str(args), str(kwargs))
        
        if evt.type_ == 'PublicPlatformTelemetryEvent':
            self._telem_evts.append(evt)
            if self._no_telem_evts > 0 and self._no_telem_evts == len(self._telem_evts):
                self._done_telem_evt.set()
                    
        elif evt.type_ == 'RemoteQueueModifiedEvent':
            self._queue_mod_evts.append(evt)
            if self._no_queue_mod_evts > 0 and self._no_queue_mod_evts == len(self._queue_mod_evts):
                self._done_queue_mod_evt.set()
            
        elif evt.type_ == 'RemoteCommandTransmittedEvent':
            self._cmd_tx_evts.append(evt)
            if self._no_cmd_tx_evts > 0 and self._no_cmd_tx_evts == len(self._cmd_tx_evts):
                self._done_cmd_tx_evt.set()
        
        elif evt.type_ == 'RemoteCommandResult':
            cmd = evt.command
            self._results_recv[cmd.command_id] = cmd
            if len(self._results_recv) == self._no_requests:
                self._done_cmd_evt.set()
        
    ###################################################################
    # Misc helpers.
    ###################################################################
        
    def make_fake_command(self, no):
        """
        Build a fake command for use in tests.
        """
            
        cmdstr = 'fake_cmd_%i' % no
        cmd = IonObject('RemoteCommand',
                             resource_id='fake_id',
                             command=cmdstr,
                             args=['arg1', 23],
                             kwargs={'worktime':3})
        return cmd
        
    ###################################################################
    # Tests.
    ###################################################################

    def test_queued_fake(self):
        """
        test_queued_fake
        Test fake resource commands queued prior to linkup.
        """
                
        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            cmd = self.te_client.enqueue_command(cmd)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()
        
        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    # The following error occurs when queue persistence is enabled.
    # Need to verify correct solution to enable persistent queues.
    """
    2012-11-06 14:27:13,517 INFO     pyon.container.procs ProcManager.terminate_process: org_management -> pid=Edwards-MacBook-Pro_local_8975.8
    Traceback (most recent call last):
      File "/Users/edward/Documents/Dev/code/coi-services/eggs/gevent-0.13.7-py2.7-macosx-10.5-intel.egg/gevent/greenlet.py", line 390, in run
        result = self._run(*self.args, **self.kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/ion/services/sa/tcaa/remote_endpoint.py", line 97, in command_loop
        self._callback(cmd_result)
      File "/Users/edward/Documents/Dev/code/coi-services/ion/services/sa/tcaa/remote_endpoint.py", line 284, in _result_complete
        self._client.enqueue(result)
    AttributeError: 'NoneType' object has no attribute 'enqueue'
    <Greenlet at 0x1059ca9b0: command_loop> failed with AttributeError
    
    2012-11-06 14:27:13,586 INFO     pyon.container.procs ProcManager.terminate_process: exchange_management -> pid=Edwards-MacBook-Pro_local_8975.7
    2012-11-06 14:27:13,665 INFO     pyon.container.procs ProcManager.terminate_process: policy_management -> pid=Edwards-MacBook-Pro_local_8975.6
    2012-11-06 14:27:13,739 INFO     pyon.container.procs ProcManager.terminate_process: identity_management -> pid=Edwards-MacBook-Pro_local_8975.5
    2012-11-06 14:27:13,807 INFO     pyon.container.procs ProcManager.terminate_process: directory -> pid=Edwards-MacBook-Pro_local_8975.4
    2012-11-06 14:27:13,874 INFO     pyon.container.procs ProcManager.terminate_process: resource_registry -> pid=Edwards-MacBook-Pro_local_8975.3
    2012-11-06 14:27:13,941 INFO     pyon.container.procs ProcManager.terminate_process: event_persister -> pid=Edwards-MacBook-Pro_local_8975.1
    2012-11-06 14:27:13,945 INFO     pyon.event.event EventSubscriber stopped. Event pattern=#
    2012-11-06 14:27:14,124 INFO     pyon.datastore.couchdb.couchdb_standalone Connecting to CouchDB server: http://localhost:5984
    2012-11-06 14:27:14,399 INFO     pyon.datastore.couchdb.couchdb_standalone Closing connection to CouchDB
    
    ======================================================================
    ERROR: test_process_online
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/Users/edward/Documents/Dev/code/coi-services/eggs/mock-0.8.0-py2.7.egg/mock.py", line 1605, in _inner
        return f(*args, **kw)
      File "/Users/edward/Documents/Dev/code/coi-services/ion/services/sa/tcaa/test/test_2caa.py", line 492, in test_process_online
        cmd = self.te_client.enqueue_command(cmd)
      File "/Users/edward/Documents/Dev/code/coi-services/interface/services/sa/iterrestrial_endpoint.py", line 188, in enqueue_command
        return self.request(IonObject('terrestrial_endpoint_enqueue_command_in', **{'command': command or None,'link': link}), op='enqueue_command', headers=headers, timeout=timeout)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 1012, in request
        return RequestResponseClient.request(self, msg, headers=headers, timeout=timeout)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 822, in request
        retval, headers = e.send(msg, headers=headers, timeout=timeout)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 310, in send
        result_data, result_headers = c.send(self.conv_type.server_role, msg, headers, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 126, in send
        return self._invite_and_send(to_role, msg, headers, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 145, in _invite_and_send
        return self._send(to_role, to_role_name, msg, header, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 169, in _send
        return self._end_point_unit._message_send(msg, headers, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/ion/conversation.py", line 289, in _message_send
        return ProcessRPCRequestEndpointUnit.send(self, msg, headers,  **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 134, in send
        return self._send(_msg, _header, **kwargs)
      File "/Users/edward/Documents/Dev/code/coi-services/extern/pyon/pyon/net/endpoint.py", line 880, in _send
        raise ex
    Conflict: 409 - Object not based on most current version
    
    """

    def test_process_online(self):
        """
        test_process_online
        Test fake resource commands queued while link is up.
        """
                
        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Queue up a series of fake commands to be handled by the remote side.
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            cmd = self.te_client.enqueue_command(cmd)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mods, command transmissions and results.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())

    def test_remote_late(self):
        """
        test_remote_late
        Test fake resource commands queued prior to linkup.
        Delay remote side linkup substantially to test terrestrial
        behavior when remote server not initially available.
        """
                
        for i in range(self._no_requests):
            cmd = self.make_fake_command(i)
            cmd = self.te_client.enqueue_command(cmd)
            self._requests_sent[cmd.command_id] = cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Actually stop the remote process, since the server is
        # available even if it hasn't linked up yet and the test is running
        # in the same container. The test will remember the appropriate
        # remote port numbers.
        self._container_client.terminate_process(self._remote_pid)
        self.terrestrial_link_up()
        gevent.sleep(10)
        
        # Restart remote side and publish remote link up.
        self._start_remote()
        self.remote_link_up()
        
        # Block for transmission and result events.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()
        
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())
        
    def test_resource_commands(self):
        """
        test_resource_commands
        """

        # Set up to verify the two commands queued.        
        self._no_requests = 2
        self._no_telem_evts = 2
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests

        # Use IA client to verify IA.
        state = self._ia_client.get_agent_state()
        log.debug('Agent state is: %s', state)
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        retval = self._ia_client.ping_agent()
        log.debug('Agent ping is: %s', str(retval))
        self.assertIn('ping from InstrumentAgent', retval)

        # Create and enqueue commands.
        state_cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='get_agent_state',
                             args=[],
                             kwargs={})
        state_cmd = self.te_client.enqueue_command(state_cmd)
        self._requests_sent[state_cmd.command_id] = state_cmd

        ping_cmd = IonObject('RemoteCommand',
                             resource_id=IA_RESOURCE_ID,
                             svc_name='',
                             command='ping_agent',
                             args=[],
                             kwargs={})
        ping_cmd = self.te_client.enqueue_command(ping_cmd)
        self._requests_sent[ping_cmd.command_id] = ping_cmd

        # Block on queue mod events.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Block on command transmissions and results.
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)                
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()

        # Block on terrestrial public telemetry events.
        self._done_telem_evt.get(timeout=CFG.endpoint.receive.timeout)

        self.assertItemsEqual(self._requests_sent.keys(),
                                  self._results_recv.keys())        
        
        self.assertEqual(self._results_recv[state_cmd.command_id].result,
                         ResourceAgentState.UNINITIALIZED)
        self.assertIn('ping from InstrumentAgent',
                      self._results_recv[ping_cmd.command_id].result)
        
    def test_service_command_sequence(self):
        """
        test_service_commands
        """
        # Publish link up events.
        self.terrestrial_link_up()
        self.remote_link_up()

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []        
        self._cmd_tx_evts = []
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        obj = IonObject("UserInfo", name="some_name")
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='create',
                             args=[obj],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)

        # Returns obj_id, obj_rev.
        obj_id, obj_rev = self._results_recv[cmd.command_id].result
        
        # Confirm the results are valid.
        
        #Result is a tuple of strings.
        #{'result': ['ad183ff26bae4f329ddd85fd69d160a9',
        #'1-00a308c45fff459c7cda1db9a7314de6'],
        #'command_id': 'cc2ae00d-40b0-47d2-af61-8ffb87f1aca2'}
        
        self.assertIsInstance(obj_id, str)
        self.assertNotEqual(obj_id, '')
        self.assertIsInstance(obj_rev, str)
        self.assertNotEqual(obj_rev, '')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []        
        self._cmd_tx_evts = []
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='read',
                             args=[obj_id],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        
                
        # Returns read_obj.
        read_obj = self._results_recv[cmd.command_id].result
        
        # Confirm the results are valid.
        
        #Result is a user info object with the name set.
        #{'lcstate': 'DEPLOYED_AVAILABLE',
        #'_rev': '1-851f067bac3c34b2238c0188b3340d0f',
        #'description': '',
        #'ts_updated': '1349213207638',
        #'type_': 'UserInfo',
        #'contact': <interface.objects.ContactInformation object at 0x10d7df590>,
        #'_id': '27832d93f4cd4535a75ac75c06e00a7e',
        #'ts_created': '1349213207638',
        #'variables': [{'name': '', 'value': ''}],
        #'name': 'some_name'}
        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_name')

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []
        self._cmd_tx_evts = []        
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        read_obj.name = 'some_other_name'
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='update',
                             args=[read_obj],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        

        # Returns nothing.

        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []
        self._cmd_tx_evts = []        
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='read',
                             args=[obj_id],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        
        
        # Returns read_obj.
        read_obj = self._results_recv[cmd.command_id].result
        
        self.assertIsInstance(read_obj, UserInfo)
        self.assertEquals(read_obj.name, 'some_other_name')
        
        # Send commands one at a time.
        # Reset queues and events.
        self._no_requests = 1
        self._no_cmd_tx_evts = self._no_requests
        self._no_queue_mod_evts = self._no_requests
        self._done_queue_mod_evt = AsyncResult()
        self._done_cmd_tx_evt = AsyncResult()
        self._done_cmd_evt = AsyncResult()
        self._queue_mod_evts = []
        self._cmd_tx_evts = []        
        self._requests_sent = {}
        self._results_recv = {}

        # Create user object.
        cmd = IonObject('RemoteCommand',
                             resource_id='',
                             svc_name='resource_registry',
                             command='delete',
                             args=[obj_id],
                             kwargs='')
        cmd = self.te_client.enqueue_command(cmd)

        # Block for the queue to be modified, the command to be transmitted,
        # and the result to be received.
        self._done_queue_mod_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_tx_evt.get(timeout=CFG.endpoint.receive.timeout)
        self._done_cmd_evt.get(timeout=CFG.endpoint.receive.timeout)        

        # Returns nothing.
        
        # Publish link down events.
        self.terrestrial_link_down()
        self.remote_link_down()