blob: 85d2f27f3878ee5066d427317285d323e41c65a6 [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 +000017from oslo_log import log
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -070018from oslo_utils import uuidutils
Andrea Frittolif4510a12017-03-07 19:17:11 +000019from tempest.common import image as common_image
Andrea Frittolif4510a12017-03-07 19:17:11 +000020from tempest import config
Ken'ichi Ohmichi02d1f242017-03-12 18:56:27 -070021from tempest.lib.common.utils import data_utils
Andrea Frittolif4510a12017-03-07 19:17:11 +000022from tempest.lib.common.utils import test_utils
23from tempest.lib import exceptions as lib_exc
Roman Popelka290ef292022-02-28 10:41:04 +010024from tempest.scenario import manager
Andrea Frittolif4510a12017-03-07 19:17:11 +000025
26CONF = config.CONF
27
28LOG = log.getLogger(__name__)
29
30
Roman Popelka1118f3e2022-03-21 09:18:53 +010031class ScenarioTest(manager.NetworkScenarioTest):
Andrea Frittolif4510a12017-03-07 19:17:11 +000032 """Base class for scenario tests. Uses tempest own clients. """
33
34 credentials = ['primary']
35
36 @classmethod
37 def setup_clients(cls):
38 super(ScenarioTest, cls).setup_clients()
39 # Clients (in alphabetical order)
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070040 cls.flavors_client = cls.os_primary.flavors_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000041 cls.compute_floating_ips_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070042 cls.os_primary.compute_floating_ips_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000043 if CONF.service_available.glance:
44 # Check if glance v1 is available to determine which client to use.
45 if CONF.image_feature_enabled.api_v1:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070046 cls.image_client = cls.os_primary.image_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000047 elif CONF.image_feature_enabled.api_v2:
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070048 cls.image_client = cls.os_primary.image_client_v2
Andrea Frittolif4510a12017-03-07 19:17:11 +000049 else:
50 raise lib_exc.InvalidConfiguration(
51 'Either api_v1 or api_v2 must be True in '
52 '[image-feature-enabled].')
53 # Compute image client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070054 cls.compute_images_client = cls.os_primary.compute_images_client
55 cls.keypairs_client = cls.os_primary.keypairs_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000056 # Nova security groups client
57 cls.compute_security_groups_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070058 cls.os_primary.compute_security_groups_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000059 cls.compute_security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070060 cls.os_primary.compute_security_group_rules_client)
61 cls.servers_client = cls.os_primary.servers_client
62 cls.interface_client = cls.os_primary.interfaces_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000063 # Neutron network client
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070064 cls.networks_client = cls.os_primary.networks_client
65 cls.ports_client = cls.os_primary.ports_client
66 cls.routers_client = cls.os_primary.routers_client
67 cls.subnets_client = cls.os_primary.subnets_client
68 cls.floating_ips_client = cls.os_primary.floating_ips_client
69 cls.security_groups_client = cls.os_primary.security_groups_client
Andrea Frittolif4510a12017-03-07 19:17:11 +000070 cls.security_group_rules_client = (
Vu Cong Tuandb2abab2017-06-21 20:38:39 +070071 cls.os_primary.security_group_rules_client)
Andrea Frittolif4510a12017-03-07 19:17:11 +000072
Andrea Frittolif4510a12017-03-07 19:17:11 +000073 # ## Test functions library
74 #
75 # The create_[resource] functions only return body and discard the
76 # resp part which is not used in scenario tests
77
Andrea Frittolif4510a12017-03-07 19:17:11 +000078 def _image_create(self, name, fmt, path,
79 disk_format=None, properties=None):
80 if properties is None:
81 properties = {}
82 name = data_utils.rand_name('%s-' % name)
83 params = {
84 'name': name,
85 'container_format': fmt,
86 'disk_format': disk_format or fmt,
87 }
88 if CONF.image_feature_enabled.api_v1:
89 params['is_public'] = 'False'
90 params['properties'] = properties
91 params = {'headers': common_image.image_meta_to_headers(**params)}
92 else:
93 params['visibility'] = 'private'
94 # Additional properties are flattened out in the v2 API.
95 params.update(properties)
96 body = self.image_client.create_image(**params)
97 image = body['image'] if 'image' in body else body
98 self.addCleanup(self.image_client.delete_image, image['id'])
99 self.assertEqual("queued", image['status'])
100 with open(path, 'rb') as image_file:
101 if CONF.image_feature_enabled.api_v1:
102 self.image_client.update_image(image['id'], data=image_file)
103 else:
104 self.image_client.store_image_file(image['id'], image_file)
105 return image['id']
106
107 def glance_image_create(self):
Martin Kopec258cc6c2020-04-15 22:55:25 +0000108 img_path = CONF.scenario.img_file
Andrea Frittolif4510a12017-03-07 19:17:11 +0000109 img_container_format = CONF.scenario.img_container_format
110 img_disk_format = CONF.scenario.img_disk_format
111 img_properties = CONF.scenario.img_properties
112 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec258cc6c2020-04-15 22:55:25 +0000113 "properties: %s",
Andrea Frittolif4510a12017-03-07 19:17:11 +0000114 img_path, img_container_format, img_disk_format,
Martin Kopec258cc6c2020-04-15 22:55:25 +0000115 img_properties)
116 image = self._image_create('scenario-img',
117 img_container_format,
118 img_path,
119 disk_format=img_disk_format,
120 properties=img_properties)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000121 LOG.debug("image:%s", image)
122
123 return image
124
Andrea Frittolif4510a12017-03-07 19:17:11 +0000125 def _log_net_info(self, exc):
126 # network debug is called as part of ssh init
127 if not isinstance(exc, lib_exc.SSHTimeout):
128 LOG.debug('Network information on a devstack host')
129
Andrea Frittolif4510a12017-03-07 19:17:11 +0000130
131class NetworkScenarioTest(ScenarioTest):
132 """Base class for network scenario tests.
133
134 This class provide helpers for network scenario tests, using the neutron
135 API. Helpers from ancestor which use the nova network API are overridden
136 with the neutron API.
137
138 This Class also enforces using Neutron instead of novanetwork.
139 Subclassed tests will be skipped if Neutron is not enabled
140
141 """
142
143 credentials = ['primary', 'admin']
144
145 @classmethod
146 def skip_checks(cls):
147 super(NetworkScenarioTest, cls).skip_checks()
148 if not CONF.service_available.neutron:
149 raise cls.skipException('Neutron not available')
150
Goutham Pacha Ravi37ee6772019-10-18 12:53:22 -0700151 def _get_network_by_name_or_id(self, identifier):
152
153 if uuidutils.is_uuid_like(identifier):
154 return self.os_admin.networks_client.show_network(
155 identifier)['network']
156
157 networks = self.os_admin.networks_client.list_networks(
158 name=identifier)['networks']
159 self.assertNotEqual(len(networks), 0,
160 "Unable to get network by name: %s" % identifier)
161 return networks[0]
162
163 def get_networks(self):
164 return self.os_admin.networks_client.list_networks()['networks']
Andrea Frittolif4510a12017-03-07 19:17:11 +0000165
166 def create_floating_ip(self, thing, external_network_id=None,
lkuchlan7636a1f2020-04-30 16:13:13 +0300167 port_id=None, ip_addr=None, client=None):
Andrea Frittolif4510a12017-03-07 19:17:11 +0000168 """Create a floating IP and associates to a resource/port on Neutron"""
169 if not external_network_id:
170 external_network_id = CONF.network.public_network_id
171 if not client:
172 client = self.floating_ips_client
173 if not port_id:
Roman Popelkaf880ce32022-03-22 13:26:51 +0100174 port_id, ip4 = self.get_server_port_id_and_ip4(thing,
175 ip_addr=ip_addr)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000176 else:
177 ip4 = None
178 result = client.create_floatingip(
179 floating_network_id=external_network_id,
180 port_id=port_id,
181 tenant_id=thing['tenant_id'],
182 fixed_ip_address=ip4
183 )
184 floating_ip = result['floatingip']
185 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
186 client.delete_floatingip,
187 floating_ip['id'])
188 return floating_ip
189
Andrea Frittolif4510a12017-03-07 19:17:11 +0000190 def _check_tenant_network_connectivity(self, server,
191 username,
192 private_key,
193 should_connect=True,
194 servers_for_debug=None):
195 if not CONF.network.project_networks_reachable:
196 msg = 'Tenant networks not configured to be reachable.'
197 LOG.info(msg)
198 return
199 # The target login is assumed to have been configured for
200 # key-based authentication by cloud-init.
201 try:
202 for ip_addresses in server['addresses'].values():
203 for ip_address in ip_addresses:
204 self.check_vm_connectivity(ip_address['addr'],
205 username,
206 private_key,
Roman Popelkafd4e2f32022-03-21 10:16:30 +0100207 should_connect=should_connect,
208 server=server)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000209 except Exception as e:
210 LOG.exception('Tenant network connectivity check failed')
Roman Popelka164898c2022-03-21 09:12:38 +0100211 self.log_console_output(servers_for_debug)
Andrea Frittolif4510a12017-03-07 19:17:11 +0000212 self._log_net_info(e)
213 raise
214
215 def _check_remote_connectivity(self, source, dest, should_succeed=True,
216 nic=None):
217 """assert ping server via source ssh connection
218
219 Note: This is an internal method. Use check_remote_connectivity
220 instead.
221
222 :param source: RemoteClient: an ssh connection from which to ping
223 :param dest: and IP to ping against
224 :param should_succeed: boolean should ping succeed or not
225 :param nic: specific network interface to ping from
226 """
227 def ping_remote():
228 try:
229 source.ping_host(dest, nic=nic)
230 except lib_exc.SSHExecCommandFailed:
231 LOG.warning('Failed to ping IP: %s via a ssh connection '
232 'from: %s.', dest, source.ssh_client.host)
233 return not should_succeed
234 return should_succeed
235
236 return test_utils.call_until_true(ping_remote,
237 CONF.validation.ping_timeout,
238 1)
239
240 def check_remote_connectivity(self, source, dest, should_succeed=True,
241 nic=None):
242 """assert ping server via source ssh connection
243
244 :param source: RemoteClient: an ssh connection from which to ping
245 :param dest: and IP to ping against
246 :param should_succeed: boolean should ping succeed or not
247 :param nic: specific network interface to ping from
248 """
249 result = self._check_remote_connectivity(source, dest, should_succeed,
250 nic)
251 source_host = source.ssh_client.host
252 if should_succeed:
zhongjun39e9c582017-06-21 15:17:11 +0800253 msg = ("Timed out waiting for %s to become reachable from %s"
254 % (dest, source_host))
Andrea Frittolif4510a12017-03-07 19:17:11 +0000255 else:
256 msg = "%s is reachable from %s" % (dest, source_host)
257 self.assertTrue(result, msg)
258
259 def _create_security_group(self, security_group_rules_client=None,
260 tenant_id=None,
261 namestart='secgroup-smoke',
262 security_groups_client=None):
263 if security_group_rules_client is None:
264 security_group_rules_client = self.security_group_rules_client
265 if security_groups_client is None:
266 security_groups_client = self.security_groups_client
267 if tenant_id is None:
268 tenant_id = security_groups_client.tenant_id
269 secgroup = self._create_empty_security_group(
270 namestart=namestart, client=security_groups_client,
271 tenant_id=tenant_id)
272
273 # Add rules to the security group
274 rules = self._create_loginable_secgroup_rule(
275 security_group_rules_client=security_group_rules_client,
276 secgroup=secgroup,
277 security_groups_client=security_groups_client)
278 for rule in rules:
279 self.assertEqual(tenant_id, rule['tenant_id'])
280 self.assertEqual(secgroup['id'], rule['security_group_id'])
281 return secgroup
282
283 def _create_empty_security_group(self, client=None, tenant_id=None,
284 namestart='secgroup-smoke'):
285 """Create a security group without rules.
286
287 Default rules will be created:
288 - IPv4 egress to any
289 - IPv6 egress to any
290
291 :param tenant_id: secgroup will be created in this tenant
292 :returns: the created security group
293 """
294 if client is None:
295 client = self.security_groups_client
296 if not tenant_id:
297 tenant_id = client.tenant_id
298 sg_name = data_utils.rand_name(namestart)
299 sg_desc = sg_name + " description"
300 sg_dict = dict(name=sg_name,
301 description=sg_desc)
302 sg_dict['tenant_id'] = tenant_id
303 result = client.create_security_group(**sg_dict)
304
305 secgroup = result['security_group']
306 self.assertEqual(secgroup['name'], sg_name)
307 self.assertEqual(tenant_id, secgroup['tenant_id'])
308 self.assertEqual(secgroup['description'], sg_desc)
309
310 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
311 client.delete_security_group, secgroup['id'])
312 return secgroup
313
314 def _default_security_group(self, client=None, tenant_id=None):
315 """Get default secgroup for given tenant_id.
316
317 :returns: default secgroup for given tenant
318 """
319 if client is None:
320 client = self.security_groups_client
321 if not tenant_id:
322 tenant_id = client.tenant_id
323 sgs = [
324 sg for sg in list(client.list_security_groups().values())[0]
325 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
326 ]
327 msg = "No default security group for tenant %s." % (tenant_id)
328 self.assertGreater(len(sgs), 0, msg)
329 return sgs[0]
330
331 def _create_security_group_rule(self, secgroup=None,
332 sec_group_rules_client=None,
333 tenant_id=None,
334 security_groups_client=None, **kwargs):
335 """Create a rule from a dictionary of rule parameters.
336
337 Create a rule in a secgroup. if secgroup not defined will search for
338 default secgroup in tenant_id.
339
340 :param secgroup: the security group.
341 :param tenant_id: if secgroup not passed -- the tenant in which to
342 search for default secgroup
343 :param kwargs: a dictionary containing rule parameters:
344 for example, to allow incoming ssh:
345 rule = {
346 direction: 'ingress'
347 protocol:'tcp',
348 port_range_min: 22,
349 port_range_max: 22
350 }
351 """
352 if sec_group_rules_client is None:
353 sec_group_rules_client = self.security_group_rules_client
354 if security_groups_client is None:
355 security_groups_client = self.security_groups_client
356 if not tenant_id:
357 tenant_id = security_groups_client.tenant_id
358 if secgroup is None:
359 secgroup = self._default_security_group(
360 client=security_groups_client, tenant_id=tenant_id)
361
362 ruleset = dict(security_group_id=secgroup['id'],
363 tenant_id=secgroup['tenant_id'])
364 ruleset.update(kwargs)
365
366 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
367 sg_rule = sg_rule['security_group_rule']
368
369 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
370 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
371
372 return sg_rule
373
374 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
375 secgroup=None,
376 security_groups_client=None):
377 """Create loginable security group rule
378
379 This function will create:
380 1. egress and ingress tcp port 22 allow rule in order to allow ssh
381 access for ipv4.
382 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
383 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
384 """
385
386 if security_group_rules_client is None:
387 security_group_rules_client = self.security_group_rules_client
388 if security_groups_client is None:
389 security_groups_client = self.security_groups_client
390 rules = []
391 rulesets = [
392 dict(
393 # ssh
394 protocol='tcp',
395 port_range_min=22,
396 port_range_max=22,
397 ),
398 dict(
Rodrigo Barbieri797257e2017-11-21 11:00:45 -0200399 # ipv6-ssh
400 protocol='tcp',
401 port_range_min=22,
402 port_range_max=22,
403 ethertype='IPv6',
404 ),
405 dict(
Andrea Frittolif4510a12017-03-07 19:17:11 +0000406 # ping
407 protocol='icmp',
408 ),
409 dict(
410 # ipv6-icmp for ping6
411 protocol='icmp',
412 ethertype='IPv6',
413 )
414 ]
415 sec_group_rules_client = security_group_rules_client
416 for ruleset in rulesets:
417 for r_direction in ['ingress', 'egress']:
418 ruleset['direction'] = r_direction
419 try:
420 sg_rule = self._create_security_group_rule(
421 sec_group_rules_client=sec_group_rules_client,
422 secgroup=secgroup,
423 security_groups_client=security_groups_client,
424 **ruleset)
425 except lib_exc.Conflict as ex:
426 # if rule already exist - skip rule and continue
427 msg = 'Security group rule already exists'
428 if msg not in ex._error_string:
429 raise ex
430 else:
431 self.assertEqual(r_direction, sg_rule['direction'])
432 rules.append(sg_rule)
433
434 return rules
435
436 def _get_router(self, client=None, tenant_id=None):
437 """Retrieve a router for the given tenant id.
438
439 If a public router has been configured, it will be returned.
440
441 If a public router has not been configured, but a public
442 network has, a tenant router will be created and returned that
443 routes traffic to the public network.
444 """
445 if not client:
446 client = self.routers_client
447 if not tenant_id:
448 tenant_id = client.tenant_id
449 router_id = CONF.network.public_router_id
450 network_id = CONF.network.public_network_id
451 if router_id:
452 body = client.show_router(router_id)
453 return body['router']
454 elif network_id:
455 router = self._create_router(client, tenant_id)
456 kwargs = {'external_gateway_info': dict(network_id=network_id)}
457 router = client.update_router(router['id'], **kwargs)['router']
458 return router
459 else:
460 raise Exception("Neither of 'public_router_id' or "
461 "'public_network_id' has been defined.")
462
463 def _create_router(self, client=None, tenant_id=None,
464 namestart='router-smoke'):
465 if not client:
466 client = self.routers_client
467 if not tenant_id:
468 tenant_id = client.tenant_id
469 name = data_utils.rand_name(namestart)
470 result = client.create_router(name=name,
471 admin_state_up=True,
472 tenant_id=tenant_id)
473 router = result['router']
474 self.assertEqual(router['name'], name)
475 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
476 client.delete_router,
477 router['id'])
478 return router
479
480 def _update_router_admin_state(self, router, admin_state_up):
481 kwargs = dict(admin_state_up=admin_state_up)
482 router = self.routers_client.update_router(
483 router['id'], **kwargs)['router']
484 self.assertEqual(admin_state_up, router['admin_state_up'])