blob: 64d6be2475f8da87e2c9b04c322a520857ee81d7 [file] [log] [blame]
Ken'ichi Ohmichid25a1a32017-03-01 13:40:35 -08001# 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
13import sys
14
15import netaddr
16from oslo_log import log as logging
17import six
18
19from tempest.lib.common import ssh
20from tempest.lib.common.utils import test_utils
21import tempest.lib.exceptions
22
23LOG = logging.getLogger(__name__)
24
25
26def debug_ssh(function):
27 """Decorator to generate extra debug info in case off SSH failure"""
28 def wrapper(self, *args, **kwargs):
29 try:
30 return function(self, *args, **kwargs)
31 except tempest.lib.exceptions.SSHTimeout:
32 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.console_output_enabled 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
49 six.reraise(*original_exception)
50 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, ssh_timeout=300,
61 connect_timeout=60, console_output_enabled=True,
62 ssh_shell_prologue="set -eu -o pipefail; PATH=$$PATH:/sbin;",
63 ping_count=1, ping_size=56):
64 """Executes commands in a VM over ssh
65
66 :param ip_address: IP address to ssh to
67 :param username: Ssh username
68 :param password: Ssh password
69 :param pkey: Ssh public key
70 :param server: Server dict, used for debugging purposes
71 :param servers_client: Servers client, used for debugging purposes
72 :param ssh_timeout: Timeout in seconds to wait for the ssh banner
73 :param connect_timeout: Timeout in seconds to wait for TCP connection
74 :param console_output_enabled: Support serial console output?
75 :param ssh_shell_prologue: Shell fragments to use before command
76 :param ping_count: Number of ping packets
77 :param ping_size: Packet size for ping packets
78 """
79 self.server = server
80 self.servers_client = servers_client
81 self.console_output_enabled = console_output_enabled
82 self.ssh_shell_prologue = ssh_shell_prologue
83 self.ping_count = ping_count
84 self.ping_size = ping_size
85
86 self.ssh_client = ssh.Client(ip_address, username, password,
87 ssh_timeout, pkey=pkey,
88 channel_timeout=connect_timeout)
89
90 @debug_ssh
91 def exec_command(self, cmd):
92 # Shell options below add more clearness on failures,
93 # path is extended for some non-cirros guest oses (centos7)
94 cmd = self.ssh_shell_prologue + " " + cmd
95 LOG.debug("Remote command: %s", cmd)
96 return self.ssh_client.exec_command(cmd)
97
98 @debug_ssh
99 def validate_authentication(self):
100 """Validate ssh connection and authentication
101
102 This method raises an Exception when the validation fails.
103 """
104 self.ssh_client.test_connection_auth()
105
106 def ping_host(self, host, count=None, size=None, nic=None):
107 if count is None:
108 count = self.ping_count
109 if size is None:
110 size = self.ping_size
111
112 addr = netaddr.IPAddress(host)
113 cmd = 'ping6' if addr.version == 6 else 'ping'
114 if nic:
115 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
116 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
117 return self.exec_command(cmd)