blob: 2eb45f8b0ad006078fa02c0b8698f18d6420406e [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
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +020024from paramiko import ssh_exception as ssh_exc
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +030025from tempest.common.utils import net_utils
Itzik Browne67ebb52016-05-15 05:34:41 +000026from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000027from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090028from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050029from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000030
Chandan Kumar667d3d32017-09-22 12:24:06 +053031from neutron_tempest_plugin.api import base as base_api
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +000032from neutron_tempest_plugin.common import ip as ip_utils
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +020033from neutron_tempest_plugin.common import shell
Chandan Kumar667d3d32017-09-22 12:24:06 +053034from neutron_tempest_plugin.common import ssh
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010035from neutron_tempest_plugin.common import utils
Chandan Kumar667d3d32017-09-22 12:24:06 +053036from neutron_tempest_plugin import config
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010037from neutron_tempest_plugin import exceptions
Chandan Kumar667d3d32017-09-22 12:24:06 +053038from neutron_tempest_plugin.scenario import constants
Itzik Browne67ebb52016-05-15 05:34:41 +000039
40CONF = config.CONF
Itzik Browne67ebb52016-05-15 05:34:41 +000041
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050042LOG = log.getLogger(__name__)
43
Itzik Browne67ebb52016-05-15 05:34:41 +000044
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010045def get_ncat_version(ssh_client=None):
46 cmd = "ncat --version 2>&1"
47 try:
48 version_result = shell.execute(cmd, ssh_client=ssh_client).stdout
49 except exceptions.ShellCommandFailed:
50 m = None
51 else:
52 m = re.match(r"Ncat: Version ([\d.]+) *.", version_result)
53 # NOTE(slaweq): by default lets assume we have ncat 7.60 which is in Ubuntu
54 # 18.04 which is used on u/s gates
55 return distutils.version.StrictVersion(m.group(1) if m else '7.60')
56
57
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010058def get_ncat_server_cmd(port, protocol, msg=None):
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010059 udp = ''
60 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
61 udp = '-u'
62 cmd = "nc %(udp)s -p %(port)s -lk " % {
63 'udp': udp, 'port': port}
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010064 if msg:
65 if CONF.neutron_plugin_options.default_image_is_advanced:
Flavio Fernandesb056ac22020-07-01 14:57:13 -040066 cmd += "-c 'echo %s' " % msg
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010067 else:
Flavio Fernandesb056ac22020-07-01 14:57:13 -040068 cmd += "-e echo %s " % msg
69 cmd += "< /dev/zero &{0}sleep 0.1{0}".format('\n')
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010070 return cmd
71
72
73def get_ncat_client_cmd(ip_address, port, protocol):
74 udp = ''
75 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
76 udp = '-u'
77 cmd = 'echo "knock knock" | nc '
78 ncat_version = get_ncat_version()
79 if ncat_version > distutils.version.StrictVersion('7.60'):
80 cmd += '-z '
81 cmd += '-w 1 %(udp)s %(host)s %(port)s' % {
82 'udp': udp, 'host': ip_address, 'port': port}
83 return cmd
84
85
Itzik Browne67ebb52016-05-15 05:34:41 +000086class BaseTempestTestCase(base_api.BaseNetworkTest):
Itzik Browne67ebb52016-05-15 05:34:41 +000087
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000088 def create_server(self, flavor_ref, image_ref, key_name, networks,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020089 **kwargs):
Itzik Brownbac51dc2016-10-31 12:25:04 +000090 """Create a server using tempest lib
Brian Haleyaee61ac2018-10-09 20:00:27 -040091
Itzik Brownbac51dc2016-10-31 12:25:04 +000092 All the parameters are the ones used in Compute API
Roee Agiman6a0a18a2017-11-16 11:51:56 +020093 * - Kwargs that require admin privileges
Itzik Brownbac51dc2016-10-31 12:25:04 +000094
95 Args:
96 flavor_ref(str): The flavor of the server to be provisioned.
97 image_ref(str): The image of the server to be provisioned.
98 key_name(str): SSH key to to be used to connect to the
99 provisioned server.
100 networks(list): List of dictionaries where each represent
101 an interface to be attached to the server. For network
102 it should be {'uuid': network_uuid} and for port it should
103 be {'port': port_uuid}
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200104 kwargs:
Itzik Brownbac51dc2016-10-31 12:25:04 +0000105 name(str): Name of the server to be provisioned.
106 security_groups(list): List of dictionaries where
107 the keys is 'name' and the value is the name of
108 the security group. If it's not passed the default
109 security group will be used.
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200110 availability_zone(str)*: The availability zone that
111 the instance will be in.
112 You can request a specific az without actually creating one,
113 Just pass 'X:Y' where X is the default availability
114 zone, and Y is the compute host name.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000115 """
116
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000117 kwargs.setdefault('name', data_utils.rand_name('server-test'))
Itzik Brownbac51dc2016-10-31 12:25:04 +0000118
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000119 # We cannot use setdefault() here because caller could have passed
120 # security_groups=None and we don't want to pass None to
121 # client.create_server()
122 if not kwargs.get('security_groups'):
123 kwargs['security_groups'] = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200124
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200125 client = kwargs.pop('client', None)
126 if client is None:
127 client = self.os_primary.servers_client
128 if kwargs.get('availability_zone'):
129 client = self.os_admin.servers_client
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200130
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000131 server = client.create_server(
132 flavorRef=flavor_ref,
133 imageRef=image_ref,
134 key_name=key_name,
135 networks=networks,
136 **kwargs)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000137
138 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200139 waiters.wait_for_server_termination,
140 client,
141 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000142 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200143 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000144 server['server']['id'])
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200145
146 self.wait_for_server_active(server['server'], client=client)
147 self.wait_for_guest_os_ready(server['server'], client=client)
148
Itzik Browne67ebb52016-05-15 05:34:41 +0000149 return server
150
151 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +0000152 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
153 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200154 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000155 if not secgroup_id:
156 sgs = client.list_security_groups()['security_groups']
157 for sg in sgs:
158 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
159 secgroup_id = sg['id']
160 break
161
Itzik Brown1ef813a2016-06-06 12:56:21 +0000162 for rule in rule_list:
163 direction = rule.pop('direction')
164 client.create_security_group_rule(
165 direction=direction,
166 security_group_id=secgroup_id,
167 **rule)
168
169 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200170 def create_loginable_secgroup_rule(cls, secgroup_id=None,
171 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000172 """This rule is intended to permit inbound ssh
173
174 Allowing ssh traffic traffic from all sources, so no group_id is
175 provided.
176 Setting a group_id would only permit traffic from ports
177 belonging to the same security group.
178 """
Federico Ressi4c590d72018-10-10 14:01:08 +0200179 return cls.create_security_group_rule(
180 security_group_id=secgroup_id,
181 client=client,
182 protocol=neutron_lib_constants.PROTO_NAME_TCP,
183 direction=neutron_lib_constants.INGRESS_DIRECTION,
184 port_range_min=22,
185 port_range_max=22)
Itzik Browne67ebb52016-05-15 05:34:41 +0000186
187 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200188 def create_pingable_secgroup_rule(cls, secgroup_id=None,
189 client=None):
Federico Ressi4c590d72018-10-10 14:01:08 +0200190 """This rule is intended to permit inbound ping
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900191
Federico Ressi4c590d72018-10-10 14:01:08 +0200192 """
193 return cls.create_security_group_rule(
194 security_group_id=secgroup_id, client=client,
195 protocol=neutron_lib_constants.PROTO_NAME_ICMP,
196 direction=neutron_lib_constants.INGRESS_DIRECTION)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900197
198 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300199 def create_router_by_client(cls, is_admin=False, **kwargs):
200 kwargs.update({'router_name': data_utils.rand_name('router'),
201 'admin_state_up': True,
202 'external_network_id': CONF.network.public_network_id})
203 if not is_admin:
204 router = cls.create_router(**kwargs)
205 else:
206 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500207 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000208 cls.routers.append(router)
209 return router
210
Federico Ressibf877c82018-08-22 08:36:37 +0200211 @removals.remove(version='Stein',
212 message="Please use create_floatingip method instead of "
213 "create_and_associate_floatingip.")
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200214 def create_and_associate_floatingip(self, port_id, client=None):
215 client = client or self.os_primary.network_client
Federico Ressibf877c82018-08-22 08:36:37 +0200216 return self.create_floatingip(port_id=port_id, client=client)
Itzik Browne67ebb52016-05-15 05:34:41 +0000217
Hongbin Lu965b03d2018-04-25 22:32:30 +0000218 def create_interface(cls, server_id, port_id, client=None):
219 client = client or cls.os_primary.interfaces_client
220 body = client.create_interface(server_id, port_id=port_id)
221 return body['interfaceAttachment']
222
223 def delete_interface(cls, server_id, port_id, client=None):
224 client = client or cls.os_primary.interfaces_client
225 client.delete_interface(server_id, port_id=port_id)
226
Brian Haleyd11f4ec2019-08-13 12:09:57 -0400227 def setup_network_and_server(self, router=None, server_name=None,
228 network=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300229 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000230
231 Creating a network, subnet, router, keypair, security group
232 and a server.
233 """
Assaf Mullerd54ae6c2018-05-31 11:38:00 -0400234 self.network = network or self.create_network()
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000235 LOG.debug("Created network %s", self.network['name'])
236 self.subnet = self.create_subnet(self.network)
237 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000238
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400239 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530240 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500241 LOG.debug("Created security group %s",
242 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000243 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300244 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000245 router = self.create_router_by_client(**kwargs)
246 self.create_router_interface(router['id'], self.subnet['id'])
247 self.keypair = self.create_keypair()
248 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000249 secgroup_id=secgroup['security_group']['id'])
Assaf Muller92fdc782018-05-31 10:32:47 -0400250
251 server_kwargs = {
252 'flavor_ref': CONF.compute.flavor_ref,
253 'image_ref': CONF.compute.image_ref,
254 'key_name': self.keypair['name'],
255 'networks': [{'uuid': self.network['id']}],
256 'security_groups': [{'name': secgroup['security_group']['name']}],
257 }
258 if server_name is not None:
259 server_kwargs['name'] = server_name
260
261 self.server = self.create_server(**server_kwargs)
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000262 self.port = self.client.list_ports(network_id=self.network['id'],
263 device_id=self.server[
264 'server']['id'])['ports'][0]
Federico Ressibf877c82018-08-22 08:36:37 +0200265 self.fip = self.create_floatingip(port=self.port)
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500266
Eduardo Olivares3eb12282020-05-29 16:43:31 +0200267 def check_connectivity(self, host, ssh_user, ssh_key,
268 servers=None, ssh_timeout=None):
269 ssh_client = ssh.Client(host, ssh_user,
270 pkey=ssh_key, timeout=ssh_timeout)
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500271 try:
272 ssh_client.test_connection_auth()
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200273 except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500274 LOG.debug(ssh_e)
275 self._log_console_output(servers)
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000276 self._log_local_network_status()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500277 raise
278
279 def _log_console_output(self, servers=None):
280 if not CONF.compute_feature_enabled.console_output:
281 LOG.debug('Console output not supported, cannot log')
282 return
283 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400284 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500285 servers = servers['servers']
286 for server in servers:
Slawek Kaplonskicff79232020-03-03 14:12:18 +0100287 # NOTE(slaweq): sometimes servers are passed in dictionary with
288 # "server" key as first level key and in other cases it may be that
289 # it is just the "inner" dict without "server" key. Lets try to
290 # handle both cases
291 server = server.get("server") or server
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500292 try:
293 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400294 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500295 server['id'])['output'])
296 LOG.debug('Console output for %s\nbody=\n%s',
297 server['id'], console_output)
298 except lib_exc.NotFound:
299 LOG.debug("Server %s disappeared(deleted) while looking "
300 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900301
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000302 def _log_local_network_status(self):
Slawek Kaplonski8033af72020-05-05 12:01:37 +0200303 self._log_ns_network_status()
304 for ns_name in ip_utils.IPCommand().list_namespaces():
305 self._log_ns_network_status(ns_name=ns_name)
306
307 def _log_ns_network_status(self, ns_name=None):
308 local_ips = ip_utils.IPCommand(namespace=ns_name).list_addresses()
309 LOG.debug('Namespace %s; IP Addresses:\n%s',
310 ns_name, '\n'.join(str(r) for r in local_ips))
311 local_routes = ip_utils.IPCommand(namespace=ns_name).list_routes()
312 LOG.debug('Namespace %s; Local routes:\n%s',
313 ns_name, '\n'.join(str(r) for r in local_routes))
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000314 arp_table = ip_utils.arp_table()
Slawek Kaplonski8033af72020-05-05 12:01:37 +0200315 LOG.debug('Namespace %s; Local ARP table:\n%s',
316 ns_name, '\n'.join(str(r) for r in arp_table))
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000317
LIU Yulong68ab2452019-05-18 10:19:49 +0800318 def _check_remote_connectivity(self, source, dest, count,
319 should_succeed=True,
Assaf Muller92fdc782018-05-31 10:32:47 -0400320 nic=None, mtu=None, fragmentation=True,
Roman Safronov12663cf2020-07-27 13:11:07 +0300321 timeout=None, pattern=None,
322 forbid_packet_loss=False):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900323 """check ping server via source ssh connection
324
325 :param source: RemoteClient: an ssh connection from which to ping
326 :param dest: and IP to ping against
LIU Yulong68ab2452019-05-18 10:19:49 +0800327 :param count: Number of ping packet(s) to send
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900328 :param should_succeed: boolean should ping succeed or not
329 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300330 :param mtu: mtu size for the packet to be sent
331 :param fragmentation: Flag for packet fragmentation
LIU Yulong68ab2452019-05-18 10:19:49 +0800332 :param timeout: Timeout for all ping packet(s) to succeed
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100333 :param pattern: hex digits included in ICMP messages
Roman Safronov12663cf2020-07-27 13:11:07 +0300334 :param forbid_packet_loss: forbid or allow some lost packets
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900335 :returns: boolean -- should_succeed == ping
336 :returns: ping is false if ping failed
337 """
LIU Yulong68ab2452019-05-18 10:19:49 +0800338 def ping_host(source, host, count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300339 size=CONF.validation.ping_size, nic=None, mtu=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100340 fragmentation=True, pattern=None):
Assaf Muller92fdc782018-05-31 10:32:47 -0400341 IP_VERSION_4 = neutron_lib_constants.IP_VERSION_4
342 IP_VERSION_6 = neutron_lib_constants.IP_VERSION_6
343
344 # Use 'ping6' for IPv6 addresses, 'ping' for IPv4 and hostnames
345 ip_version = (
346 IP_VERSION_6 if netaddr.valid_ipv6(host) else IP_VERSION_4)
347 cmd = (
348 'ping6' if ip_version == IP_VERSION_6 else 'ping')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900349 if nic:
350 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300351 if mtu:
352 if not fragmentation:
353 cmd += ' -M do'
354 size = str(net_utils.get_ping_payload_size(
Assaf Muller92fdc782018-05-31 10:32:47 -0400355 mtu=mtu, ip_version=ip_version))
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100356 if pattern:
357 cmd += ' -p {pattern}'.format(pattern=pattern)
Maciej Józefczyk3c324e02020-03-16 10:52:08 +0000358 cmd += ' -c{0} -W{0} -s{1} {2}'.format(count, size, host)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900359 return source.exec_command(cmd)
360
361 def ping_remote():
362 try:
LIU Yulong68ab2452019-05-18 10:19:49 +0800363 result = ping_host(source, dest, count, nic=nic, mtu=mtu,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100364 fragmentation=fragmentation,
365 pattern=pattern)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900366
367 except lib_exc.SSHExecCommandFailed:
368 LOG.warning('Failed to ping IP: %s via a ssh connection '
369 'from: %s.', dest, source.host)
370 return not should_succeed
371 LOG.debug('ping result: %s', result)
Assaf Muller92fdc782018-05-31 10:32:47 -0400372
Roman Safronov12663cf2020-07-27 13:11:07 +0300373 if forbid_packet_loss and ' 0% packet loss' not in result:
374 LOG.debug('Packet loss detected')
375 return not should_succeed
376
Assaf Muller92fdc782018-05-31 10:32:47 -0400377 if validators.validate_ip_address(dest) is None:
378 # Assert that the return traffic was from the correct
379 # source address.
380 from_source = 'from %s' % dest
381 self.assertIn(from_source, result)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900382 return should_succeed
383
Assaf Muller92fdc782018-05-31 10:32:47 -0400384 return test_utils.call_until_true(
385 ping_remote, timeout or CONF.validation.ping_timeout, 1)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900386
387 def check_remote_connectivity(self, source, dest, should_succeed=True,
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200388 nic=None, mtu=None, fragmentation=True,
LIU Yulong68ab2452019-05-18 10:19:49 +0800389 servers=None, timeout=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100390 ping_count=CONF.validation.ping_count,
Roman Safronov12663cf2020-07-27 13:11:07 +0300391 pattern=None, forbid_packet_loss=False):
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200392 try:
393 self.assertTrue(self._check_remote_connectivity(
LIU Yulong68ab2452019-05-18 10:19:49 +0800394 source, dest, ping_count, should_succeed, nic, mtu,
395 fragmentation,
Roman Safronov12663cf2020-07-27 13:11:07 +0300396 timeout=timeout, pattern=pattern,
397 forbid_packet_loss=forbid_packet_loss))
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200398 except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200399 LOG.debug(ssh_e)
400 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200401 self._log_local_network_status()
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200402 raise
403 except AssertionError:
404 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200405 self._log_local_network_status()
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200406 raise
Chandan Kumarc125fd12017-11-15 19:41:01 +0530407
408 def ping_ip_address(self, ip_address, should_succeed=True,
409 ping_timeout=None, mtu=None):
410 # the code is taken from tempest/scenario/manager.py in tempest git
411 timeout = ping_timeout or CONF.validation.ping_timeout
412 cmd = ['ping', '-c1', '-w1']
413
414 if mtu:
415 cmd += [
416 # don't fragment
417 '-M', 'do',
418 # ping receives just the size of ICMP payload
419 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
420 ]
421 cmd.append(ip_address)
422
423 def ping():
424 proc = subprocess.Popen(cmd,
425 stdout=subprocess.PIPE,
426 stderr=subprocess.PIPE)
427 proc.communicate()
428
429 return (proc.returncode == 0) == should_succeed
430
431 caller = test_utils.find_test_caller()
432 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
433 ' expected result is %(should_succeed)s', {
434 'caller': caller, 'ip': ip_address, 'timeout': timeout,
435 'should_succeed':
436 'reachable' if should_succeed else 'unreachable'
437 })
438 result = test_utils.call_until_true(ping, timeout, 1)
Manjeet Singh Bhatia8bbf8992019-03-04 11:59:57 -0800439
440 # To make sure ping_ip_address called by test works
441 # as expected.
442 self.assertTrue(result)
443
Chandan Kumarc125fd12017-11-15 19:41:01 +0530444 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
445 'ping result is %(result)s', {
446 'caller': caller, 'ip': ip_address, 'timeout': timeout,
447 'result': 'expected' if result else 'unexpected'
448 })
449 return result
Federico Ressie7417b72018-05-30 05:50:58 +0200450
451 def wait_for_server_status(self, server, status, client=None, **kwargs):
452 """Waits for a server to reach a given status.
453
454 :param server: mapping having schema {'id': <server_id>}
455 :param status: string status to wait for (es: 'ACTIVE')
456 :param clien: servers client (self.os_primary.servers_client as
457 default value)
458 """
459
460 client = client or self.os_primary.servers_client
461 waiters.wait_for_server_status(client, server['id'], status, **kwargs)
462
463 def wait_for_server_active(self, server, client=None):
464 """Waits for a server to reach active status.
465
466 :param server: mapping having schema {'id': <server_id>}
467 :param clien: servers client (self.os_primary.servers_client as
468 default value)
469 """
470 self.wait_for_server_status(
471 server, constants.SERVER_STATUS_ACTIVE, client)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000472
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200473 def wait_for_guest_os_ready(self, server, client=None):
474 if not CONF.compute_feature_enabled.console_output:
475 LOG.debug('Console output not supported, cannot check if server '
476 '%s is ready.', server['server']['id'])
477 return
478
479 client = client or self.os_primary.servers_client
480
481 def system_booted():
482 console_output = client.get_console_output(server['id'])['output']
483 for line in console_output.split('\n'):
484 if 'login:' in line.lower():
485 return True
486 return False
487
488 try:
489 utils.wait_until_true(system_booted, sleep=5)
490 except utils.WaitTimeout:
491 LOG.debug("No correct output in console of server %s found. "
492 "Guest operating system status can't be checked.",
493 server['id'])
494
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400495 def check_servers_hostnames(self, servers, timeout=None, log_errors=True,
496 external_port=None):
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000497 """Compare hostnames of given servers with their names."""
498 try:
499 for server in servers:
500 kwargs = {}
nfridmand8969542020-06-02 14:59:09 +0300501 if timeout:
502 kwargs['timeout'] = timeout
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000503 try:
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400504 kwargs['port'] = external_port or (
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200505 server['port_forwarding_tcp']['external_port'])
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000506 except KeyError:
507 pass
508 ssh_client = ssh.Client(
509 self.fip['floating_ip_address'],
510 CONF.validation.image_ssh_user,
511 pkey=self.keypair['private_key'],
512 **kwargs)
513 self.assertIn(server['name'],
514 ssh_client.exec_command('hostname'))
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200515 except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000516 LOG.debug(ssh_e)
517 if log_errors:
518 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200519 self._log_local_network_status()
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000520 raise
521 except AssertionError as assert_e:
522 LOG.debug(assert_e)
523 if log_errors:
524 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200525 self._log_local_network_status()
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000526 raise
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200527
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +0100528 def ensure_nc_listen(self, ssh_client, port, protocol, echo_msg=None,
529 servers=None):
530 """Ensure that nc server listening on the given TCP/UDP port is up.
531
532 Listener is created always on remote host.
533 """
534 def spawn_and_check_process():
535 self.nc_listen(ssh_client, port, protocol, echo_msg, servers)
536 return utils.process_is_running(ssh_client, "nc")
537
538 utils.wait_until_true(spawn_and_check_process)
539
540 def nc_listen(self, ssh_client, port, protocol, echo_msg=None,
541 servers=None):
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200542 """Create nc server listening on the given TCP/UDP port.
543
544 Listener is created always on remote host.
545 """
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200546 try:
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +0100547 return ssh_client.execute_script(
548 get_ncat_server_cmd(port, protocol, echo_msg),
Flavio Fernandesb056ac22020-07-01 14:57:13 -0400549 become_root=True, combine_stderr=True)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200550 except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200551 LOG.debug(ssh_e)
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +0100552 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200553 self._log_local_network_status()
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200554 raise
555
556 def nc_client(self, ip_address, port, protocol):
557 """Check connectivity to TCP/UDP port at host via nc.
558
559 Client is always executed locally on host where tests are executed.
560 """
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +0100561 cmd = get_ncat_client_cmd(ip_address, port, protocol)
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200562 result = shell.execute_local_command(cmd)
563 self.assertEqual(0, result.exit_status)
564 return result.stdout