blob: 42bd33b990fdb234089149176f8ae00b940c7d3c [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.
Chandan Kumarc125fd12017-11-15 19:41:01 +053015import subprocess
Itzik Browne67ebb52016-05-15 05:34:41 +000016
Federico Ressibf877c82018-08-22 08:36:37 +020017from debtcollector import removals
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090018import netaddr
Assaf Muller92fdc782018-05-31 10:32:47 -040019from neutron_lib.api import validators
20from neutron_lib import constants as neutron_lib_constants
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050021from oslo_log import log
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +030022from tempest.common.utils import net_utils
Itzik Browne67ebb52016-05-15 05:34:41 +000023from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000024from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090025from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050026from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000027
Chandan Kumar667d3d32017-09-22 12:24:06 +053028from neutron_tempest_plugin.api import base as base_api
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +000029from neutron_tempest_plugin.common import ip as ip_utils
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +020030from neutron_tempest_plugin.common import shell
Chandan Kumar667d3d32017-09-22 12:24:06 +053031from neutron_tempest_plugin.common import ssh
32from neutron_tempest_plugin import config
33from neutron_tempest_plugin.scenario import constants
Itzik Browne67ebb52016-05-15 05:34:41 +000034
35CONF = config.CONF
Itzik Browne67ebb52016-05-15 05:34:41 +000036
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050037LOG = log.getLogger(__name__)
38
Itzik Browne67ebb52016-05-15 05:34:41 +000039
40class BaseTempestTestCase(base_api.BaseNetworkTest):
Itzik Browne67ebb52016-05-15 05:34:41 +000041
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000042 def create_server(self, flavor_ref, image_ref, key_name, networks,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020043 **kwargs):
Itzik Brownbac51dc2016-10-31 12:25:04 +000044 """Create a server using tempest lib
Brian Haleyaee61ac2018-10-09 20:00:27 -040045
Itzik Brownbac51dc2016-10-31 12:25:04 +000046 All the parameters are the ones used in Compute API
Roee Agiman6a0a18a2017-11-16 11:51:56 +020047 * - Kwargs that require admin privileges
Itzik Brownbac51dc2016-10-31 12:25:04 +000048
49 Args:
50 flavor_ref(str): The flavor of the server to be provisioned.
51 image_ref(str): The image of the server to be provisioned.
52 key_name(str): SSH key to to be used to connect to the
53 provisioned server.
54 networks(list): List of dictionaries where each represent
55 an interface to be attached to the server. For network
56 it should be {'uuid': network_uuid} and for port it should
57 be {'port': port_uuid}
Roee Agiman6a0a18a2017-11-16 11:51:56 +020058 kwargs:
Itzik Brownbac51dc2016-10-31 12:25:04 +000059 name(str): Name of the server to be provisioned.
60 security_groups(list): List of dictionaries where
61 the keys is 'name' and the value is the name of
62 the security group. If it's not passed the default
63 security group will be used.
Roee Agiman6a0a18a2017-11-16 11:51:56 +020064 availability_zone(str)*: The availability zone that
65 the instance will be in.
66 You can request a specific az without actually creating one,
67 Just pass 'X:Y' where X is the default availability
68 zone, and Y is the compute host name.
Itzik Brownbac51dc2016-10-31 12:25:04 +000069 """
70
Jakub Libosvarffd9b912017-11-16 09:54:14 +000071 kwargs.setdefault('name', data_utils.rand_name('server-test'))
Itzik Brownbac51dc2016-10-31 12:25:04 +000072
Jakub Libosvarffd9b912017-11-16 09:54:14 +000073 # We cannot use setdefault() here because caller could have passed
74 # security_groups=None and we don't want to pass None to
75 # client.create_server()
76 if not kwargs.get('security_groups'):
77 kwargs['security_groups'] = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +020078
Jakub Libosvarffd9b912017-11-16 09:54:14 +000079 client = self.os_primary.servers_client
80 if kwargs.get('availability_zone'):
Roee Agiman6a0a18a2017-11-16 11:51:56 +020081 client = self.os_admin.servers_client
Roee Agiman6a0a18a2017-11-16 11:51:56 +020082
Jakub Libosvarffd9b912017-11-16 09:54:14 +000083 server = client.create_server(
84 flavorRef=flavor_ref,
85 imageRef=image_ref,
86 key_name=key_name,
87 networks=networks,
88 **kwargs)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000089
90 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020091 waiters.wait_for_server_termination,
92 client,
93 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000094 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020095 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000096 server['server']['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +000097 return server
98
99 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +0000100 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
101 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200102 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000103 if not secgroup_id:
104 sgs = client.list_security_groups()['security_groups']
105 for sg in sgs:
106 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
107 secgroup_id = sg['id']
108 break
109
Itzik Brown1ef813a2016-06-06 12:56:21 +0000110 for rule in rule_list:
111 direction = rule.pop('direction')
112 client.create_security_group_rule(
113 direction=direction,
114 security_group_id=secgroup_id,
115 **rule)
116
117 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200118 def create_loginable_secgroup_rule(cls, secgroup_id=None,
119 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000120 """This rule is intended to permit inbound ssh
121
122 Allowing ssh traffic traffic from all sources, so no group_id is
123 provided.
124 Setting a group_id would only permit traffic from ports
125 belonging to the same security group.
126 """
Federico Ressi4c590d72018-10-10 14:01:08 +0200127 return cls.create_security_group_rule(
128 security_group_id=secgroup_id,
129 client=client,
130 protocol=neutron_lib_constants.PROTO_NAME_TCP,
131 direction=neutron_lib_constants.INGRESS_DIRECTION,
132 port_range_min=22,
133 port_range_max=22)
Itzik Browne67ebb52016-05-15 05:34:41 +0000134
135 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200136 def create_pingable_secgroup_rule(cls, secgroup_id=None,
137 client=None):
Federico Ressi4c590d72018-10-10 14:01:08 +0200138 """This rule is intended to permit inbound ping
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900139
Federico Ressi4c590d72018-10-10 14:01:08 +0200140 """
141 return cls.create_security_group_rule(
142 security_group_id=secgroup_id, client=client,
143 protocol=neutron_lib_constants.PROTO_NAME_ICMP,
144 direction=neutron_lib_constants.INGRESS_DIRECTION)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900145
146 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300147 def create_router_by_client(cls, is_admin=False, **kwargs):
148 kwargs.update({'router_name': data_utils.rand_name('router'),
149 'admin_state_up': True,
150 'external_network_id': CONF.network.public_network_id})
151 if not is_admin:
152 router = cls.create_router(**kwargs)
153 else:
154 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500155 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000156 cls.routers.append(router)
157 return router
158
Federico Ressibf877c82018-08-22 08:36:37 +0200159 @removals.remove(version='Stein',
160 message="Please use create_floatingip method instead of "
161 "create_and_associate_floatingip.")
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200162 def create_and_associate_floatingip(self, port_id, client=None):
163 client = client or self.os_primary.network_client
Federico Ressibf877c82018-08-22 08:36:37 +0200164 return self.create_floatingip(port_id=port_id, client=client)
Itzik Browne67ebb52016-05-15 05:34:41 +0000165
Hongbin Lu965b03d2018-04-25 22:32:30 +0000166 def create_interface(cls, server_id, port_id, client=None):
167 client = client or cls.os_primary.interfaces_client
168 body = client.create_interface(server_id, port_id=port_id)
169 return body['interfaceAttachment']
170
171 def delete_interface(cls, server_id, port_id, client=None):
172 client = client or cls.os_primary.interfaces_client
173 client.delete_interface(server_id, port_id=port_id)
174
Brian Haleyd11f4ec2019-08-13 12:09:57 -0400175 def setup_network_and_server(self, router=None, server_name=None,
176 network=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300177 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000178
179 Creating a network, subnet, router, keypair, security group
180 and a server.
181 """
Assaf Mullerd54ae6c2018-05-31 11:38:00 -0400182 self.network = network or self.create_network()
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000183 LOG.debug("Created network %s", self.network['name'])
184 self.subnet = self.create_subnet(self.network)
185 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000186
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400187 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530188 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500189 LOG.debug("Created security group %s",
190 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000191 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300192 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000193 router = self.create_router_by_client(**kwargs)
194 self.create_router_interface(router['id'], self.subnet['id'])
195 self.keypair = self.create_keypair()
196 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000197 secgroup_id=secgroup['security_group']['id'])
Assaf Muller92fdc782018-05-31 10:32:47 -0400198
199 server_kwargs = {
200 'flavor_ref': CONF.compute.flavor_ref,
201 'image_ref': CONF.compute.image_ref,
202 'key_name': self.keypair['name'],
203 'networks': [{'uuid': self.network['id']}],
204 'security_groups': [{'name': secgroup['security_group']['name']}],
205 }
206 if server_name is not None:
207 server_kwargs['name'] = server_name
208
209 self.server = self.create_server(**server_kwargs)
Federico Ressie7417b72018-05-30 05:50:58 +0200210 self.wait_for_server_active(self.server['server'])
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000211 self.port = self.client.list_ports(network_id=self.network['id'],
212 device_id=self.server[
213 'server']['id'])['ports'][0]
Federico Ressibf877c82018-08-22 08:36:37 +0200214 self.fip = self.create_floatingip(port=self.port)
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500215
216 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
217 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
218 try:
219 ssh_client.test_connection_auth()
220 except lib_exc.SSHTimeout as ssh_e:
221 LOG.debug(ssh_e)
222 self._log_console_output(servers)
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000223 self._log_local_network_status()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500224 raise
225
226 def _log_console_output(self, servers=None):
227 if not CONF.compute_feature_enabled.console_output:
228 LOG.debug('Console output not supported, cannot log')
229 return
230 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400231 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500232 servers = servers['servers']
233 for server in servers:
234 try:
235 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400236 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500237 server['id'])['output'])
238 LOG.debug('Console output for %s\nbody=\n%s',
239 server['id'], console_output)
240 except lib_exc.NotFound:
241 LOG.debug("Server %s disappeared(deleted) while looking "
242 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900243
Rodolfo Alonso Hernandez4849f002020-01-16 16:01:10 +0000244 def _log_local_network_status(self):
245 local_routes = ip_utils.IPCommand().list_routes()
246 LOG.debug('Local routes:\n%s', '\n'.join(str(r) for r in local_routes))
247 arp_table = ip_utils.arp_table()
248 LOG.debug('Local ARP table:\n%s', '\n'.join(str(r) for r in arp_table))
249
LIU Yulong68ab2452019-05-18 10:19:49 +0800250 def _check_remote_connectivity(self, source, dest, count,
251 should_succeed=True,
Assaf Muller92fdc782018-05-31 10:32:47 -0400252 nic=None, mtu=None, fragmentation=True,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100253 timeout=None, pattern=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900254 """check ping server via source ssh connection
255
256 :param source: RemoteClient: an ssh connection from which to ping
257 :param dest: and IP to ping against
LIU Yulong68ab2452019-05-18 10:19:49 +0800258 :param count: Number of ping packet(s) to send
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900259 :param should_succeed: boolean should ping succeed or not
260 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300261 :param mtu: mtu size for the packet to be sent
262 :param fragmentation: Flag for packet fragmentation
LIU Yulong68ab2452019-05-18 10:19:49 +0800263 :param timeout: Timeout for all ping packet(s) to succeed
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100264 :param pattern: hex digits included in ICMP messages
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900265 :returns: boolean -- should_succeed == ping
266 :returns: ping is false if ping failed
267 """
LIU Yulong68ab2452019-05-18 10:19:49 +0800268 def ping_host(source, host, count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300269 size=CONF.validation.ping_size, nic=None, mtu=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100270 fragmentation=True, pattern=None):
Assaf Muller92fdc782018-05-31 10:32:47 -0400271 IP_VERSION_4 = neutron_lib_constants.IP_VERSION_4
272 IP_VERSION_6 = neutron_lib_constants.IP_VERSION_6
273
274 # Use 'ping6' for IPv6 addresses, 'ping' for IPv4 and hostnames
275 ip_version = (
276 IP_VERSION_6 if netaddr.valid_ipv6(host) else IP_VERSION_4)
277 cmd = (
278 'ping6' if ip_version == IP_VERSION_6 else 'ping')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900279 if nic:
280 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300281 if mtu:
282 if not fragmentation:
283 cmd += ' -M do'
284 size = str(net_utils.get_ping_payload_size(
Assaf Muller92fdc782018-05-31 10:32:47 -0400285 mtu=mtu, ip_version=ip_version))
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100286 if pattern:
287 cmd += ' -p {pattern}'.format(pattern=pattern)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900288 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
289 return source.exec_command(cmd)
290
291 def ping_remote():
292 try:
LIU Yulong68ab2452019-05-18 10:19:49 +0800293 result = ping_host(source, dest, count, nic=nic, mtu=mtu,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100294 fragmentation=fragmentation,
295 pattern=pattern)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900296
297 except lib_exc.SSHExecCommandFailed:
298 LOG.warning('Failed to ping IP: %s via a ssh connection '
299 'from: %s.', dest, source.host)
300 return not should_succeed
301 LOG.debug('ping result: %s', result)
Assaf Muller92fdc782018-05-31 10:32:47 -0400302
303 if validators.validate_ip_address(dest) is None:
304 # Assert that the return traffic was from the correct
305 # source address.
306 from_source = 'from %s' % dest
307 self.assertIn(from_source, result)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900308 return should_succeed
309
Assaf Muller92fdc782018-05-31 10:32:47 -0400310 return test_utils.call_until_true(
311 ping_remote, timeout or CONF.validation.ping_timeout, 1)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900312
313 def check_remote_connectivity(self, source, dest, should_succeed=True,
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200314 nic=None, mtu=None, fragmentation=True,
LIU Yulong68ab2452019-05-18 10:19:49 +0800315 servers=None, timeout=None,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100316 ping_count=CONF.validation.ping_count,
317 pattern=None):
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200318 try:
319 self.assertTrue(self._check_remote_connectivity(
LIU Yulong68ab2452019-05-18 10:19:49 +0800320 source, dest, ping_count, should_succeed, nic, mtu,
321 fragmentation,
Eduardo Olivaresf2b60542020-01-30 09:37:22 +0100322 timeout=timeout, pattern=pattern))
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200323 except lib_exc.SSHTimeout as ssh_e:
324 LOG.debug(ssh_e)
325 self._log_console_output(servers)
326 raise
327 except AssertionError:
328 self._log_console_output(servers)
329 raise
Chandan Kumarc125fd12017-11-15 19:41:01 +0530330
331 def ping_ip_address(self, ip_address, should_succeed=True,
332 ping_timeout=None, mtu=None):
333 # the code is taken from tempest/scenario/manager.py in tempest git
334 timeout = ping_timeout or CONF.validation.ping_timeout
335 cmd = ['ping', '-c1', '-w1']
336
337 if mtu:
338 cmd += [
339 # don't fragment
340 '-M', 'do',
341 # ping receives just the size of ICMP payload
342 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
343 ]
344 cmd.append(ip_address)
345
346 def ping():
347 proc = subprocess.Popen(cmd,
348 stdout=subprocess.PIPE,
349 stderr=subprocess.PIPE)
350 proc.communicate()
351
352 return (proc.returncode == 0) == should_succeed
353
354 caller = test_utils.find_test_caller()
355 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
356 ' expected result is %(should_succeed)s', {
357 'caller': caller, 'ip': ip_address, 'timeout': timeout,
358 'should_succeed':
359 'reachable' if should_succeed else 'unreachable'
360 })
361 result = test_utils.call_until_true(ping, timeout, 1)
Manjeet Singh Bhatia8bbf8992019-03-04 11:59:57 -0800362
363 # To make sure ping_ip_address called by test works
364 # as expected.
365 self.assertTrue(result)
366
Chandan Kumarc125fd12017-11-15 19:41:01 +0530367 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
368 'ping result is %(result)s', {
369 'caller': caller, 'ip': ip_address, 'timeout': timeout,
370 'result': 'expected' if result else 'unexpected'
371 })
372 return result
Federico Ressie7417b72018-05-30 05:50:58 +0200373
374 def wait_for_server_status(self, server, status, client=None, **kwargs):
375 """Waits for a server to reach a given status.
376
377 :param server: mapping having schema {'id': <server_id>}
378 :param status: string status to wait for (es: 'ACTIVE')
379 :param clien: servers client (self.os_primary.servers_client as
380 default value)
381 """
382
383 client = client or self.os_primary.servers_client
384 waiters.wait_for_server_status(client, server['id'], status, **kwargs)
385
386 def wait_for_server_active(self, server, client=None):
387 """Waits for a server to reach active status.
388
389 :param server: mapping having schema {'id': <server_id>}
390 :param clien: servers client (self.os_primary.servers_client as
391 default value)
392 """
393 self.wait_for_server_status(
394 server, constants.SERVER_STATUS_ACTIVE, client)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000395
396 def check_servers_hostnames(self, servers, log_errors=True):
397 """Compare hostnames of given servers with their names."""
398 try:
399 for server in servers:
400 kwargs = {}
401 try:
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200402 kwargs['port'] = (
403 server['port_forwarding_tcp']['external_port'])
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000404 except KeyError:
405 pass
406 ssh_client = ssh.Client(
407 self.fip['floating_ip_address'],
408 CONF.validation.image_ssh_user,
409 pkey=self.keypair['private_key'],
410 **kwargs)
411 self.assertIn(server['name'],
412 ssh_client.exec_command('hostname'))
413 except lib_exc.SSHTimeout as ssh_e:
414 LOG.debug(ssh_e)
415 if log_errors:
416 self._log_console_output(servers)
417 raise
418 except AssertionError as assert_e:
419 LOG.debug(assert_e)
420 if log_errors:
421 self._log_console_output(servers)
422 raise
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200423
424 def nc_listen(self, server, ssh_client, port, protocol, echo_msg):
425 """Create nc server listening on the given TCP/UDP port.
426
427 Listener is created always on remote host.
428 """
429 udp = ''
430 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
431 udp = '-u'
Slawek Kaplonskibc200b52020-01-16 15:48:20 +0100432 cmd = "sudo nc %(udp)s -p %(port)s -lk -c echo %(msg)s &" % {
Slawek Kaplonskic4e963e2019-09-11 22:55:34 +0200433 'udp': udp, 'port': port, 'msg': echo_msg}
434 try:
435 return ssh_client.exec_command(cmd)
436 except lib_exc.SSHTimeout as ssh_e:
437 LOG.debug(ssh_e)
438 self._log_console_output([server])
439 raise
440
441 def nc_client(self, ip_address, port, protocol):
442 """Check connectivity to TCP/UDP port at host via nc.
443
444 Client is always executed locally on host where tests are executed.
445 """
446 udp = ''
447 if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
448 udp = '-u'
449 cmd = 'echo "knock knock" | nc -w 1 %(udp)s %(host)s %(port)s' % {
450 'udp': udp, 'host': ip_address, 'port': port}
451 result = shell.execute_local_command(cmd)
452 self.assertEqual(0, result.exit_status)
453 return result.stdout