SSH connection related cleanups
Catching only the SSHException the AuthenticationException is
a subclass of the SSHException in the ssh.py.
test_connection_auth method changed to exception raiser method, in order
to avid unwanted catch-and-raise-new-exception code from the
RemoteClient.
Use similar ssh connectivity check with the test_network_basic_ops,
as with all other test cases, so using the implicit
connection validation of the RemoteClient.
Improve ssh connection logging by logging the reason of the connection
failure.
Change-Id: Ia2599f7f2c2fdc6fcbf7ad3337d82adcc50e4d16
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 24ade96..cbd0eb1 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -93,13 +93,6 @@
@testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
@attr(type='gate')
- def test_can_log_into_created_server(self):
- # Check that the user can authenticate with the generated password
- linux_client = RemoteClient(self.server, self.ssh_user, self.password)
- self.assertTrue(linux_client.can_authenticate())
-
- @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
- @attr(type='gate')
def test_verify_created_server_vcpus(self):
# Verify that the number of vcpus reported by the instance matches
# the amount stated by the flavor
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5552d0b..e009888 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -67,7 +67,7 @@
# Verify that the user can authenticate with the new password
resp, server = self.client.get_server(self.server_id)
linux_client = RemoteClient(server, self.ssh_user, new_password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
@attr(type='smoke')
def test_reboot_server_hard(self):
@@ -141,7 +141,7 @@
if self.run_ssh:
# Verify that the user can authenticate with the provided password
linux_client = RemoteClient(server, self.ssh_user, password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
@attr(type='gate')
def test_rebuild_server_in_stop_state(self):
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 090f4dd..ee37502 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -67,7 +67,7 @@
# Verify that the user can authenticate with the new password
resp, server = self.client.get_server(self.server_id)
linux_client = RemoteClient(server, self.ssh_user, new_password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
@attr(type='smoke')
def test_reboot_server_hard(self):
@@ -140,7 +140,7 @@
if self.run_ssh:
# Verify that the user can authenticate with the provided password
linux_client = RemoteClient(server, self.ssh_user, password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
def _detect_server_image_flavor(self, server_id):
# Detects the current server image flavor ref.
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
index 3c2a2d2..0480570 100644
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_server_cfn_init.py
@@ -169,9 +169,9 @@
body['physical_resource_id'])
# Check that the user can authenticate with the generated password
- linux_client = RemoteClient(
- server, 'ec2-user', pkey=self.keypair['private_key'])
- self.assertTrue(linux_client.can_authenticate())
+ linux_client = RemoteClient(server, 'ec2-user',
+ pkey=self.keypair['private_key'])
+ linux_client.validate_authentication()
@attr(type='slow')
def test_stack_wait_condition_data(self):
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index c397b7c..bca2f9e 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -23,6 +23,7 @@
import warnings
from tempest import exceptions
+from tempest.openstack.common import log as logging
with warnings.catch_warnings():
@@ -30,6 +31,9 @@
import paramiko
+LOG = logging.getLogger(__name__)
+
+
class Client(object):
def __init__(self, host, username, password=None, timeout=300, pkey=None,
@@ -49,33 +53,44 @@
def _get_ssh_connection(self, sleep=1.5, backoff=1.01):
"""Returns an ssh connection to the specified host."""
- _timeout = True
bsleep = sleep
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(
paramiko.AutoAddPolicy())
_start_time = time.time()
-
- while not self._is_timed_out(_start_time):
+ if self.pkey is not None:
+ LOG.info("Creating ssh connection to '%s' as '%s'"
+ " with public key authentication",
+ self.host, self.username)
+ else:
+ LOG.info("Creating ssh connection to '%s' as '%s'"
+ " with password %s",
+ self.host, self.username, str(self.password))
+ attempts = 0
+ while True:
try:
ssh.connect(self.host, username=self.username,
password=self.password,
look_for_keys=self.look_for_keys,
key_filename=self.key_filename,
timeout=self.channel_timeout, pkey=self.pkey)
- _timeout = False
- break
+ LOG.info("ssh connection to %s@%s sucessfuly created",
+ self.username, self.host)
+ return ssh
except (socket.error,
- paramiko.AuthenticationException,
paramiko.SSHException):
+ attempts += 1
time.sleep(bsleep)
bsleep *= backoff
- continue
- if _timeout:
- raise exceptions.SSHTimeout(host=self.host,
- user=self.username,
- password=self.password)
- return ssh
+ if not self._is_timed_out(_start_time):
+ continue
+ else:
+ LOG.exception("Failed to establish authenticated ssh"
+ " connection to %s@%s after %d attempts",
+ self.username, self.host, attempts)
+ raise exceptions.SSHTimeout(host=self.host,
+ user=self.username,
+ password=self.password)
def _is_timed_out(self, start_time):
return (time.time() - self.timeout) > start_time
@@ -144,11 +159,6 @@
return ''.join(out_data)
def test_connection_auth(self):
- """Returns true if ssh can connect to server."""
- try:
- connection = self._get_ssh_connection()
- connection.close()
- except paramiko.AuthenticationException:
- return False
-
- return True
+ """Raises an exception when we can not connect to server via ssh."""
+ connection = self._get_ssh_connection()
+ connection.close()
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index e64a257..1fbd370 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -19,7 +19,6 @@
from tempest.common import utils
from tempest.config import TempestConfig
from tempest.exceptions import ServerUnreachable
-from tempest.exceptions import SSHTimeout
class RemoteClient():
@@ -40,16 +39,15 @@
break
else:
raise ServerUnreachable()
-
self.ssh_client = Client(ip_address, username, password, ssh_timeout,
pkey=pkey,
channel_timeout=ssh_channel_timeout)
- if not self.ssh_client.test_connection_auth():
- raise SSHTimeout()
- def can_authenticate(self):
- # Re-authenticate
- return self.ssh_client.test_connection_auth()
+ def validate_authentication(self):
+ """Validate ssh connection and authentication
+ This method raises an Exception when the validation fails.
+ """
+ self.ssh_client.test_connection_auth()
def hostname_equals_servername(self, expected_hostname):
# Get host name using command "hostname"
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index b24d2b9..a2bd1b0 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -33,7 +33,6 @@
from tempest.api.network import common as net_common
from tempest.common import isolated_creds
-from tempest.common import ssh
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
from tempest import exceptions
@@ -649,13 +648,6 @@
return tempest.test.call_until_true(
ping, self.config.compute.ping_timeout, 1)
- def _is_reachable_via_ssh(self, ip_address, username, private_key,
- timeout):
- ssh_client = ssh.Client(ip_address, username,
- pkey=private_key,
- timeout=timeout)
- return ssh_client.test_connection_auth()
-
def _check_vm_connectivity(self, ip_address,
username=None,
private_key=None,
@@ -680,13 +672,9 @@
msg=msg)
if should_connect:
# no need to check ssh for negative connectivity
- self.assertTrue(self._is_reachable_via_ssh(
- ip_address,
- username,
- private_key,
- timeout=self.config.compute.ssh_timeout),
- 'Auth failure in connecting to %s@%s via ssh' %
- (username, ip_address))
+ linux_client = self.get_remote_client(ip_address, username,
+ private_key)
+ linux_client.validate_authentication()
def _create_security_group_nova(self, client=None,
namestart='secgroup-smoke-',