浏览代码

Move the instance poll utils to provider utils

By doing so we avoid potential circular imports. Besides, those
are expected to be used by the providers only.
Lucian Petrut 2 月之前
父节点
当前提交
69a66326ba
共有 4 个文件被更改,包括 233 次插入226 次删除
  1. 102 1
      coriolis/providers/provider_utils.py
  2. 131 0
      coriolis/tests/providers/test_provider_utils.py
  3. 0 128
      coriolis/tests/test_utils.py
  4. 0 97
      coriolis/utils.py

+ 102 - 1
coriolis/providers/provider_utils.py

@@ -1,9 +1,15 @@
 # Copyright 2018 Cloudbase Solutions Srl
 # Copyright 2018 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
-from oslo_log import log as logging
 import requests
 import requests
+import time
+
+from oslo_log import log as logging
+import paramiko
 
 
+from coriolis import constants
 from coriolis import exception
 from coriolis import exception
+from coriolis import utils
+from coriolis import wsman
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
@@ -140,3 +146,98 @@ class ProviderSession(requests.Session):
         verify = self.verify
         verify = self.verify
         return super(ProviderSession, self).merge_environment_settings(
         return super(ProviderSession, self).merge_environment_settings(
             url, proxies, stream, verify, *args, **kwargs)
             url, proxies, stream, verify, *args, **kwargs)
+
+
+def _poll_instance_until_reachable_ssh(
+    connection_info: dict,
+    timeout: int = 600,
+    poll_interval: int = 10,
+):
+    start = time.time()
+    while (time.time() - start) < timeout:
+        try:
+            ssh = utils.connect_ssh(
+                hostname=connection_info["ip"],
+                port=connection_info["port"],
+                username=connection_info["username"],
+                password=connection_info["password"],
+                pkey=connection_info["pkey"],
+            )
+            try:
+                # "exit 0" should work across platforms.
+                # "whoami" would also work.
+                utils.exec_ssh_cmd(ssh, "exit 0")
+            finally:
+                ssh.close()
+            LOG.debug("Instance reachable: %s", connection_info["ip"])
+            return
+        except Exception as err:
+            LOG.debug(
+                f"Could not connect to remote instance: {str(err)}. "
+                f"Retrying, time left: {timeout - (time.time() - start)}."
+            )
+        time.sleep(poll_interval)
+
+    raise exception.CoriolisException(
+        f"Operation timed out after waiting {timeout}s for the instance to be "
+        f"accessible via SSH."
+    )
+
+
+def _poll_instance_until_reachable_winrm(
+    connection_info: dict,
+    timeout: int = 600,
+    poll_interval: int = 10,
+):
+    start = time.time()
+    while (time.time() - start) < timeout:
+        try:
+            conn = wsman.WSManConnection.from_connection_info(connection_info)
+            conn.exec_ps_command("whoami")
+            return
+        except Exception as ex:
+            LOG.debug(
+                f"Could not conect to Windows host: {str(ex)}. "
+                f"Retrying, time left: {timeout - (time.time() - start)}."
+            )
+        time.sleep(poll_interval)
+
+    raise exception.CoriolisException(
+        f"Operation timed out after waiting {timeout}s for Windows host to "
+        f"be accessible via WinRM."
+    )
+
+
+def poll_instance_until_reachable(
+    connection_info: dict,
+    protocol: str = constants.PROTOCOL_SSH,
+    timeout: int = 600,
+    poll_interval: int = 10,
+) -> paramiko.SSHClient:
+    """Poll until a given instance becomes reachable.
+
+    :param connection_info: a dict containing the following keys:
+        * ip
+        * port
+        * username
+        * password
+        * pkey - Paramiko keypair
+    :param protocol: connection protocol, "ssh" or "winrm"
+    :param timeout: the maximum amount of time to wait
+    :param poll_interval: the amount of time to wait between retries
+    """
+    # TODO(lpetrut): consider including the connection protocol in the
+    # connection info. We'd have to modify a few schemas used during os
+    # morphing. We currently pick the protocol based on the OS type but
+    # we may want to use SSH on Windows as well.
+    if protocol == constants.PROTOCOL_SSH:
+        helper = _poll_instance_until_reachable_ssh
+    elif protocol == constants.PROTOCOL_WINRM:
+        helper = _poll_instance_until_reachable_winrm
+    else:
+        raise exception.InvalidInput(
+            f"Unsupported instance connection protocol: {protocol}")
+    return helper(
+        connection_info=connection_info,
+        timeout=timeout,
+        poll_interval=poll_interval)

