blob: 078a515f4848c51c735576f0338c7b17360b82b5 [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.
Rodolfo Alonso Hernandez13546b82022-05-12 23:56:47 +000015
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010016import 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
Rodolfo Alonso Hernandez13546b82022-05-12 23:56:47 +000024from packaging import version as packaging_version
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +020025from paramiko import ssh_exception as ssh_exc
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +030026from tempest.common.utils import net_utils
Itzik Browne67ebb52016-05-15 05:34:41 +000027from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000028from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090029from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050030from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000031
Chandan Kumar667d3d32017-09-22 12:24:06 +053032from neutron_tempest_plugin.api import base as base_api
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +000033from neutron_tempest_plugin.common import ip as ip_utils
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +020034from neutron_tempest_plugin.common import shell
Chandan Kumar667d3d32017-09-22 12:24:06 +053035from neutron_tempest_plugin.common import ssh
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010036from neutron_tempest_plugin.common import utils
Chandan Kumar667d3d32017-09-22 12:24:06 +053037from neutron_tempest_plugin import config
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010038from neutron_tempest_plugin import exceptions
Chandan Kumar667d3d32017-09-22 12:24:06 +053039from neutron_tempest_plugin.scenario import constants
Itzik Browne67ebb52016-05-15 05:34:41 +000040
41CONF = config.CONF
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050042LOG = log.getLogger(__name__)
Eduardo Olivares46fa4242022-04-18 12:47:43 +020043SSH_EXC_TUPLE = (lib_exc.SSHTimeout,
44 ssh_exc.AuthenticationException,
45 ssh_exc.NoValidConnectionsError,
46 ConnectionResetError)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050047
Itzik Browne67ebb52016-05-15 05:34:41 +000048
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010049def get_ncat_version(ssh_client=None):
50 cmd = "ncat --version 2>&1"
51 try:
52 version_result = shell.execute(cmd, ssh_client=ssh_client).stdout
53 except exceptions.ShellCommandFailed:
54 m = None
55 else:
56 m = re.match(r"Ncat: Version ([\d.]+) *.", version_result)
57 # NOTE(slaweq): by default lets assume we have ncat 7.60 which is in Ubuntu
58 # 18.04 which is used on u/s gates
Rodolfo Alonso Hernandez13546b82022-05-12 23:56:47 +000059 return packaging_version.Version(m.group(1) if m else '7.60')
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010060
61
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010062def get_ncat_server_cmd(port, protocol, msg=None):
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010063 udp = ''
64 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
65 udp = '-u'
66 cmd = "nc %(udp)s -p %(port)s -lk " % {
67 'udp': udp, 'port': port}
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010068 if msg:
69 if CONF.neutron_plugin_options.default_image_is_advanced:
Flavio Fernandesb056ac22020-07-01 14:57:13 -040070 cmd += "-c 'echo %s' " % msg
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010071 else:
Flavio Fernandesb056ac22020-07-01 14:57:13 -040072 cmd += "-e echo %s " % msg
73 cmd += "< /dev/zero &{0}sleep 0.1{0}".format('\n')
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010074 return cmd
75
76
Slawek Kaplonski663bbc22023-04-05 16:30:20 +020077def get_ncat_client_cmd(ip_address, port, protocol, ssh_client=None):
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010078 cmd = 'echo "knock knock" | nc '
Slawek Kaplonskidd5b0e62023-05-31 13:57:25 +020079 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
80 cmd += '-u '
81 ncat_version = get_ncat_version(ssh_client=ssh_client)
82 if ncat_version > packaging_version.Version('7.60'):
83 cmd += '-z '
84 cmd += '-w 1 %(host)s %(port)s' % {'host': ip_address, 'port': port}
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +010085 return cmd
86
87
Itzik Browne67ebb52016-05-15 05:34:41 +000088class BaseTempestTestCase(base_api.BaseNetworkTest):
Itzik Browne67ebb52016-05-15 05:34:41 +000089
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000090 def create_server(self, flavor_ref, image_ref, key_name, networks,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020091 **kwargs):
Itzik Brownbac51dc2016-10-31 12:25:04 +000092 """Create a server using tempest lib
Brian Haleyaee61ac2018-10-09 20:00:27 -040093
Itzik Brownbac51dc2016-10-31 12:25:04 +000094 All the parameters are the ones used in Compute API
Roee Agiman6a0a18a2017-11-16 11:51:56 +020095 * - Kwargs that require admin privileges
Itzik Brownbac51dc2016-10-31 12:25:04 +000096
97 Args:
98 flavor_ref(str): The flavor of the server to be provisioned.
99 image_ref(str): The image of the server to be provisioned.
100 key_name(str): SSH key to to be used to connect to the
101 provisioned server.
102 networks(list): List of dictionaries where each represent
103 an interface to be attached to the server. For network
104 it should be {'uuid': network_uuid} and for port it should
105 be {'port': port_uuid}
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200106 kwargs:
Itzik Brownbac51dc2016-10-31 12:25:04 +0000107 name(str): Name of the server to be provisioned.
108 security_groups(list): List of dictionaries where
109 the keys is 'name' and the value is the name of
110 the security group. If it's not passed the default
111 security group will be used.
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200112 availability_zone(str)*: The availability zone that
113 the instance will be in.
114 You can request a specific az without actually creating one,
115 Just pass 'X:Y' where X is the default availability
116 zone, and Y is the compute host name.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000117 """
118
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000119 kwargs.setdefault('name', data_utils.rand_name('server-test'))
Itzik Brownbac51dc2016-10-31 12:25:04 +0000120
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000121 # We cannot use setdefault() here because caller could have passed
122 # security_groups=None and we don't want to pass None to
123 # client.create_server()
124 if not kwargs.get('security_groups'):
125 kwargs['security_groups'] = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200126
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200127 client = kwargs.pop('client', None)
128 if client is None:
129 client = self.os_primary.servers_client
130 if kwargs.get('availability_zone'):
131 client = self.os_admin.servers_client
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200132
Jakub Libosvarffd9b912017-11-16 09:54:14 +0000133 server = client.create_server(
134 flavorRef=flavor_ref,
135 imageRef=image_ref,
136 key_name=key_name,
137 networks=networks,
138 **kwargs)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000139
140 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200141 waiters.wait_for_server_termination,
142 client,
143 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000144 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200145 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000146 server['server']['id'])
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200147
148 self.wait_for_server_active(server['server'], client=client)
149 self.wait_for_guest_os_ready(server['server'], client=client)
150
Itzik Browne67ebb52016-05-15 05:34:41 +0000151 return server
152
153 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +0000154 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
155 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200156 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000157 if not secgroup_id:
158 sgs = client.list_security_groups()['security_groups']
159 for sg in sgs:
160 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
161 secgroup_id = sg['id']
162 break
Hang Yange6e0ccf2021-02-26 15:07:05 -0600163 resp = []
Itzik Brown1ef813a2016-06-06 12:56:21 +0000164 for rule in rule_list:
165 direction = rule.pop('direction')
Hang Yange6e0ccf2021-02-26 15:07:05 -0600166 resp.append(client.create_security_group_rule(
167 direction=direction,
168 security_group_id=secgroup_id,
169 **rule)['security_group_rule'])
170 return resp
Itzik Brown1ef813a2016-06-06 12:56:21 +0000171
172 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200173 def create_loginable_secgroup_rule(cls, secgroup_id=None,
174 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000175 """This rule is intended to permit inbound ssh
176
177 Allowing ssh traffic traffic from all sources, so no group_id is
178 provided.
179 Setting a group_id would only permit traffic from ports
180 belonging to the same security group.
181 """
Federico Ressi4c590d72018-10-10 14:01:08 +0200182 return cls.create_security_group_rule(
183 security_group_id=secgroup_id,
184 client=client,
185 protocol=neutron_lib_constants.PROTO_NAME_TCP,
186 direction=neutron_lib_constants.INGRESS_DIRECTION,
187 port_range_min=22,
188 port_range_max=22)
Itzik Browne67ebb52016-05-15 05:34:41 +0000189
190 @classmethod
Slawek Kaplonski83979b92022-12-15 14:15:12 +0100191 def create_ingress_metadata_secgroup_rule(cls, secgroup_id=None):
192 """This rule is intended to permit inbound metadata traffic
193
194 Allowing ingress traffic from metadata server, required only for
195 stateless security groups.
196 """
Maor Blaustein0ea053c2023-02-16 17:23:53 +0200197 # NOTE(slaweq): in case of stateless security groups, there is no
198 # "related" or "established" traffic matching at all so even if
199 # egress traffic to 169.254.169.254 is allowed by default SG, we
200 # need to explicitly allow ingress traffic from the metadata server
201 # to be able to receive responses in the guest vm
202 cls.create_security_group_rule(
203 security_group_id=secgroup_id,
204 direction=neutron_lib_constants.INGRESS_DIRECTION,
205 protocol=neutron_lib_constants.PROTO_NAME_TCP,
206 remote_ip_prefix='169.254.169.254/32',
207 description='metadata out'
208 )
Slawek Kaplonski83979b92022-12-15 14:15:12 +0100209
210 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200211 def create_pingable_secgroup_rule(cls, secgroup_id=None,
212 client=None):
Federico Ressi4c590d72018-10-10 14:01:08 +0200213 """This rule is intended to permit inbound ping
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900214
Federico Ressi4c590d72018-10-10 14:01:08 +0200215 """
216 return cls.create_security_group_rule(
217 security_group_id=secgroup_id, client=client,
218 protocol=neutron_lib_constants.PROTO_NAME_ICMP,
219 direction=neutron_lib_constants.INGRESS_DIRECTION)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900220
221 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300222 def create_router_by_client(cls, is_admin=False, **kwargs):
223 kwargs.update({'router_name': data_utils.rand_name('router'),
224 'admin_state_up': True,
225 'external_network_id': CONF.network.public_network_id})
226 if not is_admin:
227 router = cls.create_router(**kwargs)
228 else:
229 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500230 LOG.debug("Created router %s", router['name'])
Slawek Kaplonskiedf3cba2021-04-21 10:34:02 +0200231 cls._wait_for_router_ha_active(router['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000232 return router
233
ccamposre6b10042021-02-12 12:26:08 +0100234 @classmethod
Slawek Kaplonskiedf3cba2021-04-21 10:34:02 +0200235 def _wait_for_router_ha_active(cls, router_id):
236 router = cls.os_admin.network_client.show_router(router_id)['router']
237 if not router.get('ha'):
238 return
239
240 def _router_active_on_l3_agent():
241 agents = cls.os_admin.network_client.list_l3_agents_hosting_router(
242 router_id)['agents']
243 return "active" in [agent['ha_state'] for agent in agents]
244
245 error_msg = (
246 "Router %s is not active on any of the L3 agents" % router_id)
Slawek Kaplonskibb1532a2021-06-17 13:45:56 +0000247 # NOTE(slaweq): timeout here should be lower for sure, but due to
248 # the bug https://launchpad.net/bugs/1923633 let's wait even 10
249 # minutes until router will be active on some of the L3 agents
250 utils.wait_until_true(_router_active_on_l3_agent,
251 timeout=600, sleep=5,
252 exception=lib_exc.TimeoutException(error_msg))
Slawek Kaplonskiedf3cba2021-04-21 10:34:02 +0200253
254 @classmethod
ccamposre6b10042021-02-12 12:26:08 +0100255 def skip_if_no_extension_enabled_in_l3_agents(cls, extension):
256 l3_agents = cls.os_admin.network_client.list_agents(
257 binary='neutron-l3-agent')['agents']
258 if not l3_agents:
259 # the tests should not be skipped when neutron-l3-agent does not
260 # exist (this validation doesn't apply to the setups like
261 # e.g. ML2/OVN)
262 return
263 for agent in l3_agents:
264 if extension in agent['configurations'].get('extensions', []):
265 return
266 raise cls.skipTest("No L3 agent with '%s' extension enabled found." %
267 extension)
268
Federico Ressibf877c82018-08-22 08:36:37 +0200269 @removals.remove(version='Stein',
270 message="Please use create_floatingip method instead of "
271 "create_and_associate_floatingip.")
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200272 def create_and_associate_floatingip(self, port_id, client=None):
273 client = client or self.os_primary.network_client
Federico Ressibf877c82018-08-22 08:36:37 +0200274 return self.create_floatingip(port_id=port_id, client=client)
Itzik Browne67ebb52016-05-15 05:34:41 +0000275
Hongbin Lu965b03d2018-04-25 22:32:30 +0000276 def create_interface(cls, server_id, port_id, client=None):
277 client = client or cls.os_primary.interfaces_client
278 body = client.create_interface(server_id, port_id=port_id)
279 return body['interfaceAttachment']
280
281 def delete_interface(cls, server_id, port_id, client=None):
282 client = client or cls.os_primary.interfaces_client
283 client.delete_interface(server_id, port_id=port_id)
284
Brian Haleyd11f4ec2019-08-13 12:09:57 -0400285 def setup_network_and_server(self, router=None, server_name=None,
286 network=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300287 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000288
289 Creating a network, subnet, router, keypair, security group
290 and a server.
291 """
Assaf Mullerd54ae6c2018-05-31 11:38:00 -0400292 self.network = network or self.create_network()
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000293 LOG.debug("Created network %s", self.network['name'])
294 self.subnet = self.create_subnet(self.network)
295 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000296
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400297 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530298 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500299 LOG.debug("Created security group %s",
300 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000301 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300302 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000303 router = self.create_router_by_client(**kwargs)
304 self.create_router_interface(router['id'], self.subnet['id'])
305 self.keypair = self.create_keypair()
306 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000307 secgroup_id=secgroup['security_group']['id'])
Assaf Muller92fdc782018-05-31 10:32:47 -0400308
309 server_kwargs = {
310 'flavor_ref': CONF.compute.flavor_ref,
311 'image_ref': CONF.compute.image_ref,
312 'key_name': self.keypair['name'],
313 'networks': [{'uuid': self.network['id']}],
314 'security_groups': [{'name': secgroup['security_group']['name']}],
315 }
316 if server_name is not None:
317 server_kwargs['name'] = server_name
318
319 self.server = self.create_server(**server_kwargs)
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000320 self.port = self.client.list_ports(network_id=self.network['id'],
321 device_id=self.server[
322 'server']['id'])['ports'][0]
Federico Ressibf877c82018-08-22 08:36:37 +0200323 self.fip = self.create_floatingip(port=self.port)
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500324
Bence Romsics2abbc922020-09-30 16:10:07 +0200325 def check_connectivity(self, host, ssh_user=None, ssh_key=None,
326 servers=None, ssh_timeout=None, ssh_client=None):
327 # Either ssh_client or ssh_user+ssh_key is mandatory.
328 if ssh_client is None:
329 ssh_client = ssh.Client(host, ssh_user,
330 pkey=ssh_key, timeout=ssh_timeout)
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500331 try:
332 ssh_client.test_connection_auth()
Eduardo Olivares46fa4242022-04-18 12:47:43 +0200333 except SSH_EXC_TUPLE as ssh_e:
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500334 LOG.debug(ssh_e)
335 self._log_console_output(servers)
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000336 self._log_local_network_status()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500337 raise
338
339 def _log_console_output(self, servers=None):
340 if not CONF.compute_feature_enabled.console_output:
341 LOG.debug('Console output not supported, cannot log')
342 return
343 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400344 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500345 servers = servers['servers']
346 for server in servers:
Slawek Kaplonskicff79232020-03-03 14:12:18 +0100347 # NOTE(slaweq): sometimes servers are passed in dictionary with
348 # "server" key as first level key and in other cases it may be that
349 # it is just the "inner" dict without "server" key. Lets try to
350 # handle both cases
351 server = server.get("server") or server
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500352 try:
353 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400354 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500355 server['id'])['output'])
356 LOG.debug('Console output for %s\nbody=\n%s',
357 server['id'], console_output)
358 except lib_exc.NotFound:
359 LOG.debug("Server %s disappeared(deleted) while looking "
360 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900361
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000362 def _log_local_network_status(self):
Slawek Kaplonski8033af72020-05-05 12:01:37 +0200363 self._log_ns_network_status()
364 for ns_name in ip_utils.IPCommand().list_namespaces():
365 self._log_ns_network_status(ns_name=ns_name)
366
367 def _log_ns_network_status(self, ns_name=None):
Rodolfo Alonso Hernandez9817d4f2020-11-17 08:50:50 +0000368 try:
369 local_ips = ip_utils.IPCommand(namespace=ns_name).list_addresses()
370 local_routes = ip_utils.IPCommand(namespace=ns_name).list_routes()
Rodolfo Alonso Hernandezc134ea92021-04-14 15:15:01 +0000371 arp_table = ip_utils.arp_table(namespace=ns_name)
372 iptables = ip_utils.list_iptables(namespace=ns_name)
373 lsockets = ip_utils.list_listening_sockets(namespace=ns_name)
Rodolfo Alonso Hernandez9817d4f2020-11-17 08:50:50 +0000374 except exceptions.ShellCommandFailed:
375 LOG.debug('Namespace %s has been deleted synchronously during the '
376 'host network collection process', ns_name)
377 return
378
Slawek Kaplonski8033af72020-05-05 12:01:37 +0200379 LOG.debug('Namespace %s; IP Addresses:\n%s',
380 ns_name, '\n'.join(str(r) for r in local_ips))
Slawek Kaplonski8033af72020-05-05 12:01:37 +0200381 LOG.debug('Namespace %s; Local routes:\n%s',
382 ns_name, '\n'.join(str(r) for r in local_routes))
Slawek Kaplonski8033af72020-05-05 12:01:37 +0200383 LOG.debug('Namespace %s; Local ARP table:\n%s',
384 ns_name, '\n'.join(str(r) for r in arp_table))
Rodolfo Alonso Hernandezc134ea92021-04-14 15:15:01 +0000385 LOG.debug('Namespace %s; Local iptables:\n%s', ns_name, iptables)
386 LOG.debug('Namespace %s; Listening sockets:\n%s', ns_name, lsockets)
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000387
LIU Yulong68ab2452019-05-18 10:19:49 +0800388 def _check_remote_connectivity(self, source, dest, count,
389 should_succeed=True,
Assaf Muller92fdc782018-05-31 10:32:47 -0400390 nic=None, mtu=None, fragmentation=True,
Roman Safronov12663cf2020-07-27 13:11:07 +0300391 timeout=None, pattern=None,
Nurmatov Mamatisa1b1c9d32021-12-27 15:37:03 +0300392 forbid_packet_loss=False,
393 check_response_ip=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900394 """check ping server via source ssh connection
395
396 :param source: RemoteClient: an ssh connection from which to ping
397 :param dest: and IP to ping against
LIU Yulong68ab2452019-05-18 10:19:49 +0800398 :param count: Number of ping packet(s) to send
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900399 :param should_succeed: boolean should ping succeed or not
400 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300401 :param mtu: mtu size for the packet to be sent
402 :param fragmentation: Flag for packet fragmentation
LIU Yulong68ab2452019-05-18 10:19:49 +0800403 :param timeout: Timeout for all ping packet(s) to succeed
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100404 :param pattern: hex digits included in ICMP messages
Roman Safronov12663cf2020-07-27 13:11:07 +0300405 :param forbid_packet_loss: forbid or allow some lost packets
Nurmatov Mamatisa1b1c9d32021-12-27 15:37:03 +0300406 :param check_response_ip: check response ip
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900407 :returns: boolean -- should_succeed == ping
408 :returns: ping is false if ping failed
409 """
LIU Yulong68ab2452019-05-18 10:19:49 +0800410 def ping_host(source, host, count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300411 size=CONF.validation.ping_size, nic=None, mtu=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100412 fragmentation=True, pattern=None):
Assaf Muller92fdc782018-05-31 10:32:47 -0400413 IP_VERSION_4 = neutron_lib_constants.IP_VERSION_4
414 IP_VERSION_6 = neutron_lib_constants.IP_VERSION_6
415
416 # Use 'ping6' for IPv6 addresses, 'ping' for IPv4 and hostnames
417 ip_version = (
418 IP_VERSION_6 if netaddr.valid_ipv6(host) else IP_VERSION_4)
419 cmd = (
420 'ping6' if ip_version == IP_VERSION_6 else 'ping')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900421 if nic:
422 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300423 if mtu:
424 if not fragmentation:
425 cmd += ' -M do'
426 size = str(net_utils.get_ping_payload_size(
Assaf Muller92fdc782018-05-31 10:32:47 -0400427 mtu=mtu, ip_version=ip_version))
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100428 if pattern:
429 cmd += ' -p {pattern}'.format(pattern=pattern)
Maciej Józefczyk3c324e02020-03-16 10:52:08 +0000430 cmd += ' -c{0} -W{0} -s{1} {2}'.format(count, size, host)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900431 return source.exec_command(cmd)
432
433 def ping_remote():
434 try:
LIU Yulong68ab2452019-05-18 10:19:49 +0800435 result = ping_host(source, dest, count, nic=nic, mtu=mtu,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100436 fragmentation=fragmentation,
437 pattern=pattern)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900438
439 except lib_exc.SSHExecCommandFailed:
440 LOG.warning('Failed to ping IP: %s via a ssh connection '
441 'from: %s.', dest, source.host)
442 return not should_succeed
443 LOG.debug('ping result: %s', result)
Assaf Muller92fdc782018-05-31 10:32:47 -0400444
Roman Safronov12663cf2020-07-27 13:11:07 +0300445 if forbid_packet_loss and ' 0% packet loss' not in result:
446 LOG.debug('Packet loss detected')
447 return not should_succeed
448
Nurmatov Mamatisa1b1c9d32021-12-27 15:37:03 +0300449 if (check_response_ip and
450 validators.validate_ip_address(dest) is None):
Assaf Muller92fdc782018-05-31 10:32:47 -0400451 # Assert that the return traffic was from the correct
452 # source address.
453 from_source = 'from %s' % dest
454 self.assertIn(from_source, result)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900455 return should_succeed
456
Assaf Muller92fdc782018-05-31 10:32:47 -0400457 return test_utils.call_until_true(
458 ping_remote, timeout or CONF.validation.ping_timeout, 1)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900459
460 def check_remote_connectivity(self, source, dest, should_succeed=True,
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200461 nic=None, mtu=None, fragmentation=True,
LIU Yulong68ab2452019-05-18 10:19:49 +0800462 servers=None, timeout=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100463 ping_count=CONF.validation.ping_count,
Nurmatov Mamatisa1b1c9d32021-12-27 15:37:03 +0300464 pattern=None, forbid_packet_loss=False,
465 check_response_ip=True):
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200466 try:
467 self.assertTrue(self._check_remote_connectivity(
LIU Yulong68ab2452019-05-18 10:19:49 +0800468 source, dest, ping_count, should_succeed, nic, mtu,
469 fragmentation,
Roman Safronov12663cf2020-07-27 13:11:07 +0300470 timeout=timeout, pattern=pattern,
Nurmatov Mamatisa1b1c9d32021-12-27 15:37:03 +0300471 forbid_packet_loss=forbid_packet_loss,
472 check_response_ip=check_response_ip))
Eduardo Olivares46fa4242022-04-18 12:47:43 +0200473 except SSH_EXC_TUPLE as ssh_e:
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200474 LOG.debug(ssh_e)
475 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200476 self._log_local_network_status()
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200477 raise
478 except AssertionError:
479 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200480 self._log_local_network_status()
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200481 raise
Chandan Kumarc125fd12017-11-15 19:41:01 +0530482
483 def ping_ip_address(self, ip_address, should_succeed=True,
484 ping_timeout=None, mtu=None):
485 # the code is taken from tempest/scenario/manager.py in tempest git
486 timeout = ping_timeout or CONF.validation.ping_timeout
487 cmd = ['ping', '-c1', '-w1']
488
489 if mtu:
490 cmd += [
491 # don't fragment
492 '-M', 'do',
493 # ping receives just the size of ICMP payload
494 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
495 ]
496 cmd.append(ip_address)
497
498 def ping():
499 proc = subprocess.Popen(cmd,
500 stdout=subprocess.PIPE,
501 stderr=subprocess.PIPE)
502 proc.communicate()
503
504 return (proc.returncode == 0) == should_succeed
505
506 caller = test_utils.find_test_caller()
507 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
508 ' expected result is %(should_succeed)s', {
509 'caller': caller, 'ip': ip_address, 'timeout': timeout,
510 'should_succeed':
511 'reachable' if should_succeed else 'unreachable'
512 })
513 result = test_utils.call_until_true(ping, timeout, 1)
Manjeet Singh Bhatia8bbf8992019-03-04 11:59:57 -0800514
515 # To make sure ping_ip_address called by test works
516 # as expected.
517 self.assertTrue(result)
518
Chandan Kumarc125fd12017-11-15 19:41:01 +0530519 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
520 'ping result is %(result)s', {
521 'caller': caller, 'ip': ip_address, 'timeout': timeout,
522 'result': 'expected' if result else 'unexpected'
523 })
524 return result
Federico Ressie7417b72018-05-30 05:50:58 +0200525
526 def wait_for_server_status(self, server, status, client=None, **kwargs):
527 """Waits for a server to reach a given status.
528
529 :param server: mapping having schema {'id': <server_id>}
530 :param status: string status to wait for (es: 'ACTIVE')
531 :param clien: servers client (self.os_primary.servers_client as
532 default value)
533 """
534
535 client = client or self.os_primary.servers_client
536 waiters.wait_for_server_status(client, server['id'], status, **kwargs)
537
538 def wait_for_server_active(self, server, client=None):
539 """Waits for a server to reach active status.
540
541 :param server: mapping having schema {'id': <server_id>}
542 :param clien: servers client (self.os_primary.servers_client as
543 default value)
544 """
545 self.wait_for_server_status(
546 server, constants.SERVER_STATUS_ACTIVE, client)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000547
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200548 def wait_for_guest_os_ready(self, server, client=None):
549 if not CONF.compute_feature_enabled.console_output:
550 LOG.debug('Console output not supported, cannot check if server '
Rodolfo Alonso Hernandezdff870b2020-11-06 08:41:44 +0000551 '%s is ready.', server['id'])
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200552 return
553
554 client = client or self.os_primary.servers_client
555
556 def system_booted():
557 console_output = client.get_console_output(server['id'])['output']
558 for line in console_output.split('\n'):
559 if 'login:' in line.lower():
560 return True
561 return False
562
563 try:
ccamposr1bd28ae2021-11-29 10:06:02 +0100564 utils.wait_until_true(system_booted, timeout=90, sleep=5)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200565 except utils.WaitTimeout:
566 LOG.debug("No correct output in console of server %s found. "
567 "Guest operating system status can't be checked.",
568 server['id'])
569
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400570 def check_servers_hostnames(self, servers, timeout=None, log_errors=True,
571 external_port=None):
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000572 """Compare hostnames of given servers with their names."""
573 try:
574 for server in servers:
575 kwargs = {}
nfridmand8969542020-06-02 14:59:09 +0300576 if timeout:
577 kwargs['timeout'] = timeout
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000578 try:
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400579 kwargs['port'] = external_port or (
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200580 server['port_forwarding_tcp']['external_port'])
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000581 except KeyError:
582 pass
583 ssh_client = ssh.Client(
584 self.fip['floating_ip_address'],
585 CONF.validation.image_ssh_user,
586 pkey=self.keypair['private_key'],
587 **kwargs)
588 self.assertIn(server['name'],
Rodolfo Alonso Hernandezaf394dd2020-11-12 14:26:13 +0000589 ssh_client.get_hostname())
Eduardo Olivares46fa4242022-04-18 12:47:43 +0200590 except SSH_EXC_TUPLE as ssh_e:
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000591 LOG.debug(ssh_e)
592 if log_errors:
593 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200594 self._log_local_network_status()
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000595 raise
596 except AssertionError as assert_e:
597 LOG.debug(assert_e)
598 if log_errors:
599 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200600 self._log_local_network_status()
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000601 raise
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200602
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +0100603 def ensure_nc_listen(self, ssh_client, port, protocol, echo_msg=None,
604 servers=None):
605 """Ensure that nc server listening on the given TCP/UDP port is up.
606
607 Listener is created always on remote host.
608 """
609 def spawn_and_check_process():
610 self.nc_listen(ssh_client, port, protocol, echo_msg, servers)
611 return utils.process_is_running(ssh_client, "nc")
612
613 utils.wait_until_true(spawn_and_check_process)
614
615 def nc_listen(self, ssh_client, port, protocol, echo_msg=None,
616 servers=None):
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200617 """Create nc server listening on the given TCP/UDP port.
618
619 Listener is created always on remote host.
620 """
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200621 try:
Slawek Kaplonskiaf83e832020-02-05 12:11:54 +0100622 return ssh_client.execute_script(
623 get_ncat_server_cmd(port, protocol, echo_msg),
Flavio Fernandesb056ac22020-07-01 14:57:13 -0400624 become_root=True, combine_stderr=True)
Eduardo Olivares46fa4242022-04-18 12:47:43 +0200625 except SSH_EXC_TUPLE as ssh_e:
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200626 LOG.debug(ssh_e)
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +0100627 self._log_console_output(servers)
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200628 self._log_local_network_status()
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200629 raise
630
Slawek Kaplonskib327a632023-02-23 07:59:12 +0100631 def nc_client(self, ip_address, port, protocol, ssh_client=None):
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200632 """Check connectivity to TCP/UDP port at host via nc.
633
Slawek Kaplonskib327a632023-02-23 07:59:12 +0100634 If ssh_client is not given, it is executed locally on host where tests
635 are executed. Otherwise ssh_client object is used to execute it.
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200636 """
Slawek Kaplonski663bbc22023-04-05 16:30:20 +0200637 cmd = get_ncat_client_cmd(ip_address, port, protocol,
638 ssh_client=ssh_client)
Slawek Kaplonski03bd62e2023-06-02 10:54:05 +0200639 result = shell.execute(cmd, ssh_client=ssh_client, check=False)
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200640 return result.stdout
elajkat1f275e42021-10-15 12:47:55 +0200641
642 def _ensure_public_router(self, client=None, tenant_id=None):
643 """Retrieve a router for the given tenant id.
644
645 If a public router has been configured, it will be returned.
646
647 If a public router has not been configured, but a public
648 network has, a tenant router will be created and returned that
649 routes traffic to the public network.
650 """
651 if not client:
652 client = self.client
653 if not tenant_id:
Takashi Kajinamida451772023-03-22 00:19:39 +0900654 tenant_id = client.project_id
elajkat1f275e42021-10-15 12:47:55 +0200655 router_id = CONF.network.public_router_id
656 network_id = CONF.network.public_network_id
657 if router_id:
658 body = client.show_router(router_id)
659 return body['router']
660 elif network_id:
661 router = self.create_router_by_client()
662 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
663 client.delete_router, router['id'])
664 kwargs = {'external_gateway_info': dict(network_id=network_id)}
665 router = client.update_router(router['id'], **kwargs)['router']
666 return router
667 else:
668 raise Exception("Neither of 'public_router_id' or "
669 "'public_network_id' has been defined.")
670
671 def _update_router_admin_state(self, router, admin_state_up):
672 kwargs = dict(admin_state_up=admin_state_up)
673 router = self.client.update_router(
674 router['id'], **kwargs)['router']
675 self.assertEqual(admin_state_up, router['admin_state_up'])
Slawek Kaplonski32b0f8b2023-03-10 17:03:20 +0100676
677 def _check_cmd_installed_on_server(self, ssh_client, server, cmd):
678 try:
679 ssh_client.execute_script('which %s' % cmd)
680 except SSH_EXC_TUPLE as ssh_e:
681 LOG.debug(ssh_e)
682 self._log_console_output([server])
683 self._log_local_network_status()
684 raise
685 except exceptions.SSHScriptFailed:
686 raise self.skipException(
687 "%s is not available on server %s" % (cmd, server['id']))