diff --git a/ironic/common/json_rpc/client.py b/ironic/common/json_rpc/client.py index 10c44928e6..b1953d4863 100644 --- a/ironic/common/json_rpc/client.py +++ b/ironic/common/json_rpc/client.py @@ -18,6 +18,7 @@ This client is compatible with any JSON RPC 2.0 implementation, including ours. from oslo_config import cfg from oslo_log import log from oslo_utils import importutils +from oslo_utils import netutils from oslo_utils import strutils from oslo_utils import uuidutils @@ -156,7 +157,9 @@ class _CallContext(object): scheme = 'http' if CONF.json_rpc.use_ssl: scheme = 'https' - url = '%s://%s:%d' % (scheme, self.host, CONF.json_rpc.port) + url = '%s://%s:%d' % (scheme, + netutils.escape_ipv6(self.host), + CONF.json_rpc.port) result = _get_session().post(url, json=body) LOG.debug('RPC %s returned %s', method, strutils.mask_password(result.text or '')) diff --git a/ironic/tests/unit/common/test_json_rpc.py b/ironic/tests/unit/common/test_json_rpc.py index 99288f481a..e30e660d38 100644 --- a/ironic/tests/unit/common/test_json_rpc.py +++ b/ironic/tests/unit/common/test_json_rpc.py @@ -318,6 +318,40 @@ class TestClient(test_base.TestCase): 'params': {'answer': 42, 'context': self.ctx_json}, 'id': self.context.request_id}) + def test_call_ipv4_success(self, mock_session): + response = mock_session.return_value.post.return_value + response.json.return_value = { + 'jsonrpc': '2.0', + 'result': 42 + } + cctx = self.client.prepare('foo.192.0.2.1') + self.assertEqual('192.0.2.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://192.0.2.1:8089', + json={'jsonrpc': '2.0', + 'method': 'do_something', + 'params': {'answer': 42, 'context': self.ctx_json}, + 'id': self.context.request_id}) + + def test_call_ipv6_success(self, mock_session): + 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') + 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]:8089', + json={'jsonrpc': '2.0', + 'method': 'do_something', + 'params': {'answer': 42, 'context': self.ctx_json}, + 'id': self.context.request_id}) + def test_call_success_with_version(self, mock_session): response = mock_session.return_value.post.return_value response.json.return_value = { diff --git a/releasenotes/notes/json-rpc-ipv6-host-30eca350f34bc091.yaml b/releasenotes/notes/json-rpc-ipv6-host-30eca350f34bc091.yaml new file mode 100644 index 0000000000..96944012c6 --- /dev/null +++ b/releasenotes/notes/json-rpc-ipv6-host-30eca350f34bc091.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + When configured to use json-rpc, the ``[DEFAULT].host`` configuration + option to ironic-conductor can now be set to an IPv6 address. Previously + it could only be an IPv4 address or a DNS name.