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))
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)
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, key=self._ssh_key_file, key_material=self._ssh_key_material, port=port) try: client.connect() except: error = 'Failed connecting to host %s.' % hostname LOG.exception(error) _, ex, tb = sys.exc_info() if raise_on_any_error: raise error = ' '.join([self.CONNECT_ERROR, str(ex)]) error_dict = self._generate_error_result(error, tb) 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.'}
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)
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)
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)
def test_deprecated_key_argument(self): conn_params = {'hostname': 'dummy.host.org', 'username': '******', 'key': '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', 'port': 22} mock.client.connect.assert_called_once_with(**expected_conn)
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, 'port': 22} mock.client.connect.assert_called_once_with(**expected_conn)
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)
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)
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)
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)
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)
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_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, 'port': 22} mock.client.connect.assert_called_once_with(**expected_conn)
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)
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 setUp(self): """ Creates the object patching the actual connection. """ conn_params = {'hostname': 'dummy.host.org', 'port': 8822, 'username': '******', 'key': '~/.ssh/ubuntu_ssh', 'timeout': '600'} self.ssh_cli = ParamikoSSHClient(**conn_params)
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_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)
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_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)
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)
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)
def setUp(self): """ Creates the object patching the actual connection. """ cfg.CONF.set_override(name='ssh_key_file', override=None, group='system_user') conn_params = {'hostname': 'dummy.host.org', 'port': 8822, 'username': '******', 'key_files': '~/.ssh/ubuntu_ssh', 'timeout': '600'} self.ssh_cli = ParamikoSSHClient(**conn_params)
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, 'port': 22} mock.client.connect.assert_called_once_with(**expected_conn)
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)
def test_incorrect_passphrase(self): path = os.path.join(get_resources_base_path(), 'ssh', 'dummy_rsa_passphrase') with open(path, 'r') as fp: private_key = fp.read() conn_params = {'hostname': 'dummy.host.org', 'username': '******', 'key_material': private_key, 'passphrase': 'incorrect'} mock = ParamikoSSHClient(**conn_params) expected_msg = 'Invalid passphrase or invalid/unsupported key type' self.assertRaisesRegexp(paramiko.ssh_exception.SSHException, expected_msg, mock.connect)
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)
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)
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)
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)