blob: fdd81a57ba60309f11cf230a174b3cfaaab20984 [file] [log] [blame]
Andrea Frittolif4510a12017-03-07 19:17:11 +00001# Copyright 2012 OpenStack Foundation
2# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Andrea Frittolif4510a12017-03-07 19:17:11 +000017import netaddr
18from oslo_log import log
Andrea Frittolif4510a12017-03-07 19:17:11 +000019from oslo_utils import netutils
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -070020from oslo_utils import uuidutils
Andrea Frittolif4510a12017-03-07 19:17:11 +000021from tempest.common import image as common_image
Andrea Frittolif4510a12017-03-07 19:17:11 +000022from tempest import config
23from tempest import exceptions
Ken'ichi Ohmichi02d1f242017-03-12 18:56:27 -070024from tempest.lib.common.utils import data_utils
Andrea Frittolif4510a12017-03-07 19:17:11 +000025from tempest.lib.common.utils import test_utils
26from tempest.lib import exceptions as lib_exc
Roman Popelka290ef292022-02-28 10:41:04 +010027from tempest.scenario import manager
Andrea Frittolif4510a12017-03-07 19:17:11 +000028
29CONF = config.CONF
30
31LOG = log.getLogger(__name__)
32
33
Roman Popelka1118f3e2022-03-21 09:18:53 +010034class ScenarioTest(manager.NetworkScenarioTest):
Andrea Frittolif4510a12017-03-07 19:17:11 +000035 """Base class for scenario tests. Uses tempest own clients. """
36
37 credentials = ['primary']
38
39 @classmethod
40 def setup_clients(cls):
41 super(ScenarioTest, cls).setup_clients()
42 # Clients (in alphabetical order)
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070043 cls.flavors_client = cls.os_primary.flavors_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000044 cls.compute_floating_ips_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070045 cls.os_primary.compute_floating_ips_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000046 if CONF.service_available.glance:
47 # Check if glance v1 is available to determine which client to use.
48 if CONF.image_feature_enabled.api_v1:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070049 cls.image_client = cls.os_primary.image_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000050 elif CONF.image_feature_enabled.api_v2:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070051 cls.image_client = cls.os_primary.image_client_v2
Andrea Frittolif4510a12017-03-07 19:17:11 +000052 else:
53 raise lib_exc.InvalidConfiguration(
54 'Either api_v1 or api_v2 must be True in '
55 '[image-feature-enabled].')
56 # Compute image client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070057 cls.compute_images_client = cls.os_primary.compute_images_client
58 cls.keypairs_client = cls.os_primary.keypairs_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000059 # Nova security groups client
60 cls.compute_security_groups_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070061 cls.os_primary.compute_security_groups_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000062 cls.compute_security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070063 cls.os_primary.compute_security_group_rules_client)
64 cls.servers_client = cls.os_primary.servers_client
65 cls.interface_client = cls.os_primary.interfaces_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000066 # Neutron network client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070067 cls.networks_client = cls.os_primary.networks_client
68 cls.ports_client = cls.os_primary.ports_client
69 cls.routers_client = cls.os_primary.routers_client
70 cls.subnets_client = cls.os_primary.subnets_client
71 cls.floating_ips_client = cls.os_primary.floating_ips_client
72 cls.security_groups_client = cls.os_primary.security_groups_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000073 cls.security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070074 cls.os_primary.security_group_rules_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000075
Andrea Frittolif4510a12017-03-07 19:17:11 +000076 # ## Test functions library
77 #
78 # The create_[resource] functions only return body and discard the
79 # resp part which is not used in scenario tests
80
Andrea Frittolif4510a12017-03-07 19:17:11 +000081 def _create_loginable_secgroup_rule(self, secgroup_id=None):
82 _client = self.compute_security_groups_client
83 _client_rules = self.compute_security_group_rules_client
84 if secgroup_id is None:
85 sgs = _client.list_security_groups()['security_groups']
86 for sg in sgs:
87 if sg['name'] == 'default':
88 secgroup_id = sg['id']
89
90 # These rules are intended to permit inbound ssh and icmp
91 # traffic from all sources, so no group_id is provided.
92 # Setting a group_id would only permit traffic from ports
93 # belonging to the same security group.
94 rulesets = [
95 {
96 # ssh
97 'ip_protocol': 'tcp',
98 'from_port': 22,
99 'to_port': 22,
100 'cidr': '0.0.0.0/0',
101 },
102 {
103 # ping
104 'ip_protocol': 'icmp',
105 'from_port': -1,
106 'to_port': -1,
107 'cidr': '0.0.0.0/0',
108 }
109 ]
110 rules = list()
111 for ruleset in rulesets:
112 sg_rule = _client_rules.create_security_group_rule(
113 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
114 rules.append(sg_rule)
115 return rules
116
117 def _create_security_group(self):
118 # Create security group
119 sg_name = data_utils.rand_name(self.__class__.__name__)
120 sg_desc = sg_name + " description"
121 secgroup = self.compute_security_groups_client.create_security_group(
122 name=sg_name, description=sg_desc)['security_group']
123 self.assertEqual(secgroup['name'], sg_name)
124 self.assertEqual(secgroup['description'], sg_desc)
125 self.addCleanup(
126 test_utils.call_and_ignore_notfound_exc,
127 self.compute_security_groups_client.delete_security_group,
128 secgroup['id'])
129
130 # Add rules to the security group
131 self._create_loginable_secgroup_rule(secgroup['id'])
132
133 return secgroup
134
Andrea Frittolif4510a12017-03-07 19:17:11 +0000135 def _image_create(self, name, fmt, path,
136 disk_format=None, properties=None):
137 if properties is None:
138 properties = {}
139 name = data_utils.rand_name('%s-' % name)
140 params = {
141 'name': name,
142 'container_format': fmt,
143 'disk_format': disk_format or fmt,
144 }
145 if CONF.image_feature_enabled.api_v1:
146 params['is_public'] = 'False'
147 params['properties'] = properties
148 params = {'headers': common_image.image_meta_to_headers(**params)}
149 else:
150 params['visibility'] = 'private'
151 # Additional properties are flattened out in the v2 API.
152 params.update(properties)
153 body = self.image_client.create_image(**params)
154 image = body['image'] if 'image' in body else body
155 self.addCleanup(self.image_client.delete_image, image['id'])
156 self.assertEqual("queued", image['status'])
157 with open(path, 'rb') as image_file:
158 if CONF.image_feature_enabled.api_v1:
159 self.image_client.update_image(image['id'], data=image_file)
160 else:
161 self.image_client.store_image_file(image['id'], image_file)
162 return image['id']
163
164 def glance_image_create(self):
Martin Kopec258cc6c2020-04-15 22:55:25 +0000165 img_path = CONF.scenario.img_file
Andrea Frittolif4510a12017-03-07 19:17:11 +0000166 img_container_format = CONF.scenario.img_container_format
167 img_disk_format = CONF.scenario.img_disk_format
168 img_properties = CONF.scenario.img_properties
169 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec258cc6c2020-04-15 22:55:25 +0000170 "properties: %s",
Andrea Frittolif4510a12017-03-07 19:17:11 +0000171 img_path, img_container_format, img_disk_format,
Martin Kopec258cc6c2020-04-15 22:55:25 +0000172 img_properties)
173 image = self._image_create('scenario-img',
174 img_container_format,
175 img_path,
176 disk_format=img_disk_format,
177 properties=img_properties)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000178 LOG.debug("image:%s", image)
179
180 return image
181
Andrea Frittolif4510a12017-03-07 19:17:11 +0000182 def _log_net_info(self, exc):
183 # network debug is called as part of ssh init
184 if not isinstance(exc, lib_exc.SSHTimeout):
185 LOG.debug('Network information on a devstack host')
186
Andrea Frittolif4510a12017-03-07 19:17:11 +0000187 def check_public_network_connectivity(self, ip_address, username,
188 private_key, should_connect=True,
189 msg=None, servers=None, mtu=None):
190 # The target login is assumed to have been configured for
191 # key-based authentication by cloud-init.
192 LOG.debug('checking network connections to IP %s with user: %s',
193 ip_address, username)
194 try:
195 self.check_vm_connectivity(ip_address,
196 username,
197 private_key,
198 should_connect=should_connect,
199 mtu=mtu)
200 except Exception:
201 ex_msg = 'Public network connectivity check failed'
202 if msg:
203 ex_msg += ": " + msg
204 LOG.exception(ex_msg)
Roman Popelka164898c2022-03-21 09:12:38 +0100205 self.log_console_output(servers)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000206 raise
207
208 def create_floating_ip(self, thing, pool_name=None):
209 """Create a floating IP and associates to a server on Nova"""
210
211 if not pool_name:
212 pool_name = CONF.network.floating_network_name
213 floating_ip = (self.compute_floating_ips_client.
214 create_floating_ip(pool=pool_name)['floating_ip'])
215 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
216 self.compute_floating_ips_client.delete_floating_ip,
217 floating_ip['id'])
218 self.compute_floating_ips_client.associate_floating_ip_to_server(
219 floating_ip['ip'], thing['id'])
220 return floating_ip
221
222 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
223 private_key=None):
224 ssh_client = self.get_remote_client(ip_address,
225 private_key=private_key)
226 if dev_name is not None:
227 ssh_client.make_fs(dev_name)
228 ssh_client.mount(dev_name, mount_path)
229 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
230 ssh_client.exec_command(cmd_timestamp)
231 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
232 % mount_path)
233 if dev_name is not None:
234 ssh_client.umount(mount_path)
235 return timestamp
236
237 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
238 private_key=None):
239 ssh_client = self.get_remote_client(ip_address,
240 private_key=private_key)
241 if dev_name is not None:
242 ssh_client.mount(dev_name, mount_path)
243 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
244 % mount_path)
245 if dev_name is not None:
246 ssh_client.umount(mount_path)
247 return timestamp
248
249 def get_server_ip(self, server):
250 """Get the server fixed or floating IP.
251
252 Based on the configuration we're in, return a correct ip
253 address for validating that a guest is up.
254 """
255 if CONF.validation.connect_method == 'floating':
256 # The tests calling this method don't have a floating IP
257 # and can't make use of the validation resources. So the
258 # method is creating the floating IP there.
259 return self.create_floating_ip(server)['ip']
260 elif CONF.validation.connect_method == 'fixed':
261 # Determine the network name to look for based on config or creds
262 # provider network resources.
263 if CONF.validation.network_for_ssh:
264 addresses = server['addresses'][
265 CONF.validation.network_for_ssh]
266 else:
267 creds_provider = self._get_credentials_provider()
268 net_creds = creds_provider.get_primary_creds()
269 network = getattr(net_creds, 'network', None)
270 addresses = (server['addresses'][network['name']]
271 if network else [])
272 for address in addresses:
273 if (address['version'] == CONF.validation.ip_version_for_ssh
274 and address['OS-EXT-IPS:type'] == 'fixed'):
275 return address['addr']
276 raise exceptions.ServerUnreachable(server_id=server['id'])
277 else:
278 raise lib_exc.InvalidConfiguration()
279
280
281class NetworkScenarioTest(ScenarioTest):
282 """Base class for network scenario tests.
283
284 This class provide helpers for network scenario tests, using the neutron
285 API. Helpers from ancestor which use the nova network API are overridden
286 with the neutron API.
287
288 This Class also enforces using Neutron instead of novanetwork.
289 Subclassed tests will be skipped if Neutron is not enabled
290
291 """
292
293 credentials = ['primary', 'admin']
294
295 @classmethod
296 def skip_checks(cls):
297 super(NetworkScenarioTest, cls).skip_checks()
298 if not CONF.service_available.neutron:
299 raise cls.skipException('Neutron not available')
300
Andrea Frittolif4510a12017-03-07 19:17:11 +0000301 def _create_subnet(self, network, subnets_client=None,
302 routers_client=None, namestart='subnet-smoke',
303 **kwargs):
304 """Create a subnet for the given network
305
306 within the cidr block configured for tenant networks.
307 """
308 if not subnets_client:
309 subnets_client = self.subnets_client
310 if not routers_client:
311 routers_client = self.routers_client
312
313 def cidr_in_use(cidr, tenant_id):
314 """Check cidr existence
315
316 :returns: True if subnet with cidr already exist in tenant
317 False else
318 """
Vu Cong Tuan99751862017-06-23 19:46:40 +0700319 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000320 tenant_id=tenant_id, cidr=cidr)['subnets']
321 return len(cidr_in_use) != 0
322
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200323 def _make_create_subnet_request(namestart, network,
324 ip_version, subnets_client, **kwargs):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000325
326 subnet = dict(
327 name=data_utils.rand_name(namestart),
328 network_id=network['id'],
329 tenant_id=network['tenant_id'],
Andrea Frittolif4510a12017-03-07 19:17:11 +0000330 ip_version=ip_version,
331 **kwargs
332 )
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200333
334 if ip_version == 6:
335 subnet['ipv6_address_mode'] = 'slaac'
336 subnet['ipv6_ra_mode'] = 'slaac'
337
Andrea Frittolif4510a12017-03-07 19:17:11 +0000338 try:
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200339 return subnets_client.create_subnet(**subnet)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000340 except lib_exc.Conflict as e:
haixin48895812020-09-30 13:50:37 +0800341 if 'overlaps with another subnet' not in str(e):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000342 raise
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200343
344 result = None
345 str_cidr = None
346
347 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
348
349 ip_version = kwargs.pop('ip_version', 4)
350
351 if not use_default_subnetpool:
352
353 if ip_version == 6:
354 tenant_cidr = netaddr.IPNetwork(
355 CONF.network.project_network_v6_cidr)
356 num_bits = CONF.network.project_network_v6_mask_bits
357 else:
358 tenant_cidr = netaddr.IPNetwork(
359 CONF.network.project_network_cidr)
360 num_bits = CONF.network.project_network_mask_bits
361
362 # Repeatedly attempt subnet creation with sequential cidr
363 # blocks until an unallocated block is found.
364 for subnet_cidr in tenant_cidr.subnet(num_bits):
365 str_cidr = str(subnet_cidr)
366 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
367 continue
368
369 result = _make_create_subnet_request(
370 namestart, network, ip_version, subnets_client,
371 cidr=str_cidr, **kwargs)
372 if result is not None:
373 break
374 else:
375 result = _make_create_subnet_request(
376 namestart, network, ip_version, subnets_client,
377 **kwargs)
378
379 self.assertIsNotNone(result)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000380
381 subnet = result['subnet']
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200382 if str_cidr is not None:
383 self.assertEqual(subnet['cidr'], str_cidr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000384
385 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
386 subnets_client.delete_subnet, subnet['id'])
387
388 return subnet
389
390 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Rockyd3432662019-07-02 14:58:30 +1000391 if ip_addr:
392 ports = self.os_admin.ports_client.list_ports(
393 device_id=server['id'],
394 fixed_ips='ip_address=%s' % ip_addr)['ports']
395 else:
396 ports = self.os_admin.ports_client.list_ports(
397 device_id=server['id'])['ports']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000398 # A port can have more than one IP address in some cases.
399 # If the network is dual-stack (IPv4 + IPv6), this port is associated
400 # with 2 subnets
Rockyd3432662019-07-02 14:58:30 +1000401
402 def _is_active(port):
403 # NOTE(vsaienko) With Ironic, instances live on separate hardware
404 # servers. Neutron does not bind ports for Ironic instances, as a
405 # result the port remains in the DOWN state. This has been fixed
406 # with the introduction of the networking-baremetal plugin but
407 # it's not mandatory (and is not used on all stable branches).
408 return (port['status'] == 'ACTIVE' or
409 port.get('binding:vnic_type') == 'baremetal')
410
Andrea Frittolif4510a12017-03-07 19:17:11 +0000411 port_map = [(p["id"], fxip["ip_address"])
412 for p in ports
413 for fxip in p["fixed_ips"]
Rockyd3432662019-07-02 14:58:30 +1000414 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
415 _is_active(p))]
Andrea Frittolif4510a12017-03-07 19:17:11 +0000416 inactive = [p for p in ports if p['status'] != 'ACTIVE']
417 if inactive:
418 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
419
Rockyd3432662019-07-02 14:58:30 +1000420 self.assertNotEmpty(port_map,
Andrea Frittolif4510a12017-03-07 19:17:11 +0000421 "No IPv4 addresses found in: %s" % ports)
422 self.assertEqual(len(port_map), 1,
423 "Found multiple IPv4 addresses: %s. "
424 "Unable to determine which port to target."
425 % port_map)
426 return port_map[0]
427
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700428 def _get_network_by_name_or_id(self, identifier):
429
430 if uuidutils.is_uuid_like(identifier):
431 return self.os_admin.networks_client.show_network(
432 identifier)['network']
433
434 networks = self.os_admin.networks_client.list_networks(
435 name=identifier)['networks']
436 self.assertNotEqual(len(networks), 0,
437 "Unable to get network by name: %s" % identifier)
438 return networks[0]
439
440 def get_networks(self):
441 return self.os_admin.networks_client.list_networks()['networks']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000442
443 def create_floating_ip(self, thing, external_network_id=None,
lkuchlan7636a1f2020-04-30 16:13:13 +0300444 port_id=None, ip_addr=None, client=None):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000445 """Create a floating IP and associates to a resource/port on Neutron"""
446 if not external_network_id:
447 external_network_id = CONF.network.public_network_id
448 if not client:
449 client = self.floating_ips_client
450 if not port_id:
lkuchlan7636a1f2020-04-30 16:13:13 +0300451 port_id, ip4 = self._get_server_port_id_and_ip4(thing,
452 ip_addr=ip_addr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000453 else:
454 ip4 = None
455 result = client.create_floatingip(
456 floating_network_id=external_network_id,
457 port_id=port_id,
458 tenant_id=thing['tenant_id'],
459 fixed_ip_address=ip4
460 )
461 floating_ip = result['floatingip']
462 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
463 client.delete_floatingip,
464 floating_ip['id'])
465 return floating_ip
466
467 def _associate_floating_ip(self, floating_ip, server):
468 port_id, _ = self._get_server_port_id_and_ip4(server)
469 kwargs = dict(port_id=port_id)
470 floating_ip = self.floating_ips_client.update_floatingip(
471 floating_ip['id'], **kwargs)['floatingip']
472 self.assertEqual(port_id, floating_ip['port_id'])
473 return floating_ip
474
Andrea Frittolif4510a12017-03-07 19:17:11 +0000475 def check_floating_ip_status(self, floating_ip, status):
476 """Verifies floatingip reaches the given status
477
478 :param dict floating_ip: floating IP dict to check status
479 :param status: target status
480 :raises: AssertionError if status doesn't match
481 """
482 floatingip_id = floating_ip['id']
483
484 def refresh():
485 result = (self.floating_ips_client.
486 show_floatingip(floatingip_id)['floatingip'])
487 return status == result['status']
488
489 test_utils.call_until_true(refresh,
490 CONF.network.build_timeout,
491 CONF.network.build_interval)
492 floating_ip = self.floating_ips_client.show_floatingip(
493 floatingip_id)['floatingip']
494 self.assertEqual(status, floating_ip['status'],
495 message="FloatingIP: {fp} is at status: {cst}. "
496 "failed to reach status: {st}"
497 .format(fp=floating_ip, cst=floating_ip['status'],
498 st=status))
499 LOG.info("FloatingIP: {fp} is at status: {st}"
500 .format(fp=floating_ip, st=status))
501
502 def _check_tenant_network_connectivity(self, server,
503 username,
504 private_key,
505 should_connect=True,
506 servers_for_debug=None):
507 if not CONF.network.project_networks_reachable:
508 msg = 'Tenant networks not configured to be reachable.'
509 LOG.info(msg)
510 return
511 # The target login is assumed to have been configured for
512 # key-based authentication by cloud-init.
513 try:
514 for ip_addresses in server['addresses'].values():
515 for ip_address in ip_addresses:
516 self.check_vm_connectivity(ip_address['addr'],
517 username,
518 private_key,
Roman Popelkafd4e2f32022-03-21 10:16:30 +0100519 should_connect=should_connect,
520 server=server)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000521 except Exception as e:
522 LOG.exception('Tenant network connectivity check failed')
Roman Popelka164898c2022-03-21 09:12:38 +0100523 self.log_console_output(servers_for_debug)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000524 self._log_net_info(e)
525 raise
526
527 def _check_remote_connectivity(self, source, dest, should_succeed=True,
528 nic=None):
529 """assert ping server via source ssh connection
530
531 Note: This is an internal method. Use check_remote_connectivity
532 instead.
533
534 :param source: RemoteClient: an ssh connection from which to ping
535 :param dest: and IP to ping against
536 :param should_succeed: boolean should ping succeed or not
537 :param nic: specific network interface to ping from
538 """
539 def ping_remote():
540 try:
541 source.ping_host(dest, nic=nic)
542 except lib_exc.SSHExecCommandFailed:
543 LOG.warning('Failed to ping IP: %s via a ssh connection '
544 'from: %s.', dest, source.ssh_client.host)
545 return not should_succeed
546 return should_succeed
547
548 return test_utils.call_until_true(ping_remote,
549 CONF.validation.ping_timeout,
550 1)
551
552 def check_remote_connectivity(self, source, dest, should_succeed=True,
553 nic=None):
554 """assert ping server via source ssh connection
555
556 :param source: RemoteClient: an ssh connection from which to ping
557 :param dest: and IP to ping against
558 :param should_succeed: boolean should ping succeed or not
559 :param nic: specific network interface to ping from
560 """
561 result = self._check_remote_connectivity(source, dest, should_succeed,
562 nic)
563 source_host = source.ssh_client.host
564 if should_succeed:
zhongjun39e9c582017-06-21 15:17:11 +0800565 msg = ("Timed out waiting for %s to become reachable from %s"
566 % (dest, source_host))
Andrea Frittolif4510a12017-03-07 19:17:11 +0000567 else:
568 msg = "%s is reachable from %s" % (dest, source_host)
569 self.assertTrue(result, msg)
570
571 def _create_security_group(self, security_group_rules_client=None,
572 tenant_id=None,
573 namestart='secgroup-smoke',
574 security_groups_client=None):
575 if security_group_rules_client is None:
576 security_group_rules_client = self.security_group_rules_client
577 if security_groups_client is None:
578 security_groups_client = self.security_groups_client
579 if tenant_id is None:
580 tenant_id = security_groups_client.tenant_id
581 secgroup = self._create_empty_security_group(
582 namestart=namestart, client=security_groups_client,
583 tenant_id=tenant_id)
584
585 # Add rules to the security group
586 rules = self._create_loginable_secgroup_rule(
587 security_group_rules_client=security_group_rules_client,
588 secgroup=secgroup,
589 security_groups_client=security_groups_client)
590 for rule in rules:
591 self.assertEqual(tenant_id, rule['tenant_id'])
592 self.assertEqual(secgroup['id'], rule['security_group_id'])
593 return secgroup
594
595 def _create_empty_security_group(self, client=None, tenant_id=None,
596 namestart='secgroup-smoke'):
597 """Create a security group without rules.
598
599 Default rules will be created:
600 - IPv4 egress to any
601 - IPv6 egress to any
602
603 :param tenant_id: secgroup will be created in this tenant
604 :returns: the created security group
605 """
606 if client is None:
607 client = self.security_groups_client
608 if not tenant_id:
609 tenant_id = client.tenant_id
610 sg_name = data_utils.rand_name(namestart)
611 sg_desc = sg_name + " description"
612 sg_dict = dict(name=sg_name,
613 description=sg_desc)
614 sg_dict['tenant_id'] = tenant_id
615 result = client.create_security_group(**sg_dict)
616
617 secgroup = result['security_group']
618 self.assertEqual(secgroup['name'], sg_name)
619 self.assertEqual(tenant_id, secgroup['tenant_id'])
620 self.assertEqual(secgroup['description'], sg_desc)
621
622 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
623 client.delete_security_group, secgroup['id'])
624 return secgroup
625
626 def _default_security_group(self, client=None, tenant_id=None):
627 """Get default secgroup for given tenant_id.
628
629 :returns: default secgroup for given tenant
630 """
631 if client is None:
632 client = self.security_groups_client
633 if not tenant_id:
634 tenant_id = client.tenant_id
635 sgs = [
636 sg for sg in list(client.list_security_groups().values())[0]
637 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
638 ]
639 msg = "No default security group for tenant %s." % (tenant_id)
640 self.assertGreater(len(sgs), 0, msg)
641 return sgs[0]
642
643 def _create_security_group_rule(self, secgroup=None,
644 sec_group_rules_client=None,
645 tenant_id=None,
646 security_groups_client=None, **kwargs):
647 """Create a rule from a dictionary of rule parameters.
648
649 Create a rule in a secgroup. if secgroup not defined will search for
650 default secgroup in tenant_id.
651
652 :param secgroup: the security group.
653 :param tenant_id: if secgroup not passed -- the tenant in which to
654 search for default secgroup
655 :param kwargs: a dictionary containing rule parameters:
656 for example, to allow incoming ssh:
657 rule = {
658 direction: 'ingress'
659 protocol:'tcp',
660 port_range_min: 22,
661 port_range_max: 22
662 }
663 """
664 if sec_group_rules_client is None:
665 sec_group_rules_client = self.security_group_rules_client
666 if security_groups_client is None:
667 security_groups_client = self.security_groups_client
668 if not tenant_id:
669 tenant_id = security_groups_client.tenant_id
670 if secgroup is None:
671 secgroup = self._default_security_group(
672 client=security_groups_client, tenant_id=tenant_id)
673
674 ruleset = dict(security_group_id=secgroup['id'],
675 tenant_id=secgroup['tenant_id'])
676 ruleset.update(kwargs)
677
678 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
679 sg_rule = sg_rule['security_group_rule']
680
681 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
682 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
683
684 return sg_rule
685
686 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
687 secgroup=None,
688 security_groups_client=None):
689 """Create loginable security group rule
690
691 This function will create:
692 1. egress and ingress tcp port 22 allow rule in order to allow ssh
693 access for ipv4.
694 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
695 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
696 """
697
698 if security_group_rules_client is None:
699 security_group_rules_client = self.security_group_rules_client
700 if security_groups_client is None:
701 security_groups_client = self.security_groups_client
702 rules = []
703 rulesets = [
704 dict(
705 # ssh
706 protocol='tcp',
707 port_range_min=22,
708 port_range_max=22,
709 ),
710 dict(
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200711 # ipv6-ssh
712 protocol='tcp',
713 port_range_min=22,
714 port_range_max=22,
715 ethertype='IPv6',
716 ),
717 dict(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000718 # ping
719 protocol='icmp',
720 ),
721 dict(
722 # ipv6-icmp for ping6
723 protocol='icmp',
724 ethertype='IPv6',
725 )
726 ]
727 sec_group_rules_client = security_group_rules_client
728 for ruleset in rulesets:
729 for r_direction in ['ingress', 'egress']:
730 ruleset['direction'] = r_direction
731 try:
732 sg_rule = self._create_security_group_rule(
733 sec_group_rules_client=sec_group_rules_client,
734 secgroup=secgroup,
735 security_groups_client=security_groups_client,
736 **ruleset)
737 except lib_exc.Conflict as ex:
738 # if rule already exist - skip rule and continue
739 msg = 'Security group rule already exists'
740 if msg not in ex._error_string:
741 raise ex
742 else:
743 self.assertEqual(r_direction, sg_rule['direction'])
744 rules.append(sg_rule)
745
746 return rules
747
748 def _get_router(self, client=None, tenant_id=None):
749 """Retrieve a router for the given tenant id.
750
751 If a public router has been configured, it will be returned.
752
753 If a public router has not been configured, but a public
754 network has, a tenant router will be created and returned that
755 routes traffic to the public network.
756 """
757 if not client:
758 client = self.routers_client
759 if not tenant_id:
760 tenant_id = client.tenant_id
761 router_id = CONF.network.public_router_id
762 network_id = CONF.network.public_network_id
763 if router_id:
764 body = client.show_router(router_id)
765 return body['router']
766 elif network_id:
767 router = self._create_router(client, tenant_id)
768 kwargs = {'external_gateway_info': dict(network_id=network_id)}
769 router = client.update_router(router['id'], **kwargs)['router']
770 return router
771 else:
772 raise Exception("Neither of 'public_router_id' or "
773 "'public_network_id' has been defined.")
774
775 def _create_router(self, client=None, tenant_id=None,
776 namestart='router-smoke'):
777 if not client:
778 client = self.routers_client
779 if not tenant_id:
780 tenant_id = client.tenant_id
781 name = data_utils.rand_name(namestart)
782 result = client.create_router(name=name,
783 admin_state_up=True,
784 tenant_id=tenant_id)
785 router = result['router']
786 self.assertEqual(router['name'], name)
787 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
788 client.delete_router,
789 router['id'])
790 return router
791
792 def _update_router_admin_state(self, router, admin_state_up):
793 kwargs = dict(admin_state_up=admin_state_up)
794 router = self.routers_client.update_router(
795 router['id'], **kwargs)['router']
796 self.assertEqual(admin_state_up, router['admin_state_up'])
797
798 def create_networks(self, networks_client=None,
799 routers_client=None, subnets_client=None,
800 tenant_id=None, dns_nameservers=None,
801 port_security_enabled=True):
802 """Create a network with a subnet connected to a router.
803
804 The baremetal driver is a special case since all nodes are
805 on the same shared network.
806
807 :param tenant_id: id of tenant to create resources in.
808 :param dns_nameservers: list of dns servers to send to subnet.
809 :returns: network, subnet, router
810 """
811 if CONF.network.shared_physical_network:
812 # NOTE(Shrews): This exception is for environments where tenant
813 # credential isolation is available, but network separation is
814 # not (the current baremetal case). Likely can be removed when
815 # test account mgmt is reworked:
816 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
817 if not CONF.compute.fixed_network_name:
818 m = 'fixed_network_name must be specified in config'
819 raise lib_exc.InvalidConfiguration(m)
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700820 network = self._get_network_by_name_or_id(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000821 CONF.compute.fixed_network_name)
822 router = None
823 subnet = None
824 else:
Roman Popelka1118f3e2022-03-21 09:18:53 +0100825 network = self.create_network(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000826 networks_client=networks_client,
827 tenant_id=tenant_id,
828 port_security_enabled=port_security_enabled)
829 router = self._get_router(client=routers_client,
830 tenant_id=tenant_id)
831 subnet_kwargs = dict(network=network,
832 subnets_client=subnets_client,
833 routers_client=routers_client)
834 # use explicit check because empty list is a valid option
835 if dns_nameservers is not None:
836 subnet_kwargs['dns_nameservers'] = dns_nameservers
837 subnet = self._create_subnet(**subnet_kwargs)
838 if not routers_client:
839 routers_client = self.routers_client
840 router_id = router['id']
841 routers_client.add_router_interface(router_id,
842 subnet_id=subnet['id'])
843
844 # save a cleanup job to remove this association between
845 # router and subnet
846 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
847 routers_client.remove_router_interface, router_id,
848 subnet_id=subnet['id'])
849 return network, subnet, router