blob: 37725ff63de6c0bb526b39a9b254f22380047549 [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
Andrea Frittolif4510a12017-03-07 19:17:11 +0000222 def get_server_ip(self, server):
223 """Get the server fixed or floating IP.
224
225 Based on the configuration we're in, return a correct ip
226 address for validating that a guest is up.
227 """
228 if CONF.validation.connect_method == 'floating':
229 # The tests calling this method don't have a floating IP
230 # and can't make use of the validation resources. So the
231 # method is creating the floating IP there.
232 return self.create_floating_ip(server)['ip']
233 elif CONF.validation.connect_method == 'fixed':
234 # Determine the network name to look for based on config or creds
235 # provider network resources.
236 if CONF.validation.network_for_ssh:
237 addresses = server['addresses'][
238 CONF.validation.network_for_ssh]
239 else:
240 creds_provider = self._get_credentials_provider()
241 net_creds = creds_provider.get_primary_creds()
242 network = getattr(net_creds, 'network', None)
243 addresses = (server['addresses'][network['name']]
244 if network else [])
245 for address in addresses:
246 if (address['version'] == CONF.validation.ip_version_for_ssh
247 and address['OS-EXT-IPS:type'] == 'fixed'):
248 return address['addr']
249 raise exceptions.ServerUnreachable(server_id=server['id'])
250 else:
251 raise lib_exc.InvalidConfiguration()
252
253
254class NetworkScenarioTest(ScenarioTest):
255 """Base class for network scenario tests.
256
257 This class provide helpers for network scenario tests, using the neutron
258 API. Helpers from ancestor which use the nova network API are overridden
259 with the neutron API.
260
261 This Class also enforces using Neutron instead of novanetwork.
262 Subclassed tests will be skipped if Neutron is not enabled
263
264 """
265
266 credentials = ['primary', 'admin']
267
268 @classmethod
269 def skip_checks(cls):
270 super(NetworkScenarioTest, cls).skip_checks()
271 if not CONF.service_available.neutron:
272 raise cls.skipException('Neutron not available')
273
Andrea Frittolif4510a12017-03-07 19:17:11 +0000274 def _create_subnet(self, network, subnets_client=None,
275 routers_client=None, namestart='subnet-smoke',
276 **kwargs):
277 """Create a subnet for the given network
278
279 within the cidr block configured for tenant networks.
280 """
281 if not subnets_client:
282 subnets_client = self.subnets_client
283 if not routers_client:
284 routers_client = self.routers_client
285
286 def cidr_in_use(cidr, tenant_id):
287 """Check cidr existence
288
289 :returns: True if subnet with cidr already exist in tenant
290 False else
291 """
Vu Cong Tuan99751862017-06-23 19:46:40 +0700292 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000293 tenant_id=tenant_id, cidr=cidr)['subnets']
294 return len(cidr_in_use) != 0
295
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200296 def _make_create_subnet_request(namestart, network,
297 ip_version, subnets_client, **kwargs):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000298
299 subnet = dict(
300 name=data_utils.rand_name(namestart),
301 network_id=network['id'],
302 tenant_id=network['tenant_id'],
Andrea Frittolif4510a12017-03-07 19:17:11 +0000303 ip_version=ip_version,
304 **kwargs
305 )
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200306
307 if ip_version == 6:
308 subnet['ipv6_address_mode'] = 'slaac'
309 subnet['ipv6_ra_mode'] = 'slaac'
310
Andrea Frittolif4510a12017-03-07 19:17:11 +0000311 try:
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200312 return subnets_client.create_subnet(**subnet)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000313 except lib_exc.Conflict as e:
haixin48895812020-09-30 13:50:37 +0800314 if 'overlaps with another subnet' not in str(e):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000315 raise
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200316
317 result = None
318 str_cidr = None
319
320 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
321
322 ip_version = kwargs.pop('ip_version', 4)
323
324 if not use_default_subnetpool:
325
326 if ip_version == 6:
327 tenant_cidr = netaddr.IPNetwork(
328 CONF.network.project_network_v6_cidr)
329 num_bits = CONF.network.project_network_v6_mask_bits
330 else:
331 tenant_cidr = netaddr.IPNetwork(
332 CONF.network.project_network_cidr)
333 num_bits = CONF.network.project_network_mask_bits
334
335 # Repeatedly attempt subnet creation with sequential cidr
336 # blocks until an unallocated block is found.
337 for subnet_cidr in tenant_cidr.subnet(num_bits):
338 str_cidr = str(subnet_cidr)
339 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
340 continue
341
342 result = _make_create_subnet_request(
343 namestart, network, ip_version, subnets_client,
344 cidr=str_cidr, **kwargs)
345 if result is not None:
346 break
347 else:
348 result = _make_create_subnet_request(
349 namestart, network, ip_version, subnets_client,
350 **kwargs)
351
352 self.assertIsNotNone(result)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000353
354 subnet = result['subnet']
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200355 if str_cidr is not None:
356 self.assertEqual(subnet['cidr'], str_cidr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000357
358 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
359 subnets_client.delete_subnet, subnet['id'])
360
361 return subnet
362
363 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Rockyd3432662019-07-02 14:58:30 +1000364 if ip_addr:
365 ports = self.os_admin.ports_client.list_ports(
366 device_id=server['id'],
367 fixed_ips='ip_address=%s' % ip_addr)['ports']
368 else:
369 ports = self.os_admin.ports_client.list_ports(
370 device_id=server['id'])['ports']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000371 # A port can have more than one IP address in some cases.
372 # If the network is dual-stack (IPv4 + IPv6), this port is associated
373 # with 2 subnets
Rockyd3432662019-07-02 14:58:30 +1000374
375 def _is_active(port):
376 # NOTE(vsaienko) With Ironic, instances live on separate hardware
377 # servers. Neutron does not bind ports for Ironic instances, as a
378 # result the port remains in the DOWN state. This has been fixed
379 # with the introduction of the networking-baremetal plugin but
380 # it's not mandatory (and is not used on all stable branches).
381 return (port['status'] == 'ACTIVE' or
382 port.get('binding:vnic_type') == 'baremetal')
383
Andrea Frittolif4510a12017-03-07 19:17:11 +0000384 port_map = [(p["id"], fxip["ip_address"])
385 for p in ports
386 for fxip in p["fixed_ips"]
Rockyd3432662019-07-02 14:58:30 +1000387 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
388 _is_active(p))]
Andrea Frittolif4510a12017-03-07 19:17:11 +0000389 inactive = [p for p in ports if p['status'] != 'ACTIVE']
390 if inactive:
391 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
392
Rockyd3432662019-07-02 14:58:30 +1000393 self.assertNotEmpty(port_map,
Andrea Frittolif4510a12017-03-07 19:17:11 +0000394 "No IPv4 addresses found in: %s" % ports)
395 self.assertEqual(len(port_map), 1,
396 "Found multiple IPv4 addresses: %s. "
397 "Unable to determine which port to target."
398 % port_map)
399 return port_map[0]
400
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700401 def _get_network_by_name_or_id(self, identifier):
402
403 if uuidutils.is_uuid_like(identifier):
404 return self.os_admin.networks_client.show_network(
405 identifier)['network']
406
407 networks = self.os_admin.networks_client.list_networks(
408 name=identifier)['networks']
409 self.assertNotEqual(len(networks), 0,
410 "Unable to get network by name: %s" % identifier)
411 return networks[0]
412
413 def get_networks(self):
414 return self.os_admin.networks_client.list_networks()['networks']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000415
416 def create_floating_ip(self, thing, external_network_id=None,
lkuchlan7636a1f2020-04-30 16:13:13 +0300417 port_id=None, ip_addr=None, client=None):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000418 """Create a floating IP and associates to a resource/port on Neutron"""
419 if not external_network_id:
420 external_network_id = CONF.network.public_network_id
421 if not client:
422 client = self.floating_ips_client
423 if not port_id:
lkuchlan7636a1f2020-04-30 16:13:13 +0300424 port_id, ip4 = self._get_server_port_id_and_ip4(thing,
425 ip_addr=ip_addr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000426 else:
427 ip4 = None
428 result = client.create_floatingip(
429 floating_network_id=external_network_id,
430 port_id=port_id,
431 tenant_id=thing['tenant_id'],
432 fixed_ip_address=ip4
433 )
434 floating_ip = result['floatingip']
435 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
436 client.delete_floatingip,
437 floating_ip['id'])
438 return floating_ip
439
440 def _associate_floating_ip(self, floating_ip, server):
441 port_id, _ = self._get_server_port_id_and_ip4(server)
442 kwargs = dict(port_id=port_id)
443 floating_ip = self.floating_ips_client.update_floatingip(
444 floating_ip['id'], **kwargs)['floatingip']
445 self.assertEqual(port_id, floating_ip['port_id'])
446 return floating_ip
447
Andrea Frittolif4510a12017-03-07 19:17:11 +0000448 def check_floating_ip_status(self, floating_ip, status):
449 """Verifies floatingip reaches the given status
450
451 :param dict floating_ip: floating IP dict to check status
452 :param status: target status
453 :raises: AssertionError if status doesn't match
454 """
455 floatingip_id = floating_ip['id']
456
457 def refresh():
458 result = (self.floating_ips_client.
459 show_floatingip(floatingip_id)['floatingip'])
460 return status == result['status']
461
462 test_utils.call_until_true(refresh,
463 CONF.network.build_timeout,
464 CONF.network.build_interval)
465 floating_ip = self.floating_ips_client.show_floatingip(
466 floatingip_id)['floatingip']
467 self.assertEqual(status, floating_ip['status'],
468 message="FloatingIP: {fp} is at status: {cst}. "
469 "failed to reach status: {st}"
470 .format(fp=floating_ip, cst=floating_ip['status'],
471 st=status))
472 LOG.info("FloatingIP: {fp} is at status: {st}"
473 .format(fp=floating_ip, st=status))
474
475 def _check_tenant_network_connectivity(self, server,
476 username,
477 private_key,
478 should_connect=True,
479 servers_for_debug=None):
480 if not CONF.network.project_networks_reachable:
481 msg = 'Tenant networks not configured to be reachable.'
482 LOG.info(msg)
483 return
484 # The target login is assumed to have been configured for
485 # key-based authentication by cloud-init.
486 try:
487 for ip_addresses in server['addresses'].values():
488 for ip_address in ip_addresses:
489 self.check_vm_connectivity(ip_address['addr'],
490 username,
491 private_key,
Roman Popelkafd4e2f32022-03-21 10:16:30 +0100492 should_connect=should_connect,
493 server=server)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000494 except Exception as e:
495 LOG.exception('Tenant network connectivity check failed')
Roman Popelka164898c2022-03-21 09:12:38 +0100496 self.log_console_output(servers_for_debug)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000497 self._log_net_info(e)
498 raise
499
500 def _check_remote_connectivity(self, source, dest, should_succeed=True,
501 nic=None):
502 """assert ping server via source ssh connection
503
504 Note: This is an internal method. Use check_remote_connectivity
505 instead.
506
507 :param source: RemoteClient: an ssh connection from which to ping
508 :param dest: and IP to ping against
509 :param should_succeed: boolean should ping succeed or not
510 :param nic: specific network interface to ping from
511 """
512 def ping_remote():
513 try:
514 source.ping_host(dest, nic=nic)
515 except lib_exc.SSHExecCommandFailed:
516 LOG.warning('Failed to ping IP: %s via a ssh connection '
517 'from: %s.', dest, source.ssh_client.host)
518 return not should_succeed
519 return should_succeed
520
521 return test_utils.call_until_true(ping_remote,
522 CONF.validation.ping_timeout,
523 1)
524
525 def check_remote_connectivity(self, source, dest, should_succeed=True,
526 nic=None):
527 """assert ping server via source ssh connection
528
529 :param source: RemoteClient: an ssh connection from which to ping
530 :param dest: and IP to ping against
531 :param should_succeed: boolean should ping succeed or not
532 :param nic: specific network interface to ping from
533 """
534 result = self._check_remote_connectivity(source, dest, should_succeed,
535 nic)
536 source_host = source.ssh_client.host
537 if should_succeed:
zhongjun39e9c582017-06-21 15:17:11 +0800538 msg = ("Timed out waiting for %s to become reachable from %s"
539 % (dest, source_host))
Andrea Frittolif4510a12017-03-07 19:17:11 +0000540 else:
541 msg = "%s is reachable from %s" % (dest, source_host)
542 self.assertTrue(result, msg)
543
544 def _create_security_group(self, security_group_rules_client=None,
545 tenant_id=None,
546 namestart='secgroup-smoke',
547 security_groups_client=None):
548 if security_group_rules_client is None:
549 security_group_rules_client = self.security_group_rules_client
550 if security_groups_client is None:
551 security_groups_client = self.security_groups_client
552 if tenant_id is None:
553 tenant_id = security_groups_client.tenant_id
554 secgroup = self._create_empty_security_group(
555 namestart=namestart, client=security_groups_client,
556 tenant_id=tenant_id)
557
558 # Add rules to the security group
559 rules = self._create_loginable_secgroup_rule(
560 security_group_rules_client=security_group_rules_client,
561 secgroup=secgroup,
562 security_groups_client=security_groups_client)
563 for rule in rules:
564 self.assertEqual(tenant_id, rule['tenant_id'])
565 self.assertEqual(secgroup['id'], rule['security_group_id'])
566 return secgroup
567
568 def _create_empty_security_group(self, client=None, tenant_id=None,
569 namestart='secgroup-smoke'):
570 """Create a security group without rules.
571
572 Default rules will be created:
573 - IPv4 egress to any
574 - IPv6 egress to any
575
576 :param tenant_id: secgroup will be created in this tenant
577 :returns: the created security group
578 """
579 if client is None:
580 client = self.security_groups_client
581 if not tenant_id:
582 tenant_id = client.tenant_id
583 sg_name = data_utils.rand_name(namestart)
584 sg_desc = sg_name + " description"
585 sg_dict = dict(name=sg_name,
586 description=sg_desc)
587 sg_dict['tenant_id'] = tenant_id
588 result = client.create_security_group(**sg_dict)
589
590 secgroup = result['security_group']
591 self.assertEqual(secgroup['name'], sg_name)
592 self.assertEqual(tenant_id, secgroup['tenant_id'])
593 self.assertEqual(secgroup['description'], sg_desc)
594
595 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
596 client.delete_security_group, secgroup['id'])
597 return secgroup
598
599 def _default_security_group(self, client=None, tenant_id=None):
600 """Get default secgroup for given tenant_id.
601
602 :returns: default secgroup for given tenant
603 """
604 if client is None:
605 client = self.security_groups_client
606 if not tenant_id:
607 tenant_id = client.tenant_id
608 sgs = [
609 sg for sg in list(client.list_security_groups().values())[0]
610 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
611 ]
612 msg = "No default security group for tenant %s." % (tenant_id)
613 self.assertGreater(len(sgs), 0, msg)
614 return sgs[0]
615
616 def _create_security_group_rule(self, secgroup=None,
617 sec_group_rules_client=None,
618 tenant_id=None,
619 security_groups_client=None, **kwargs):
620 """Create a rule from a dictionary of rule parameters.
621
622 Create a rule in a secgroup. if secgroup not defined will search for
623 default secgroup in tenant_id.
624
625 :param secgroup: the security group.
626 :param tenant_id: if secgroup not passed -- the tenant in which to
627 search for default secgroup
628 :param kwargs: a dictionary containing rule parameters:
629 for example, to allow incoming ssh:
630 rule = {
631 direction: 'ingress'
632 protocol:'tcp',
633 port_range_min: 22,
634 port_range_max: 22
635 }
636 """
637 if sec_group_rules_client is None:
638 sec_group_rules_client = self.security_group_rules_client
639 if security_groups_client is None:
640 security_groups_client = self.security_groups_client
641 if not tenant_id:
642 tenant_id = security_groups_client.tenant_id
643 if secgroup is None:
644 secgroup = self._default_security_group(
645 client=security_groups_client, tenant_id=tenant_id)
646
647 ruleset = dict(security_group_id=secgroup['id'],
648 tenant_id=secgroup['tenant_id'])
649 ruleset.update(kwargs)
650
651 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
652 sg_rule = sg_rule['security_group_rule']
653
654 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
655 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
656
657 return sg_rule
658
659 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
660 secgroup=None,
661 security_groups_client=None):
662 """Create loginable security group rule
663
664 This function will create:
665 1. egress and ingress tcp port 22 allow rule in order to allow ssh
666 access for ipv4.
667 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
668 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
669 """
670
671 if security_group_rules_client is None:
672 security_group_rules_client = self.security_group_rules_client
673 if security_groups_client is None:
674 security_groups_client = self.security_groups_client
675 rules = []
676 rulesets = [
677 dict(
678 # ssh
679 protocol='tcp',
680 port_range_min=22,
681 port_range_max=22,
682 ),
683 dict(
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200684 # ipv6-ssh
685 protocol='tcp',
686 port_range_min=22,
687 port_range_max=22,
688 ethertype='IPv6',
689 ),
690 dict(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000691 # ping
692 protocol='icmp',
693 ),
694 dict(
695 # ipv6-icmp for ping6
696 protocol='icmp',
697 ethertype='IPv6',
698 )
699 ]
700 sec_group_rules_client = security_group_rules_client
701 for ruleset in rulesets:
702 for r_direction in ['ingress', 'egress']:
703 ruleset['direction'] = r_direction
704 try:
705 sg_rule = self._create_security_group_rule(
706 sec_group_rules_client=sec_group_rules_client,
707 secgroup=secgroup,
708 security_groups_client=security_groups_client,
709 **ruleset)
710 except lib_exc.Conflict as ex:
711 # if rule already exist - skip rule and continue
712 msg = 'Security group rule already exists'
713 if msg not in ex._error_string:
714 raise ex
715 else:
716 self.assertEqual(r_direction, sg_rule['direction'])
717 rules.append(sg_rule)
718
719 return rules
720
721 def _get_router(self, client=None, tenant_id=None):
722 """Retrieve a router for the given tenant id.
723
724 If a public router has been configured, it will be returned.
725
726 If a public router has not been configured, but a public
727 network has, a tenant router will be created and returned that
728 routes traffic to the public network.
729 """
730 if not client:
731 client = self.routers_client
732 if not tenant_id:
733 tenant_id = client.tenant_id
734 router_id = CONF.network.public_router_id
735 network_id = CONF.network.public_network_id
736 if router_id:
737 body = client.show_router(router_id)
738 return body['router']
739 elif network_id:
740 router = self._create_router(client, tenant_id)
741 kwargs = {'external_gateway_info': dict(network_id=network_id)}
742 router = client.update_router(router['id'], **kwargs)['router']
743 return router
744 else:
745 raise Exception("Neither of 'public_router_id' or "
746 "'public_network_id' has been defined.")
747
748 def _create_router(self, client=None, tenant_id=None,
749 namestart='router-smoke'):
750 if not client:
751 client = self.routers_client
752 if not tenant_id:
753 tenant_id = client.tenant_id
754 name = data_utils.rand_name(namestart)
755 result = client.create_router(name=name,
756 admin_state_up=True,
757 tenant_id=tenant_id)
758 router = result['router']
759 self.assertEqual(router['name'], name)
760 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
761 client.delete_router,
762 router['id'])
763 return router
764
765 def _update_router_admin_state(self, router, admin_state_up):
766 kwargs = dict(admin_state_up=admin_state_up)
767 router = self.routers_client.update_router(
768 router['id'], **kwargs)['router']
769 self.assertEqual(admin_state_up, router['admin_state_up'])
770
771 def create_networks(self, networks_client=None,
772 routers_client=None, subnets_client=None,
773 tenant_id=None, dns_nameservers=None,
774 port_security_enabled=True):
775 """Create a network with a subnet connected to a router.
776
777 The baremetal driver is a special case since all nodes are
778 on the same shared network.
779
780 :param tenant_id: id of tenant to create resources in.
781 :param dns_nameservers: list of dns servers to send to subnet.
782 :returns: network, subnet, router
783 """
784 if CONF.network.shared_physical_network:
785 # NOTE(Shrews): This exception is for environments where tenant
786 # credential isolation is available, but network separation is
787 # not (the current baremetal case). Likely can be removed when
788 # test account mgmt is reworked:
789 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
790 if not CONF.compute.fixed_network_name:
791 m = 'fixed_network_name must be specified in config'
792 raise lib_exc.InvalidConfiguration(m)
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700793 network = self._get_network_by_name_or_id(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000794 CONF.compute.fixed_network_name)
795 router = None
796 subnet = None
797 else:
Roman Popelka1118f3e2022-03-21 09:18:53 +0100798 network = self.create_network(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000799 networks_client=networks_client,
800 tenant_id=tenant_id,
801 port_security_enabled=port_security_enabled)
802 router = self._get_router(client=routers_client,
803 tenant_id=tenant_id)
804 subnet_kwargs = dict(network=network,
805 subnets_client=subnets_client,
806 routers_client=routers_client)
807 # use explicit check because empty list is a valid option
808 if dns_nameservers is not None:
809 subnet_kwargs['dns_nameservers'] = dns_nameservers
810 subnet = self._create_subnet(**subnet_kwargs)
811 if not routers_client:
812 routers_client = self.routers_client
813 router_id = router['id']
814 routers_client.add_router_interface(router_id,
815 subnet_id=subnet['id'])
816
817 # save a cleanup job to remove this association between
818 # router and subnet
819 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
820 routers_client.remove_router_interface, router_id,
821 subnet_id=subnet['id'])
822 return network, subnet, router