Przeglądaj źródła

Add proxy support

Alessandro Pilotti 8 lat temu
rodzic
commit
a222c50403

+ 7 - 2
coriolis/osmorphing/base.py

@@ -23,6 +23,7 @@ class BaseOSMorphingTools(object):
         self._version = None
         self._hypervisor = hypervisor
         self._event_manager = event_manager
+        self._environment = {}
 
     def check_os(self):
         if not self._distro:
@@ -61,6 +62,9 @@ class BaseOSMorphingTools(object):
     def post_packages_uninstall(self, package_names):
         pass
 
+    def set_environment(self, environment):
+        self._environment = environment
+
 
 class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
     __metaclass__ = abc.ABCMeta
@@ -117,10 +121,11 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
         return utils.list_ssh_dir(self._ssh, path)
 
     def _exec_cmd(self, cmd):
-        return utils.exec_ssh_cmd(self._ssh, cmd)
+        return utils.exec_ssh_cmd(self._ssh, cmd, self._environment)
 
     def _exec_cmd_chroot(self, cmd):
-        return utils.exec_ssh_cmd_chroot(self._ssh, self._os_root_dir, cmd)
+        return utils.exec_ssh_cmd_chroot(
+            self._ssh, self._os_root_dir, cmd, self._environment)
 
     def _check_user_exists(self, username):
         try:

+ 35 - 0
coriolis/osmorphing/manager.py

@@ -1,15 +1,43 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 
+from oslo_config import cfg
 from oslo_log import log as logging
 
 from coriolis import exception
 from coriolis import events
 from coriolis.osmorphing.osmount import factory as osmount_factory
 
