Add jsonrpc client port capability
Adds the capability for the topic to contain a port to which the json rpc client will connect to. This allows for distinct json-rpc targets to be configured in an environment. Change-Id: I999316880639cd410543eb54475b0c647b35147b
This commit is contained in:
		@@ -82,18 +82,36 @@ class Client(object):
 | 
				
			|||||||
        return _can_send_version(version, self.version_cap)
 | 
					        return _can_send_version(version, self.version_cap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def prepare(self, topic, version=None):
 | 
					    def prepare(self, topic, version=None):
 | 
				
			||||||
 | 
					        """Prepare the client to transmit a request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param topic: Topic which is being addressed. Typically this
 | 
				
			||||||
 | 
					                      is the hostname of the remote json-rpc service.
 | 
				
			||||||
 | 
					        :param version: The RPC API version to utilize.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        host = topic.split('.', 1)[1]
 | 
					        host = topic.split('.', 1)[1]
 | 
				
			||||||
 | 
					        # NOTE(TheJulia): Originally, I was worried about ip addresses being
 | 
				
			||||||
 | 
					        # used, but looking at the topic split, it would have never really
 | 
				
			||||||
 | 
					        # worked.
 | 
				
			||||||
 | 
					        host, port = netutils.parse_host_port(host)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return _CallContext(
 | 
					        return _CallContext(
 | 
				
			||||||
            host, self.serializer, version=version,
 | 
					            host, self.serializer, version=version,
 | 
				
			||||||
            version_cap=self.version_cap,
 | 
					            version_cap=self.version_cap,
 | 
				
			||||||
            allowed_exception_namespaces=self.allowed_exception_namespaces)
 | 
					            allowed_exception_namespaces=self.allowed_exception_namespaces,
 | 
				
			||||||
 | 
					            port=port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _CallContext(object):
 | 
					class _CallContext(object):
 | 
				
			||||||
    """Wrapper object for compatibility with oslo.messaging API."""
 | 
					    """Wrapper object for compatibility with oslo.messaging API."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, host, serializer, version=None, version_cap=None,
 | 
					    def __init__(self, host, serializer, version=None, version_cap=None,
 | 
				
			||||||
 | 
					                 port=None,
 | 
				
			||||||
                 allowed_exception_namespaces=()):
 | 
					                 allowed_exception_namespaces=()):
 | 
				
			||||||
 | 
					        if not port:
 | 
				
			||||||
 | 
					            self.port = CONF.json_rpc.port
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.port = int(port)
 | 
				
			||||||
        self.host = host
 | 
					        self.host = host
 | 
				
			||||||
        self.serializer = serializer
 | 
					        self.serializer = serializer
 | 
				
			||||||
        self.version = version
 | 
					        self.version = version
 | 
				
			||||||
