blob: 9970b0ba288b49713f6b338143b5c75a3675c0a0 [file] [log] [blame]
Daniel Mellado21d4d5c2016-11-08 17:02:42 +00001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000013import sys
14
15from oslo_log import log
lkuchlan1d1461d2020-08-04 11:19:11 +030016import six
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000017from tempest import config
18from tempest.lib.common import ssh
19from tempest.lib.common.utils import test_utils
lkuchlan1d1461d2020-08-04 11:19:11 +030020from tempest.lib import exceptions
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000021
22CONF = config.CONF
23
24LOG = log.getLogger(__name__)
25
26
27def debug_ssh(function):
28 """Decorator to generate extra debug info in case of ssh failure"""
29 def wrapper(self, *args, **kwargs):
30 try:
31 return function(self, *args, **kwargs)
lkuchlan1d1461d2020-08-04 11:19:11 +030032 except exceptions.SSHTimeout:
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000033 try:
34 original_exception = sys.exc_info()
35 caller = test_utils.find_test_caller() or "not found"
36 if self.server:
37 msg = 'Caller: %s. Timeout trying to ssh to server %s'
38 LOG.debug(msg, caller, self.server)
39 if self.log_console and self.servers_client:
40 try:
41 msg = 'Console log for server %s: %s'
42 console_log = (
43 self.servers_client.get_console_output(
44 self.server['id'])['output'])
45 LOG.debug(msg, self.server['id'], console_log)
46 except Exception:
47 msg = 'Could not get console_log for server %s'
48 LOG.debug(msg, self.server['id'])
49 # re-raise the original ssh timeout exception
50 six.reraise(*original_exception)
51 finally:
52 # Delete the traceback to avoid circular references
53 _, _, trace = original_exception
54 del trace
55 return wrapper
56
57
58class RemoteClient(object):
59
60 def __init__(self, ip_address, username, password=None, pkey=None,
61 server=None, servers_client=None):
62 """Executes commands in a VM over ssh
63
64 :param ip_address: IP address to ssh to
65 :param username: ssh username
66 :param password: ssh password (optional)
67 :param pkey: ssh public key (optional)
68 :param server: server dict, used for debugging purposes
69 :param servers_client: servers client, used for debugging purposes
70 """
71 self.server = server
72 self.servers_client = servers_client
73 self.log_console = CONF.compute_feature_enabled.console_output
74
75 self.ssh_client = ssh.Client(ip_address, username, password, pkey=pkey)
76
77 @debug_ssh
78 def exec_command(self, cmd):
79 # Shell options below add more clearness on failures,
80 # path is extended for some non-cirros guest oses (centos7)
81 cmd = CONF.validation.ssh_shell_prologue + " " + cmd
junbolib236c242017-07-18 18:12:37 +080082 LOG.debug("Remote command: %s", cmd)
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000083 return self.ssh_client.exec_command(cmd)
84
85 @debug_ssh
86 def validate_authentication(self):
87 """Validate ssh connection and authentication
88
89 This method raises an Exception when the validation fails.
90 """
91 self.ssh_client.test_connection_auth()