+ 131 - 0
coriolis/tests/providers/test_provider_utils.py

@@ -2,15 +2,27 @@
 # All Rights Reserved.
 # All Rights Reserved.
 
 
 import logging
 import logging
+from unittest import mock
 
 
+from coriolis import constants
 from coriolis import exception
 from coriolis import exception
 from coriolis.providers import provider_utils
 from coriolis.providers import provider_utils
 from coriolis.tests import test_base
 from coriolis.tests import test_base
+from coriolis import utils
 
 
 
 
 class ProviderUtilsTestCase(test_base.CoriolisBaseTestCase):
 class ProviderUtilsTestCase(test_base.CoriolisBaseTestCase):
     """Test suite for the Coriolis provider utils module."""
     """Test suite for the Coriolis provider utils module."""
 
 
+    def _get_mock_conn_info(self):
+        return {
+            "ip": "1.2.3.4",
+            "port": 2222,
+            "username": "someUser",
+            "password": "pwned",
+            "pkey": mock.Mock(),
+        }
+
     def test_get_storage_mapping_for_disk(self):
     def test_get_storage_mapping_for_disk(self):
         storage_mappings = {
         storage_mappings = {
             'disk_mappings': [{'disk_id': '1', 'destination': 'backend1'}],
             'disk_mappings': [{'disk_id': '1', 'destination': 'backend1'}],
@@ -257,3 +269,122 @@ class ProviderUtilsTestCase(test_base.CoriolisBaseTestCase):
             provider_utils.check_changed_storage_mappings,
             provider_utils.check_changed_storage_mappings,
             volumes_info, old_storage_mappings,
             volumes_info, old_storage_mappings,
             new_storage_mappings)
             new_storage_mappings)
