Example #1
0
    def test_key_material_argument(self):
        path = os.path.join(get_resources_base_path(), 'ssh', 'dummy_rsa')

        with open(path, 'r') as fp:
            private_key = fp.read()

        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'key_material': private_key
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        pkey = paramiko.RSAKey.from_private_key(StringIO(private_key))
        expected_conn = {
            'username': '******',
            'allow_agent': False,
            'hostname': 'dummy.host.org',
            'look_for_keys': False,
            'pkey': pkey,
            'timeout': 60,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #2
0
    def test_create_with_key_via_bastion(self):
        conn_params = {'hostname': 'dummy.host.org',
                       'bastion_host': 'bastion.host.org',
                       'username': '******',
                       'key_files': 'id_rsa'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_bastion_conn = {'username': '******',
                                 'allow_agent': False,
                                 'hostname': 'bastion.host.org',
                                 'look_for_keys': False,
                                 'key_filename': 'id_rsa',
                                 'timeout': 60,
                                 'port': 22}
        mock.bastion_client.connect.assert_called_once_with(**expected_bastion_conn)

        expected_conn = {'username': '******',
                         'allow_agent': False,
                         'hostname': 'dummy.host.org',
                         'look_for_keys': False,
                         'key_filename': 'id_rsa',
                         'timeout': 60,
                         'port': 22,
                         'sock': mock.bastion_socket}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #3
0
    def _connect(self, host, results, raise_on_any_error=False):
        (hostname, port) = self._get_host_port_info(host)

        extra = {'host': host, 'port': port, 'user': self._ssh_user}
        if self._ssh_password:
            extra['password'] = '******'
        elif self._ssh_key_file:
            extra['key_file_path'] = self._ssh_key_file
        else:
            extra['private_key'] = '<redacted>'

        LOG.debug('Connecting to host.', extra=extra)

        client = ParamikoSSHClient(hostname, username=self._ssh_user,
                                   password=self._ssh_password,
                                   bastion_host=self._bastion_host,
                                   key_files=self._ssh_key_file,
                                   key_material=self._ssh_key_material,
                                   passphrase=self._passphrase,
                                   port=port)
        try:
            client.connect()
        except Exception as ex:
            error = 'Failed connecting to host %s.' % hostname
            LOG.exception(error)
            if raise_on_any_error:
                raise
            error_dict = self._generate_error_result(exc=ex, message=error)
            self._bad_hosts[hostname] = error_dict
            results[hostname] = error_dict
        else:
            self._successful_connects += 1
            self._hosts_client[hostname] = client
            results[hostname] = {'message': 'Connected to host.'}
Example #4
0
    def run(self, hostname, port, username, password=None, keyfile=None, ssh_timeout=5, sleep_delay=20, retries=10):
        # Note: If neither password nor key file is provided, we try to use system user
        # key file
        if not password and not keyfile:
            keyfile = cfg.CONF.system_user.ssh_key_file
            self.logger.info(
                'Neither "password" nor "keyfile" parameter provided, ' 'defaulting to using "%s" key file' % (keyfile)
            )

        client = ParamikoSSHClient(
            hostname=hostname, port=port, username=username, password=password, key_files=keyfile, timeout=ssh_timeout
        )

        for index in range(retries):
            attempt = index + 1

            try:
                self.logger.debug("SSH connection attempt: %s" % (attempt))
                client.connect()
                return True
            except Exception as e:
                self.logger.info("Attempt %s failed (%s), sleeping for %s seconds..." % (attempt, str(e), sleep_delay))
                time.sleep(sleep_delay)

        raise Exception("Exceeded max retries (%s)" % (retries))
Example #5
0
    def test_create_with_key_via_bastion(self):
        conn_params = {
            'hostname': 'dummy.host.org',
            'bastion_host': 'bastion.host.org',
            'username': '******',
            'key_files': 'id_rsa'
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_bastion_conn = {
            'username': '******',
            'allow_agent': False,
            'hostname': 'bastion.host.org',
            'look_for_keys': False,
            'key_filename': 'id_rsa',
            'timeout': 60,
            'port': 22
        }
        mock.bastion_client.connect.assert_called_once_with(
            **expected_bastion_conn)

        expected_conn = {
            'username': '******',
            'allow_agent': False,
            'hostname': 'dummy.host.org',
            'look_for_keys': False,
            'key_filename': 'id_rsa',
            'timeout': 60,
            'port': 22,
            'sock': mock.bastion_socket
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #6
0
    def test_socket_closed(self):
        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'password': '******',
            'timeout': '600'
        }
        ssh_client = ParamikoSSHClient(**conn_params)

        # Make sure .close() doesn't actually call anything real
        ssh_client.client = Mock()
        ssh_client.sftp_client = Mock()
        ssh_client.bastion_client = Mock()

        ssh_client.socket = Mock()
        ssh_client.bastion_socket = Mock()

        # Make sure we havent called any close methods at this point
        # TODO: Replace these with .assert_not_called() once it's Python 3.6+ only
        self.assertEqual(ssh_client.socket.close.call_count, 0)
        self.assertEqual(ssh_client.client.close.call_count, 0)
        self.assertEqual(ssh_client.sftp_client.close.call_count, 0)
        self.assertEqual(ssh_client.bastion_socket.close.call_count, 0)
        self.assertEqual(ssh_client.bastion_client.close.call_count, 0)

        # Call the function that has changed
        ssh_client.close()

        # TODO: Replace these with .assert_called_once() once it's Python 3.6+ only
        self.assertEqual(ssh_client.socket.close.call_count, 1)
        self.assertEqual(ssh_client.client.close.call_count, 1)
        self.assertEqual(ssh_client.sftp_client.close.call_count, 1)
        self.assertEqual(ssh_client.bastion_socket.close.call_count, 1)
        self.assertEqual(ssh_client.bastion_client.close.call_count, 1)
Example #7
0
    def test_key_with_passphrase_success(self):
        path = os.path.join(get_resources_base_path(), "ssh",
                            "dummy_rsa_passphrase")

        with open(path, "r") as fp:
            private_key = fp.read()

        # Key material provided
        conn_params = {
            "hostname": "dummy.host.org",
            "username": "******",
            "key_material": private_key,
            "passphrase": "testphrase",
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        pkey = paramiko.RSAKey.from_private_key(StringIO(private_key),
                                                "testphrase")
        expected_conn = {
            "username": "******",
            "allow_agent": False,
            "hostname": "dummy.host.org",
            "look_for_keys": False,
            "pkey": pkey,
            "timeout": 30,
            "port": 22,
        }
        mock.client.connect.assert_called_once_with(**expected_conn)

        # Path to private key file provided
        conn_params = {
            "hostname": "dummy.host.org",
            "username": "******",
            "key_files": path,
            "passphrase": "testphrase",
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {
            "username": "******",
            "allow_agent": False,
            "hostname": "dummy.host.org",
            "look_for_keys": False,
            "key_filename": path,
            "password": "******",
            "timeout": 30,
            "port": 22,
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #8
0
    def test_key_with_passphrase_success(self):
        path = os.path.join(get_resources_base_path(), 'ssh',
                            'dummy_rsa_passphrase')

        with open(path, 'r') as fp:
            private_key = fp.read()

        # Key material provided
        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'key_material': private_key,
            'passphrase': 'testphrase'
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        pkey = paramiko.RSAKey.from_private_key(StringIO(private_key),
                                                'testphrase')
        expected_conn = {
            'username': '******',
            'allow_agent': False,
            'hostname': 'dummy.host.org',
            'look_for_keys': False,
            'pkey': pkey,
            'timeout': 30,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)

        # Path to private key file provided
        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'key_files': path,
            'passphrase': 'testphrase'
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {
            'username': '******',
            'allow_agent': False,
            'hostname': 'dummy.host.org',
            'look_for_keys': False,
            'key_filename': path,
            'password': '******',
            'timeout': 30,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #9
0
    def test_create_with_password(self):
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {'username': '******',
                         'password': '******',
                         'allow_agent': False,
                         'hostname': 'dummy.host.org',
                         'look_for_keys': False,
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #10
0
    def test_deprecated_key_argument(self):
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'key_files': 'id_rsa'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {'username': '******',
                         'allow_agent': False,
                         'hostname': 'dummy.host.org',
                         'look_for_keys': False,
                         'key_filename': 'id_rsa',
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #11
0
    def _connect(self, host, results, raise_on_any_error=False):
        (hostname, port) = self._get_host_port_info(host)

        extra = {"host": host, "port": port, "user": self._ssh_user}
        if self._ssh_password:
            extra["password"] = "******"
        elif self._ssh_key_file:
            extra["key_file_path"] = self._ssh_key_file
        else:
            extra["private_key"] = "<redacted>"

        LOG.debug("Connecting to host.", extra=extra)

        client = ParamikoSSHClient(
            hostname=hostname,
            port=port,
            username=self._ssh_user,
            password=self._ssh_password,
            bastion_host=self._bastion_host,
            key_files=self._ssh_key_file,
            key_material=self._ssh_key_material,
            passphrase=self._passphrase,
            handle_stdout_line_func=self._handle_stdout_line_func,
            handle_stderr_line_func=self._handle_stderr_line_func,
        )
        try:
            client.connect()
        except SSHException as ex:
            LOG.exception(ex)
            if raise_on_any_error:
                raise
            error_dict = self._generate_error_result(
                exc=ex, message="Connection error."
            )
            self._bad_hosts[hostname] = error_dict
            results[hostname] = error_dict
        except Exception as ex:
            error = "Failed connecting to host %s." % hostname
            LOG.exception(error)
            if raise_on_any_error:
                raise
            error_dict = self._generate_error_result(exc=ex, message=error)
            self._bad_hosts[hostname] = error_dict
            results[hostname] = error_dict
        else:
            self._successful_connects += 1
            self._hosts_client[hostname] = client
            results[hostname] = {"message": "Connected to host."}
Example #12
0
    def test_create_with_key_via_bastion(self):
        conn_params = {
            "hostname": "dummy.host.org",
            "bastion_host": "bastion.host.org",
            "username": "******",
            "key_files": "id_rsa",
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_bastion_conn = {
            "username": "******",
            "allow_agent": False,
            "hostname": "bastion.host.org",
            "look_for_keys": False,
            "key_filename": "id_rsa",
            "timeout": 30,
            "port": 22,
        }
        mock.bastion_client.connect.assert_called_once_with(
            **expected_bastion_conn)

        expected_conn = {
            "username": "******",
            "allow_agent": False,
            "hostname": "dummy.host.org",
            "look_for_keys": False,
            "key_filename": "id_rsa",
            "timeout": 30,
            "port": 22,
            "sock": mock.bastion_socket,
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #13
0
    def test_set_proxycommand(self, mock_ProxyCommand):
        """
        Loads proxy commands from ssh config file
        """
        ssh_config_file_path = os.path.join(get_resources_base_path(),
                                            'ssh', 'dummy_ssh_config')
        cfg.CONF.set_override(name='ssh_config_file_path',
                              override=ssh_config_file_path,
                              group='ssh_runner')
        cfg.CONF.set_override(name='use_ssh_config', override=True,
                              group='ssh_runner')

        conn_params = {'hostname': 'dummy.host.org', 'username': '******', 'password': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()
        mock_ProxyCommand.assert_called_once_with('ssh -q -W dummy.host.org:22 dummy_bastion')
Example #14
0
    def test_key_material_argument(self):
        path = os.path.join(get_resources_base_path(), "ssh", "dummy_rsa")

        with open(path, "r") as fp:
            private_key = fp.read()

        conn_params = {
            "hostname": "dummy.host.org",
            "username": "******",
            "key_material": private_key,
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        pkey = paramiko.RSAKey.from_private_key(StringIO(private_key))
        expected_conn = {
            "username": "******",
            "allow_agent": False,
            "hostname": "dummy.host.org",
            "look_for_keys": False,
            "pkey": pkey,
            "timeout": 30,
            "port": 22,
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
 def test_passphrase_not_provided_for_encrypted_key_file(self):
     path = os.path.join(get_resources_base_path(),
                         'ssh', 'dummy_rsa_passphrase')
     conn_params = {'hostname': 'dummy.host.org',
                    'username': '******',
                    'key_files': path}
     mock = ParamikoSSHClient(**conn_params)
     self.assertRaises(paramiko.ssh_exception.PasswordRequiredException, mock.connect)
    def test_passphrase_no_key_provided(self):
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'passphrase': 'testphrase'}

        expected_msg = 'passphrase should accompany private key material'
        client = ParamikoSSHClient(**conn_params)
        self.assertRaisesRegexp(ValueError, expected_msg, client.connect)
Example #17
0
    def test_create_without_credentials_use_default_key(self):
        # No credentials are provided by default stanley ssh key exists so it should use that
        cfg.CONF.set_override(name='ssh_key_file', override='stanley_rsa', group='system_user')

        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {'username': '******',
                         'hostname': 'dummy.host.org',
                         'key_filename': 'stanley_rsa',
                         'allow_agent': False,
                         'look_for_keys': False,
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #18
0
    def test_consume_stderr(self):
        # Test utf-8 decoding of ``stderr`` still works fine when reading CHUNK_SIZE splits a
        # multi-byte utf-8 character in the middle. We should wait to collect all bytes
        # and finally decode.
        conn_params = {'hostname': 'dummy.host.org', 'username': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.CHUNK_SIZE = 1
        chan = Mock()
        chan.recv_stderr_ready.side_effect = [True, True, True, True, False]

        chan.recv_stderr.side_effect = [b'\xF0', b'\x90', b'\x8D', b'\x88']
        try:
            b'\xF0'.decode('utf-8')
            self.fail('Test fixture is not right.')
        except UnicodeDecodeError:
            pass
        stderr = mock._consume_stderr(chan)
        self.assertEqual(u'\U00010348', stderr.getvalue())
Example #19
0
    def test_consume_stderr(self):
        # Test utf-8 decoding of ``stderr`` still works fine when reading CHUNK_SIZE splits a
        # multi-byte utf-8 character in the middle. We should wait to collect all bytes
        # and finally decode.
        conn_params = {"hostname": "dummy.host.org", "username": "******"}
        mock = ParamikoSSHClient(**conn_params)
        mock.CHUNK_SIZE = 1
        chan = Mock()
        chan.recv_stderr_ready.side_effect = [True, True, True, True, False]

        chan.recv_stderr.side_effect = [b"\xF0", b"\x90", b"\x8D", b"\x88"]
        try:
            b"\xF0".decode("utf-8")
            self.fail("Test fixture is not right.")
        except UnicodeDecodeError:
            pass
        stderr = mock._consume_stderr(chan)
        self.assertEqual("\U00010348", stderr.getvalue())
Example #20
0
    def test_consume_stderr(self):
        # Test utf-8 decoding of ``stderr`` still works fine when reading CHUNK_SIZE splits a
        # multi-byte utf-8 character in the middle. We should wait to collect all bytes
        # and finally decode.
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.CHUNK_SIZE = 1
        chan = Mock()
        chan.recv_stderr_ready.side_effect = [True, True, True, True, False]

        chan.recv_stderr.side_effect = ['\xF0', '\x90', '\x8D', '\x88']
        try:
            '\xF0'.decode('utf-8')
            self.fail('Test fixture is not right.')
        except UnicodeDecodeError:
            pass
        stderr = mock._consume_stderr(chan)
        self.assertEqual(u'\U00010348', stderr.getvalue())
    def test_key_material_argument_invalid_key(self):
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'key_material': 'id_rsa'}

        mock = ParamikoSSHClient(**conn_params)

        expected_msg = 'Invalid or unsupported key type'
        self.assertRaisesRegexp(paramiko.ssh_exception.SSHException,
                                expected_msg, mock.connect)
Example #22
0
    def test_create_with_password(self):
        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'password': '******'
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {
            'username': '******',
            'password': '******',
            'allow_agent': False,
            'hostname': 'dummy.host.org',
            'look_for_keys': False,
            'timeout': 60,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #23
0
    def test_deprecated_key_argument(self):
        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'key_files': 'id_rsa'
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {
            'username': '******',
            'allow_agent': False,
            'hostname': 'dummy.host.org',
            'look_for_keys': False,
            'key_filename': 'id_rsa',
            'timeout': 60,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #24
0
    def test_passphrase_no_key_provided(self):
        conn_params = {
            "hostname": "dummy.host.org",
            "username": "******",
            "passphrase": "testphrase",
        }

        expected_msg = "passphrase should accompany private key material"
        client = ParamikoSSHClient(**conn_params)
        self.assertRaisesRegexp(ValueError, expected_msg, client.connect)
Example #25
0
    def test_create_without_credentials(self):
        """
        Initialize object with no credentials.

        Just to have better coverage, initialize the object
        without 'password' neither 'key'.
        """
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {'username': '******',
                         'hostname': 'dummy.host.org',
                         'allow_agent': True,
                         'look_for_keys': True,
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #26
0
    def run(
        self,
        hostname,
        port,
        username,
        password=None,
        keyfile=None,
        ssh_timeout=5,
        sleep_delay=20,
        retries=10,
    ):
        # Note: If neither password nor key file is provided, we try to use system user
        # key file
        if not password and not keyfile:
            keyfile = cfg.CONF.system_user.ssh_key_file
            self.logger.info(
                'Neither "password" nor "keyfile" parameter provided, '
                'defaulting to using "%s" key file' % (keyfile))

        client = ParamikoSSHClient(
            hostname=hostname,
            port=port,
            username=username,
            password=password,
            key_files=keyfile,
            timeout=ssh_timeout,
        )

        for index in range(retries):
            attempt = index + 1

            try:
                self.logger.debug("SSH connection attempt: %s" % (attempt))
                client.connect()
                return True
            except Exception as e:
                self.logger.info(
                    "Attempt %s failed (%s), sleeping for %s seconds..." %
                    (attempt, six.text_type(e), sleep_delay))
                time.sleep(sleep_delay)

        raise Exception("Exceeded max retries (%s)" % (retries))
Example #27
0
 def test_passphrase_not_provided_for_encrypted_key_file(self):
     path = os.path.join(get_resources_base_path(), "ssh",
                         "dummy_rsa_passphrase")
     conn_params = {
         "hostname": "dummy.host.org",
         "username": "******",
         "key_files": path,
     }
     mock = ParamikoSSHClient(**conn_params)
     self.assertRaises(paramiko.ssh_exception.PasswordRequiredException,
                       mock.connect)
Example #28
0
    def test_create_without_credentials_use_default_key(self):
        # No credentials are provided by default stanley ssh key exists so it should use that
        cfg.CONF.set_override(name='ssh_key_file',
                              override='stanley_rsa',
                              group='system_user')

        conn_params = {'hostname': 'dummy.host.org', 'username': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {
            'username': '******',
            'hostname': 'dummy.host.org',
            'key_filename': 'stanley_rsa',
            'allow_agent': False,
            'look_for_keys': False,
            'timeout': 60,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #29
0
    def test_create_without_credentials(self):
        """
        Initialize object with no credentials.

        Just to have better coverage, initialize the object
        without 'password' neither 'key'.
        """
        conn_params = {'hostname': 'dummy.host.org', 'username': '******'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {
            'username': '******',
            'hostname': 'dummy.host.org',
            'allow_agent': True,
            'look_for_keys': True,
            'timeout': 60,
            'port': 22
        }
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #30
0
    def test_key_material_argument_invalid_key(self):
        conn_params = {
            "hostname": "dummy.host.org",
            "username": "******",
            "key_material": "id_rsa",
        }

        mock = ParamikoSSHClient(**conn_params)

        expected_msg = "Invalid or unsupported key type"
        self.assertRaisesRegexp(paramiko.ssh_exception.SSHException,
                                expected_msg, mock.connect)
    def test_key_files_and_key_material_arguments_are_mutual_exclusive(self):
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'key_files': 'id_rsa',
                       'key_material': 'key'}

        expected_msg = ('key_files and key_material arguments are mutually exclusive. '
                        'Supply only one.')

        client = ParamikoSSHClient(**conn_params)

        self.assertRaisesRegexp(ValueError, expected_msg,
                                client.connect)
Example #32
0
    def test_set_proxycommand(self, mock_ProxyCommand):
        """
        Loads proxy commands from ssh config file
        """
        ssh_config_file_path = os.path.join(get_resources_base_path(), 'ssh',
                                            'dummy_ssh_config')
        cfg.CONF.set_override(name='ssh_config_file_path',
                              override=ssh_config_file_path,
                              group='ssh_runner')
        cfg.CONF.set_override(name='use_ssh_config',
                              override=True,
                              group='ssh_runner')

        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'password': '******'
        }
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()
        mock_ProxyCommand.assert_called_once_with(
            'ssh -q -W dummy.host.org:22 dummy_bastion')
    def setUp(self):
        """
        Creates the object patching the actual connection.
        """
        cfg.CONF.set_override(name='ssh_key_file', override=None, group='system_user')
        cfg.CONF.set_override(name='use_ssh_config', override=False, group='ssh_runner')

        conn_params = {'hostname': 'dummy.host.org',
                       'port': 8822,
                       'username': '******',
                       'key_files': '~/.ssh/ubuntu_ssh',
                       'timeout': '600'}
        self.ssh_cli = ParamikoSSHClient(**conn_params)
Example #34
0
    def test_key_material_argument(self):
        path = os.path.join(get_resources_base_path(),
                            'ssh', 'dummy_rsa')

        with open(path, 'r') as fp:
            private_key = fp.read()

        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'key_material': private_key}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        pkey = paramiko.RSAKey.from_private_key(StringIO(private_key))
        expected_conn = {'username': '******',
                         'allow_agent': False,
                         'hostname': 'dummy.host.org',
                         'look_for_keys': False,
                         'pkey': pkey,
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #35
0
    def test_socket_not_closed_if_none(self):
        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'password': '******',
            'timeout': '600'
        }
        ssh_client = ParamikoSSHClient(**conn_params)

        # Make sure .close() doesn't actually call anything real
        ssh_client.client = None
        ssh_client.sftp_client = None
        ssh_client.bastion_client = None

        ssh_client.socket = None
        ssh_client.bastion_socket = None

        # Call the function, this should not throw an exception
        ssh_client.close()
Example #36
0
    def test_socket_not_closed_if_none(self):
        conn_params = {
            "hostname": "dummy.host.org",
            "username": "******",
            "password": "******",
            "timeout": "600",
        }
        ssh_client = ParamikoSSHClient(**conn_params)

        # Make sure .close() doesn't actually call anything real
        ssh_client.client = None
        ssh_client.sftp_client = None
        ssh_client.bastion_client = None

        ssh_client.socket = None
        ssh_client.bastion_socket = None

        # Call the function, this should not throw an exception
        ssh_client.close()
Example #37
0
    def test_create_without_credentials(self):
        """
        Initialize object with no credentials.

        Just to have better coverage, initialize the object
        without 'password' neither 'key'. Now that we only reconcile
        the final parameters at the last moment when we explicitly
        try to connect, all the credentials should be set to None.
        """
        conn_params = {'hostname': 'dummy.host.org', 'username': '******'}
        mock = ParamikoSSHClient(**conn_params)

        self.assertEqual(mock.password, None)
        self.assertEqual(mock.key_material, None)
        self.assertEqual(mock.key_files, None)
Example #38
0
    def test_key_material_contains_path_not_contents(self):
        conn_params = {'hostname': 'dummy.host.org', 'username': '******'}
        key_materials = ['~/.ssh/id_rsa', '/tmp/id_rsa', 'C:\\id_rsa']

        expected_msg = (
            '"private_key" parameter needs to contain private key data / content and '
            'not a path')

        for key_material in key_materials:
            conn_params = conn_params.copy()
            conn_params['key_material'] = key_material
            mock = ParamikoSSHClient(**conn_params)

            self.assertRaisesRegexp(paramiko.ssh_exception.SSHException,
                                    expected_msg, mock.connect)
Example #39
0
    def test_key_material_contains_path_not_contents(self):
        conn_params = {"hostname": "dummy.host.org", "username": "******"}
        key_materials = ["~/.ssh/id_rsa", "/tmp/id_rsa", "C:\\id_rsa"]

        expected_msg = (
            '"private_key" parameter needs to contain private key data / content and '
            "not a path")

        for key_material in key_materials:
            conn_params = conn_params.copy()
            conn_params["key_material"] = key_material
            mock = ParamikoSSHClient(**conn_params)

            self.assertRaisesRegexp(paramiko.ssh_exception.SSHException,
                                    expected_msg, mock.connect)
Example #40
0
    def test_key_with_passphrase_success(self):
        path = os.path.join(get_resources_base_path(),
                            'ssh', 'dummy_rsa_passphrase')

        with open(path, 'r') as fp:
            private_key = fp.read()

        # Key material provided
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'key_material': private_key,
                       'passphrase': 'testphrase'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        pkey = paramiko.RSAKey.from_private_key(StringIO(private_key), 'testphrase')
        expected_conn = {'username': '******',
                         'allow_agent': False,
                         'hostname': 'dummy.host.org',
                         'look_for_keys': False,
                         'pkey': pkey,
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)

        # Path to private key file provided
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'key_files': path,
                       'passphrase': 'testphrase'}
        mock = ParamikoSSHClient(**conn_params)
        mock.connect()

        expected_conn = {'username': '******',
                         'allow_agent': False,
                         'hostname': 'dummy.host.org',
                         'look_for_keys': False,
                         'key_filename': path,
                         'password': '******',
                         'timeout': 60,
                         'port': 22}
        mock.client.connect.assert_called_once_with(**expected_conn)
Example #41
0
    def test_use_ssh_config_port_value_provided_in_the_config(self, mock_sshclient):
        cfg.CONF.set_override(name='use_ssh_config', override=True, group='ssh_runner')

        ssh_config_file_path = os.path.join(get_resources_base_path(), 'ssh', 'empty_config')
        cfg.CONF.set_override(name='ssh_config_file_path', override=ssh_config_file_path,
                              group='ssh_runner')

        # 1. Default port is used (not explicitly provided)
        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******',
                       'timeout': '600'}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], 22)

        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******',
                       'port': None,
                       'timeout': '600'}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], 22)

        # 2. Default port is used (explicitly provided)
        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******',
                       'port': DEFAULT_SSH_PORT,
                       'timeout': '600'}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], DEFAULT_SSH_PORT)
        self.assertEqual(call_kwargs['port'], 22)

        # 3. Custom port is used (explicitly provided)
        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******',
                       'port': 5555,
                       'timeout': '600'}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], 5555)

        # 4. Custom port is specified in the ssh config (it has precedence over default port)
        ssh_config_file_path = os.path.join(get_resources_base_path(), 'ssh',
                                            'ssh_config_custom_port')
        cfg.CONF.set_override(name='ssh_config_file_path', override=ssh_config_file_path,
                              group='ssh_runner')

        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******'}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], 6677)

        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******',
                       'port': DEFAULT_SSH_PORT}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], 6677)

        # 5. Custom port is specified in ssh config, but one is also provided via runner parameter
        # (runner parameter one has precedence)
        ssh_config_file_path = os.path.join(get_resources_base_path(), 'ssh',
                                            'ssh_config_custom_port')
        cfg.CONF.set_override(name='ssh_config_file_path', override=ssh_config_file_path,
                              group='ssh_runner')

        mock_client = mock.Mock()
        mock_sshclient.return_value = mock_client
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******',
                       'password': '******',
                       'port': 9999}
        ssh_client = ParamikoSSHClient(**conn_params)
        ssh_client.connect()

        call_kwargs = mock_client.connect.call_args[1]
        self.assertEqual(call_kwargs['port'], 9999)
