blob: fa91b31f7fcbdcf0dad8d1d3dc78fb595f1d49cc [file] [log] [blame]
Itzik Browne67ebb52016-05-15 05:34:41 +00001# Copyright 2016 Red Hat, Inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010015import distutils
16import re
Chandan Kumarc125fd12017-11-15 19:41:01 +053017import subprocess
Itzik Browne67ebb52016-05-15 05:34:41 +000018
Federico Ressibf877c82018-08-22 08:36:37 +020019from debtcollector import removals
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090020import netaddr
Assaf Muller92fdc782018-05-31 10:32:47 -040021from neutron_lib.api import validators
22from neutron_lib import constants as neutron_lib_constants
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050023from oslo_log import log
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +030024from tempest.common.utils import net_utils
Itzik Browne67ebb52016-05-15 05:34:41 +000025from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000026from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090027from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050028from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000029
Chandan Kumar667d3d32017-09-22 12:24:06 +053030from neutron_tempest_plugin.api import base as base_api
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +000031from neutron_tempest_plugin.common import ip as ip_utils
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +020032from neutron_tempest_plugin.common import shell
Chandan Kumar667d3d32017-09-22 12:24:06 +053033from neutron_tempest_plugin.common import ssh
34from neutron_tempest_plugin import config
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010035from neutron_tempest_plugin import exceptions
Chandan Kumar667d3d32017-09-22 12:24:06 +053036from neutron_tempest_plugin.scenario import constants
Itzik Browne67ebb52016-05-15 05:34:41 +000037
38CONF = config.CONF
Itzik Browne67ebb52016-05-15 05:34:41 +000039
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050040LOG = log.getLogger(__name__)
41
Itzik Browne67ebb52016-05-15 05:34:41 +000042
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010043def get_ncat_version(ssh_client=None):
44 cmd = "ncat --version 2>&1"
45 try:
46 version_result = shell.execute(cmd, ssh_client=ssh_client).stdout
47 except exceptions.ShellCommandFailed:
48 m = None
49 else:
50 m = re.match(r"Ncat: Version ([\d.]+) *.", version_result)
51 # NOTE(slaweq): by default lets assume we have ncat 7.60 which is in Ubuntu
52 # 18.04 which is used on u/s gates
53 return distutils.version.StrictVersion(m.group(1) if m else '7.60')
54
55
56def get_ncat_server_cmd(port, protocol, msg):
57 udp = ''
58 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
59 udp = '-u'
60 cmd = "nc %(udp)s -p %(port)s -lk " % {
61 'udp': udp, 'port': port}
62 if CONF.neutron_plugin_options.default_image_is_advanced:
63 cmd += "-c 'echo %s' &" % msg
64 else:
65 cmd += "-e echo %s &" % msg
66 return cmd
67
68
69def get_ncat_client_cmd(ip_address, port, protocol):
70 udp = ''
71 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
72 udp = '-u'
73 cmd = 'echo "knock knock" | nc '
74 ncat_version = get_ncat_version()
75 if ncat_version > distutils.version.StrictVersion('7.60'):
76 cmd += '-z '
77 cmd += '-w 1 %(udp)s %(host)s %(port)s' % {
78 'udp': udp, 'host': ip_address, 'port': port}
79 return cmd
80
81
Itzik Browne67ebb52016-05-15 05:34:41 +000082class BaseTempestTestCase(base_api.BaseNetworkTest):
Itzik Browne67ebb52016-05-15 05:34:41 +000083
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000084 def create_server(self, flavor_ref, image_ref, key_name, networks,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020085 **kwargs):
Itzik Brownbac51dc2016-10-31 12:25:04 +000086 """Create a server using tempest lib
Brian Haleyaee61ac2018-10-09 20:00:27 -040087
Itzik Brownbac51dc2016-10-31 12:25:04 +000088 All the parameters are the ones used in Compute API
Roee Agiman6a0a18a2017-11-16 11:51:56 +020089 * - Kwargs that require admin privileges
Itzik Brownbac51dc2016-10-31 12:25:04 +000090
91 Args:
92 flavor_ref(str): The flavor of the server to be provisioned.
93 image_ref(str): The image of the server to be provisioned.
94 key_name(str): SSH key to to be used to connect to the
95 provisioned server.
96 networks(list): List of dictionaries where each represent
97 an interface to be attached to the server. For network
98 it should be {'uuid': network_uuid} and for port it should
99 be {'port': port_uuid}
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200100 kwargs:
Itzik Brownbac51dc2016-10-31 12:25:04 +0000101 name(str): Name of the server to be provisioned.
102 security_groups(list): List of dictionaries where
103 the keys is 'name' and the value is the name of
104 the security group. If it's not passed the default
105 security group will be used.
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200106 availability_zone(str)*: The availability zone that
107 the instance will be in.
108 You can request a specific az without actually creating one,
109 Just pass 'X:Y' where X is the default availability
110 zone, and Y is the compute host name.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000111 """
112
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000113 kwargs.setdefault('name', data_utils.rand_name('server-test'))
Itzik Brownbac51dc2016-10-31 12:25:04 +0000114
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000115 # We cannot use setdefault() here because caller could have passed
116 # security_groups=None and we don't want to pass None to
117 # client.create_server()
118 if not kwargs.get('security_groups'):
119 kwargs['security_groups'] = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200120
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000121 client = self.os_primary.servers_client
122 if kwargs.get('availability_zone'):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200123 client = self.os_admin.servers_client
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200124
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000125 server = client.create_server(
126 flavorRef=flavor_ref,
127 imageRef=image_ref,
128 key_name=key_name,
129 networks=networks,
130 **kwargs)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000131
132 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200133 waiters.wait_for_server_termination,
134 client,
135 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000136 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200137 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000138 server['server']['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000139 return server
140
141 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +0000142 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
143 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200144 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000145 if not secgroup_id:
146 sgs = client.list_security_groups()['security_groups']
147 for sg in sgs:
148 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
149 secgroup_id = sg['id']
150 break
151
Itzik Brown1ef813a2016-06-06 12:56:21 +0000152 for rule in rule_list:
153 direction = rule.pop('direction')
154 client.create_security_group_rule(
155 direction=direction,
156 security_group_id=secgroup_id,
157 **rule)
158
159 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200160 def create_loginable_secgroup_rule(cls, secgroup_id=None,
161 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000162 """This rule is intended to permit inbound ssh
163
164 Allowing ssh traffic traffic from all sources, so no group_id is
165 provided.
166 Setting a group_id would only permit traffic from ports
167 belonging to the same security group.
168 """
Federico Ressi4c590d72018-10-10 14:01:08 +0200169 return cls.create_security_group_rule(
170 security_group_id=secgroup_id,
171 client=client,
172 protocol=neutron_lib_constants.PROTO_NAME_TCP,
173 direction=neutron_lib_constants.INGRESS_DIRECTION,
174 port_range_min=22,
175 port_range_max=22)
Itzik Browne67ebb52016-05-15 05:34:41 +0000176
177 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200178 def create_pingable_secgroup_rule(cls, secgroup_id=None,
179 client=None):
Federico Ressi4c590d72018-10-10 14:01:08 +0200180 """This rule is intended to permit inbound ping
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900181
Federico Ressi4c590d72018-10-10 14:01:08 +0200182 """
183 return cls.create_security_group_rule(
184 security_group_id=secgroup_id, client=client,
185 protocol=neutron_lib_constants.PROTO_NAME_ICMP,
186 direction=neutron_lib_constants.INGRESS_DIRECTION)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900187
188 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300189 def create_router_by_client(cls, is_admin=False, **kwargs):
190 kwargs.update({'router_name': data_utils.rand_name('router'),
191 'admin_state_up': True,
192 'external_network_id': CONF.network.public_network_id})
193 if not is_admin:
194 router = cls.create_router(**kwargs)
195 else:
196 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500197 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000198 cls.routers.append(router)
199 return router
200
Federico Ressibf877c82018-08-22 08:36:37 +0200201 @removals.remove(version='Stein',
202 message="Please use create_floatingip method instead of "
203 "create_and_associate_floatingip.")
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200204 def create_and_associate_floatingip(self, port_id, client=None):
205 client = client or self.os_primary.network_client
Federico Ressibf877c82018-08-22 08:36:37 +0200206 return self.create_floatingip(port_id=port_id, client=client)
Itzik Browne67ebb52016-05-15 05:34:41 +0000207
Hongbin Lu965b03d2018-04-25 22:32:30 +0000208 def create_interface(cls, server_id, port_id, client=None):
209 client = client or cls.os_primary.interfaces_client
210 body = client.create_interface(server_id, port_id=port_id)
211 return body['interfaceAttachment']
212
213 def delete_interface(cls, server_id, port_id, client=None):
214 client = client or cls.os_primary.interfaces_client
215 client.delete_interface(server_id, port_id=port_id)
216
Brian Haleyd11f4ec2019-08-13 12:09:57 -0400217 def setup_network_and_server(self, router=None, server_name=None,
218 network=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300219 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000220
221 Creating a network, subnet, router, keypair, security group
222 and a server.
223 """
Assaf Mullerd54ae6c2018-05-31 11:38:00 -0400224 self.network = network or self.create_network()
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000225 LOG.debug("Created network %s", self.network['name'])
226 self.subnet = self.create_subnet(self.network)
227 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000228
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400229 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530230 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500231 LOG.debug("Created security group %s",
232 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000233 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300234 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000235 router = self.create_router_by_client(**kwargs)
236 self.create_router_interface(router['id'], self.subnet['id'])
237 self.keypair = self.create_keypair()
238 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000239 secgroup_id=secgroup['security_group']['id'])
Assaf Muller92fdc782018-05-31 10:32:47 -0400240
241 server_kwargs = {
242 'flavor_ref': CONF.compute.flavor_ref,
243 'image_ref': CONF.compute.image_ref,
244 'key_name': self.keypair['name'],
245 'networks': [{'uuid': self.network['id']}],
246 'security_groups': [{'name': secgroup['security_group']['name']}],
247 }
248 if server_name is not None:
249 server_kwargs['name'] = server_name
250
251 self.server = self.create_server(**server_kwargs)
Federico Ressie7417b72018-05-30 05:50:58 +0200252 self.wait_for_server_active(self.server['server'])
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000253 self.port = self.client.list_ports(network_id=self.network['id'],
254 device_id=self.server[
255 'server']['id'])['ports'][0]
Federico Ressibf877c82018-08-22 08:36:37 +0200256 self.fip = self.create_floatingip(port=self.port)
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500257
258 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
259 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
260 try:
261 ssh_client.test_connection_auth()
262 except lib_exc.SSHTimeout as ssh_e:
263 LOG.debug(ssh_e)
264 self._log_console_output(servers)
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000265 self._log_local_network_status()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500266 raise
267
268 def _log_console_output(self, servers=None):
269 if not CONF.compute_feature_enabled.console_output:
270 LOG.debug('Console output not supported, cannot log')
271 return
272 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400273 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500274 servers = servers['servers']
275 for server in servers:
Slawek Kaplonskicff79232020-03-03 14:12:18 +0100276 # NOTE(slaweq): sometimes servers are passed in dictionary with
277 # "server" key as first level key and in other cases it may be that
278 # it is just the "inner" dict without "server" key. Lets try to
279 # handle both cases
280 server = server.get("server") or server
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500281 try:
282 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400283 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500284 server['id'])['output'])
285 LOG.debug('Console output for %s\nbody=\n%s',
286 server['id'], console_output)
287 except lib_exc.NotFound:
288 LOG.debug("Server %s disappeared(deleted) while looking "
289 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900290
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000291 def _log_local_network_status(self):
292 local_routes = ip_utils.IPCommand().list_routes()
293 LOG.debug('Local routes:\n%s', '\n'.join(str(r) for r in local_routes))
294 arp_table = ip_utils.arp_table()
295 LOG.debug('Local ARP table:\n%s', '\n'.join(str(r) for r in arp_table))
296
LIU Yulong68ab2452019-05-18 10:19:49 +0800297 def _check_remote_connectivity(self, source, dest, count,
298 should_succeed=True,
Assaf Muller92fdc782018-05-31 10:32:47 -0400299 nic=None, mtu=None, fragmentation=True,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100300 timeout=None, pattern=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900301 """check ping server via source ssh connection
302
303 :param source: RemoteClient: an ssh connection from which to ping
304 :param dest: and IP to ping against
LIU Yulong68ab2452019-05-18 10:19:49 +0800305 :param count: Number of ping packet(s) to send
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900306 :param should_succeed: boolean should ping succeed or not
307 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300308 :param mtu: mtu size for the packet to be sent
309 :param fragmentation: Flag for packet fragmentation
LIU Yulong68ab2452019-05-18 10:19:49 +0800310 :param timeout: Timeout for all ping packet(s) to succeed
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100311 :param pattern: hex digits included in ICMP messages
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900312 :returns: boolean -- should_succeed == ping
313 :returns: ping is false if ping failed
314 """
LIU Yulong68ab2452019-05-18 10:19:49 +0800315 def ping_host(source, host, count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300316 size=CONF.validation.ping_size, nic=None, mtu=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100317 fragmentation=True, pattern=None):
Assaf Muller92fdc782018-05-31 10:32:47 -0400318 IP_VERSION_4 = neutron_lib_constants.IP_VERSION_4
319 IP_VERSION_6 = neutron_lib_constants.IP_VERSION_6
320
321 # Use 'ping6' for IPv6 addresses, 'ping' for IPv4 and hostnames
322 ip_version = (
323 IP_VERSION_6 if netaddr.valid_ipv6(host) else IP_VERSION_4)
324 cmd = (
325 'ping6' if ip_version == IP_VERSION_6 else 'ping')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900326 if nic:
327 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300328 if mtu:
329 if not fragmentation:
330 cmd += ' -M do'
331 size = str(net_utils.get_ping_payload_size(
Assaf Muller92fdc782018-05-31 10:32:47 -0400332 mtu=mtu, ip_version=ip_version))
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100333 if pattern:
334 cmd += ' -p {pattern}'.format(pattern=pattern)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900335 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
336 return source.exec_command(cmd)
337
338 def ping_remote():
339 try:
LIU Yulong68ab2452019-05-18 10:19:49 +0800340 result = ping_host(source, dest, count, nic=nic, mtu=mtu,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100341 fragmentation=fragmentation,
342 pattern=pattern)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900343
344 except lib_exc.SSHExecCommandFailed:
345 LOG.warning('Failed to ping IP: %s via a ssh connection '
346 'from: %s.', dest, source.host)
347 return not should_succeed
348 LOG.debug('ping result: %s', result)
Assaf Muller92fdc782018-05-31 10:32:47 -0400349
350 if validators.validate_ip_address(dest) is None:
351 # Assert that the return traffic was from the correct
352 # source address.
353 from_source = 'from %s' % dest
354 self.assertIn(from_source, result)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900355 return should_succeed
356
Assaf Muller92fdc782018-05-31 10:32:47 -0400357 return test_utils.call_until_true(
358 ping_remote, timeout or CONF.validation.ping_timeout, 1)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900359
360 def check_remote_connectivity(self, source, dest, should_succeed=True,
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200361 nic=None, mtu=None, fragmentation=True,
LIU Yulong68ab2452019-05-18 10:19:49 +0800362 servers=None, timeout=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100363 ping_count=CONF.validation.ping_count,
364 pattern=None):
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200365 try:
366 self.assertTrue(self._check_remote_connectivity(
LIU Yulong68ab2452019-05-18 10:19:49 +0800367 source, dest, ping_count, should_succeed, nic, mtu,
368 fragmentation,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100369 timeout=timeout, pattern=pattern))
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200370 except lib_exc.SSHTimeout as ssh_e:
371 LOG.debug(ssh_e)
372 self._log_console_output(servers)
373 raise
374 except AssertionError:
375 self._log_console_output(servers)
376 raise
Chandan Kumarc125fd12017-11-15 19:41:01 +0530377
378 def ping_ip_address(self, ip_address, should_succeed=True,
379 ping_timeout=None, mtu=None):
380 # the code is taken from tempest/scenario/manager.py in tempest git
381 timeout = ping_timeout or CONF.validation.ping_timeout
382 cmd = ['ping', '-c1', '-w1']
383
384 if mtu:
385 cmd += [
386 # don't fragment
387 '-M', 'do',
388 # ping receives just the size of ICMP payload
389 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
390 ]
391 cmd.append(ip_address)
392
393 def ping():
394 proc = subprocess.Popen(cmd,
395 stdout=subprocess.PIPE,
396 stderr=subprocess.PIPE)
397 proc.communicate()
398
399 return (proc.returncode == 0) == should_succeed
400
401 caller = test_utils.find_test_caller()
402 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
403 ' expected result is %(should_succeed)s', {
404 'caller': caller, 'ip': ip_address, 'timeout': timeout,
405 'should_succeed':
406 'reachable' if should_succeed else 'unreachable'
407 })
408 result = test_utils.call_until_true(ping, timeout, 1)
Manjeet Singh Bhatia8bbf8992019-03-04 11:59:57 -0800409
410 # To make sure ping_ip_address called by test works
411 # as expected.
412 self.assertTrue(result)
413
Chandan Kumarc125fd12017-11-15 19:41:01 +0530414 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
415 'ping result is %(result)s', {
416 'caller': caller, 'ip': ip_address, 'timeout': timeout,
417 'result': 'expected' if result else 'unexpected'
418 })
419 return result
Federico Ressie7417b72018-05-30 05:50:58 +0200420
421 def wait_for_server_status(self, server, status, client=None, **kwargs):
422 """Waits for a server to reach a given status.
423
424 :param server: mapping having schema {'id': <server_id>}
425 :param status: string status to wait for (es: 'ACTIVE')
426 :param clien: servers client (self.os_primary.servers_client as
427 default value)
428 """
429
430 client = client or self.os_primary.servers_client
431 waiters.wait_for_server_status(client, server['id'], status, **kwargs)
432
433 def wait_for_server_active(self, server, client=None):
434 """Waits for a server to reach active status.
435
436 :param server: mapping having schema {'id': <server_id>}
437 :param clien: servers client (self.os_primary.servers_client as
438 default value)
439 """
440 self.wait_for_server_status(
441 server, constants.SERVER_STATUS_ACTIVE, client)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000442
443 def check_servers_hostnames(self, servers, log_errors=True):
444 """Compare hostnames of given servers with their names."""
445 try:
446 for server in servers:
447 kwargs = {}
448 try:
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200449 kwargs['port'] = (
450 server['port_forwarding_tcp']['external_port'])
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000451 except KeyError:
452 pass
453 ssh_client = ssh.Client(
454 self.fip['floating_ip_address'],
455 CONF.validation.image_ssh_user,
456 pkey=self.keypair['private_key'],
457 **kwargs)
458 self.assertIn(server['name'],
459 ssh_client.exec_command('hostname'))
460 except lib_exc.SSHTimeout as ssh_e:
461 LOG.debug(ssh_e)
462 if log_errors:
463 self._log_console_output(servers)
464 raise
465 except AssertionError as assert_e:
466 LOG.debug(assert_e)
467 if log_errors:
468 self._log_console_output(servers)
469 raise
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200470
471 def nc_listen(self, server, ssh_client, port, protocol, echo_msg):
472 """Create nc server listening on the given TCP/UDP port.
473
474 Listener is created always on remote host.
475 """
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200476 try:
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +0100477 return ssh_client.execute_script(
478 get_ncat_server_cmd(port, protocol, echo_msg),
479 become_root=True)
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200480 except lib_exc.SSHTimeout as ssh_e:
481 LOG.debug(ssh_e)
482 self._log_console_output([server])
483 raise
484
485 def nc_client(self, ip_address, port, protocol):
486 """Check connectivity to TCP/UDP port at host via nc.
487
488 Client is always executed locally on host where tests are executed.
489 """
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +0100490 cmd = get_ncat_client_cmd(ip_address, port, protocol)
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200491 result = shell.execute_local_command(cmd)
492 self.assertEqual(0, result.exit_status)
493 return result.stdout