+proxy_opts = [
+    cfg.StrOpt('url',
+               default=None,
+               help='Proxy URL.'),
+    cfg.StrOpt('username',
+               default=None,
+               help='Proxy username.'),
+    cfg.StrOpt('password',
+               default=None,
+               help='Proxy password.'),
+    cfg.ListOpt('no_proxy',
+                default=[],
+                help='List of proxy exclusions.'),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(proxy_opts, 'proxy')
+
 LOG = logging.getLogger(__name__)
 
 
+def _get_proxy_settings():
+    return {
+        "url": CONF.proxy.url,
+        "username": CONF.proxy.username,
+        "password": CONF.proxy.password,
+        "no_proxy": CONF.proxy.no_proxy,
+    }
+
+
 def morph_image(origin_provider, destination_provider, connection_info,
                 osmorphing_info, event_handler):
     event_manager = events.EventManager(event_handler)
@@ -22,6 +50,9 @@ def morph_image(origin_provider, destination_provider, connection_info,
     os_mount_tools = osmount_factory.get_os_mount_tools(
         os_type, connection_info, event_manager, ignore_devices)
 
+    proxy_settings = _get_proxy_settings()
+    os_mount_tools.set_proxy(proxy_settings)
+
     event_manager.progress_update("Discovering and mounting OS partitions")
     os_root_dir, other_mounted_dirs, os_root_dev = os_mount_tools.mount_os()
 
@@ -29,9 +60,12 @@ def morph_image(origin_provider, destination_provider, connection_info,
     osmorphing_info['os_root_dev'] = os_root_dev
     conn = os_mount_tools.get_connection()
 
+    environment = os_mount_tools.get_environment()
+
     try:
         (export_os_morphing_tools, _) = origin_provider.get_os_morphing_tools(
             conn, osmorphing_info)
+        export_os_morphing_tools.set_environment(environment)
     except exception.OSMorphingToolsNotFound:
         export_os_morphing_tools = None
 
@@ -39,6 +73,7 @@ def morph_image(origin_provider, destination_provider, connection_info,
         (import_os_morphing_tools,
          os_info) = destination_provider.get_os_morphing_tools(
             conn, osmorphing_info)
+        import_os_morphing_tools.set_environment(environment)
     except exception.OSMorphingToolsNotFound:
         import_os_morphing_tools = None
         os_info = None

+ 43 - 3
coriolis/osmorphing/osmount/base.py

@@ -20,6 +20,7 @@ class BaseOSMountTools(object):
     def __init__(self, connection_info, event_manager, ignore_devices):
         self._event_manager = event_manager
         self._ignore_devices = ignore_devices
+        self._environment = {}
         self._connect(connection_info)
 
     @abc.abstractmethod
@@ -42,6 +43,12 @@ class BaseOSMountTools(object):
     def dismount_os(self, dirs):
         pass
 
+    def set_proxy(self, proxy_settings):
+        pass
+
+    def get_environment(self):
+        return self._environment
+
 
 class BaseSSHOSMountTools(BaseOSMountTools):
     def _connect(self, connection_info):
@@ -60,18 +67,30 @@ class BaseSSHOSMountTools(BaseOSMountTools):
             {"ip": ip, "port": port})
         ssh = paramiko.SSHClient()
         ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+
         ssh.connect(hostname=ip, port=port, username=username, pkey=pkey,
                     password=password)
         self._ssh = ssh
 
+        if self._allow_ssh_env_vars():
+            # Reconnect after a reload
+            ssh.close()
+            ssh.connect(hostname=ip, port=port, username=username, pkey=pkey,
+                        password=password)
+
+    def _allow_ssh_env_vars(self):
+        self._exec_cmd('sudo sed -i -e "\$aAcceptEnv *" /etc/ssh/sshd_config')
+        self._exec_cmd("sudo service sshd reload")
+        return True
+
+    def _exec_cmd(self, cmd):
+        return utils.exec_ssh_cmd(self._ssh, cmd, self._environment)
+
     def get_connection(self):
         return self._ssh
 
 
 class BaseLinuxOSMountTools(BaseSSHOSMountTools):
-    def _exec_cmd(self, cmd):
-        return utils.exec_ssh_cmd(self._ssh, cmd)
-
     def _get_pvs(self):
         out = self._exec_cmd("sudo pvdisplay -c").decode().split("\n")
         pvs = {}
@@ -206,3 +225,24 @@ class BaseLinuxOSMountTools(BaseSSHOSMountTools):
     def dismount_os(self, dirs):
         for dir in dirs:
             self._exec_cmd('sudo umount %s' % dir)
+
+    def set_proxy(self, proxy_settings):
+        url = proxy_settings.get('url')
+        if not url:
+            return
+
+        username = proxy_settings.get('username')
+        if username:
+            password = proxy_settings.get('password', '')
+            url = utils.get_url_with_credentials(url, username, password)
+
+        LOG.debug("Proxy URL: %s", url)
+        for var in ['http_proxy', 'https_proxy', 'ftp_proxy']:
+            self._environment[var] = url
+            # Some commands look for the uppercase var name
+            self._environment[var.upper()] = url
+
+        no_proxy = proxy_settings.get('no_proxy')
+        if no_proxy:
+            LOG.debug("Proxy exclusions: %s", no_proxy)
+            self._environment["no_proxy"] = '.'.join(no_proxy)

+ 1 - 1
coriolis/osmorphing/osmount/redhat.py

@@ -19,5 +19,5 @@ class RedHatOSMountTools(base.BaseLinuxOSMountTools):
             return True
 
     def _pre_mount_os(self):
-        self._exec_cmd("sudo yum install -y lvm2")
+        self._exec_cmd("sudo -E yum install -y lvm2")
         self._exec_cmd("sudo modprobe dm-mod")

+ 2 - 2
coriolis/osmorphing/osmount/ubuntu.py

@@ -16,6 +16,6 @@ class UbuntuOSMountTools(base.BaseLinuxOSMountTools):
             return True
 
     def _pre_mount_os(self):
-        self._exec_cmd("sudo apt-get update -y")
-        self._exec_cmd("sudo apt-get install lvm2 -y")
+        self._exec_cmd("sudo -E apt-get update -y")
+        self._exec_cmd("sudo -E apt-get install lvm2 -y")
         self._exec_cmd("sudo modprobe dm-mod")

+ 21 - 4
coriolis/utils.py

@@ -20,6 +20,7 @@ from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_serialization import jsonutils
 import paramiko
+from six.moves.urllib import parse
 
 from coriolis import constants
 from coriolis import exception
@@ -162,9 +163,10 @@ def list_ssh_dir(ssh, remote_path):
 
 
 @retry_on_error()
-def exec_ssh_cmd(ssh, cmd):
+def exec_ssh_cmd(ssh, cmd, environment=None):
     LOG.debug("Executing SSH command: %s", cmd)
-    stdin, stdout, stderr = ssh.exec_command(cmd)
+    LOG.debug("SSH command environment: %s", environment)
+    stdin, stdout, stderr = ssh.exec_command(cmd, environment=environment)
     exit_code = stdout.channel.recv_exit_status()
     std_out = stdout.read()
     std_err = stderr.read()
@@ -176,8 +178,9 @@ def exec_ssh_cmd(ssh, cmd):
     return std_out
 
 
-def exec_ssh_cmd_chroot(ssh, chroot_dir, cmd):
-    return exec_ssh_cmd(ssh, "sudo chroot %s %s" % (chroot_dir, cmd))
+def exec_ssh_cmd_chroot(ssh, chroot_dir, cmd, environment=None):
+    return exec_ssh_cmd(ssh, "sudo -E chroot %s %s" % (chroot_dir, cmd),
+                        environment=environment)
 
 
 def check_fs(ssh, fs_type, dev_path):
@@ -393,3 +396,17 @@ def decode_base64_param(value, is_json=False):
         return decoded
     except (binascii.Error, TypeError, json.decoder.JSONDecodeError) as ex:
         raise exception.InvalidInput(reason=str(ex))
+
+
+def quote_url(text):
+    return parse.quote(text.encode('UTF-8'), safe='')
+
+
+def get_url_with_credentials(url, username, password):
+    parts = parse.urlsplit(url)
+    # Remove previous credentials if set
+    netloc = parts.netloc[parts.netloc.find('@')+1:]
+    netloc = "%s:%s@%s" % (
+        quote_url(username), quote_url(password or ''), netloc)
+    parts = parts._replace(netloc=netloc)
+    return parse.urlunsplit(parts)

+ 1 - 1
requirements.txt

@@ -14,7 +14,7 @@ oslo.middleware
 oslo.serialization
 oslo.service>=1.12.0
 oslo.versionedobjects
-paramiko
+paramiko>=2.1.0
 paste
 pbr
 psutil