blob: 30e64c75ca544e262b64ad3f0ea6bf3d6a7d6992 [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
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000016from tempest import config
17from tempest.lib.common import ssh
18from tempest.lib.common.utils import test_utils
lkuchlan1d1461d2020-08-04 11:19:11 +030019from tempest.lib import exceptions
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000020
21CONF = config.CONF
22
23LOG = log.getLogger(__name__)
24
25
26def debug_ssh(function):
27 """Decorator to generate extra debug info in case of ssh failure"""
28 def wrapper(self, *args, **kwargs):
29 try:
30 return function(self, *args, **kwargs)
lkuchlan1d1461d2020-08-04 11:19:11 +030031 except exceptions.SSHTimeout:
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000032 try:
33 original_exception = sys.exc_info()
34 caller = test_utils.find_test_caller() or "not found"
35 if self.server:
36 msg = 'Caller: %s. Timeout trying to ssh to server %s'
37 LOG.debug(msg, caller, self.server)
38 if self.log_console and self.servers_client:
39 try:
40 msg = 'Console log for server %s: %s'
41 console_log = (
42 self.servers_client.get_console_output(
43 self.server['id'])['output'])
44 LOG.debug(msg, self.server['id'], console_log)
45 except Exception:
46 msg = 'Could not get console_log for server %s'
47 LOG.debug(msg, self.server['id'])
48 # re-raise the original ssh timeout exception
haixin48895812020-09-30 13:50:37 +080049 raise original_exception
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000050 finally:
51 # Delete the traceback to avoid circular references
52 _, _, trace = original_exception
53 del trace
54 return wrapper
55
56
57class RemoteClient(object):
58
59 def __init__(self, ip_address, username, password=None, pkey=None,
60 server=None, servers_client=None):
61 """Executes commands in a VM over ssh
62
63 :param ip_address: IP address to ssh to
64 :param username: ssh username
65 :param password: ssh password (optional)
66 :param pkey: ssh public key (optional)
67 :param server: server dict, used for debugging purposes
68 :param servers_client: servers client, used for debugging purposes
69 """
70 self.server = server
71 self.servers_client = servers_client
72 self.log_console = CONF.compute_feature_enabled.console_output
73
74 self.ssh_client = ssh.Client(ip_address, username, password, pkey=pkey)
75
76 @debug_ssh
77 def exec_command(self, cmd):
78 # Shell options below add more clearness on failures,
79 # path is extended for some non-cirros guest oses (centos7)
80 cmd = CONF.validation.ssh_shell_prologue + " " + cmd
junbolib236c242017-07-18 18:12:37 +080081 LOG.debug("Remote command: %s", cmd)
Daniel Mellado21d4d5c2016-11-08 17:02:42 +000082 return self.ssh_client.exec_command(cmd)
83
84 @debug_ssh
85 def validate_authentication(self):
86 """Validate ssh connection and authentication
87
88 This method raises an Exception when the validation fails.
89 """
90 self.ssh_client.test_connection_auth()