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-',