Example #42
0
    def test_sftp_connection_is_only_established_if_required(self):
        # Verify that SFTP connection is lazily established only if and when needed.
        conn_params = {'hostname': 'dummy.host.org',
                       'username': '******'}

        # Verify sftp connection and client hasn't been established yet
        client = ParamikoSSHClient(**conn_params)
        client.connect()

        self.assertTrue(client.sftp_client is None)

        # run method doesn't require sftp access so it shouldn't establish connection
        client = ParamikoSSHClient(**conn_params)
        client.connect()
        client.run(cmd='whoami')

        self.assertTrue(client.sftp_client is None)

        # Methods bellow require SFTP access so they should cause SFTP connection to be established
        # put
        client = ParamikoSSHClient(**conn_params)
        client.connect()
        path = '/root/random_script.sh'
        client.put(path, path, mirror_local_mode=False)

        self.assertTrue(client.sftp_client is not None)

        # exists
        client = ParamikoSSHClient(**conn_params)
        client.connect()
        client.exists('/root/somepath.txt')

        self.assertTrue(client.sftp_client is not None)

        # mkdir
        client = ParamikoSSHClient(**conn_params)
        client.connect()
        client.mkdir('/root/somedirfoo')

        self.assertTrue(client.sftp_client is not None)

        # Verify close doesn't throw if SFTP connection is not established
        client = ParamikoSSHClient(**conn_params)
        client.connect()

        self.assertTrue(client.sftp_client is None)
        client.close()

        # Verify SFTP connection is closed if it's opened
        client = ParamikoSSHClient(**conn_params)
        client.connect()
        client.mkdir('/root/somedirfoo')

        self.assertTrue(client.sftp_client is not None)
        client.close()

        self.assertEqual(client.sftp_client.close.call_count, 1)