+
+    @mock.patch.object(utils, "connect_ssh")
+    @mock.patch.object(utils, "exec_ssh_cmd")
+    @mock.patch("time.sleep")
+    def test_poll_instance_ssh(
+        self,
+        mock_sleep,
+        mock_exec_ssh,
+        mock_connect,
+    ):
+        mock_ssh_conn = mock.Mock()
+        mock_connect.side_effect = [
+            Exception,
+            mock_ssh_conn,
+            mock_ssh_conn,
+        ]
+        mock_exec_ssh.side_effect = [
+            Exception,
+            mock.sentinel.stdout,
+        ]
+        poll_interval = 5
+
+        connection_info = self._get_mock_conn_info()
+        provider_utils.poll_instance_until_reachable(
+            connection_info=connection_info,
+            protocol=constants.PROTOCOL_SSH,
+            timeout=30,
+            poll_interval=poll_interval,
+        )
+
+        mock_connect.assert_has_calls(
+            [
+                mock.call(
+                    hostname=connection_info["ip"],
+                    port=connection_info["port"],
+                    username=connection_info["username"],
+                    password=connection_info["password"],
+                    pkey=connection_info["pkey"],
+                )
+            ] * 2)
+        mock_exec_ssh.assert_has_calls(
+            [mock.call(mock_ssh_conn, "exit 0")] * 2)
+        mock_ssh_conn.close.assert_has_calls([mock.call()] * 2)
+        mock_sleep.assert_has_calls([mock.call(poll_interval)] * 2)
+
+    @mock.patch.object(utils, "connect_ssh")
+    @mock.patch.object(utils, "exec_ssh_cmd")
+    @mock.patch("time.sleep")
+    @mock.patch("time.time")
+    def test_poll_instance_ssh_timeout(
+        self,
+        mock_time,
+        mock_sleep,
+        mock_exec_ssh,
+        mock_connect,
+    ):
+        poll_interval = 5
+        mock_time.side_effect = [x * 10 for x in range(20)]
+        mock_connect.side_effect = IOError
+        connection_info = self._get_mock_conn_info()
+        self.assertRaises(
+            exception.CoriolisException,
+            provider_utils.poll_instance_until_reachable,
+            connection_info=connection_info,
+            protocol=constants.PROTOCOL_SSH,
+            timeout=30,
+            poll_interval=poll_interval,
+        )
+
+    @mock.patch("coriolis.wsman.WSManConnection", new_callable=mock.Mock)
+    @mock.patch("time.sleep")
+    def test_poll_instance_winrm(
+        self,
+        mock_sleep,
+        mock_wsman,
+    ):
+        mock_conn = mock.Mock()
+        mock_conn.exec_ps_command.side_effect = [
+            Exception, mock.sentinel.stdout]
+        mock_wsman.from_connection_info.return_value = mock_conn
+        poll_interval = 5
+
+        connection_info = self._get_mock_conn_info()
+        provider_utils.poll_instance_until_reachable(
+            connection_info=connection_info,
+            protocol=constants.PROTOCOL_WINRM,
+            timeout=30,
+            poll_interval=poll_interval,
+        )
+
+        mock_wsman.from_connection_info.assert_has_calls(
+            [mock.call(connection_info)] * 2, any_order=True)
+        mock_conn.exec_ps_command.assert_has_calls(
+            [mock.call("whoami")] * 2)
+        mock_sleep.assert_called_once_with(poll_interval)
+
+    @mock.patch("coriolis.wsman.WSManConnection", new_callable=mock.Mock)
+    @mock.patch("time.sleep")
+    @mock.patch("time.time")
+    def test_poll_instance_winrm_timeout(
+        self,
+        mock_time,
+        mock_sleep,
+        mock_wsman,
+    ):
+        poll_interval = 5
+        mock_time.side_effect = [x * 10 for x in range(20)]
+        mock_conn = mock.Mock()
+        mock_conn.exec_ps_command.side_effect = IOError
+        mock_wsman.from_connection_info.return_value = mock_conn
+        connection_info = self._get_mock_conn_info()
+        self.assertRaises(
+            exception.CoriolisException,
+            provider_utils.poll_instance_until_reachable,
+            connection_info=connection_info,
+            protocol=constants.PROTOCOL_WINRM,
+            timeout=30,
+            poll_interval=poll_interval,
+        )

+ 0 - 128
coriolis/tests/test_utils.py

@@ -1274,134 +1274,6 @@ class UtilsTestCase(test_base.CoriolisBaseTestCase):
         self.assertRaises(exception.UnrecognizedWorkerInitSystem,
         self.assertRaises(exception.UnrecognizedWorkerInitSystem,
                           stop_svc_undecorated, self.mock_ssh, 'svc_name')
                           stop_svc_undecorated, self.mock_ssh, 'svc_name')
 
 
