text: #    Licensed under the Apache License, Version 2.0 (the "License"); you may
  #    not use this file except in compliance with the License. You may obtain
  #    a copy of the License at
  #
  #         http://www.apache.org/licenses/LICENSE-2.0
  #
  #    Unless required by applicable law or agreed to in writing, software
  #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  #    License for the specific language governing permissions and limitations
  #    under the License.
  
  import sys
  
  from oslo_log import log
  import six
  from tempest import config
  from tempest.lib.common import ssh
  from tempest.lib.common.utils import test_utils
  from tempest.lib import exceptions
  
  CONF = config.CONF
  
  LOG = log.getLogger(__name__)
  
  
  def debug_ssh(function):
      """Decorator to generate extra debug info in case of ssh failure"""
      def wrapper(self, *args, **kwargs):
          try:
              return function(self, *args, **kwargs)
          except exceptions.SSHTimeout:
              try:
                  original_exception = sys.exc_info()
                  caller = test_utils.find_test_caller() or "not found"
                  if self.server:
                      msg = 'Caller: %s. Timeout trying to ssh to server %s'
                      LOG.debug(msg, caller, self.server)
                      if self.log_console and self.servers_client:
                          try:
                              msg = 'Console log for server %s: %s'
                              console_log = (
                                  self.servers_client.get_console_output(
                                      self.server['id'])['output'])
                              LOG.debug(msg, self.server['id'], console_log)
                          except Exception:
                              msg = 'Could not get console_log for server %s'
                              LOG.debug(msg, self.server['id'])
                  # re-raise the original ssh timeout exception
                  six.reraise(*original_exception)
              finally:
                  # Delete the traceback to avoid circular references
                  _, _, trace = original_exception
                  del trace
      return wrapper
  
  
  class RemoteClient(object):
  
      def __init__(self, ip_address, username, password=None, pkey=None,
                   server=None, servers_client=None):
          """Executes commands in a VM over ssh
  
          :param ip_address: IP address to ssh to
          :param username: ssh username
          :param password: ssh password (optional)
          :param pkey: ssh public key (optional)
          :param server: server dict, used for debugging purposes
          :param servers_client: servers client, used for debugging purposes
          """
          self.server = server
          self.servers_client = servers_client
          self.log_console = CONF.compute_feature_enabled.console_output
  
          self.ssh_client = ssh.Client(ip_address, username, password, pkey=pkey)
  
      @debug_ssh
      def exec_command(self, cmd):
          # Shell options below add more clearness on failures,
          # path is extended for some non-cirros guest oses (centos7)
          cmd = CONF.validation.ssh_shell_prologue + " " + cmd
          LOG.debug("Remote command: %s", cmd)
          return self.ssh_client.exec_command(cmd)
  
      @debug_ssh
      def validate_authentication(self):
          """Validate ssh connection and authentication
  
             This method raises an Exception when the validation fails.
          """
          self.ssh_client.test_connection_auth()
  