blob: 82b8b07dd8cc5313d96af67b43cb7d3f3e32e8b8 [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.common.utils.linux import remote_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000023from tempest import config
24from tempest import exceptions
Ken'ichi Ohmichi02d1f242017-03-12 18:56:27 -070025from tempest.lib.common.utils import data_utils
Andrea Frittolif4510a12017-03-07 19:17:11 +000026from tempest.lib.common.utils import test_utils
27from tempest.lib import exceptions as lib_exc
Roman Popelka290ef292022-02-28 10:41:04 +010028from tempest.scenario import manager
Andrea Frittolif4510a12017-03-07 19:17:11 +000029
30CONF = config.CONF
31
32LOG = log.getLogger(__name__)
33
34
Roman Popelka1118f3e2022-03-21 09:18:53 +010035class ScenarioTest(manager.NetworkScenarioTest):
Andrea Frittolif4510a12017-03-07 19:17:11 +000036 """Base class for scenario tests. Uses tempest own clients. """
37
38 credentials = ['primary']
39
40 @classmethod
41 def setup_clients(cls):
42 super(ScenarioTest, cls).setup_clients()
43 # Clients (in alphabetical order)
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070044 cls.flavors_client = cls.os_primary.flavors_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000045 cls.compute_floating_ips_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070046 cls.os_primary.compute_floating_ips_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000047 if CONF.service_available.glance:
48 # Check if glance v1 is available to determine which client to use.
49 if CONF.image_feature_enabled.api_v1:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070050 cls.image_client = cls.os_primary.image_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000051 elif CONF.image_feature_enabled.api_v2:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070052 cls.image_client = cls.os_primary.image_client_v2
Andrea Frittolif4510a12017-03-07 19:17:11 +000053 else:
54 raise lib_exc.InvalidConfiguration(
55 'Either api_v1 or api_v2 must be True in '
56 '[image-feature-enabled].')
57 # Compute image client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070058 cls.compute_images_client = cls.os_primary.compute_images_client
59 cls.keypairs_client = cls.os_primary.keypairs_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000060 # Nova security groups client
61 cls.compute_security_groups_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070062 cls.os_primary.compute_security_groups_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000063 cls.compute_security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070064 cls.os_primary.compute_security_group_rules_client)
65 cls.servers_client = cls.os_primary.servers_client
66 cls.interface_client = cls.os_primary.interfaces_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000067 # Neutron network client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070068 cls.networks_client = cls.os_primary.networks_client
69 cls.ports_client = cls.os_primary.ports_client
70 cls.routers_client = cls.os_primary.routers_client
71 cls.subnets_client = cls.os_primary.subnets_client
72 cls.floating_ips_client = cls.os_primary.floating_ips_client
73 cls.security_groups_client = cls.os_primary.security_groups_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000074 cls.security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070075 cls.os_primary.security_group_rules_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000076
Andrea Frittolif4510a12017-03-07 19:17:11 +000077 # ## Test functions library
78 #
79 # The create_[resource] functions only return body and discard the
80 # resp part which is not used in scenario tests
81
Andrea Frittolif4510a12017-03-07 19:17:11 +000082 def _create_loginable_secgroup_rule(self, secgroup_id=None):
83 _client = self.compute_security_groups_client
84 _client_rules = self.compute_security_group_rules_client
85 if secgroup_id is None:
86 sgs = _client.list_security_groups()['security_groups']
87 for sg in sgs:
88 if sg['name'] == 'default':
89 secgroup_id = sg['id']
90
91 # These rules are intended to permit inbound ssh and icmp
92 # traffic from all sources, so no group_id is provided.
93 # Setting a group_id would only permit traffic from ports
94 # belonging to the same security group.
95 rulesets = [
96 {
97 # ssh
98 'ip_protocol': 'tcp',
99 'from_port': 22,
100 'to_port': 22,
101 'cidr': '0.0.0.0/0',
102 },
103 {
104 # ping
105 'ip_protocol': 'icmp',
106 'from_port': -1,
107 'to_port': -1,
108 'cidr': '0.0.0.0/0',
109 }
110 ]
111 rules = list()
112 for ruleset in rulesets:
113 sg_rule = _client_rules.create_security_group_rule(
114 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
115 rules.append(sg_rule)
116 return rules
117
118 def _create_security_group(self):
119 # Create security group
120 sg_name = data_utils.rand_name(self.__class__.__name__)
121 sg_desc = sg_name + " description"
122 secgroup = self.compute_security_groups_client.create_security_group(
123 name=sg_name, description=sg_desc)['security_group']
124 self.assertEqual(secgroup['name'], sg_name)
125 self.assertEqual(secgroup['description'], sg_desc)
126 self.addCleanup(
127 test_utils.call_and_ignore_notfound_exc,
128 self.compute_security_groups_client.delete_security_group,
129 secgroup['id'])
130
131 # Add rules to the security group
132 self._create_loginable_secgroup_rule(secgroup['id'])
133
134 return secgroup
135
136 def get_remote_client(self, ip_address, username=None, private_key=None):
137 """Get a SSH client to a remote server
138
139 @param ip_address the server floating or fixed IP address to use
140 for ssh validation
141 @param username name of the Linux account on the remote server
142 @param private_key the SSH private key to use
143 @return a RemoteClient object
144 """
145
146 if username is None:
147 username = CONF.validation.image_ssh_user
148 # Set this with 'keypair' or others to log in with keypair or
149 # username/password.
150 if CONF.validation.auth_method == 'keypair':
151 password = None
152 if private_key is None:
153 private_key = self.keypair['private_key']
154 else:
155 password = CONF.validation.image_ssh_password
156 private_key = None
157 linux_client = remote_client.RemoteClient(ip_address, username,
158 pkey=private_key,
159 password=password)
160 try:
161 linux_client.validate_authentication()
162 except Exception as e:
163 message = ('Initializing SSH connection to %(ip)s failed. '
164 'Error: %(error)s' % {'ip': ip_address,
165 'error': e})
166 caller = test_utils.find_test_caller()
167 if caller:
168 message = '(%s) %s' % (caller, message)
169 LOG.exception(message)
Roman Popelka164898c2022-03-21 09:12:38 +0100170 self.log_console_output()
Andrea Frittolif4510a12017-03-07 19:17:11 +0000171 raise
172
173 return linux_client
174
175 def _image_create(self, name, fmt, path,
176 disk_format=None, properties=None):
177 if properties is None:
178 properties = {}
179 name = data_utils.rand_name('%s-' % name)
180 params = {
181 'name': name,
182 'container_format': fmt,
183 'disk_format': disk_format or fmt,
184 }
185 if CONF.image_feature_enabled.api_v1:
186 params['is_public'] = 'False'
187 params['properties'] = properties
188 params = {'headers': common_image.image_meta_to_headers(**params)}
189 else:
190 params['visibility'] = 'private'
191 # Additional properties are flattened out in the v2 API.
192 params.update(properties)
193 body = self.image_client.create_image(**params)
194 image = body['image'] if 'image' in body else body
195 self.addCleanup(self.image_client.delete_image, image['id'])
196 self.assertEqual("queued", image['status'])
197 with open(path, 'rb') as image_file:
198 if CONF.image_feature_enabled.api_v1:
199 self.image_client.update_image(image['id'], data=image_file)
200 else:
201 self.image_client.store_image_file(image['id'], image_file)
202 return image['id']
203
204 def glance_image_create(self):
Martin Kopec258cc6c2020-04-15 22:55:25 +0000205 img_path = CONF.scenario.img_file
Andrea Frittolif4510a12017-03-07 19:17:11 +0000206 img_container_format = CONF.scenario.img_container_format
207 img_disk_format = CONF.scenario.img_disk_format
208 img_properties = CONF.scenario.img_properties
209 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec258cc6c2020-04-15 22:55:25 +0000210 "properties: %s",
Andrea Frittolif4510a12017-03-07 19:17:11 +0000211 img_path, img_container_format, img_disk_format,
Martin Kopec258cc6c2020-04-15 22:55:25 +0000212 img_properties)
213 image = self._image_create('scenario-img',
214 img_container_format,
215 img_path,
216 disk_format=img_disk_format,
217 properties=img_properties)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000218 LOG.debug("image:%s", image)
219
220 return image
221
Andrea Frittolif4510a12017-03-07 19:17:11 +0000222 def _log_net_info(self, exc):
223 # network debug is called as part of ssh init
224 if not isinstance(exc, lib_exc.SSHTimeout):
225 LOG.debug('Network information on a devstack host')
226
Andrea Frittolif4510a12017-03-07 19:17:11 +0000227 def check_vm_connectivity(self, ip_address,
228 username=None,
229 private_key=None,
230 should_connect=True,
231 mtu=None):
232 """Check server connectivity
233
234 :param ip_address: server to test against
235 :param username: server's ssh username
236 :param private_key: server's ssh private key to be used
237 :param should_connect: True/False indicates positive/negative test
238 positive - attempt ping and ssh
239 negative - attempt ping and fail if succeed
240 :param mtu: network MTU to use for connectivity validation
241
242 :raises: AssertError if the result of the connectivity check does
243 not match the value of the should_connect param
244 """
245 if should_connect:
246 msg = "Timed out waiting for %s to become reachable" % ip_address
247 else:
248 msg = "ip address %s is reachable" % ip_address
249 self.assertTrue(self.ping_ip_address(ip_address,
250 should_succeed=should_connect,
251 mtu=mtu),
252 msg=msg)
253 if should_connect:
254 # no need to check ssh for negative connectivity
255 self.get_remote_client(ip_address, username, private_key)
256
257 def check_public_network_connectivity(self, ip_address, username,
258 private_key, should_connect=True,
259 msg=None, servers=None, mtu=None):
260 # The target login is assumed to have been configured for
261 # key-based authentication by cloud-init.
262 LOG.debug('checking network connections to IP %s with user: %s',
263 ip_address, username)
264 try:
265 self.check_vm_connectivity(ip_address,
266 username,
267 private_key,
268 should_connect=should_connect,
269 mtu=mtu)
270 except Exception:
271 ex_msg = 'Public network connectivity check failed'
272 if msg:
273 ex_msg += ": " + msg
274 LOG.exception(ex_msg)
Roman Popelka164898c2022-03-21 09:12:38 +0100275 self.log_console_output(servers)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000276 raise
277
278 def create_floating_ip(self, thing, pool_name=None):
279 """Create a floating IP and associates to a server on Nova"""
280
281 if not pool_name:
282 pool_name = CONF.network.floating_network_name
283 floating_ip = (self.compute_floating_ips_client.
284 create_floating_ip(pool=pool_name)['floating_ip'])
285 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
286 self.compute_floating_ips_client.delete_floating_ip,
287 floating_ip['id'])
288 self.compute_floating_ips_client.associate_floating_ip_to_server(
289 floating_ip['ip'], thing['id'])
290 return floating_ip
291
292 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
293 private_key=None):
294 ssh_client = self.get_remote_client(ip_address,
295 private_key=private_key)
296 if dev_name is not None:
297 ssh_client.make_fs(dev_name)
298 ssh_client.mount(dev_name, mount_path)
299 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
300 ssh_client.exec_command(cmd_timestamp)
301 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
302 % mount_path)
303 if dev_name is not None:
304 ssh_client.umount(mount_path)
305 return timestamp
306
307 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
308 private_key=None):
309 ssh_client = self.get_remote_client(ip_address,
310 private_key=private_key)
311 if dev_name is not None:
312 ssh_client.mount(dev_name, mount_path)
313 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
314 % mount_path)
315 if dev_name is not None:
316 ssh_client.umount(mount_path)
317 return timestamp
318
319 def get_server_ip(self, server):
320 """Get the server fixed or floating IP.
321
322 Based on the configuration we're in, return a correct ip
323 address for validating that a guest is up.
324 """
325 if CONF.validation.connect_method == 'floating':
326 # The tests calling this method don't have a floating IP
327 # and can't make use of the validation resources. So the
328 # method is creating the floating IP there.
329 return self.create_floating_ip(server)['ip']
330 elif CONF.validation.connect_method == 'fixed':
331 # Determine the network name to look for based on config or creds
332 # provider network resources.
333 if CONF.validation.network_for_ssh:
334 addresses = server['addresses'][
335 CONF.validation.network_for_ssh]
336 else:
337 creds_provider = self._get_credentials_provider()
338 net_creds = creds_provider.get_primary_creds()
339 network = getattr(net_creds, 'network', None)
340 addresses = (server['addresses'][network['name']]
341 if network else [])
342 for address in addresses:
343 if (address['version'] == CONF.validation.ip_version_for_ssh
344 and address['OS-EXT-IPS:type'] == 'fixed'):
345 return address['addr']
346 raise exceptions.ServerUnreachable(server_id=server['id'])
347 else:
348 raise lib_exc.InvalidConfiguration()
349
350
351class NetworkScenarioTest(ScenarioTest):
352 """Base class for network scenario tests.
353
354 This class provide helpers for network scenario tests, using the neutron
355 API. Helpers from ancestor which use the nova network API are overridden
356 with the neutron API.
357
358 This Class also enforces using Neutron instead of novanetwork.
359 Subclassed tests will be skipped if Neutron is not enabled
360
361 """
362
363 credentials = ['primary', 'admin']
364
365 @classmethod
366 def skip_checks(cls):
367 super(NetworkScenarioTest, cls).skip_checks()
368 if not CONF.service_available.neutron:
369 raise cls.skipException('Neutron not available')
370
Andrea Frittolif4510a12017-03-07 19:17:11 +0000371 def _create_subnet(self, network, subnets_client=None,
372 routers_client=None, namestart='subnet-smoke',
373 **kwargs):
374 """Create a subnet for the given network
375
376 within the cidr block configured for tenant networks.
377 """
378 if not subnets_client:
379 subnets_client = self.subnets_client
380 if not routers_client:
381 routers_client = self.routers_client
382
383 def cidr_in_use(cidr, tenant_id):
384 """Check cidr existence
385
386 :returns: True if subnet with cidr already exist in tenant
387 False else
388 """
Vu Cong Tuan99751862017-06-23 19:46:40 +0700389 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000390 tenant_id=tenant_id, cidr=cidr)['subnets']
391 return len(cidr_in_use) != 0
392
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200393 def _make_create_subnet_request(namestart, network,
394 ip_version, subnets_client, **kwargs):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000395
396 subnet = dict(
397 name=data_utils.rand_name(namestart),
398 network_id=network['id'],
399 tenant_id=network['tenant_id'],
Andrea Frittolif4510a12017-03-07 19:17:11 +0000400 ip_version=ip_version,
401 **kwargs
402 )
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200403
404 if ip_version == 6:
405 subnet['ipv6_address_mode'] = 'slaac'
406 subnet['ipv6_ra_mode'] = 'slaac'
407
Andrea Frittolif4510a12017-03-07 19:17:11 +0000408 try:
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200409 return subnets_client.create_subnet(**subnet)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000410 except lib_exc.Conflict as e:
haixin48895812020-09-30 13:50:37 +0800411 if 'overlaps with another subnet' not in str(e):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000412 raise
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200413
414 result = None
415 str_cidr = None
416
417 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
418
419 ip_version = kwargs.pop('ip_version', 4)
420
421 if not use_default_subnetpool:
422
423 if ip_version == 6:
424 tenant_cidr = netaddr.IPNetwork(
425 CONF.network.project_network_v6_cidr)
426 num_bits = CONF.network.project_network_v6_mask_bits
427 else:
428 tenant_cidr = netaddr.IPNetwork(
429 CONF.network.project_network_cidr)
430 num_bits = CONF.network.project_network_mask_bits
431
432 # Repeatedly attempt subnet creation with sequential cidr
433 # blocks until an unallocated block is found.
434 for subnet_cidr in tenant_cidr.subnet(num_bits):
435 str_cidr = str(subnet_cidr)
436 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
437 continue
438
439 result = _make_create_subnet_request(
440 namestart, network, ip_version, subnets_client,
441 cidr=str_cidr, **kwargs)
442 if result is not None:
443 break
444 else:
445 result = _make_create_subnet_request(
446 namestart, network, ip_version, subnets_client,
447 **kwargs)
448
449 self.assertIsNotNone(result)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000450
451 subnet = result['subnet']
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200452 if str_cidr is not None:
453 self.assertEqual(subnet['cidr'], str_cidr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000454
455 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
456 subnets_client.delete_subnet, subnet['id'])
457
458 return subnet
459
460 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Rockyd3432662019-07-02 14:58:30 +1000461 if ip_addr:
462 ports = self.os_admin.ports_client.list_ports(
463 device_id=server['id'],
464 fixed_ips='ip_address=%s' % ip_addr)['ports']
465 else:
466 ports = self.os_admin.ports_client.list_ports(
467 device_id=server['id'])['ports']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000468 # A port can have more than one IP address in some cases.
469 # If the network is dual-stack (IPv4 + IPv6), this port is associated
470 # with 2 subnets
Rockyd3432662019-07-02 14:58:30 +1000471
472 def _is_active(port):
473 # NOTE(vsaienko) With Ironic, instances live on separate hardware
474 # servers. Neutron does not bind ports for Ironic instances, as a
475 # result the port remains in the DOWN state. This has been fixed
476 # with the introduction of the networking-baremetal plugin but
477 # it's not mandatory (and is not used on all stable branches).
478 return (port['status'] == 'ACTIVE' or
479 port.get('binding:vnic_type') == 'baremetal')
480
Andrea Frittolif4510a12017-03-07 19:17:11 +0000481 port_map = [(p["id"], fxip["ip_address"])
482 for p in ports
483 for fxip in p["fixed_ips"]
Rockyd3432662019-07-02 14:58:30 +1000484 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
485 _is_active(p))]
Andrea Frittolif4510a12017-03-07 19:17:11 +0000486 inactive = [p for p in ports if p['status'] != 'ACTIVE']
487 if inactive:
488 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
489
Rockyd3432662019-07-02 14:58:30 +1000490 self.assertNotEmpty(port_map,
Andrea Frittolif4510a12017-03-07 19:17:11 +0000491 "No IPv4 addresses found in: %s" % ports)
492 self.assertEqual(len(port_map), 1,
493 "Found multiple IPv4 addresses: %s. "
494 "Unable to determine which port to target."
495 % port_map)
496 return port_map[0]
497
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700498 def _get_network_by_name_or_id(self, identifier):
499
500 if uuidutils.is_uuid_like(identifier):
501 return self.os_admin.networks_client.show_network(
502 identifier)['network']
503
504 networks = self.os_admin.networks_client.list_networks(
505 name=identifier)['networks']
506 self.assertNotEqual(len(networks), 0,
507 "Unable to get network by name: %s" % identifier)
508 return networks[0]
509
510 def get_networks(self):
511 return self.os_admin.networks_client.list_networks()['networks']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000512
513 def create_floating_ip(self, thing, external_network_id=None,
lkuchlan7636a1f2020-04-30 16:13:13 +0300514 port_id=None, ip_addr=None, client=None):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000515 """Create a floating IP and associates to a resource/port on Neutron"""
516 if not external_network_id:
517 external_network_id = CONF.network.public_network_id
518 if not client:
519 client = self.floating_ips_client
520 if not port_id:
lkuchlan7636a1f2020-04-30 16:13:13 +0300521 port_id, ip4 = self._get_server_port_id_and_ip4(thing,
522 ip_addr=ip_addr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000523 else:
524 ip4 = None
525 result = client.create_floatingip(
526 floating_network_id=external_network_id,
527 port_id=port_id,
528 tenant_id=thing['tenant_id'],
529 fixed_ip_address=ip4
530 )
531 floating_ip = result['floatingip']
532 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
533 client.delete_floatingip,
534 floating_ip['id'])
535 return floating_ip
536
537 def _associate_floating_ip(self, floating_ip, server):
538 port_id, _ = self._get_server_port_id_and_ip4(server)
539 kwargs = dict(port_id=port_id)
540 floating_ip = self.floating_ips_client.update_floatingip(
541 floating_ip['id'], **kwargs)['floatingip']
542 self.assertEqual(port_id, floating_ip['port_id'])
543 return floating_ip
544
Andrea Frittolif4510a12017-03-07 19:17:11 +0000545 def check_floating_ip_status(self, floating_ip, status):
546 """Verifies floatingip reaches the given status
547
548 :param dict floating_ip: floating IP dict to check status
549 :param status: target status
550 :raises: AssertionError if status doesn't match
551 """
552 floatingip_id = floating_ip['id']
553
554 def refresh():
555 result = (self.floating_ips_client.
556 show_floatingip(floatingip_id)['floatingip'])
557 return status == result['status']
558
559 test_utils.call_until_true(refresh,
560 CONF.network.build_timeout,
561 CONF.network.build_interval)
562 floating_ip = self.floating_ips_client.show_floatingip(
563 floatingip_id)['floatingip']
564 self.assertEqual(status, floating_ip['status'],
565 message="FloatingIP: {fp} is at status: {cst}. "
566 "failed to reach status: {st}"
567 .format(fp=floating_ip, cst=floating_ip['status'],
568 st=status))
569 LOG.info("FloatingIP: {fp} is at status: {st}"
570 .format(fp=floating_ip, st=status))
571
572 def _check_tenant_network_connectivity(self, server,
573 username,
574 private_key,
575 should_connect=True,
576 servers_for_debug=None):
577 if not CONF.network.project_networks_reachable:
578 msg = 'Tenant networks not configured to be reachable.'
579 LOG.info(msg)
580 return
581 # The target login is assumed to have been configured for
582 # key-based authentication by cloud-init.
583 try:
584 for ip_addresses in server['addresses'].values():
585 for ip_address in ip_addresses:
586 self.check_vm_connectivity(ip_address['addr'],
587 username,
588 private_key,
589 should_connect=should_connect)
590 except Exception as e:
591 LOG.exception('Tenant network connectivity check failed')
Roman Popelka164898c2022-03-21 09:12:38 +0100592 self.log_console_output(servers_for_debug)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000593 self._log_net_info(e)
594 raise
595
596 def _check_remote_connectivity(self, source, dest, should_succeed=True,
597 nic=None):
598 """assert ping server via source ssh connection
599
600 Note: This is an internal method. Use check_remote_connectivity
601 instead.
602
603 :param source: RemoteClient: an ssh connection from which to ping
604 :param dest: and IP to ping against
605 :param should_succeed: boolean should ping succeed or not
606 :param nic: specific network interface to ping from
607 """
608 def ping_remote():
609 try:
610 source.ping_host(dest, nic=nic)
611 except lib_exc.SSHExecCommandFailed:
612 LOG.warning('Failed to ping IP: %s via a ssh connection '
613 'from: %s.', dest, source.ssh_client.host)
614 return not should_succeed
615 return should_succeed
616
617 return test_utils.call_until_true(ping_remote,
618 CONF.validation.ping_timeout,
619 1)
620
621 def check_remote_connectivity(self, source, dest, should_succeed=True,
622 nic=None):
623 """assert ping server via source ssh connection
624
625 :param source: RemoteClient: an ssh connection from which to ping
626 :param dest: and IP to ping against
627 :param should_succeed: boolean should ping succeed or not
628 :param nic: specific network interface to ping from
629 """
630 result = self._check_remote_connectivity(source, dest, should_succeed,
631 nic)
632 source_host = source.ssh_client.host
633 if should_succeed:
zhongjun39e9c582017-06-21 15:17:11 +0800634 msg = ("Timed out waiting for %s to become reachable from %s"
635 % (dest, source_host))
Andrea Frittolif4510a12017-03-07 19:17:11 +0000636 else:
637 msg = "%s is reachable from %s" % (dest, source_host)
638 self.assertTrue(result, msg)
639
640 def _create_security_group(self, security_group_rules_client=None,
641 tenant_id=None,
642 namestart='secgroup-smoke',
643 security_groups_client=None):
644 if security_group_rules_client is None:
645 security_group_rules_client = self.security_group_rules_client
646 if security_groups_client is None:
647 security_groups_client = self.security_groups_client
648 if tenant_id is None:
649 tenant_id = security_groups_client.tenant_id
650 secgroup = self._create_empty_security_group(
651 namestart=namestart, client=security_groups_client,
652 tenant_id=tenant_id)
653
654 # Add rules to the security group
655 rules = self._create_loginable_secgroup_rule(
656 security_group_rules_client=security_group_rules_client,
657 secgroup=secgroup,
658 security_groups_client=security_groups_client)
659 for rule in rules:
660 self.assertEqual(tenant_id, rule['tenant_id'])
661 self.assertEqual(secgroup['id'], rule['security_group_id'])
662 return secgroup
663
664 def _create_empty_security_group(self, client=None, tenant_id=None,
665 namestart='secgroup-smoke'):
666 """Create a security group without rules.
667
668 Default rules will be created:
669 - IPv4 egress to any
670 - IPv6 egress to any
671
672 :param tenant_id: secgroup will be created in this tenant
673 :returns: the created security group
674 """
675 if client is None:
676 client = self.security_groups_client
677 if not tenant_id:
678 tenant_id = client.tenant_id
679 sg_name = data_utils.rand_name(namestart)
680 sg_desc = sg_name + " description"
681 sg_dict = dict(name=sg_name,
682 description=sg_desc)
683 sg_dict['tenant_id'] = tenant_id
684 result = client.create_security_group(**sg_dict)
685
686 secgroup = result['security_group']
687 self.assertEqual(secgroup['name'], sg_name)
688 self.assertEqual(tenant_id, secgroup['tenant_id'])
689 self.assertEqual(secgroup['description'], sg_desc)
690
691 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
692 client.delete_security_group, secgroup['id'])
693 return secgroup
694
695 def _default_security_group(self, client=None, tenant_id=None):
696 """Get default secgroup for given tenant_id.
697
698 :returns: default secgroup for given tenant
699 """
700 if client is None:
701 client = self.security_groups_client
702 if not tenant_id:
703 tenant_id = client.tenant_id
704 sgs = [
705 sg for sg in list(client.list_security_groups().values())[0]
706 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
707 ]
708 msg = "No default security group for tenant %s." % (tenant_id)
709 self.assertGreater(len(sgs), 0, msg)
710 return sgs[0]
711
712 def _create_security_group_rule(self, secgroup=None,
713 sec_group_rules_client=None,
714 tenant_id=None,
715 security_groups_client=None, **kwargs):
716 """Create a rule from a dictionary of rule parameters.
717
718 Create a rule in a secgroup. if secgroup not defined will search for
719 default secgroup in tenant_id.
720
721 :param secgroup: the security group.
722 :param tenant_id: if secgroup not passed -- the tenant in which to
723 search for default secgroup
724 :param kwargs: a dictionary containing rule parameters:
725 for example, to allow incoming ssh:
726 rule = {
727 direction: 'ingress'
728 protocol:'tcp',
729 port_range_min: 22,
730 port_range_max: 22
731 }
732 """
733 if sec_group_rules_client is None:
734 sec_group_rules_client = self.security_group_rules_client
735 if security_groups_client is None:
736 security_groups_client = self.security_groups_client
737 if not tenant_id:
738 tenant_id = security_groups_client.tenant_id
739 if secgroup is None:
740 secgroup = self._default_security_group(
741 client=security_groups_client, tenant_id=tenant_id)
742
743 ruleset = dict(security_group_id=secgroup['id'],
744 tenant_id=secgroup['tenant_id'])
745 ruleset.update(kwargs)
746
747 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
748 sg_rule = sg_rule['security_group_rule']
749
750 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
751 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
752
753 return sg_rule
754
755 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
756 secgroup=None,
757 security_groups_client=None):
758 """Create loginable security group rule
759
760 This function will create:
761 1. egress and ingress tcp port 22 allow rule in order to allow ssh
762 access for ipv4.
763 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
764 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
765 """
766
767 if security_group_rules_client is None:
768 security_group_rules_client = self.security_group_rules_client
769 if security_groups_client is None:
770 security_groups_client = self.security_groups_client
771 rules = []
772 rulesets = [
773 dict(
774 # ssh
775 protocol='tcp',
776 port_range_min=22,
777 port_range_max=22,
778 ),
779 dict(
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200780 # ipv6-ssh
781 protocol='tcp',
782 port_range_min=22,
783 port_range_max=22,
784 ethertype='IPv6',
785 ),
786 dict(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000787 # ping
788 protocol='icmp',
789 ),
790 dict(
791 # ipv6-icmp for ping6
792 protocol='icmp',
793 ethertype='IPv6',
794 )
795 ]
796 sec_group_rules_client = security_group_rules_client
797 for ruleset in rulesets:
798 for r_direction in ['ingress', 'egress']:
799 ruleset['direction'] = r_direction
800 try:
801 sg_rule = self._create_security_group_rule(
802 sec_group_rules_client=sec_group_rules_client,
803 secgroup=secgroup,
804 security_groups_client=security_groups_client,
805 **ruleset)
806 except lib_exc.Conflict as ex:
807 # if rule already exist - skip rule and continue
808 msg = 'Security group rule already exists'
809 if msg not in ex._error_string:
810 raise ex
811 else:
812 self.assertEqual(r_direction, sg_rule['direction'])
813 rules.append(sg_rule)
814
815 return rules
816
817 def _get_router(self, client=None, tenant_id=None):
818 """Retrieve a router for the given tenant id.
819
820 If a public router has been configured, it will be returned.
821
822 If a public router has not been configured, but a public
823 network has, a tenant router will be created and returned that
824 routes traffic to the public network.
825 """
826 if not client:
827 client = self.routers_client
828 if not tenant_id:
829 tenant_id = client.tenant_id
830 router_id = CONF.network.public_router_id
831 network_id = CONF.network.public_network_id
832 if router_id:
833 body = client.show_router(router_id)
834 return body['router']
835 elif network_id:
836 router = self._create_router(client, tenant_id)
837 kwargs = {'external_gateway_info': dict(network_id=network_id)}
838 router = client.update_router(router['id'], **kwargs)['router']
839 return router
840 else:
841 raise Exception("Neither of 'public_router_id' or "
842 "'public_network_id' has been defined.")
843
844 def _create_router(self, client=None, tenant_id=None,
845 namestart='router-smoke'):
846 if not client:
847 client = self.routers_client
848 if not tenant_id:
849 tenant_id = client.tenant_id
850 name = data_utils.rand_name(namestart)
851 result = client.create_router(name=name,
852 admin_state_up=True,
853 tenant_id=tenant_id)
854 router = result['router']
855 self.assertEqual(router['name'], name)
856 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
857 client.delete_router,
858 router['id'])
859 return router
860
861 def _update_router_admin_state(self, router, admin_state_up):
862 kwargs = dict(admin_state_up=admin_state_up)
863 router = self.routers_client.update_router(
864 router['id'], **kwargs)['router']
865 self.assertEqual(admin_state_up, router['admin_state_up'])
866
867 def create_networks(self, networks_client=None,
868 routers_client=None, subnets_client=None,
869 tenant_id=None, dns_nameservers=None,
870 port_security_enabled=True):
871 """Create a network with a subnet connected to a router.
872
873 The baremetal driver is a special case since all nodes are
874 on the same shared network.
875
876 :param tenant_id: id of tenant to create resources in.
877 :param dns_nameservers: list of dns servers to send to subnet.
878 :returns: network, subnet, router
879 """
880 if CONF.network.shared_physical_network:
881 # NOTE(Shrews): This exception is for environments where tenant
882 # credential isolation is available, but network separation is
883 # not (the current baremetal case). Likely can be removed when
884 # test account mgmt is reworked:
885 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
886 if not CONF.compute.fixed_network_name:
887 m = 'fixed_network_name must be specified in config'
888 raise lib_exc.InvalidConfiguration(m)
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700889 network = self._get_network_by_name_or_id(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000890 CONF.compute.fixed_network_name)
891 router = None
892 subnet = None
893 else:
Roman Popelka1118f3e2022-03-21 09:18:53 +0100894 network = self.create_network(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000895 networks_client=networks_client,
896 tenant_id=tenant_id,
897 port_security_enabled=port_security_enabled)
898 router = self._get_router(client=routers_client,
899 tenant_id=tenant_id)
900 subnet_kwargs = dict(network=network,
901 subnets_client=subnets_client,
902 routers_client=routers_client)
903 # use explicit check because empty list is a valid option
904 if dns_nameservers is not None:
905 subnet_kwargs['dns_nameservers'] = dns_nameservers
906 subnet = self._create_subnet(**subnet_kwargs)
907 if not routers_client:
908 routers_client = self.routers_client
909 router_id = router['id']
910 routers_client.add_router_interface(router_id,
911 subnet_id=subnet['id'])
912
913 # save a cleanup job to remove this association between
914 # router and subnet
915 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
916 routers_client.remove_router_interface, router_id,
917 subnet_id=subnet['id'])
918 return network, subnet, router