-    def _get_mock_conn_info(self):
-        return {
-            "ip": "1.2.3.4",
-            "port": 2222,
-            "username": "someUser",
-            "password": "pwned",
-            "pkey": mock.Mock(),
-        }
-
-    @mock.patch.object(utils, "connect_ssh")
-    @mock.patch.object(utils, "exec_ssh_cmd")
-    @mock.patch("time.sleep")
-    def test_poll_instance_ssh(
-        self,
-        mock_sleep,
-        mock_exec_ssh,
-        mock_connect,
-    ):
-        mock_ssh_conn = mock.Mock()
-        mock_connect.side_effect = [
-            Exception,
-            mock_ssh_conn,
-            mock_ssh_conn,
-        ]
-        mock_exec_ssh.side_effect = [
-            Exception,
-            mock.sentinel.stdout,
-        ]
-        poll_interval = 5
-
-        connection_info = self._get_mock_conn_info()
-        utils.poll_instance_until_reachable(
-            connection_info=connection_info,
-            protocol=constants.PROTOCOL_SSH,
-            timeout=30,
-            poll_interval=poll_interval,
-        )
-
-        mock_connect.assert_has_calls(
-            [
-                mock.call(
-                    hostname=connection_info["ip"],
-                    port=connection_info["port"],
-                    username=connection_info["username"],
-                    password=connection_info["password"],
-                    pkey=connection_info["pkey"],
-                )
-            ] * 2)
-        mock_exec_ssh.assert_has_calls(
-            [mock.call(mock_ssh_conn, "exit 0")] * 2)
-        mock_ssh_conn.close.assert_has_calls([mock.call()] * 2)
-        mock_sleep.assert_has_calls([mock.call(poll_interval)] * 2)
-
-    @mock.patch.object(utils, "connect_ssh")
-    @mock.patch.object(utils, "exec_ssh_cmd")
-    @mock.patch("time.sleep")
-    @mock.patch("time.time")
-    def test_poll_instance_ssh_timeout(
-        self,
-        mock_time,
-        mock_sleep,
-        mock_exec_ssh,
-        mock_connect,
-    ):
-        poll_interval = 5
-        mock_time.side_effect = [x * 10 for x in range(20)]
-        mock_connect.side_effect = IOError
-        connection_info = self._get_mock_conn_info()
-        self.assertRaises(
-            exception.CoriolisException,
-            utils.poll_instance_until_reachable,
-            connection_info=connection_info,
-            protocol=constants.PROTOCOL_SSH,
-            timeout=30,
-            poll_interval=poll_interval,
-        )
-
-    @mock.patch("coriolis.wsman.WSManConnection", new_callable=mock.Mock)
-    @mock.patch("time.sleep")
-    def test_poll_instance_winrm(
-        self,
-        mock_sleep,
-        mock_wsman,
-    ):
-        mock_conn = mock.Mock()
-        mock_conn.exec_ps_command.side_effect = [
-            Exception, mock.sentinel.stdout]
-        mock_wsman.from_connection_info.return_value = mock_conn
-        poll_interval = 5
-
-        connection_info = self._get_mock_conn_info()
-        utils.poll_instance_until_reachable(
-            connection_info=connection_info,
-            protocol=constants.PROTOCOL_WINRM,
-            timeout=30,
-            poll_interval=poll_interval,
-        )
-
-        mock_wsman.from_connection_info.assert_has_calls(
-            [mock.call(connection_info)] * 2, any_order=True)
-        mock_conn.exec_ps_command.assert_has_calls(
-            [mock.call("whoami")] * 2)
-        mock_sleep.assert_called_once_with(poll_interval)
-
-    @mock.patch("coriolis.wsman.WSManConnection", new_callable=mock.Mock)
-    @mock.patch("time.sleep")
-    @mock.patch("time.time")
-    def test_poll_instance_winrm_timeout(
-        self,
-        mock_time,
-        mock_sleep,
-        mock_wsman,
-    ):
-        poll_interval = 5
-        mock_time.side_effect = [x * 10 for x in range(20)]
-        mock_conn = mock.Mock()
-        mock_conn.exec_ps_command.side_effect = IOError
-        mock_wsman.from_connection_info.return_value = mock_conn
-        connection_info = self._get_mock_conn_info()
-        self.assertRaises(
-            exception.CoriolisException,
-            utils.poll_instance_until_reachable,
-            connection_info=connection_info,
-            protocol=constants.PROTOCOL_WINRM,
-            timeout=30,
-            poll_interval=poll_interval,
-        )
-
 
 
 @ddt.ddt
 @ddt.ddt
 class Grub2ConfigEditorTestCase(test_base.CoriolisBaseTestCase):
 class Grub2ConfigEditorTestCase(test_base.CoriolisBaseTestCase):

