blob: 3854fcc6d2c1e093709789920fe976cefec188c7 [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
Ken'ichi Ohmichi02d1f242017-03-12 18:56:27 -070023from tempest.lib.common.utils import data_utils
Andrea Frittolif4510a12017-03-07 19:17:11 +000024from tempest.lib.common.utils import test_utils
25from tempest.lib import exceptions as lib_exc
Roman Popelka290ef292022-02-28 10:41:04 +010026from tempest.scenario import manager
Andrea Frittolif4510a12017-03-07 19:17:11 +000027
28CONF = config.CONF
29
30LOG = log.getLogger(__name__)
31
32
Roman Popelka1118f3e2022-03-21 09:18:53 +010033class ScenarioTest(manager.NetworkScenarioTest):
Andrea Frittolif4510a12017-03-07 19:17:11 +000034 """Base class for scenario tests. Uses tempest own clients. """
35
36 credentials = ['primary']
37
38 @classmethod
39 def setup_clients(cls):
40 super(ScenarioTest, cls).setup_clients()
41 # Clients (in alphabetical order)
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070042 cls.flavors_client = cls.os_primary.flavors_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000043 cls.compute_floating_ips_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070044 cls.os_primary.compute_floating_ips_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000045 if CONF.service_available.glance:
46 # Check if glance v1 is available to determine which client to use.
47 if CONF.image_feature_enabled.api_v1:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070048 cls.image_client = cls.os_primary.image_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000049 elif CONF.image_feature_enabled.api_v2:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070050 cls.image_client = cls.os_primary.image_client_v2
Andrea Frittolif4510a12017-03-07 19:17:11 +000051 else:
52 raise lib_exc.InvalidConfiguration(
53 'Either api_v1 or api_v2 must be True in '
54 '[image-feature-enabled].')
55 # Compute image client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070056 cls.compute_images_client = cls.os_primary.compute_images_client
57 cls.keypairs_client = cls.os_primary.keypairs_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000058 # Nova security groups client
59 cls.compute_security_groups_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070060 cls.os_primary.compute_security_groups_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000061 cls.compute_security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070062 cls.os_primary.compute_security_group_rules_client)
63 cls.servers_client = cls.os_primary.servers_client
64 cls.interface_client = cls.os_primary.interfaces_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000065 # Neutron network client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070066 cls.networks_client = cls.os_primary.networks_client
67 cls.ports_client = cls.os_primary.ports_client
68 cls.routers_client = cls.os_primary.routers_client
69 cls.subnets_client = cls.os_primary.subnets_client
70 cls.floating_ips_client = cls.os_primary.floating_ips_client
71 cls.security_groups_client = cls.os_primary.security_groups_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000072 cls.security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070073 cls.os_primary.security_group_rules_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000074
Andrea Frittolif4510a12017-03-07 19:17:11 +000075 # ## Test functions library
76 #
77 # The create_[resource] functions only return body and discard the
78 # resp part which is not used in scenario tests
79
Andrea Frittolif4510a12017-03-07 19:17:11 +000080 def _create_loginable_secgroup_rule(self, secgroup_id=None):
81 _client = self.compute_security_groups_client
82 _client_rules = self.compute_security_group_rules_client
83 if secgroup_id is None:
84 sgs = _client.list_security_groups()['security_groups']
85 for sg in sgs:
86 if sg['name'] == 'default':
87 secgroup_id = sg['id']
88
89 # These rules are intended to permit inbound ssh and icmp
90 # traffic from all sources, so no group_id is provided.
91 # Setting a group_id would only permit traffic from ports
92 # belonging to the same security group.
93 rulesets = [
94 {
95 # ssh
96 'ip_protocol': 'tcp',
97 'from_port': 22,
98 'to_port': 22,
99 'cidr': '0.0.0.0/0',
100 },
101 {
102 # ping
103 'ip_protocol': 'icmp',
104 'from_port': -1,
105 'to_port': -1,
106 'cidr': '0.0.0.0/0',
107 }
108 ]
109 rules = list()
110 for ruleset in rulesets:
111 sg_rule = _client_rules.create_security_group_rule(
112 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
113 rules.append(sg_rule)
114 return rules
115
116 def _create_security_group(self):
117 # Create security group
118 sg_name = data_utils.rand_name(self.__class__.__name__)
119 sg_desc = sg_name + " description"
120 secgroup = self.compute_security_groups_client.create_security_group(
121 name=sg_name, description=sg_desc)['security_group']
122 self.assertEqual(secgroup['name'], sg_name)
123 self.assertEqual(secgroup['description'], sg_desc)
124 self.addCleanup(
125 test_utils.call_and_ignore_notfound_exc,
126 self.compute_security_groups_client.delete_security_group,
127 secgroup['id'])
128
129 # Add rules to the security group
130 self._create_loginable_secgroup_rule(secgroup['id'])
131
132 return secgroup
133
Andrea Frittolif4510a12017-03-07 19:17:11 +0000134 def _image_create(self, name, fmt, path,
135 disk_format=None, properties=None):
136 if properties is None:
137 properties = {}
138 name = data_utils.rand_name('%s-' % name)
139 params = {
140 'name': name,
141 'container_format': fmt,
142 'disk_format': disk_format or fmt,
143 }
144 if CONF.image_feature_enabled.api_v1:
145 params['is_public'] = 'False'
146 params['properties'] = properties
147 params = {'headers': common_image.image_meta_to_headers(**params)}
148 else:
149 params['visibility'] = 'private'
150 # Additional properties are flattened out in the v2 API.
151 params.update(properties)
152 body = self.image_client.create_image(**params)
153 image = body['image'] if 'image' in body else body
154 self.addCleanup(self.image_client.delete_image, image['id'])
155 self.assertEqual("queued", image['status'])
156 with open(path, 'rb') as image_file:
157 if CONF.image_feature_enabled.api_v1:
158 self.image_client.update_image(image['id'], data=image_file)
159 else:
160 self.image_client.store_image_file(image['id'], image_file)
161 return image['id']
162
163 def glance_image_create(self):
Martin Kopec258cc6c2020-04-15 22:55:25 +0000164 img_path = CONF.scenario.img_file
Andrea Frittolif4510a12017-03-07 19:17:11 +0000165 img_container_format = CONF.scenario.img_container_format
166 img_disk_format = CONF.scenario.img_disk_format
167 img_properties = CONF.scenario.img_properties
168 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec258cc6c2020-04-15 22:55:25 +0000169 "properties: %s",
Andrea Frittolif4510a12017-03-07 19:17:11 +0000170 img_path, img_container_format, img_disk_format,
Martin Kopec258cc6c2020-04-15 22:55:25 +0000171 img_properties)
172 image = self._image_create('scenario-img',
173 img_container_format,
174 img_path,
175 disk_format=img_disk_format,
176 properties=img_properties)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000177 LOG.debug("image:%s", image)
178
179 return image
180
Andrea Frittolif4510a12017-03-07 19:17:11 +0000181 def _log_net_info(self, exc):
182 # network debug is called as part of ssh init
183 if not isinstance(exc, lib_exc.SSHTimeout):
184 LOG.debug('Network information on a devstack host')
185
Andrea Frittolif4510a12017-03-07 19:17:11 +0000186 def check_public_network_connectivity(self, ip_address, username,
187 private_key, should_connect=True,
188 msg=None, servers=None, mtu=None):
189 # The target login is assumed to have been configured for
190 # key-based authentication by cloud-init.
191 LOG.debug('checking network connections to IP %s with user: %s',
192 ip_address, username)
193 try:
194 self.check_vm_connectivity(ip_address,
195 username,
196 private_key,
197 should_connect=should_connect,
198 mtu=mtu)
199 except Exception:
200 ex_msg = 'Public network connectivity check failed'
201 if msg:
202 ex_msg += ": " + msg
203 LOG.exception(ex_msg)
Roman Popelka164898c2022-03-21 09:12:38 +0100204 self.log_console_output(servers)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000205 raise
206
Andrea Frittolif4510a12017-03-07 19:17:11 +0000207
208class NetworkScenarioTest(ScenarioTest):
209 """Base class for network scenario tests.
210
211 This class provide helpers for network scenario tests, using the neutron
212 API. Helpers from ancestor which use the nova network API are overridden
213 with the neutron API.
214
215 This Class also enforces using Neutron instead of novanetwork.
216 Subclassed tests will be skipped if Neutron is not enabled
217
218 """
219
220 credentials = ['primary', 'admin']
221
222 @classmethod
223 def skip_checks(cls):
224 super(NetworkScenarioTest, cls).skip_checks()
225 if not CONF.service_available.neutron:
226 raise cls.skipException('Neutron not available')
227
Andrea Frittolif4510a12017-03-07 19:17:11 +0000228 def _create_subnet(self, network, subnets_client=None,
229 routers_client=None, namestart='subnet-smoke',
230 **kwargs):
231 """Create a subnet for the given network
232
233 within the cidr block configured for tenant networks.
234 """
235 if not subnets_client:
236 subnets_client = self.subnets_client
237 if not routers_client:
238 routers_client = self.routers_client
239
240 def cidr_in_use(cidr, tenant_id):
241 """Check cidr existence
242
243 :returns: True if subnet with cidr already exist in tenant
244 False else
245 """
Vu Cong Tuan99751862017-06-23 19:46:40 +0700246 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000247 tenant_id=tenant_id, cidr=cidr)['subnets']
248 return len(cidr_in_use) != 0
249
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200250 def _make_create_subnet_request(namestart, network,
251 ip_version, subnets_client, **kwargs):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000252
253 subnet = dict(
254 name=data_utils.rand_name(namestart),
255 network_id=network['id'],
256 tenant_id=network['tenant_id'],
Andrea Frittolif4510a12017-03-07 19:17:11 +0000257 ip_version=ip_version,
258 **kwargs
259 )
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200260
261 if ip_version == 6:
262 subnet['ipv6_address_mode'] = 'slaac'
263 subnet['ipv6_ra_mode'] = 'slaac'
264
Andrea Frittolif4510a12017-03-07 19:17:11 +0000265 try:
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200266 return subnets_client.create_subnet(**subnet)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000267 except lib_exc.Conflict as e:
haixin48895812020-09-30 13:50:37 +0800268 if 'overlaps with another subnet' not in str(e):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000269 raise
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200270
271 result = None
272 str_cidr = None
273
274 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
275
276 ip_version = kwargs.pop('ip_version', 4)
277
278 if not use_default_subnetpool:
279
280 if ip_version == 6:
281 tenant_cidr = netaddr.IPNetwork(
282 CONF.network.project_network_v6_cidr)
283 num_bits = CONF.network.project_network_v6_mask_bits
284 else:
285 tenant_cidr = netaddr.IPNetwork(
286 CONF.network.project_network_cidr)
287 num_bits = CONF.network.project_network_mask_bits
288
289 # Repeatedly attempt subnet creation with sequential cidr
290 # blocks until an unallocated block is found.
291 for subnet_cidr in tenant_cidr.subnet(num_bits):
292 str_cidr = str(subnet_cidr)
293 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
294 continue
295
296 result = _make_create_subnet_request(
297 namestart, network, ip_version, subnets_client,
298 cidr=str_cidr, **kwargs)
299 if result is not None:
300 break
301 else:
302 result = _make_create_subnet_request(
303 namestart, network, ip_version, subnets_client,
304 **kwargs)
305
306 self.assertIsNotNone(result)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000307
308 subnet = result['subnet']
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200309 if str_cidr is not None:
310 self.assertEqual(subnet['cidr'], str_cidr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000311
312 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
313 subnets_client.delete_subnet, subnet['id'])
314
315 return subnet
316
317 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Rockyd3432662019-07-02 14:58:30 +1000318 if ip_addr:
319 ports = self.os_admin.ports_client.list_ports(
320 device_id=server['id'],
321 fixed_ips='ip_address=%s' % ip_addr)['ports']
322 else:
323 ports = self.os_admin.ports_client.list_ports(
324 device_id=server['id'])['ports']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000325 # A port can have more than one IP address in some cases.
326 # If the network is dual-stack (IPv4 + IPv6), this port is associated
327 # with 2 subnets
Rockyd3432662019-07-02 14:58:30 +1000328
329 def _is_active(port):
330 # NOTE(vsaienko) With Ironic, instances live on separate hardware
331 # servers. Neutron does not bind ports for Ironic instances, as a
332 # result the port remains in the DOWN state. This has been fixed
333 # with the introduction of the networking-baremetal plugin but
334 # it's not mandatory (and is not used on all stable branches).
335 return (port['status'] == 'ACTIVE' or
336 port.get('binding:vnic_type') == 'baremetal')
337
Andrea Frittolif4510a12017-03-07 19:17:11 +0000338 port_map = [(p["id"], fxip["ip_address"])
339 for p in ports
340 for fxip in p["fixed_ips"]
Rockyd3432662019-07-02 14:58:30 +1000341 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
342 _is_active(p))]
Andrea Frittolif4510a12017-03-07 19:17:11 +0000343 inactive = [p for p in ports if p['status'] != 'ACTIVE']
344 if inactive:
345 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
346
Rockyd3432662019-07-02 14:58:30 +1000347 self.assertNotEmpty(port_map,
Andrea Frittolif4510a12017-03-07 19:17:11 +0000348 "No IPv4 addresses found in: %s" % ports)
349 self.assertEqual(len(port_map), 1,
350 "Found multiple IPv4 addresses: %s. "
351 "Unable to determine which port to target."
352 % port_map)
353 return port_map[0]
354
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700355 def _get_network_by_name_or_id(self, identifier):
356
357 if uuidutils.is_uuid_like(identifier):
358 return self.os_admin.networks_client.show_network(
359 identifier)['network']
360
361 networks = self.os_admin.networks_client.list_networks(
362 name=identifier)['networks']
363 self.assertNotEqual(len(networks), 0,
364 "Unable to get network by name: %s" % identifier)
365 return networks[0]
366
367 def get_networks(self):
368 return self.os_admin.networks_client.list_networks()['networks']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000369
370 def create_floating_ip(self, thing, external_network_id=None,
lkuchlan7636a1f2020-04-30 16:13:13 +0300371 port_id=None, ip_addr=None, client=None):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000372 """Create a floating IP and associates to a resource/port on Neutron"""
373 if not external_network_id:
374 external_network_id = CONF.network.public_network_id
375 if not client:
376 client = self.floating_ips_client
377 if not port_id:
lkuchlan7636a1f2020-04-30 16:13:13 +0300378 port_id, ip4 = self._get_server_port_id_and_ip4(thing,
379 ip_addr=ip_addr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000380 else:
381 ip4 = None
382 result = client.create_floatingip(
383 floating_network_id=external_network_id,
384 port_id=port_id,
385 tenant_id=thing['tenant_id'],
386 fixed_ip_address=ip4
387 )
388 floating_ip = result['floatingip']
389 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
390 client.delete_floatingip,
391 floating_ip['id'])
392 return floating_ip
393
394 def _associate_floating_ip(self, floating_ip, server):
395 port_id, _ = self._get_server_port_id_and_ip4(server)
396 kwargs = dict(port_id=port_id)
397 floating_ip = self.floating_ips_client.update_floatingip(
398 floating_ip['id'], **kwargs)['floatingip']
399 self.assertEqual(port_id, floating_ip['port_id'])
400 return floating_ip
401
Andrea Frittolif4510a12017-03-07 19:17:11 +0000402 def check_floating_ip_status(self, floating_ip, status):
403 """Verifies floatingip reaches the given status
404
405 :param dict floating_ip: floating IP dict to check status
406 :param status: target status
407 :raises: AssertionError if status doesn't match
408 """
409 floatingip_id = floating_ip['id']
410
411 def refresh():
412 result = (self.floating_ips_client.
413 show_floatingip(floatingip_id)['floatingip'])
414 return status == result['status']
415
416 test_utils.call_until_true(refresh,
417 CONF.network.build_timeout,
418 CONF.network.build_interval)
419 floating_ip = self.floating_ips_client.show_floatingip(
420 floatingip_id)['floatingip']
421 self.assertEqual(status, floating_ip['status'],
422 message="FloatingIP: {fp} is at status: {cst}. "
423 "failed to reach status: {st}"
424 .format(fp=floating_ip, cst=floating_ip['status'],
425 st=status))
426 LOG.info("FloatingIP: {fp} is at status: {st}"
427 .format(fp=floating_ip, st=status))
428
429 def _check_tenant_network_connectivity(self, server,
430 username,
431 private_key,
432 should_connect=True,
433 servers_for_debug=None):
434 if not CONF.network.project_networks_reachable:
435 msg = 'Tenant networks not configured to be reachable.'
436 LOG.info(msg)
437 return
438 # The target login is assumed to have been configured for
439 # key-based authentication by cloud-init.
440 try:
441 for ip_addresses in server['addresses'].values():
442 for ip_address in ip_addresses:
443 self.check_vm_connectivity(ip_address['addr'],
444 username,
445 private_key,
Roman Popelkafd4e2f32022-03-21 10:16:30 +0100446 should_connect=should_connect,
447 server=server)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000448 except Exception as e:
449 LOG.exception('Tenant network connectivity check failed')
Roman Popelka164898c2022-03-21 09:12:38 +0100450 self.log_console_output(servers_for_debug)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000451 self._log_net_info(e)
452 raise
453
454 def _check_remote_connectivity(self, source, dest, should_succeed=True,
455 nic=None):
456 """assert ping server via source ssh connection
457
458 Note: This is an internal method. Use check_remote_connectivity
459 instead.
460
461 :param source: RemoteClient: an ssh connection from which to ping
462 :param dest: and IP to ping against
463 :param should_succeed: boolean should ping succeed or not
464 :param nic: specific network interface to ping from
465 """
466 def ping_remote():
467 try:
468 source.ping_host(dest, nic=nic)
469 except lib_exc.SSHExecCommandFailed:
470 LOG.warning('Failed to ping IP: %s via a ssh connection '
471 'from: %s.', dest, source.ssh_client.host)
472 return not should_succeed
473 return should_succeed
474
475 return test_utils.call_until_true(ping_remote,
476 CONF.validation.ping_timeout,
477 1)
478
479 def check_remote_connectivity(self, source, dest, should_succeed=True,
480 nic=None):
481 """assert ping server via source ssh connection
482
483 :param source: RemoteClient: an ssh connection from which to ping
484 :param dest: and IP to ping against
485 :param should_succeed: boolean should ping succeed or not
486 :param nic: specific network interface to ping from
487 """
488 result = self._check_remote_connectivity(source, dest, should_succeed,
489 nic)
490 source_host = source.ssh_client.host
491 if should_succeed:
zhongjun39e9c582017-06-21 15:17:11 +0800492 msg = ("Timed out waiting for %s to become reachable from %s"
493 % (dest, source_host))
Andrea Frittolif4510a12017-03-07 19:17:11 +0000494 else:
495 msg = "%s is reachable from %s" % (dest, source_host)
496 self.assertTrue(result, msg)
497
498 def _create_security_group(self, security_group_rules_client=None,
499 tenant_id=None,
500 namestart='secgroup-smoke',
501 security_groups_client=None):
502 if security_group_rules_client is None:
503 security_group_rules_client = self.security_group_rules_client
504 if security_groups_client is None:
505 security_groups_client = self.security_groups_client
506 if tenant_id is None:
507 tenant_id = security_groups_client.tenant_id
508 secgroup = self._create_empty_security_group(
509 namestart=namestart, client=security_groups_client,
510 tenant_id=tenant_id)
511
512 # Add rules to the security group
513 rules = self._create_loginable_secgroup_rule(
514 security_group_rules_client=security_group_rules_client,
515 secgroup=secgroup,
516 security_groups_client=security_groups_client)
517 for rule in rules:
518 self.assertEqual(tenant_id, rule['tenant_id'])
519 self.assertEqual(secgroup['id'], rule['security_group_id'])
520 return secgroup
521
522 def _create_empty_security_group(self, client=None, tenant_id=None,
523 namestart='secgroup-smoke'):
524 """Create a security group without rules.
525
526 Default rules will be created:
527 - IPv4 egress to any
528 - IPv6 egress to any
529
530 :param tenant_id: secgroup will be created in this tenant
531 :returns: the created security group
532 """
533 if client is None:
534 client = self.security_groups_client
535 if not tenant_id:
536 tenant_id = client.tenant_id
537 sg_name = data_utils.rand_name(namestart)
538 sg_desc = sg_name + " description"
539 sg_dict = dict(name=sg_name,
540 description=sg_desc)
541 sg_dict['tenant_id'] = tenant_id
542 result = client.create_security_group(**sg_dict)
543
544 secgroup = result['security_group']
545 self.assertEqual(secgroup['name'], sg_name)
546 self.assertEqual(tenant_id, secgroup['tenant_id'])
547 self.assertEqual(secgroup['description'], sg_desc)
548
549 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
550 client.delete_security_group, secgroup['id'])
551 return secgroup
552
553 def _default_security_group(self, client=None, tenant_id=None):
554 """Get default secgroup for given tenant_id.
555
556 :returns: default secgroup for given tenant
557 """
558 if client is None:
559 client = self.security_groups_client
560 if not tenant_id:
561 tenant_id = client.tenant_id
562 sgs = [
563 sg for sg in list(client.list_security_groups().values())[0]
564 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
565 ]
566 msg = "No default security group for tenant %s." % (tenant_id)
567 self.assertGreater(len(sgs), 0, msg)
568 return sgs[0]
569
570 def _create_security_group_rule(self, secgroup=None,
571 sec_group_rules_client=None,
572 tenant_id=None,
573 security_groups_client=None, **kwargs):
574 """Create a rule from a dictionary of rule parameters.
575
576 Create a rule in a secgroup. if secgroup not defined will search for
577 default secgroup in tenant_id.
578
579 :param secgroup: the security group.
580 :param tenant_id: if secgroup not passed -- the tenant in which to
581 search for default secgroup
582 :param kwargs: a dictionary containing rule parameters:
583 for example, to allow incoming ssh:
584 rule = {
585 direction: 'ingress'
586 protocol:'tcp',
587 port_range_min: 22,
588 port_range_max: 22
589 }
590 """
591 if sec_group_rules_client is None:
592 sec_group_rules_client = self.security_group_rules_client
593 if security_groups_client is None:
594 security_groups_client = self.security_groups_client
595 if not tenant_id:
596 tenant_id = security_groups_client.tenant_id
597 if secgroup is None:
598 secgroup = self._default_security_group(
599 client=security_groups_client, tenant_id=tenant_id)
600
601 ruleset = dict(security_group_id=secgroup['id'],
602 tenant_id=secgroup['tenant_id'])
603 ruleset.update(kwargs)
604
605 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
606 sg_rule = sg_rule['security_group_rule']
607
608 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
609 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
610
611 return sg_rule
612
613 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
614 secgroup=None,
615 security_groups_client=None):
616 """Create loginable security group rule
617
618 This function will create:
619 1. egress and ingress tcp port 22 allow rule in order to allow ssh
620 access for ipv4.
621 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
622 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
623 """
624
625 if security_group_rules_client is None:
626 security_group_rules_client = self.security_group_rules_client
627 if security_groups_client is None:
628 security_groups_client = self.security_groups_client
629 rules = []
630 rulesets = [
631 dict(
632 # ssh
633 protocol='tcp',
634 port_range_min=22,
635 port_range_max=22,
636 ),
637 dict(
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200638 # ipv6-ssh
639 protocol='tcp',
640 port_range_min=22,
641 port_range_max=22,
642 ethertype='IPv6',
643 ),
644 dict(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000645 # ping
646 protocol='icmp',
647 ),
648 dict(
649 # ipv6-icmp for ping6
650 protocol='icmp',
651 ethertype='IPv6',
652 )
653 ]
654 sec_group_rules_client = security_group_rules_client
655 for ruleset in rulesets:
656 for r_direction in ['ingress', 'egress']:
657 ruleset['direction'] = r_direction
658 try:
659 sg_rule = self._create_security_group_rule(
660 sec_group_rules_client=sec_group_rules_client,
661 secgroup=secgroup,
662 security_groups_client=security_groups_client,
663 **ruleset)
664 except lib_exc.Conflict as ex:
665 # if rule already exist - skip rule and continue
666 msg = 'Security group rule already exists'
667 if msg not in ex._error_string:
668 raise ex
669 else:
670 self.assertEqual(r_direction, sg_rule['direction'])
671 rules.append(sg_rule)
672
673 return rules
674
675 def _get_router(self, client=None, tenant_id=None):
676 """Retrieve a router for the given tenant id.
677
678 If a public router has been configured, it will be returned.
679
680 If a public router has not been configured, but a public
681 network has, a tenant router will be created and returned that
682 routes traffic to the public network.
683 """
684 if not client:
685 client = self.routers_client
686 if not tenant_id:
687 tenant_id = client.tenant_id
688 router_id = CONF.network.public_router_id
689 network_id = CONF.network.public_network_id
690 if router_id:
691 body = client.show_router(router_id)
692 return body['router']
693 elif network_id:
694 router = self._create_router(client, tenant_id)
695 kwargs = {'external_gateway_info': dict(network_id=network_id)}
696 router = client.update_router(router['id'], **kwargs)['router']
697 return router
698 else:
699 raise Exception("Neither of 'public_router_id' or "
700 "'public_network_id' has been defined.")
701
702 def _create_router(self, client=None, tenant_id=None,
703 namestart='router-smoke'):
704 if not client:
705 client = self.routers_client
706 if not tenant_id:
707 tenant_id = client.tenant_id
708 name = data_utils.rand_name(namestart)
709 result = client.create_router(name=name,
710 admin_state_up=True,
711 tenant_id=tenant_id)
712 router = result['router']
713 self.assertEqual(router['name'], name)
714 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
715 client.delete_router,
716 router['id'])
717 return router
718
719 def _update_router_admin_state(self, router, admin_state_up):
720 kwargs = dict(admin_state_up=admin_state_up)
721 router = self.routers_client.update_router(
722 router['id'], **kwargs)['router']
723 self.assertEqual(admin_state_up, router['admin_state_up'])
724
725 def create_networks(self, networks_client=None,
726 routers_client=None, subnets_client=None,
727 tenant_id=None, dns_nameservers=None,
728 port_security_enabled=True):
729 """Create a network with a subnet connected to a router.
730
731 The baremetal driver is a special case since all nodes are
732 on the same shared network.
733
734 :param tenant_id: id of tenant to create resources in.
735 :param dns_nameservers: list of dns servers to send to subnet.
736 :returns: network, subnet, router
737 """
738 if CONF.network.shared_physical_network:
739 # NOTE(Shrews): This exception is for environments where tenant
740 # credential isolation is available, but network separation is
741 # not (the current baremetal case). Likely can be removed when
742 # test account mgmt is reworked:
743 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
744 if not CONF.compute.fixed_network_name:
745 m = 'fixed_network_name must be specified in config'
746 raise lib_exc.InvalidConfiguration(m)
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700747 network = self._get_network_by_name_or_id(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000748 CONF.compute.fixed_network_name)
749 router = None
750 subnet = None
751 else:
Roman Popelka1118f3e2022-03-21 09:18:53 +0100752 network = self.create_network(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000753 networks_client=networks_client,
754 tenant_id=tenant_id,
755 port_security_enabled=port_security_enabled)
756 router = self._get_router(client=routers_client,
757 tenant_id=tenant_id)
758 subnet_kwargs = dict(network=network,
759 subnets_client=subnets_client,
760 routers_client=routers_client)
761 # use explicit check because empty list is a valid option
762 if dns_nameservers is not None:
763 subnet_kwargs['dns_nameservers'] = dns_nameservers
764 subnet = self._create_subnet(**subnet_kwargs)
765 if not routers_client:
766 routers_client = self.routers_client
767 router_id = router['id']
768 routers_client.add_router_interface(router_id,
769 subnet_id=subnet['id'])
770
771 # save a cleanup job to remove this association between
772 # router and subnet
773 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
774 routers_client.remove_router_interface, router_id,
775 subnet_id=subnet['id'])
776 return network, subnet, router