From ef51c7a004e5331ccfa34bdbc1fc66d278e8e5bf Mon Sep 17 00:00:00 2001 From: Jeremy Freudberg Date: Mon, 3 Jul 2017 14:05:53 +0000 Subject: [PATCH] Allow proxy_command to optionally use internal IP New config option allows clusters to be provisioned with floating IPs present, but still with communication over internal IP. Related-Blueprint: refactor-use-floating-ip Change-Id: Ieec4da7d852371bc9eabe6756de859b6b8189b8e --- .../userdoc/advanced.configuration.guide.rst | 6 +++++ sahara/service/engine.py | 10 +++++--- sahara/tests/unit/utils/test_ssh_remote.py | 23 +++++++++++++++++++ sahara/utils/remote.py | 4 ++++ sahara/utils/ssh_remote.py | 20 +++++++++++----- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/doc/source/userdoc/advanced.configuration.guide.rst b/doc/source/userdoc/advanced.configuration.guide.rst index 26600f99..cca3386e 100644 --- a/doc/source/userdoc/advanced.configuration.guide.rst +++ b/doc/source/userdoc/advanced.configuration.guide.rst @@ -24,6 +24,12 @@ port. The ``{host}`` and ``{port}`` keywords should be used to describe the destination, they will be substituted at runtime. Other keywords that can be used are: ``{tenant_id}``, ``{network_id}`` and ``{router_id}``. +Additionally, if ``proxy_command_use_internal_ip`` is set to ``True``, +then the internal IP will be subsituted for ``{host}`` in the command. +Otherwise (if ``False``, by default) the management IP will be used: this +corresponds to floating IP if present in the relevant node group, else the +internal IP. The option is ignored if ``proxy_command`` is not also set. + For example, the following parameter in the sahara configuration file would be used if instances are accessed through a relay machine: diff --git a/sahara/service/engine.py b/sahara/service/engine.py index 654e5844..83a34520 100644 --- a/sahara/service/engine.py +++ b/sahara/service/engine.py @@ -19,6 +19,7 @@ import datetime import string from novaclient import exceptions as nova_exceptions +from oslo_config import cfg from oslo_log import log as logging import six @@ -39,6 +40,7 @@ from sahara.utils import remote LOG = logging.getLogger(__name__) conductor = c.API +CONF = cfg.CONF @six.add_metaclass(abc.ABCMeta) @@ -125,9 +127,11 @@ class Engine(object): LOG.debug('Instance is accessible') return True except Exception as ex: - LOG.debug("Can't login to node, IP: {mgmt_ip}, " - "reason {reason}".format(mgmt_ip=instance.management_ip, - reason=ex)) + ip_used = "internal_ip" if CONF.proxy_command and \ + CONF.proxy_command_use_internal_ip else "management_ip" + LOG.debug("Can't login to node, IP: {ip}, reason {reason}" + .format(ip=getattr(instance, ip_used), + reason=ex)) return False return False diff --git a/sahara/tests/unit/utils/test_ssh_remote.py b/sahara/tests/unit/utils/test_ssh_remote.py index 92412517..33b30418 100644 --- a/sahara/tests/unit/utils/test_ssh_remote.py +++ b/sahara/tests/unit/utils/test_ssh_remote.py @@ -291,6 +291,29 @@ class TestInstanceInteropHelper(base.SaharaTestCase): p_simple_exec_func.assert_any_call( shlex.split('ssh fakerelay nc 10.0.0.3 8080')) + @mock.patch('sahara.utils.ssh_remote._simple_exec_func') + @mock.patch('sahara.utils.ssh_remote.ProxiedHTTPAdapter') + def test_proxy_command_internal_ip(self, p_adapter, p_simple_exec_func): + self.override_config('proxy_command', 'ssh fakerelay nc {host} {port}') + self.override_config('proxy_command_use_internal_ip', True) + + instance = FakeInstance('inst3', '123', '10.0.0.3', '10.0.0.4', + 'user3', 'key3') + remote = ssh_remote.InstanceInteropHelper(instance) + + # Test SSH + remote.execute_command('/bin/true') + self.run_in_subprocess.assert_any_call( + 42, ssh_remote._connect, + ('10.0.0.4', 'user3', 'key3', 'ssh fakerelay nc 10.0.0.4 22', + None, None)) + # Test HTTP + remote.get_http_client(8080) + p_adapter.assert_called_once_with( + p_simple_exec_func(), '10.0.0.4', 8080) + p_simple_exec_func.assert_any_call( + shlex.split('ssh fakerelay nc 10.0.0.4 8080')) + def test_proxy_command_bad(self): self.override_config('proxy_command', '{bad_kw} nc {host} {port}') diff --git a/sahara/utils/remote.py b/sahara/utils/remote.py index 47c3e8df..9c134169 100644 --- a/sahara/utils/remote.py +++ b/sahara/utils/remote.py @@ -38,6 +38,10 @@ ssh_opts = [ 'SSH and HTTP connections. Use {host} and {port} to describe ' 'the destination. Other available keywords: {tenant_id}, ' '{network_id}, {router_id}.'), + cfg.BoolOpt('proxy_command_use_internal_ip', default=False, + help='Force proxy_command usage to be consuming internal IP ' + 'always, instead of management IP. Ignored if proxy_command ' + 'is not set.') ] diff --git a/sahara/utils/ssh_remote.py b/sahara/utils/ssh_remote.py index d8988727..3ebaf05e 100644 --- a/sahara/utils/ssh_remote.py +++ b/sahara/utils/ssh_remote.py @@ -94,6 +94,12 @@ SSH_TIMEOUTS_MAPPING = { _global_remote_semaphore = None +def _get_access_ip(instance): + if CONF.proxy_command and CONF.proxy_command_use_internal_ip: + return instance.internal_ip + return instance.management_ip + + def _default_timeout(func): timeout = SSH_TIMEOUTS_MAPPING.get(func.__name__, 'ssh_timeout_files') return getattr(CONF, timeout, CONF.ssh_timeout_common) @@ -639,7 +645,7 @@ class InstanceInteropHelper(remote.Remote): ctx = context.current() neutron_info['token'] = context.get_auth_token() neutron_info['tenant'] = ctx.tenant_name - neutron_info['host'] = instance.management_ip + neutron_info['host'] = _get_access_ip(instance) log_info = copy.deepcopy(neutron_info) del log_info['token'] @@ -664,7 +670,7 @@ class InstanceInteropHelper(remote.Remote): info['tenant'], auth=auth) keywords['router_id'] = client.get_router() - keywords['host'] = instance.management_ip + keywords['host'] = _get_access_ip(instance) keywords['port'] = port try: @@ -728,7 +734,9 @@ class InstanceInteropHelper(remote.Remote): proxy_command, instance=access_instance, port=22, info=None, rootwrap_command=rootwrap) - return (self.instance.management_ip, + host_ip = _get_access_ip(self.instance) + + return (host_ip, host_ng.image_username, cluster.management_private_key, proxy_command, @@ -771,7 +779,7 @@ class InstanceInteropHelper(remote.Remote): def get_http_client(self, port, info=None): self._log_command('Retrieving HTTP session for {0}:{1}'.format( - self.instance.management_ip, port)) + _get_access_ip(self.instance), port)) host_ng = self.instance.node_group cluster = host_ng.cluster @@ -818,7 +826,7 @@ class InstanceInteropHelper(remote.Remote): proxy_command, instance=access_instance, port=access_port, info=info, rootwrap_command=rootwrap) - return _get_http_client(self.instance.management_ip, port, + return _get_http_client(_get_access_ip(self.instance), port, proxy_command, gateway_host, gateway_username, gateway_private_key) @@ -826,7 +834,7 @@ class InstanceInteropHelper(remote.Remote): def close_http_session(self, port): global _sessions - host = self.instance.management_ip + host = _get_access_ip(self.instance) self._log_command(_("Closing HTTP session for %(host)s:%(port)s") % { 'host': host, 'port': port})