blob: 69488a18652a749cce7a0c0ad8d1144b1a63d01f [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# 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
Martin Kopec02af6a42020-03-03 12:39:12 +000017import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040018import subprocess
19
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import netaddr
Soniya Vyas795ef252020-12-10 19:07:23 +053021
Doug Hellmann583ce2c2015-03-11 14:55:46 +000022from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030023from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053024from oslo_utils import netutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
lanoux5fc14522015-09-21 08:17:35 +000026from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070027from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090028from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000029from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000030from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000031from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020032from tempest import exceptions
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000033from tempest.lib.common import api_microversion_fixture
34from tempest.lib.common import api_version_utils
Ken'ichi Ohmichibe4fb502017-03-10 10:04:48 -080035from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010036from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050037from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040038import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040039
Matthew Treinish6c072292014-01-29 19:15:52 +000040CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040041
Attila Fazekasfb7552a2013-08-27 13:02:26 +020042LOG = log.getLogger(__name__)
43
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000044LATEST_MICROVERSION = 'latest'
45
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
Andrea Frittoli2e733b52014-07-16 14:12:11 +010047class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010048 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010049
Andrea Frittolib21de6c2015-02-06 20:12:38 +000050 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000051
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000052 compute_min_microversion = None
53 compute_max_microversion = LATEST_MICROVERSION
54 volume_min_microversion = None
55 volume_max_microversion = LATEST_MICROVERSION
56 placement_min_microversion = None
57 placement_max_microversion = LATEST_MICROVERSION
58
59 @classmethod
60 def skip_checks(cls):
61 super(ScenarioTest, cls).skip_checks()
62 api_version_utils.check_skip_with_microversion(
63 cls.compute_min_microversion, cls.compute_max_microversion,
64 CONF.compute.min_microversion, CONF.compute.max_microversion)
65 api_version_utils.check_skip_with_microversion(
66 cls.volume_min_microversion, cls.volume_max_microversion,
67 CONF.volume.min_microversion, CONF.volume.max_microversion)
68 api_version_utils.check_skip_with_microversion(
69 cls.placement_min_microversion, cls.placement_max_microversion,
70 CONF.placement.min_microversion, CONF.placement.max_microversion)
71
72 @classmethod
73 def resource_setup(cls):
74 super(ScenarioTest, cls).resource_setup()
75 cls.compute_request_microversion = (
76 api_version_utils.select_request_microversion(
77 cls.compute_min_microversion,
78 CONF.compute.min_microversion))
79 cls.volume_request_microversion = (
80 api_version_utils.select_request_microversion(
81 cls.volume_min_microversion,
82 CONF.volume.min_microversion))
83 cls.placement_request_microversion = (
84 api_version_utils.select_request_microversion(
85 cls.placement_min_microversion,
86 CONF.placement.min_microversion))
87
88 def setUp(self):
89 super(ScenarioTest, self).setUp()
90 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
91 compute_microversion=self.compute_request_microversion,
92 volume_microversion=self.volume_request_microversion,
93 placement_microversion=self.placement_request_microversion))
94
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053095 def setup_compute_client(cls):
96 """Compute and Compute security groups client"""
97 cls.compute_images_client = cls.os_primary.compute_images_client
98 cls.keypairs_client = cls.os_primary.keypairs_client
99 cls.compute_security_groups_client = (
100 cls.os_primary.compute_security_groups_client)
101 cls.compute_security_group_rules_client = (
102 cls.os_primary.compute_security_group_rules_client)
103 cls.servers_client = cls.os_primary.servers_client
104 cls.interface_client = cls.os_primary.interfaces_client
105
106 def setup_network_client(cls):
107 """Neutron network client"""
108 cls.networks_client = cls.os_primary.networks_client
109 cls.ports_client = cls.os_primary.ports_client
110 cls.routers_client = cls.os_primary.routers_client
111 cls.subnets_client = cls.os_primary.subnets_client
112 cls.floating_ips_client = cls.os_primary.floating_ips_client
113 cls.security_groups_client = cls.os_primary.security_groups_client
114 cls.security_group_rules_client = (
115 cls.os_primary.security_group_rules_client)
116
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000117 @classmethod
118 def setup_clients(cls):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530119 """This setup the service clients for the tests"""
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000120 super(ScenarioTest, cls).setup_clients()
jeremy.zhang0343be52017-05-25 21:29:57 +0800121 cls.flavors_client = cls.os_primary.flavors_client
John Warrene74890a2015-11-11 15:18:01 -0500122 cls.compute_floating_ips_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +0800123 cls.os_primary.compute_floating_ips_client)
Jordan Pittier1d2e40f2016-01-05 18:49:14 +0100124 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400125 # Check if glance v1 is available to determine which client to use.
126 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +0800127 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400128 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +0800129 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400130 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400131 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400132 'Either api_v1 or api_v2 must be True in '
133 '[image-feature-enabled].')
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530134
135 cls.setup_compute_client(cls)
136 cls.setup_network_client(cls)
Andrea Frittolia6b30152017-08-04 10:46:10 +0100137 if CONF.service_available.cinder:
138 cls.volumes_client = cls.os_primary.volumes_client_latest
139 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +0300140 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300141
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200142 # ## Test functions library
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200143 # The create_[resource] functions only return body and discard the
144 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +0100145
zhufl1e446b52017-10-16 16:54:57 +0800146 def create_port(self, network_id, client=None, **kwargs):
Martin Kopec9c874412020-12-17 20:43:26 +0000147 """Creates port for the respective network_id
148
149 :param network_id: the id of the network
150 :param client: the client to use, defaults to self.ports_client
151 :param kwargs: additional arguments such as:
152 - namestart - a string to generate a name for the port from
153 - default is self.__class__.__name__
154 - 'binding:vnic_type' - defaults to CONF.network.port_vnic_type
155 - 'binding:profile' - defaults to CONF.network.port_profile
156 """
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300157 if not client:
158 client = self.ports_client
Martin Kopec9c874412020-12-17 20:43:26 +0000159 name = data_utils.rand_name(
160 kwargs.pop('namestart', self.__class__.__name__))
Edan David408a97b2018-01-15 03:52:15 -0500161 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
162 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
163 if CONF.network.port_profile and 'binding:profile' not in kwargs:
164 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300165 result = client.create_port(
166 name=name,
167 network_id=network_id,
168 **kwargs)
Soniya Vyas0123f522020-09-24 17:43:26 +0530169 self.assertIsNotNone(result, 'Unable to allocate port')
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300170 port = result['port']
171 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
172 client.delete_port, port['id'])
173 return port
174
Martin Kopec30b4d532020-10-16 12:02:43 +0000175 def create_keypair(self, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530176 """Creates keypair
177
178 Keypair is a public key of OpenSSH key pair used for accessing
179 and create servers
180 Keypair can also be created by a private key for the same purpose
181 Here, the keys are randomly generated[public/private]
182 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300183 if not client:
184 client = self.keypairs_client
Martin Kopec30b4d532020-10-16 12:02:43 +0000185 if not kwargs.get('name'):
186 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100187 # We don't need to create a keypair by pubkey in scenario
Martin Kopec30b4d532020-10-16 12:02:43 +0000188 body = client.create_keypair(**kwargs)
189 self.addCleanup(client.delete_keypair, kwargs['name'])
ghanshyamdee01f22015-08-17 11:41:47 +0900190 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100191
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530192 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800193 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200194 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000195 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100196
lanoux5fc14522015-09-21 08:17:35 +0000197 This wrapper utility calls the common create test server and
198 returns a test server. The purpose of this wrapper is to minimize
199 the impact on the code of the tests already using this
200 function.
Noam Angel6e309952019-01-27 05:52:40 +0000201
202 :param **kwargs:
203 See extra parameters below
204
205 :Keyword Arguments:
206 * *vnic_type* (``string``) --
207 used when launching instances with pre-configured ports.
208 Examples:
209 normal: a traditional virtual port that is either attached
210 to a linux bridge or an openvswitch bridge on a
211 compute node.
212 direct: an SR-IOV port that is directly attached to a VM
213 macvtap: an SR-IOV port that is attached to a VM via a macvtap
214 device.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100215 direct-physical: an SR-IOV port that is directly attached to a
216 VM using physical instead of virtual
217 functions.
218 baremetal: a baremetal port directly attached to a baremetal
219 node.
220 virtio-forwarder: an SR-IOV port that is indirectly attached
221 to a VM using a low-latency vhost-user
222 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000223 Defaults to ``CONF.network.port_vnic_type``.
224 * *port_profile* (``dict``) --
225 This attribute is a dictionary that can be used (with admin
226 credentials) to supply information influencing the binding of
227 the port.
228 example: port_profile = "capabilities:[switchdev]"
229 Defaults to ``CONF.network.port_profile``.
Martin Kopec9c874412020-12-17 20:43:26 +0000230 * *create_port_body* (``dict``) --
231 This attribute is a dictionary of additional arguments to be
232 passed to create_port method.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100233 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100234
lanoux5fc14522015-09-21 08:17:35 +0000235 # NOTE(jlanoux): As a first step, ssh checks in the scenario
236 # tests need to be run regardless of the run_validation and
237 # validatable parameters and thus until the ssh validation job
238 # becomes voting in CI. The test resources management and IP
239 # association are taken care of in the scenario tests.
240 # Therefore, the validatable parameter is set to false in all
241 # those tests. In this way create_server just return a standard
242 # server and the scenario tests always perform ssh checks.
243
244 # Needed for the cross_tenant_traffic test:
245 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800246 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000247
zhufl24208c22016-10-25 15:23:48 +0800248 if name is None:
249 name = data_utils.rand_name(self.__class__.__name__ + "-server")
250
Noam Angel6e309952019-01-27 05:52:40 +0000251 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
252 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000253
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000254 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000255 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000256 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000257 ports = []
Martin Kopec9c874412020-12-17 20:43:26 +0000258 create_port_body = kwargs.pop('create_port_body', {})
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300259
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000260 if vnic_type:
261 create_port_body['binding:vnic_type'] = vnic_type
262
263 if profile:
264 create_port_body['binding:profile'] = profile
265
lanoux5fc14522015-09-21 08:17:35 +0000266 if kwargs:
267 # Convert security group names to security group ids
268 # to pass to create_port
269 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300270 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500271 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000272 ).get('security_groups')
273 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100274 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000275
276 sec_groups_names = [s['name'] for s in kwargs.pop(
277 'security_groups')]
278 security_groups_ids = [sec_dict[s]
279 for s in sec_groups_names]
280
281 if security_groups_ids:
282 create_port_body[
283 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300284 networks = kwargs.pop('networks', [])
285 else:
286 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000287
288 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300289 # for the project's private networks and create a port.
290 # The same behaviour as we would expect when passing
291 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000292 if not networks:
293 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300294 **{'router:external': False, 'fields': 'id'})['networks']
295
296 # It's net['uuid'] if networks come from kwargs
297 # and net['id'] if they come from
298 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000299 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000300 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300301 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800302 port = self.create_port(network_id=net_id,
303 client=clients.ports_client,
304 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300305 ports.append({'port': port['id']})
306 else:
307 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000308 if ports:
309 kwargs['networks'] = ports
310 self.ports = ports
311
312 tenant_network = self.get_tenant_network()
313
Marc Koderer979e4942016-12-08 10:07:59 +0100314 if CONF.compute.compute_volume_common_az:
315 kwargs.setdefault('availability_zone',
316 CONF.compute.compute_volume_common_az)
317
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200318 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000319 clients,
320 tenant_network=tenant_network,
321 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530322 name=name, flavor=flavor,
323 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000324
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200325 self.addCleanup(waiters.wait_for_server_termination,
326 clients.servers_client, body['id'])
327 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
328 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000329 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100330 return server
331
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100332 def create_volume(self, size=None, name=None, snapshot_id=None,
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000333 imageRef=None, volume_type=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530334 """Creates volume
335
336 This wrapper utility creates volume and waits for volume to be
337 in 'available' state.
338 This method returns the volume's full representation by GET request.
339 """
340
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700341 if size is None:
342 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500343 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800344 if CONF.image_feature_enabled.api_v1:
345 resp = self.image_client.check_image(imageRef)
346 image = common_image.get_image_meta_from_headers(resp)
347 else:
348 image = self.image_client.show_image(imageRef)
349 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500350 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100351 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800352 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000353 kwargs.update({'name': name,
354 'snapshot_id': snapshot_id,
355 'imageRef': imageRef,
356 'volume_type': volume_type,
357 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100358
359 if CONF.compute.compute_volume_common_az:
360 kwargs.setdefault('availability_zone',
361 CONF.compute.compute_volume_common_az)
362
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900363 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700364
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100365 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
366 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100367 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100368 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300369 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200370 waiters.wait_for_volume_resource_status(self.volumes_client,
371 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100372 # The volume retrieved on creation has a non-up-to-date status.
373 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000374 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100375 return volume
376
lkuchlane20e6a82018-05-08 11:28:46 +0300377 def create_backup(self, volume_id, name=None, description=None,
378 force=False, snapshot_id=None, incremental=False,
Martin Kopec4a140052020-10-16 16:26:55 +0000379 container=None, **kwargs):
380 """Creates a backup of the given volume_id or snapshot_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530381
Martin Kopec4a140052020-10-16 16:26:55 +0000382 This wrapper utility creates a backup and waits until it is in
383 'available' state.
384
385 :param volume_id: UUID of the volume to back up
386 :param name: backup name, '$classname-backup' by default
387 :param description: Description of the backup, None by default
388 :param force: boolean whether to backup even if the volume is attached
389 False by default
390 :param snapshot_id: UUID of the source snapshot to back up
391 None by default
392 :param incremental: boolean, False by default
393 :param container: a container name, None by default
394 :param **kwargs: additional parameters per the documentation:
395 https://docs.openstack.org/api-ref/block-storage/v3/
396 #create-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530397 """
lkuchlane20e6a82018-05-08 11:28:46 +0300398
399 name = name or data_utils.rand_name(
400 self.__class__.__name__ + "-backup")
Martin Kopec4a140052020-10-16 16:26:55 +0000401 args = {'name': name,
402 'description': description,
403 'force': force,
404 'snapshot_id': snapshot_id,
405 'incremental': incremental,
406 'container': container}
407 args.update(kwargs)
lkuchlane20e6a82018-05-08 11:28:46 +0300408 backup = self.backups_client.create_backup(volume_id=volume_id,
409 **kwargs)['backup']
410 self.addCleanup(self.backups_client.delete_backup, backup['id'])
411 waiters.wait_for_volume_resource_status(self.backups_client,
412 backup['id'], 'available')
413 return backup
414
Martin Kopec4a140052020-10-16 16:26:55 +0000415 def restore_backup(self, backup_id, **kwargs):
416 """Restores a backup given by the backup_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530417
Martin Kopec4a140052020-10-16 16:26:55 +0000418 This wrapper utility restores a backup and waits until it is in
419 'available' state.
420
421 :param backup_id: UUID of a backup to restore
422 :param **kwargs: additional parameters per the documentation:
423 https://docs.openstack.org/api-ref/block-storage/v3/
424 #restore-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530425 """
426
Martin Kopec4a140052020-10-16 16:26:55 +0000427 body = self.backups_client.restore_backup(backup_id, **kwargs)
428 restore = body['restore']
lkuchlane20e6a82018-05-08 11:28:46 +0300429 self.addCleanup(self.volumes_client.delete_volume,
430 restore['volume_id'])
431 waiters.wait_for_volume_resource_status(self.backups_client,
432 backup_id, 'available')
433 waiters.wait_for_volume_resource_status(self.volumes_client,
434 restore['volume_id'],
435 'available')
436 self.assertEqual(backup_id, restore['backup_id'])
437 return restore
438
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000439 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
440 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530441 if image is None:
442 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530443 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
444 server_id, image, preserve_ephemeral)
445 self.servers_client.rebuild_server(
446 server_id=server_id,
447 image_ref=image,
448 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000449 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530450 if wait:
451 waiters.wait_for_server_status(self.servers_client,
452 server_id, 'ACTIVE')
453
lkuchlan73ed1f32017-07-06 16:22:12 +0300454 def create_volume_snapshot(self, volume_id, name=None, description=None,
Martin Kopeca17cca42020-10-17 16:57:51 +0000455 metadata=None, force=False, **kwargs):
456 """Creates volume's snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530457
Martin Kopeca17cca42020-10-17 16:57:51 +0000458 This wrapper utility creates volume snapshot and waits for it until
459 it is in 'available' state.
460
461 :param volume_id: UUID of a volume to create snapshot of
462 :param name: name of the snapshot, '$classname-snapshot' by default
463 :param description: description of the snapshot
464 :param metadata: metadata key and value pairs for the snapshot
465 :param force: whether snapshot even when the volume is attached
466 :param **kwargs: additional parameters per the doc
467 https://docs.openstack.org/api-ref/block-storage/v3/
468 #create-a-snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530469 """
470
lkuchlan73ed1f32017-07-06 16:22:12 +0300471 name = name or data_utils.rand_name(
472 self.__class__.__name__ + '-snapshot')
473 snapshot = self.snapshots_client.create_snapshot(
474 volume_id=volume_id,
475 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000476 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300477 description=description,
Martin Kopeca17cca42020-10-17 16:57:51 +0000478 metadata=metadata,
479 **kwargs)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530480
lkuchlan73ed1f32017-07-06 16:22:12 +0300481 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
482 snapshot['id'])
483 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
484 waiters.wait_for_volume_resource_status(self.snapshots_client,
485 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200486 snapshot = self.snapshots_client.show_snapshot(
487 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300488 return snapshot
489
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530490 def cleanup_volume_type(self, volume_type):
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100491 """Clean up a given volume type.
492
493 Ensuring all volumes associated to a type are first removed before
494 attempting to remove the type itself. This includes any image volume
495 cache volumes stored in a separate tenant to the original volumes
496 created from the type.
497 """
498 admin_volume_type_client = self.os_admin.volume_types_client_latest
499 admin_volumes_client = self.os_admin.volumes_client_latest
500 volumes = admin_volumes_client.list_volumes(
501 detail=True, params={'all_tenants': 1})['volumes']
502 type_name = volume_type['name']
503 for volume in [v for v in volumes if v['volume_type'] == type_name]:
504 test_utils.call_and_ignore_notfound_exc(
505 admin_volumes_client.delete_volume, volume['id'])
506 admin_volumes_client.wait_for_resource_deletion(volume['id'])
507 admin_volume_type_client.delete_volume_type(volume_type['id'])
508
Martin Kopec8e673a42020-10-18 17:33:02 +0000509 def create_volume_type(self, client=None, name=None, backend_name=None,
510 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530511 """Creates volume type
512
513 In a multiple-storage back-end configuration,
514 each back end has a name (volume_backend_name).
515 The name of the back end is declared as an extra-specification
516 of a volume type (such as, volume_backend_name=LVM).
517 When a volume is created, the scheduler chooses an
518 appropriate back end to handle the request, according
519 to the volume type specified by the user.
520 The scheduler uses volume types to explicitly create volumes on
521 specific back ends.
522
523 Before using volume type, a volume type has to be declared
524 to Block Storage. In addition to that, an extra-specification
525 has to be created to link the volume type to a back end name.
526 """
527
scottda61f68ac2016-06-07 12:07:55 -0600528 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000529 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000530 if not name:
531 class_name = self.__class__.__name__
532 name = data_utils.rand_name(class_name + '-volume-type')
533 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600534
535 LOG.debug("Creating a volume type: %s on backend %s",
536 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000537 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600538 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000539 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600540
Martin Kopec8e673a42020-10-18 17:33:02 +0000541 volume_type_resp = client.create_volume_type(
542 name=randomized_name, extra_specs=extra_specs, **kwargs)
543 volume_type = volume_type_resp['volume_type']
544
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530545 self.assertIn('id', volume_type)
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530546 self.addCleanup(self.cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600547 return volume_type
548
Soniya Vyascb95bba2021-02-04 19:35:10 +0530549 def create_loginable_secgroup_rule(self, secgroup_id=None, rulesets=None):
Martin Kopece1d873a2020-11-02 12:19:54 +0000550 """Create loginable security group rule by compute clients.
551
552 This function will create by default the following rules:
553 1. tcp port 22 allow rule in order to allow ssh access for ipv4
554 2. ipv4 icmp allow rule in order to allow icmpv4
555 """
556
John Warrenf2345512015-12-10 13:39:30 -0500557 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500558 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100559 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900560 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100561 for sg in sgs:
562 if sg['name'] == 'default':
563 secgroup_id = sg['id']
564
565 # These rules are intended to permit inbound ssh and icmp
566 # traffic from all sources, so no group_id is provided.
567 # Setting a group_id would only permit traffic from ports
568 # belonging to the same security group.
Martin Kopece1d873a2020-11-02 12:19:54 +0000569 if not rulesets:
570 rulesets = [
571 {
572 # ssh
573 'ip_protocol': 'tcp',
574 'from_port': 22,
575 'to_port': 22,
576 'cidr': '0.0.0.0/0',
577 },
578 {
579 # ping
580 'ip_protocol': 'icmp',
581 'from_port': -1,
582 'to_port': -1,
583 'cidr': '0.0.0.0/0',
584 }
585 ]
Andrea Frittoli247058f2014-07-16 16:09:22 +0100586 rules = list()
587 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000588 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900589 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100590 rules.append(sg_rule)
591 return rules
592
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530593 def _create_security_group(self, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530594 """Create security group and add rules to security group"""
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530595 if not kwargs.get('name'):
596 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
597 if not kwargs.get('description'):
598 kwargs['description'] = kwargs['name'] + " description"
John Warrenf2345512015-12-10 13:39:30 -0500599 secgroup = self.compute_security_groups_client.create_security_group(
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530600 **kwargs)['security_group']
601 self.assertEqual(secgroup['name'], kwargs['name'])
602 self.assertEqual(secgroup['description'], kwargs['description'])
John Warrenf2345512015-12-10 13:39:30 -0500603 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100604 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500605 self.compute_security_groups_client.delete_security_group,
606 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100607
608 # Add rules to the security group
Soniya Vyascb95bba2021-02-04 19:35:10 +0530609 self.create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100610 return secgroup
611
zhuflf52c7592017-05-25 13:55:24 +0800612 def get_remote_client(self, ip_address, username=None, private_key=None,
613 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100614 """Get a SSH client to a remote server
615
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600616 :param ip_address: the server floating or fixed IP address to use
617 for ssh validation
618 :param username: name of the Linux account on the remote server
619 :param private_key: the SSH private key to use
620 :param server: server dict, used for debugging purposes
621 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100622 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700623
Andrea Frittoli247058f2014-07-16 16:09:22 +0100624 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800625 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800626 # Set this with 'keypair' or others to log in with keypair or
627 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000628 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800629 password = None
630 if private_key is None:
631 private_key = self.keypair['private_key']
632 else:
lanoux283273b2015-12-04 03:01:54 -0800633 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800634 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800635 linux_client = remote_client.RemoteClient(
636 ip_address, username, pkey=private_key, password=password,
637 server=server, servers_client=self.servers_client)
638 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100639 return linux_client
640
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000641 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000642 img_path = CONF.scenario.img_file
643 if not os.path.exists(img_path):
644 # TODO(kopecmartin): replace LOG.warning for rasing
645 # InvalidConfiguration exception after tempest 25.0.0 is
646 # released - there will be one release which accepts both
647 # behaviors in order to avoid many failures across CIs and etc.
648 LOG.warning(
649 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
650 'a full path for the image. CONF.scenario.img_dir was '
651 'deprecated and will be removed in the next release. Till '
652 'Tempest 25.0.0, old behavior is maintained and keep working '
653 'but starting Tempest 26.0.0, you need to specify the full '
654 'path in CONF.scenario.img_file config option.')
655 img_path = os.path.join(CONF.scenario.img_dir, img_path)
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300656 img_container_format = CONF.scenario.img_container_format
657 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000658 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400659 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000660 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100661 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000662 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530663 if img_properties is None:
664 img_properties = {}
665 name = data_utils.rand_name('%s-' % name)
666 params = {
667 'name': name,
668 'container_format': img_container_format,
669 'disk_format': img_disk_format or img_container_format,
670 }
671 if CONF.image_feature_enabled.api_v1:
672 params['is_public'] = 'False'
673 if img_properties:
674 params['properties'] = img_properties
675 params = {'headers': common_image.image_meta_to_headers(**params)}
676 else:
677 params['visibility'] = 'private'
678 # Additional properties are flattened out in the v2 API.
679 if img_properties:
680 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000681 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530682 body = self.image_client.create_image(**params)
683 image = body['image'] if 'image' in body else body
684 self.addCleanup(self.image_client.delete_image, image['id'])
685 self.assertEqual("queued", image['status'])
686 with open(img_path, 'rb') as image_file:
687 if CONF.image_feature_enabled.api_v1:
688 self.image_client.update_image(image['id'], data=image_file)
689 else:
690 self.image_client.store_image_file(image['id'], image_file)
691 LOG.debug("image:%s", image['id'])
692 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100693
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530694 def log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530695 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400696 if not CONF.compute_feature_enabled.console_output:
697 LOG.debug('Console output not supported, cannot log')
698 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700699 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100700 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700701 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100702 servers = servers['servers']
703 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100704 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700705 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000706 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100707 LOG.debug('Console output for %s\nbody=\n%s',
708 server['id'], console_output)
709 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100710 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100711 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100712
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000713 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530714 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300715 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000716 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000717
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000718 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530719 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000720 # Glance client
721 _image_client = self.image_client
722 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900723 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000724 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800725 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000726 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000727 image = _images_client.create_image(server['id'], name=name, **kwargs)
David Kranza5299eb2015-01-15 17:24:05 -0500728 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300729 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200730
731 self.addCleanup(_image_client.wait_for_resource_deletion,
732 image_id)
733 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
734 _image_client.delete_image, image_id)
735
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400736 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530737 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700738 resp = _image_client.check_image(image_id)
739 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400740 image_props = snapshot_image.get('properties', {})
741 else:
742 # In glance v2 the additional properties are flattened.
743 snapshot_image = _image_client.show_image(image_id)
744 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300745
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400746 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300747 if bdm:
748 bdm = json.loads(bdm)
749 if bdm and 'snapshot_id' in bdm[0]:
750 snapshot_id = bdm[0]['snapshot_id']
751 self.addCleanup(
752 self.snapshots_client.wait_for_resource_deletion,
753 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100754 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
755 self.snapshots_client.delete_snapshot,
756 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200757 waiters.wait_for_volume_resource_status(self.snapshots_client,
758 snapshot_id,
759 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000760 image_name = snapshot_image['name']
761 self.assertEqual(name, image_name)
762 LOG.debug("Created snapshot image %s for server %s",
763 image_name, server['name'])
764 return snapshot_image
765
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000766 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530767 """Compute volume attach
768
769 This utility attaches volume from compute and waits for the
770 volume status to be 'in-use' state.
771 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000772 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000773 server['id'], volumeId=volume_to_attach['id'],
774 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200775 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200776 waiters.wait_for_volume_resource_status(self.volumes_client,
777 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000778 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
779 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200780 # Return the updated volume after the attachment
781 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900782
Jordan Pittier7cf64762015-10-14 15:01:12 +0200783 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530784 """Compute volume detach
785
Lee Yarwood5423c532020-12-17 11:24:46 +0000786 This utility detaches the volume from the server and checks whether the
787 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530788 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200789 self.servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000790 waiters.wait_for_volume_attachment_remove_from_server(
791 self.servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200792
Steven Hardyda2a8352014-10-02 12:52:20 +0100793 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800794 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530795 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000796 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000797 cmd = ['ping', '-c1', '-w1']
798
799 if mtu:
800 cmd += [
801 # don't fragment
802 '-M', 'do',
803 # ping receives just the size of ICMP payload
804 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
805 ]
806 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700807
808 def ping():
809 proc = subprocess.Popen(cmd,
810 stdout=subprocess.PIPE,
811 stderr=subprocess.PIPE)
812 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000813
Aaron Rosena7df13b2014-09-23 09:45:45 -0700814 return (proc.returncode == 0) == should_succeed
815
Jordan Pittier9e227c52016-02-09 14:35:18 +0100816 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000817 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800818 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000819 'caller': caller, 'ip': ip_address, 'timeout': timeout,
820 'should_succeed':
821 'reachable' if should_succeed else 'unreachable'
822 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200823 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000824 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800825 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000826 'caller': caller, 'ip': ip_address, 'timeout': timeout,
827 'result': 'expected' if result else 'unexpected'
828 })
zhufl0ec74c42017-11-15 14:02:28 +0800829 if server:
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530830 self.log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000831 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700832
Yair Friedae0e73d2014-11-24 11:56:26 +0200833 def check_vm_connectivity(self, ip_address,
834 username=None,
835 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000836 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800837 extra_msg="",
838 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000839 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000840 """Check server connectivity
841
Yair Friedae0e73d2014-11-24 11:56:26 +0200842 :param ip_address: server to test against
843 :param username: server's ssh username
844 :param private_key: server's ssh private key to be used
845 :param should_connect: True/False indicates positive/negative test
846 positive - attempt ping and ssh
847 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800848 :param extra_msg: Message to help with debugging if ``ping_ip_address``
849 fails
850 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000851 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200852
853 :raises: AssertError if the result of the connectivity check does
854 not match the value of the should_connect param
855 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530856
zhufl0ec74c42017-11-15 14:02:28 +0800857 LOG.debug('checking network connections to IP %s with user: %s',
858 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200859 if should_connect:
860 msg = "Timed out waiting for %s to become reachable" % ip_address
861 else:
862 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800863 if extra_msg:
864 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200865 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000866 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800867 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200868 msg=msg)
869 if should_connect:
870 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800871 try:
872 self.get_remote_client(ip_address, username, private_key,
873 server=server)
874 except Exception:
875 if not extra_msg:
876 extra_msg = 'Failed to ssh to %s' % ip_address
877 LOG.exception(extra_msg)
878 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200879
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000880 def create_floating_ip(self, server, pool_name=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000881 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200882
Marc Koderer3b57d802016-03-22 15:23:31 +0100883 if not pool_name:
884 pool_name = CONF.network.floating_network_name
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000885
John Warrene74890a2015-11-11 15:18:01 -0500886 floating_ip = (self.compute_floating_ips_client.
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000887 create_floating_ip(pool=pool_name,
888 **kwargs)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100889 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500890 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200891 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500892 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530893 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200894 return floating_ip
895
Sean Dague20e98612016-01-06 14:33:28 -0500896 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000897 private_key=None, server=None, username=None,
898 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530899 """Creates timestamp
900
901 This wrapper utility does ssh, creates timestamp and returns the
902 created timestamp.
903 """
Sean Dague20e98612016-01-06 14:33:28 -0500904 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200905 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000906 server=server,
907 username=username)
908
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300909 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000910 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800911 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
912 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300913 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
914 ssh_client.exec_command(cmd_timestamp)
915 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
916 % mount_path)
917 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800918 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300919 return timestamp
920
Sean Dague20e98612016-01-06 14:33:28 -0500921 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000922 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530923 """Returns timestamp
924
925 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000926
927 :param ip_address: The floating IP or fixed IP of the remote server
928 :param dev_name: Name of the device that stores the timestamp
929 :param mount_path: Path which should be used as mount point for
930 dev_name
931 :param private_key: The SSH private key to use for authentication
932 :param server: Server dict, used for debugging purposes
933 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530934 """
935
Sean Dague20e98612016-01-06 14:33:28 -0500936 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200937 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000938 server=server,
939 username=username)
940
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300941 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700942 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300943 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
944 % mount_path)
945 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800946 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300947 return timestamp
948
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000949 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -0500950 """Get the server fixed or floating IP.
951
952 Based on the configuration we're in, return a correct ip
953 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000954
955 If CONF.validation.connect_method is floating, then
956 a floating ip will be created passing kwargs as additional
957 argument.
Sean Dague20e98612016-01-06 14:33:28 -0500958 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530959
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200960 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500961 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800962 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500963 # method is creating the floating IP there.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000964 return self.create_floating_ip(server, **kwargs)['ip']
Sean Dague20e98612016-01-06 14:33:28 -0500965 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400966 # Determine the network name to look for based on config or creds
967 # provider network resources.
968 if CONF.validation.network_for_ssh:
969 addresses = server['addresses'][
970 CONF.validation.network_for_ssh]
971 else:
zhufl7b4a7202017-09-28 10:29:27 +0800972 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400973 addresses = (server['addresses'][network['name']]
974 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500975 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200976 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
977 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500978 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800979 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200980 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400981 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200982
zhufl7bc916d2018-08-22 14:47:39 +0800983 @classmethod
984 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530985 """Gets host of server"""
986
zhufl7bc916d2018-08-22 14:47:39 +0800987 server_details = cls.os_admin.servers_client.show_server(server_id)
988 return server_details['server']['OS-EXT-SRV-ATTR:host']
989
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000990 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
991 bd_map_v2 = [{
992 'uuid': source_id,
993 'source_type': source_type,
994 'destination_type': 'volume',
995 'boot_index': 0,
996 'delete_on_termination': delete_on_termination}]
997 return {'block_device_mapping_v2': bd_map_v2}
998
999 def boot_instance_from_resource(self, source_id,
1000 source_type,
1001 keypair=None,
1002 security_group=None,
1003 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +00001004 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301005 """Boot instance from resource
1006
1007 This wrapper utility boots instance from resource with block device
1008 mapping with source info passed in arguments
1009 """
1010
Martin Kopecbee673e2020-11-04 09:40:52 +00001011 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001012 if keypair:
1013 create_kwargs['key_name'] = keypair['name']
1014 if security_group:
1015 create_kwargs['security_groups'] = [
1016 {'name': security_group['name']}]
1017 create_kwargs.update(self._get_bdm(
1018 source_id,
1019 source_type,
1020 delete_on_termination=delete_on_termination))
1021 if name:
1022 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +00001023 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001024
Martin Kopecbee673e2020-11-04 09:40:52 +00001025 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001026
Martin Kopec0216b372020-11-04 09:32:05 +00001027 def create_volume_from_image(self, **kwargs):
1028 """Create volume from image.
1029
1030 :param image_id: ID of the image to create volume from,
1031 CONF.compute.image_ref by default
1032 :param name: name of the volume,
1033 '$classname-volume-origin' by default
1034 :param **kwargs: additional parameters
1035 """
1036 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
1037 name = kwargs.pop('name', None)
1038 if not name:
1039 namestart = self.__class__.__name__ + '-volume-origin'
1040 name = data_utils.rand_name(namestart)
1041 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001042
Andrea Frittoli2e733b52014-07-16 14:12:11 +01001043
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001044class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001045 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001046
Yair Fried1fc32a12014-08-04 09:11:30 +03001047 This class provide helpers for network scenario tests, using the neutron
1048 API. Helpers from ancestor which use the nova network API are overridden
1049 with the neutron API.
1050
1051 This Class also enforces using Neutron instead of novanetwork.
1052 Subclassed tests will be skipped if Neutron is not enabled
1053
1054 """
1055
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001056 credentials = ['primary', 'admin']
1057
Yair Fried1fc32a12014-08-04 09:11:30 +03001058 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001059 def skip_checks(cls):
1060 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001061 if not CONF.service_available.neutron:
1062 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001063
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301064 def create_network(self, networks_client=None,
1065 project_id=None,
1066 namestart='network-smoke-',
1067 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001068 if not networks_client:
1069 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001070 if not project_id:
1071 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001072 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001073 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001074 if net_dict:
1075 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001076 # Neutron disables port security by default so we have to check the
1077 # config before trying to create the network with port_security_enabled
1078 if CONF.network_feature_enabled.port_security:
1079 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001080 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001081 network = result['network']
1082
1083 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001084 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001085 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001086 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001087 return network
1088
zhufl5b0a52f2017-10-24 15:48:20 +08001089 def create_subnet(self, network, subnets_client=None,
1090 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001091 """Create a subnet for the given network
1092
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301093 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001094 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301095
1096 :param **kwargs:
1097 See extra parameters below
1098
1099 :Keyword Arguments:
1100
1101 * *ip_version = ip version of the given network,
Soniya Vyas795ef252020-12-10 19:07:23 +05301102 use_default_subnetpool = default subnetpool to
1103 manage IPv6 addresses range.
Yair Fried1fc32a12014-08-04 09:11:30 +03001104 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301105
John Warren3961acd2015-10-02 14:38:53 -04001106 if not subnets_client:
1107 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001108
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001109 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001110 """Check cidr existence
1111
yangjianfeng4ad346e2020-11-22 06:49:19 +00001112 :returns: True if subnet with cidr already exist in tenant or
1113 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001114 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001115 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001116 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001117 external_nets = self.os_admin.networks_client.list_networks(
1118 **{"router:external": True})['networks']
1119 external_subnets = []
1120 for ext_net in external_nets:
1121 external_subnets.extend(
1122 self.os_admin.subnets_client.list_subnets(
1123 network_id=ext_net['id'], cidr=cidr)['subnets'])
1124 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001125
Soniya Vyas795ef252020-12-10 19:07:23 +05301126 def _make_create_subnet_request(namestart, network,
1127 ip_version, subnets_client, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001128
1129 subnet = dict(
1130 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001131 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001132 project_id=network['project_id'],
Kirill Shileev14113572014-11-21 16:58:02 +03001133 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001134 **kwargs
1135 )
Soniya Vyas795ef252020-12-10 19:07:23 +05301136
1137 if ip_version == 6:
1138 subnet['ipv6_address_mode'] = 'slaac'
1139 subnet['ipv6_ra_mode'] = 'slaac'
1140
Yair Fried1fc32a12014-08-04 09:11:30 +03001141 try:
Soniya Vyas795ef252020-12-10 19:07:23 +05301142 return subnets_client.create_subnet(**subnet)
Masayuki Igawad9388762015-01-20 14:56:42 +09001143 except lib_exc.Conflict as e:
Soniya Vyas795ef252020-12-10 19:07:23 +05301144 if 'overlaps with another subnet' not in str(e):
Yair Fried1fc32a12014-08-04 09:11:30 +03001145 raise
Soniya Vyas795ef252020-12-10 19:07:23 +05301146
1147 result = None
1148 str_cidr = None
1149
1150 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
1151 ip_version = kwargs.pop('ip_version', 4)
1152
1153 if not use_default_subnetpool:
1154
1155 if ip_version == 6:
1156 tenant_cidr = netaddr.IPNetwork(
1157 CONF.network.project_network_v6_cidr)
1158 num_bits = CONF.network.project_network_v6_mask_bits
1159 else:
1160 tenant_cidr = netaddr.IPNetwork(
1161 CONF.network.project_network_cidr)
1162 num_bits = CONF.network.project_network_mask_bits
1163
1164 # Repeatedly attempt subnet creation with sequential cidr
1165 # blocks until an unallocated block is found.
1166 for subnet_cidr in tenant_cidr.subnet(num_bits):
1167 str_cidr = str(subnet_cidr)
1168 if cidr_in_use(str_cidr, project_id=network['project_id']):
1169 continue
1170 result = _make_create_subnet_request(
1171 namestart, network, ip_version, subnets_client,
1172 cidr=str_cidr, **kwargs)
1173
1174 if result is not None:
1175 break
1176
1177 else:
1178 result = _make_create_subnet_request(
1179 namestart, network, ip_version, subnets_client,
1180 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +03001181 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001182
1183 subnet = result['subnet']
Soniya Vyas795ef252020-12-10 19:07:23 +05301184 if str_cidr is not None:
1185 self.assertEqual(subnet['cidr'], str_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -05001186
1187 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1188 subnets_client.delete_subnet, subnet['id'])
1189
Yair Fried1fc32a12014-08-04 09:11:30 +03001190 return subnet
1191
Lukas Piwowarski9523d512020-10-30 09:37:58 +00001192 def _get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
1193
1194 if ip_addr and not kwargs.get('fixed_ips'):
1195 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
1196 ports = self.os_admin.ports_client.list_ports(
1197 device_id=server['id'], **kwargs)['ports']
1198
Kobi Samoray166500a2016-10-09 14:42:48 +03001199 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001200 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1201 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001202
1203 def _is_active(port):
1204 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1205 # servers. Neutron does not bind ports for Ironic instances, as a
1206 # result the port remains in the DOWN state. This has been fixed
1207 # with the introduction of the networking-baremetal plugin but
1208 # it's not mandatory (and is not used on all stable branches).
1209 return (port['status'] == 'ACTIVE' or
1210 port.get('binding:vnic_type') == 'baremetal')
1211
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001212 port_map = [(p["id"], fxip["ip_address"])
1213 for p in ports
1214 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001215 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001216 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001217 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1218 if inactive:
1219 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001220
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001221 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001222 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001223 self.assertEqual(len(port_map), 1,
1224 "Found multiple IPv4 addresses: %s. "
1225 "Unable to determine which port to target."
1226 % port_map)
1227 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001228
Soniya Vyasc37410f2021-02-24 15:26:27 +05301229 def get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001230 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001231 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001232 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001233 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001234 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001235
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301236 def create_floating_ip(self, server, external_network_id=None,
Martin Kopec5e761bf2020-11-03 16:11:08 +00001237 port_id=None, client=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001238 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301239
Yair Friedae0e73d2014-11-24 11:56:26 +02001240 if not external_network_id:
1241 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001242 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001243 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001244 if not port_id:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301245 port_id, ip4 = self._get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001246 else:
1247 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001248
Martin Kopec5e761bf2020-11-03 16:11:08 +00001249 floatingip_kwargs = {
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001250 'floating_network_id': external_network_id,
1251 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301252 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001253 'fixed_ip_address': ip4,
1254 }
1255 if CONF.network.subnet_id:
Martin Kopec5e761bf2020-11-03 16:11:08 +00001256 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1257
1258 floatingip_kwargs.update(kwargs)
1259 result = client.create_floatingip(**floatingip_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001260 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001261
Jordan Pittier9e227c52016-02-09 14:35:18 +01001262 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001263 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001264 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001265 return floating_ip
1266
Soniya Vyased664472020-09-23 18:40:25 +05301267 def associate_floating_ip(self, floating_ip, server):
1268 """Associate floating ip
1269
1270 This wrapper utility attaches the floating_ip for
1271 the respective port_id of server
1272 """
1273 port_id, _ = self._get_server_port_id_and_ip4(server)
1274 kwargs = dict(port_id=port_id)
1275 floating_ip = self.floating_ips_client.update_floatingip(
1276 floating_ip['id'], **kwargs)['floatingip']
1277 self.assertEqual(port_id, floating_ip['port_id'])
1278 return floating_ip
1279
1280 def disassociate_floating_ip(self, floating_ip):
1281 """Disassociates floating ip
1282
1283 This wrapper utility disassociates given floating ip.
1284 :param floating_ip: a dict which is a return value of
1285 floating_ips_client.create_floatingip method
1286 """
1287 kwargs = dict(port_id=None)
1288 floating_ip = self.floating_ips_client.update_floatingip(
1289 floating_ip['id'], **kwargs)['floatingip']
1290 self.assertIsNone(floating_ip['port_id'])
1291 return floating_ip
1292
Yair Fried45f92952014-06-26 05:19:19 +03001293 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001294 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001295
Steve Heyman33735f22016-05-24 09:28:08 -05001296 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001297 :param status: target status
1298 :raises: AssertionError if status doesn't match
1299 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301300
Steve Heyman33735f22016-05-24 09:28:08 -05001301 floatingip_id = floating_ip['id']
1302
Carl Baldwina754e2d2014-10-23 22:47:41 +00001303 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001304 floating_ip = (self.floating_ips_client.
1305 show_floatingip(floatingip_id)['floatingip'])
1306 if status == floating_ip['status']:
1307 LOG.info("FloatingIP: {fp} is at status: {st}"
1308 .format(fp=floating_ip, st=status))
1309 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001310
zhufl4dda94e2017-03-14 16:14:46 +08001311 if not test_utils.call_until_true(refresh,
1312 CONF.network.build_timeout,
1313 CONF.network.build_interval):
1314 floating_ip = self.floating_ips_client.show_floatingip(
1315 floatingip_id)['floatingip']
1316 self.assertEqual(status, floating_ip['status'],
1317 message="FloatingIP: {fp} is at status: {cst}. "
1318 "failed to reach status: {st}"
1319 .format(fp=floating_ip, cst=floating_ip['status'],
1320 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001321
zhufl420a0192017-09-28 11:04:50 +08001322 def check_tenant_network_connectivity(self, server,
1323 username,
1324 private_key,
1325 should_connect=True,
1326 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301327 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001328 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001329 msg = 'Tenant networks not configured to be reachable.'
1330 LOG.info(msg)
1331 return
1332 # The target login is assumed to have been configured for
1333 # key-based authentication by cloud-init.
1334 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001335 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001336 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001337 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001338 username,
1339 private_key,
1340 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001341 except Exception as e:
1342 LOG.exception('Tenant network connectivity check failed')
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301343 self.log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001344 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001345 raise
1346
zhufle9877c62017-10-13 09:38:19 +08001347 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001348 nic=None, protocol='icmp'):
1349 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001350
Claudiu Belu33c3e602014-08-28 16:38:01 +03001351 :param source: RemoteClient: an ssh connection from which to execute
1352 the check
1353 :param dest: an IP to check connectivity against
1354 :param should_succeed: boolean should connection succeed or not
1355 :param nic: specific network interface to test connectivity from
1356 :param protocol: the protocol used to test connectivity with.
1357 :returns: True, if the connection succeeded and it was expected to
1358 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001359 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301360
Claudiu Belu33c3e602014-08-28 16:38:01 +03001361 method_name = '%s_check' % protocol
1362 connectivity_checker = getattr(source, method_name)
1363
1364 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001365 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001366 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001367 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001368 LOG.warning('Failed to check %(protocol)s connectivity for '
1369 'IP %(dest)s via a ssh connection from: %(src)s.',
1370 dict(protocol=protocol, dest=dest,
1371 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001372 return not should_succeed
1373 return should_succeed
1374
Claudiu Belu33c3e602014-08-28 16:38:01 +03001375 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001376 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001377 if result:
1378 return
1379
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001380 source_host = source.ssh_client.host
1381 if should_succeed:
1382 msg = "Timed out waiting for %s to become reachable from %s" \
1383 % (dest, source_host)
1384 else:
1385 msg = "%s is reachable from %s" % (dest, source_host)
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301386 self.log_console_output()
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001387 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001388
John Warren456d9ae2016-01-12 15:36:33 -05001389 def _create_security_group(self, security_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001390 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001391 namestart='secgroup-smoke',
1392 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001393 if security_group_rules_client is None:
1394 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001395 if security_groups_client is None:
1396 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001397 if project_id is None:
1398 project_id = security_groups_client.project_id
John Warrenf9606e92015-12-10 12:12:42 -05001399 secgroup = self._create_empty_security_group(
1400 namestart=namestart, client=security_groups_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001401 project_id=project_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001402
1403 # Add rules to the security group
Soniya Vyascb95bba2021-02-04 19:35:10 +05301404 rules = self.create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001405 security_group_rules_client=security_group_rules_client,
1406 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001407 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001408 for rule in rules:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001409 self.assertEqual(project_id, rule['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001410 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001411 return secgroup
1412
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001413 def _create_empty_security_group(self, client=None, project_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001414 namestart='secgroup-smoke'):
1415 """Create a security group without rules.
1416
1417 Default rules will be created:
1418 - IPv4 egress to any
1419 - IPv6 egress to any
1420
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001421 :param project_id: secgroup will be created in this project
Steve Heyman33735f22016-05-24 09:28:08 -05001422 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001423 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301424
Yair Fried1fc32a12014-08-04 09:11:30 +03001425 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001426 client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001427 if not project_id:
1428 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001429 sg_name = data_utils.rand_name(namestart)
1430 sg_desc = sg_name + " description"
1431 sg_dict = dict(name=sg_name,
1432 description=sg_desc)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001433 sg_dict['project_id'] = project_id
David Kranz34e88122014-12-11 15:24:05 -05001434 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001435
1436 secgroup = result['security_group']
1437 self.assertEqual(secgroup['name'], sg_name)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001438 self.assertEqual(project_id, secgroup['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001439 self.assertEqual(secgroup['description'], sg_desc)
1440
Jordan Pittier9e227c52016-02-09 14:35:18 +01001441 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001442 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001443 return secgroup
1444
Soniya Vyas945f5a12021-02-02 23:49:12 +05301445 def create_security_group_rule(self, secgroup=None,
1446 sec_group_rules_client=None,
1447 project_id=None,
1448 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001449 """Create a rule from a dictionary of rule parameters.
1450
1451 Create a rule in a secgroup. if secgroup not defined will search for
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001452 default secgroup in project_id.
Yair Fried1fc32a12014-08-04 09:11:30 +03001453
Steve Heyman33735f22016-05-24 09:28:08 -05001454 :param secgroup: the security group.
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001455 :param project_id: if secgroup not passed -- the tenant in which to
Yair Fried1fc32a12014-08-04 09:11:30 +03001456 search for default secgroup
1457 :param kwargs: a dictionary containing rule parameters:
1458 for example, to allow incoming ssh:
1459 rule = {
1460 direction: 'ingress'
1461 protocol:'tcp',
1462 port_range_min: 22,
1463 port_range_max: 22
1464 }
1465 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301466
John Warren456d9ae2016-01-12 15:36:33 -05001467 if sec_group_rules_client is None:
1468 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001469 if security_groups_client is None:
1470 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001471 if not project_id:
1472 project_id = security_groups_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001473 if secgroup is None:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001474 # Get default secgroup for project_id
zhuflb0b272e2017-09-22 16:01:46 +08001475 default_secgroups = security_groups_client.list_security_groups(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001476 name='default', project_id=project_id)['security_groups']
1477 msg = "No default security group for project %s." % (project_id)
zhuflb0b272e2017-09-22 16:01:46 +08001478 self.assertNotEmpty(default_secgroups, msg)
1479 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001480
Steve Heyman33735f22016-05-24 09:28:08 -05001481 ruleset = dict(security_group_id=secgroup['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001482 project_id=secgroup['project_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001483 ruleset.update(kwargs)
1484
John Warren456d9ae2016-01-12 15:36:33 -05001485 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001486 sg_rule = sg_rule['security_group_rule']
1487
1488 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1489 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001490
1491 return sg_rule
1492
Soniya Vyascb95bba2021-02-04 19:35:10 +05301493 def create_loginable_secgroup_rule(self, security_group_rules_client=None,
1494 secgroup=None,
1495 security_groups_client=None):
Martin Kopece1d873a2020-11-02 12:19:54 +00001496 """Create loginable security group rule by neutron clients by default.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001497
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001498 This function will create:
1499 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1500 access for ipv4.
1501 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1502 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001503 """
1504
John Warren456d9ae2016-01-12 15:36:33 -05001505 if security_group_rules_client is None:
1506 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001507 if security_groups_client is None:
1508 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001509 rules = []
1510 rulesets = [
1511 dict(
1512 # ssh
1513 protocol='tcp',
1514 port_range_min=22,
1515 port_range_max=22,
1516 ),
1517 dict(
1518 # ping
1519 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001520 ),
1521 dict(
1522 # ipv6-icmp for ping6
1523 protocol='icmp',
1524 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001525 )
1526 ]
John Warren456d9ae2016-01-12 15:36:33 -05001527 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001528 for ruleset in rulesets:
1529 for r_direction in ['ingress', 'egress']:
1530 ruleset['direction'] = r_direction
1531 try:
Soniya Vyas945f5a12021-02-02 23:49:12 +05301532 sg_rule = self.create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001533 sec_group_rules_client=sec_group_rules_client,
1534 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001535 security_groups_client=security_groups_client,
1536 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001537 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001538 # if rule already exist - skip rule and continue
1539 msg = 'Security group rule already exists'
1540 if msg not in ex._error_string:
1541 raise ex
1542 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001543 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001544 rules.append(sg_rule)
1545
1546 return rules
1547
Soniya Vyas73555df2021-03-04 19:05:42 +05301548 def get_router(self, client=None, project_id=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001549 """Retrieve a router for the given tenant id.
1550
1551 If a public router has been configured, it will be returned.
1552
1553 If a public router has not been configured, but a public
1554 network has, a tenant router will be created and returned that
1555 routes traffic to the public network.
1556 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301557
Yair Frieddb6c9e92014-08-06 08:53:13 +03001558 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001559 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001560 if not project_id:
1561 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001562 router_id = CONF.network.public_router_id
1563 network_id = CONF.network.public_network_id
1564 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001565 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001566 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001567 elif network_id:
Martin Kopec0090a102020-11-03 13:50:19 +00001568 name = kwargs.pop('name', None)
1569 if not name:
1570 namestart = self.__class__.__name__ + '-router'
1571 name = data_utils.rand_name(namestart)
1572
1573 ext_gw_info = kwargs.pop('external_gateway_info', None)
1574 if not ext_gw_info:
1575 ext_gw_info = dict(network_id=network_id)
zhufl3484f992017-10-10 16:18:29 +08001576 router = client.create_router(
Martin Kopec0090a102020-11-03 13:50:19 +00001577 name=name,
1578 admin_state_up=kwargs.get('admin_state_up', True),
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001579 project_id=project_id,
Martin Kopec0090a102020-11-03 13:50:19 +00001580 external_gateway_info=ext_gw_info,
1581 **kwargs)['router']
zhufl3484f992017-10-10 16:18:29 +08001582 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1583 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001584 return router
1585 else:
1586 raise Exception("Neither of 'public_router_id' or "
1587 "'public_network_id' has been defined.")
1588
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001589 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001590 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001591 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001592 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001593 """Create a network with a subnet connected to a router.
1594
David Shrewsbury9bac3662014-08-07 15:07:01 -04001595 The baremetal driver is a special case since all nodes are
1596 on the same shared network.
1597
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001598 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001599 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001600 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001601 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001602 a form like this: {'provider:network_type': 'vlan',
1603 'provider:physical_network': 'foo',
1604 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001605 :returns: network, subnet, router
1606 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301607
Thiago Paiva66cded22016-08-15 14:55:58 -03001608 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001609 # NOTE(Shrews): This exception is for environments where tenant
1610 # credential isolation is available, but network separation is
1611 # not (the current baremetal case). Likely can be removed when
1612 # test account mgmt is reworked:
1613 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001614 if not CONF.compute.fixed_network_name:
1615 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001616 raise lib_exc.InvalidConfiguration(m)
Soniya Vyasc37410f2021-02-24 15:26:27 +05301617 network = self.get_network_by_name(
David Shrewsbury9bac3662014-08-07 15:07:01 -04001618 CONF.compute.fixed_network_name)
1619 router = None
1620 subnet = None
1621 else:
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301622 network = self.create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001623 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001624 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001625 port_security_enabled=port_security_enabled,
1626 **net_dict)
Soniya Vyas73555df2021-03-04 19:05:42 +05301627 router = self.get_router(client=routers_client,
1628 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001629 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001630 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001631 # use explicit check because empty list is a valid option
1632 if dns_nameservers is not None:
1633 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001634 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001635 if not routers_client:
1636 routers_client = self.routers_client
1637 router_id = router['id']
1638 routers_client.add_router_interface(router_id,
1639 subnet_id=subnet['id'])
1640
1641 # save a cleanup job to remove this association between
1642 # router and subnet
1643 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1644 routers_client.remove_router_interface, router_id,
1645 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001646 return network, subnet, router
1647
1648
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001649class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001650 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001651
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001652 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001653
1654 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001655 def setup_clients(cls):
1656 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001657 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001658 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001659 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001660
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001661 def create_encryption_type(self, client=None, type_id=None, provider=None,
1662 key_size=None, cipher=None,
1663 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301664 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001665 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001666 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001667 if not type_id:
1668 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001669 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001670 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001671 client.create_encryption_type(
1672 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001673 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001674
lkuchlan3023e752017-06-08 12:53:13 +03001675 def create_encrypted_volume(self, encryption_provider, volume_type,
1676 key_size=256, cipher='aes-xts-plain64',
1677 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301678 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001679 volume_type = self.create_volume_type(name=volume_type)
1680 self.create_encryption_type(type_id=volume_type['id'],
1681 provider=encryption_provider,
1682 key_size=key_size,
1683 cipher=cipher,
1684 control_location=control_location)
1685 return self.create_volume(volume_type=volume_type['name'])
1686
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001687
Masayuki Igawa0870db52015-09-18 21:08:36 +09001688class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001689 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001690
1691 Subclasses implement the tests that use the methods provided by this
1692 class.
1693 """
1694
1695 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001696 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001697 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001698 if not CONF.service_available.swift:
1699 skip_msg = ("%s skipped as swift is not available" %
1700 cls.__name__)
1701 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001702
1703 @classmethod
1704 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001705 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001706 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001707 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001708 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001709
1710 @classmethod
1711 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001712 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001713 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001714 cls.account_client = cls.os_operator.account_client
1715 cls.container_client = cls.os_operator.container_client
1716 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001717
Chris Dentde456a12014-09-10 12:41:15 +01001718 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301719 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001720 self.account_client.list_account_containers()
1721 LOG.debug('Swift status information obtained successfully')
1722
Chris Dentde456a12014-09-10 12:41:15 +01001723 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301724 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001725 name = container_name or data_utils.rand_name(
1726 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001727 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001728 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001729 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001730 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001731 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001732 self.container_client.delete_container,
1733 name)
Chris Dent0d494112014-08-26 13:48:30 +01001734 return name
1735
Chris Dentde456a12014-09-10 12:41:15 +01001736 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301737 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001738 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001739 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001740
Chris Dentde456a12014-09-10 12:41:15 +01001741 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301742 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001743 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001744 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001745 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001746 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001747 self.object_client.delete_object,
1748 container_name,
1749 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001750 return obj_name, obj_data
1751
Chris Dentde456a12014-09-10 12:41:15 +01001752 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301753 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001754 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001755 self.list_and_check_container_objects(container_name,
1756 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001757
Chris Dentde456a12014-09-10 12:41:15 +01001758 def list_and_check_container_objects(self, container_name,
1759 present_obj=None,
1760 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301761 """List and verify objects for a given container
1762
1763 This utility lists objects for a given container
1764 and asserts which are present and
1765 which are not
1766 """
1767
Ghanshyam2a180b82014-06-16 13:54:22 +09001768 if present_obj is None:
1769 present_obj = []
1770 if not_present_obj is None:
1771 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001772 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001773 container_name)
1774 if present_obj:
1775 for obj in present_obj:
1776 self.assertIn(obj, object_list)
1777 if not_present_obj:
1778 for obj in not_present_obj:
1779 self.assertNotIn(obj, object_list)
1780
Chris Dentde456a12014-09-10 12:41:15 +01001781 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301782 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001783 _, obj = self.object_client.get_object(container_name, obj_name)
1784 self.assertEqual(obj, expected_data)