Example #43
0
    def test_handle_stdout_and_stderr_line_funcs(self):
        mock_handle_stdout_line_func = mock.Mock()
        mock_handle_stderr_line_func = mock.Mock()

        conn_params = {
            'hostname': 'dummy.host.org',
            'username': '******',
            'password': '******',
            'handle_stdout_line_func': mock_handle_stdout_line_func,
            'handle_stderr_line_func': mock_handle_stderr_line_func
        }
        client = ParamikoSSHClient(**conn_params)
        client.connect()

        mock_get_transport = mock.Mock()
        mock_chan = mock.Mock()

        client.client.get_transport = mock.Mock()
        client.client.get_transport.return_value = mock_get_transport
        mock_get_transport.open_session.return_value = mock_chan

        def mock_recv_ready_factory(chan):
            chan.recv_counter = 0

            def mock_recv_ready():
                chan.recv_counter += 1
                if chan.recv_counter < 2:
                    return True

                return False
            return mock_recv_ready

        def mock_recv_stderr_ready_factory(chan):
            chan.recv_stderr_counter = 0

            def mock_recv_stderr_ready():
                chan.recv_stderr_counter += 1
                if chan.recv_stderr_counter < 2:
                    return True

                return False
            return mock_recv_stderr_ready

        mock_chan.recv_ready = mock_recv_ready_factory(mock_chan)
        mock_chan.recv_stderr_ready = mock_recv_stderr_ready_factory(mock_chan)
        mock_chan.recv.return_value = 'stdout 1\nstdout 2\nstdout 3'
        mock_chan.recv_stderr.return_value = 'stderr 1\nstderr 2\nstderr 3'

        # call_line_handler_func is False so handler functions shouldn't be called
        client.run(cmd='echo "test"', call_line_handler_func=False)

        self.assertEqual(mock_handle_stdout_line_func.call_count, 0)
        self.assertEqual(mock_handle_stderr_line_func.call_count, 0)

        # Reset counters
        mock_chan.recv_counter = 0
        mock_chan.recv_stderr_counter = 0

        # call_line_handler_func is True so handler functions should be called for each line
        client.run(cmd='echo "test"', call_line_handler_func=True)

        self.assertEqual(mock_handle_stdout_line_func.call_count, 3)
        self.assertEqual(mock_handle_stdout_line_func.call_args_list[0][1]['line'], 'stdout 1\n')
        self.assertEqual(mock_handle_stdout_line_func.call_args_list[1][1]['line'], 'stdout 2\n')
        self.assertEqual(mock_handle_stdout_line_func.call_args_list[2][1]['line'], 'stdout 3\n')
        self.assertEqual(mock_handle_stderr_line_func.call_count, 3)
        self.assertEqual(mock_handle_stdout_line_func.call_args_list[0][1]['line'], 'stdout 1\n')
        self.assertEqual(mock_handle_stdout_line_func.call_args_list[1][1]['line'], 'stdout 2\n')
        self.assertEqual(mock_handle_stdout_line_func.call_args_list[2][1]['line'], 'stdout 3\n')