@@ -190,7 +208,7 @@ class _CallContext(object):
 | 
				
			|||||||
            scheme = 'https'
 | 
					            scheme = 'https'
 | 
				
			||||||
        url = '%s://%s:%d' % (scheme,
 | 
					        url = '%s://%s:%d' % (scheme,
 | 
				
			||||||
                              netutils.escape_ipv6(self.host),
 | 
					                              netutils.escape_ipv6(self.host),
 | 
				
			||||||
                              CONF.json_rpc.port)
 | 
					                              self.port)
 | 
				
			||||||
        LOG.debug("RPC %s to %s with %s", method, url,
 | 
					        LOG.debug("RPC %s to %s with %s", method, url,
 | 
				
			||||||
                  strutils.mask_dict_password(body))
 | 
					                  strutils.mask_dict_password(body))
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -200,7 +218,6 @@ class _CallContext(object):
 | 
				
			|||||||
            raise
 | 
					            raise
 | 
				
			||||||
        LOG.debug('RPC %s to %s returned %s', method, url,
 | 
					        LOG.debug('RPC %s to %s returned %s', method, url,
 | 
				
			||||||
                  strutils.mask_password(result.text or '<None>'))
 | 
					                  strutils.mask_password(result.text or '<None>'))
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not cast:
 | 
					        if not cast:
 | 
				
			||||||
            result = result.json()
 | 
					            result = result.json()
 | 
				
			||||||
            self._handle_error(result.get('error'))
 | 
					            self._handle_error(result.get('error'))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -387,7 +387,8 @@ class TestClient(base.IronicLibTestCase):
 | 
				
			|||||||
                  'params': {'answer': 42, 'context': self.ctx_json},
 | 
					                  'params': {'answer': 42, 'context': self.ctx_json},
 | 
				
			||||||
                  'id': self.context.request_id})
 | 
					                  'id': self.context.request_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_call_ipv6_success(self, mock_session):
 | 
					    @mock.patch.object(client.LOG, 'warning', autospec=True)
 | 
				
			||||||
 | 
					    def test_call_ipv6_success(self, mock_session, mock_log):
 | 
				
			||||||
        response = mock_session.return_value.post.return_value
 | 
					        response = mock_session.return_value.post.return_value
 | 
				
			||||||
        response.json.return_value = {
 | 
					        response.json.return_value = {
 | 
				
			||||||
            'jsonrpc': '2.0',
 | 
					            'jsonrpc': '2.0',
 | 
				
			||||||
@@ -403,6 +404,26 @@ class TestClient(base.IronicLibTestCase):
 | 
				
			|||||||
                  'method': 'do_something',
 | 
					                  'method': 'do_something',
 | 
				
			||||||
                  'params': {'answer': 42, 'context': self.ctx_json},
 | 
					                  'params': {'answer': 42, 'context': self.ctx_json},
 | 
				
			||||||
                  'id': self.context.request_id})
 | 
					                  'id': self.context.request_id})
 | 
				
			||||||
 | 
					        self.assertEqual(1, mock_log.call_count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mock.patch.object(client.LOG, 'warning', autospec=True)
 | 
				
			||||||
 | 
					    def test_call_ipv6_success_rfc2732(self, mock_session, mock_log):
 | 
				
			||||||
 | 
					        response = mock_session.return_value.post.return_value
 | 
				
			||||||
 | 
					        response.json.return_value = {
 | 
				
			||||||
 | 
					            'jsonrpc': '2.0',
 | 
				
			||||||
 | 
					            'result': 42
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cctx = self.client.prepare('foo.[2001:db8::1]:8192')
 | 
				
			||||||
 | 
					        self.assertEqual('2001:db8::1', cctx.host)
 | 
				
			||||||
 | 
					        result = cctx.call(self.context, 'do_something', answer=42)
 | 
				
			||||||
 | 
					        self.assertEqual(42, result)
 | 
				
			||||||
 | 
					        mock_session.return_value.post.assert_called_once_with(
 | 
				
			||||||
 | 
					            'http://[2001:db8::1]:8192',
 | 
				
			||||||
 | 
					            json={'jsonrpc': '2.0',
 | 
				
			||||||
 | 
					                  'method': 'do_something',
 | 
				
			||||||
 | 
					                  'params': {'answer': 42, 'context': self.ctx_json},
 | 
				
			||||||
 | 
					                  'id': self.context.request_id})
 | 
				
			||||||
 | 
					        self.assertEqual(0, mock_log.call_count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_call_success_with_version(self, mock_session):
 | 
					    def test_call_success_with_version(self, mock_session):
 | 
				
			||||||
        response = mock_session.return_value.post.return_value
 | 
					        response = mock_session.return_value.post.return_value
 | 
				
			||||||
@@ -410,12 +431,12 @@ class TestClient(base.IronicLibTestCase):
 | 
				
			|||||||
            'jsonrpc': '2.0',
 | 
					            'jsonrpc': '2.0',
 | 
				
			||||||
            'result': 42
 | 
					            'result': 42
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        cctx = self.client.prepare('foo.example.com', version='1.42')
 | 
					        cctx = self.client.prepare('foo.example.com:8192', version='1.42')
 | 
				
			||||||
        self.assertEqual('example.com', cctx.host)
 | 
					        self.assertEqual('example.com', cctx.host)
 | 
				
			||||||
        result = cctx.call(self.context, 'do_something', answer=42)
 | 
					        result = cctx.call(self.context, 'do_something', answer=42)
 | 
				
			||||||
        self.assertEqual(42, result)
 | 
					        self.assertEqual(42, result)
 | 
				
			||||||
        mock_session.return_value.post.assert_called_once_with(
 | 
					        mock_session.return_value.post.assert_called_once_with(
 | 
				
			||||||
            'http://example.com:8089',
 | 
					            'http://example.com:8192',
 | 
				
			||||||
            json={'jsonrpc': '2.0',
 | 
					            json={'jsonrpc': '2.0',
 | 
				
			||||||
                  'method': 'do_something',
 | 
					                  'method': 'do_something',
 | 
				
			||||||
                  'params': {'answer': 42, 'context': self.ctx_json,
 | 
					                  'params': {'answer': 42, 'context': self.ctx_json,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					features:
 | 
				
			||||||
 | 
					  - |
 | 
				
			||||||
 | 
					    Adds the capability for the ``json_rpc`` client to identify and utilize
 | 
				
			||||||
 | 
					    a specific port from the supplied ``topic`` field as opposed to the
 | 
				
			||||||
 | 
					    default configured port.
 | 
				
			||||||
		Reference in New Issue
	
	Block a user