+ 0 - 97
coriolis/utils.py

@@ -1120,100 +1120,3 @@ def start_thread(target, args=(), kwargs=None, daemon=True):
     )
     )
     thread.start()
     thread.start()
     return thread
     return thread
-
-
-def _poll_instance_until_reachable_ssh(
-    connection_info: dict,
-    timeout: int = 600,
-    poll_interval: int = 10,
-):
-    start = time.time()
-    while (time.time() - start) < timeout:
-        try:
-            ssh = connect_ssh(
-                hostname=connection_info["ip"],
-                port=connection_info["port"],
-                username=connection_info["username"],
-                password=connection_info["password"],
-                pkey=connection_info["pkey"],
-            )
-            try:
-                # "exit 0" should work across platforms.
-                # "whoami" would also work.
-                exec_ssh_cmd(ssh, "exit 0")
-            finally:
-                ssh.close()
-            LOG.debug("Instance reachable: %s", connection_info["ip"])
-            return
-        except Exception as err:
-            LOG.debug(
-                f"Could not connect to remote instance: {str(err)}. "
-                f"Retrying, time left: {timeout - (time.time() - start)}."
-            )
-        time.sleep(poll_interval)
-
-    raise exception.CoriolisException(
-        f"Operation timed out after waiting {timeout}s for the instance to be "
-        f"accessible via SSH."
-    )
-
-
-def _poll_instance_until_reachable_winrm(
-    connection_info: dict,
-    timeout: int = 600,
-    poll_interval: int = 10,
-):
-    start = time.time()
-    while (time.time() - start) < timeout:
-        try:
-            # Avoid a circular dependency.
-            from coriolis import wsman
-            conn = wsman.WSManConnection.from_connection_info(connection_info)
-            conn.exec_ps_command("whoami")
-            return
-        except Exception as ex:
-            LOG.debug(
-                f"Could not conect to Windows host: {str(ex)}. "
-                f"Retrying, time left: {timeout - (time.time() - start)}."
-            )
-        time.sleep(poll_interval)
-
-    raise exception.CoriolisException(
-        f"Operation timed out after waiting {timeout}s for Windows host to "
-        f"be accessible via WinRM."
-    )
-
-
-def poll_instance_until_reachable(
-    connection_info: dict,
-    protocol: str = constants.PROTOCOL_SSH,
-    timeout: int = 600,
-    poll_interval: int = 10,
-) -> paramiko.SSHClient:
-    """Poll until a given instance becomes reachable.
-
-    :param connection_info: a dict containing the following keys:
-        * ip
-        * port
-        * username
-        * password
-        * pkey - Paramiko keypair
-    :param protocol: connection protocol, "ssh" or "winrm"
-    :param timeout: the maximum amount of time to wait
-    :param poll_interval: the amount of time to wait between retries
-    """
-    # TODO(lpetrut): consider including the connection protocol in the
-    # connection info. We'd have to modify a few schemas used during os
-    # morphing. We currently pick the protocol based on the OS type but
-    # we may want to use SSH on Windows as well.
-    if protocol == constants.PROTOCOL_SSH:
-        helper = _poll_instance_until_reachable_ssh
-    elif protocol == constants.PROTOCOL_WINRM:
-        helper = _poll_instance_until_reachable_winrm
-    else:
-        raise exception.InvalidInput(
-            f"Unsupported instance connection protocol: {protocol}")
-    return helper(
-        connection_info=connection_info,
-        timeout=timeout,
-        poll_interval=poll_interval)