blob: 20495eeafc349f81b709026ee751565cbbbb3ecc [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
Dan Smith49c2b3b2023-04-26 15:52:22 -070017import copy
Martin Kopec02af6a42020-03-03 12:39:12 +000018import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import subprocess
20
Sean Dague6dbc6da2013-05-08 17:49:46 -040021import netaddr
Soniya Vyas795ef252020-12-10 19:07:23 +053022
Doug Hellmann583ce2c2015-03-11 14:55:46 +000023from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030024from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053025from oslo_utils import netutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040026
lanoux5fc14522015-09-21 08:17:35 +000027from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070028from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090029from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000030from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000031from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000032from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020033from tempest import exceptions
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000034from 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
Ghanshyam Mann64281392021-03-24 18:48:38 -050050 credentials = ['primary', 'admin']
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
Ghanshyam Mann18b45d72021-12-07 12:37:29 -060088 cls.setup_api_microversion_fixture(
89 compute_microversion=cls.compute_request_microversion,
90 volume_microversion=cls.volume_request_microversion,
91 placement_microversion=cls.placement_request_microversion)
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000092
Dan Smith49c2b3b2023-04-26 15:52:22 -070093 @classmethod
94 def setup_credentials(cls):
95 # Setting network=True, subnet=True creates a default network
96 cls.set_network_resources(
97 network=True,
98 subnet=True,
99 router=True,
100 dhcp=True)
101 super(ScenarioTest, cls).setup_credentials()
102
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530103 def setup_compute_client(cls):
Ghanshyam Mann1072f502021-03-24 19:15:22 -0500104 """Compute client"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530105 cls.compute_images_client = cls.os_primary.compute_images_client
106 cls.keypairs_client = cls.os_primary.keypairs_client
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530107 cls.servers_client = cls.os_primary.servers_client
108 cls.interface_client = cls.os_primary.interfaces_client
Ghanshyam Mann1072f502021-03-24 19:15:22 -0500109 cls.flavors_client = cls.os_primary.flavors_client
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530110
111 def setup_network_client(cls):
112 """Neutron network client"""
113 cls.networks_client = cls.os_primary.networks_client
114 cls.ports_client = cls.os_primary.ports_client
115 cls.routers_client = cls.os_primary.routers_client
116 cls.subnets_client = cls.os_primary.subnets_client
117 cls.floating_ips_client = cls.os_primary.floating_ips_client
118 cls.security_groups_client = cls.os_primary.security_groups_client
119 cls.security_group_rules_client = (
120 cls.os_primary.security_group_rules_client)
121
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000122 @classmethod
123 def setup_clients(cls):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530124 """This setup the service clients for the tests"""
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000125 super(ScenarioTest, cls).setup_clients()
Jordan Pittier1d2e40f2016-01-05 18:49:14 +0100126 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400127 # Check if glance v1 is available to determine which client to use.
128 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +0800129 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400130 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +0800131 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400132 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400133 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400134 'Either api_v1 or api_v2 must be True in '
135 '[image-feature-enabled].')
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530136
137 cls.setup_compute_client(cls)
138 cls.setup_network_client(cls)
Andrea Frittolia6b30152017-08-04 10:46:10 +0100139 if CONF.service_available.cinder:
140 cls.volumes_client = cls.os_primary.volumes_client_latest
141 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +0300142 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300143
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200144 # ## Test functions library
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200145 # The create_[resource] functions only return body and discard the
146 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +0100147
zhufl1e446b52017-10-16 16:54:57 +0800148 def create_port(self, network_id, client=None, **kwargs):
Martin Kopec9c874412020-12-17 20:43:26 +0000149 """Creates port for the respective network_id
150
151 :param network_id: the id of the network
152 :param client: the client to use, defaults to self.ports_client
153 :param kwargs: additional arguments such as:
154 - namestart - a string to generate a name for the port from
155 - default is self.__class__.__name__
156 - 'binding:vnic_type' - defaults to CONF.network.port_vnic_type
157 - 'binding:profile' - defaults to CONF.network.port_profile
158 """
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000159
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300160 if not client:
161 client = self.ports_client
Martin Kopec9c874412020-12-17 20:43:26 +0000162 name = data_utils.rand_name(
163 kwargs.pop('namestart', self.__class__.__name__))
Edan David408a97b2018-01-15 03:52:15 -0500164 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
165 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
166 if CONF.network.port_profile and 'binding:profile' not in kwargs:
167 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300168 result = client.create_port(
169 name=name,
170 network_id=network_id,
171 **kwargs)
Soniya Vyas0123f522020-09-24 17:43:26 +0530172 self.assertIsNotNone(result, 'Unable to allocate port')
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000173 port_id = result['port']['id']
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300174 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000175 client.delete_port, port_id)
176 port = waiters.wait_for_port_status(
177 client=client, port_id=port_id, status="DOWN")
178 return port["port"]
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300179
Martin Kopec30b4d532020-10-16 12:02:43 +0000180 def create_keypair(self, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530181 """Creates keypair
182
183 Keypair is a public key of OpenSSH key pair used for accessing
184 and create servers
185 Keypair can also be created by a private key for the same purpose
186 Here, the keys are randomly generated[public/private]
187 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300188 if not client:
189 client = self.keypairs_client
Martin Kopec30b4d532020-10-16 12:02:43 +0000190 if not kwargs.get('name'):
191 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100192 # We don't need to create a keypair by pubkey in scenario
Martin Kopec30b4d532020-10-16 12:02:43 +0000193 body = client.create_keypair(**kwargs)
194 self.addCleanup(client.delete_keypair, kwargs['name'])
ghanshyamdee01f22015-08-17 11:41:47 +0900195 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100196
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530197 def create_server(self, name=None, image_id=None, flavor=None,
Dan Smith65744692023-05-04 09:06:41 -0700198 validatable=None, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200199 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000200 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100201
lanoux5fc14522015-09-21 08:17:35 +0000202 This wrapper utility calls the common create test server and
203 returns a test server. The purpose of this wrapper is to minimize
204 the impact on the code of the tests already using this
205 function.
Noam Angel6e309952019-01-27 05:52:40 +0000206
207 :param **kwargs:
208 See extra parameters below
209
210 :Keyword Arguments:
211 * *vnic_type* (``string``) --
212 used when launching instances with pre-configured ports.
213 Examples:
214 normal: a traditional virtual port that is either attached
215 to a linux bridge or an openvswitch bridge on a
216 compute node.
217 direct: an SR-IOV port that is directly attached to a VM
218 macvtap: an SR-IOV port that is attached to a VM via a macvtap
219 device.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100220 direct-physical: an SR-IOV port that is directly attached to a
221 VM using physical instead of virtual
222 functions.
223 baremetal: a baremetal port directly attached to a baremetal
224 node.
225 virtio-forwarder: an SR-IOV port that is indirectly attached
226 to a VM using a low-latency vhost-user
227 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000228 Defaults to ``CONF.network.port_vnic_type``.
229 * *port_profile* (``dict``) --
230 This attribute is a dictionary that can be used (with admin
231 credentials) to supply information influencing the binding of
232 the port.
233 example: port_profile = "capabilities:[switchdev]"
234 Defaults to ``CONF.network.port_profile``.
Martin Kopec9c874412020-12-17 20:43:26 +0000235 * *create_port_body* (``dict``) --
236 This attribute is a dictionary of additional arguments to be
237 passed to create_port method.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100238 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100239
lanoux5fc14522015-09-21 08:17:35 +0000240 # NOTE(jlanoux): As a first step, ssh checks in the scenario
241 # tests need to be run regardless of the run_validation and
242 # validatable parameters and thus until the ssh validation job
243 # becomes voting in CI. The test resources management and IP
244 # association are taken care of in the scenario tests.
245 # Therefore, the validatable parameter is set to false in all
246 # those tests. In this way create_server just return a standard
247 # server and the scenario tests always perform ssh checks.
248
249 # Needed for the cross_tenant_traffic test:
250 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800251 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000252
zhufl24208c22016-10-25 15:23:48 +0800253 if name is None:
254 name = data_utils.rand_name(self.__class__.__name__ + "-server")
255
Noam Angel6e309952019-01-27 05:52:40 +0000256 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
257 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000258
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000259 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000260 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000261 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000262 ports = []
Martin Kopec9c874412020-12-17 20:43:26 +0000263 create_port_body = kwargs.pop('create_port_body', {})
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300264
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000265 if vnic_type:
266 create_port_body['binding:vnic_type'] = vnic_type
267
268 if profile:
269 create_port_body['binding:profile'] = profile
270
lanoux5fc14522015-09-21 08:17:35 +0000271 if kwargs:
272 # Convert security group names to security group ids
273 # to pass to create_port
274 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300275 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500276 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000277 ).get('security_groups')
278 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100279 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000280
281 sec_groups_names = [s['name'] for s in kwargs.pop(
282 'security_groups')]
283 security_groups_ids = [sec_dict[s]
284 for s in sec_groups_names]
285
286 if security_groups_ids:
287 create_port_body[
288 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300289 networks = kwargs.pop('networks', [])
290 else:
291 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000292
293 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300294 # for the project's private networks and create a port.
295 # The same behaviour as we would expect when passing
296 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000297 if not networks:
298 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300299 **{'router:external': False, 'fields': 'id'})['networks']
300
301 # It's net['uuid'] if networks come from kwargs
302 # and net['id'] if they come from
303 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000304 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000305 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300306 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800307 port = self.create_port(network_id=net_id,
308 client=clients.ports_client,
309 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300310 ports.append({'port': port['id']})
311 else:
312 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000313 if ports:
314 kwargs['networks'] = ports
315 self.ports = ports
316
317 tenant_network = self.get_tenant_network()
318
Marc Koderer979e4942016-12-08 10:07:59 +0100319 if CONF.compute.compute_volume_common_az:
320 kwargs.setdefault('availability_zone',
321 CONF.compute.compute_volume_common_az)
322
Dan Smith65744692023-05-04 09:06:41 -0700323 kwargs['validatable'] = bool(validatable)
Dan Smith49c2b3b2023-04-26 15:52:22 -0700324 keypair = kwargs.pop('keypair', None)
Dan Smith65744692023-05-04 09:06:41 -0700325 if wait_until == 'SSHABLE' and (
326 kwargs.get('validation_resources') is None):
Dan Smith49c2b3b2023-04-26 15:52:22 -0700327 # NOTE(danms): We should do this whether valdiation is enabled or
328 # not to consistently provide the resources to the
329 # create_test_server() function. If validation is disabled, then
330 # get_test_validation_resources() is basically a no-op for
331 # performance.
332 validation_resources = self.get_test_validation_resources(
333 self.os_primary)
334 if keypair:
335 validation_resources = copy.deepcopy(validation_resources)
336 validation_resources.update(
337 keypair=keypair)
Dan Smith65744692023-05-04 09:06:41 -0700338 kwargs.update({
339 'validatable': (validatable if validatable is not None
340 else True),
341 'validation_resources': validation_resources})
Dan Smith49c2b3b2023-04-26 15:52:22 -0700342 if keypair:
343 kwargs.update({'key_name': keypair['name']})
344
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200345 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000346 clients,
347 tenant_network=tenant_network,
348 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530349 name=name, flavor=flavor,
350 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000351
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200352 self.addCleanup(waiters.wait_for_server_termination,
353 clients.servers_client, body['id'])
354 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
355 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000356 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100357 return server
358
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100359 def create_volume(self, size=None, name=None, snapshot_id=None,
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300360 imageRef=None, volume_type=None, wait_until='available',
Milana Levye9a58a12023-02-21 12:55:20 +0000361 client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530362 """Creates volume
363
364 This wrapper utility creates volume and waits for volume to be
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300365 in 'available' state by default. If wait_until is None, means no wait.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530366 This method returns the volume's full representation by GET request.
367 """
Milana Levye9a58a12023-02-21 12:55:20 +0000368 if client is None:
369 client = self.volumes_client
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530370
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700371 if size is None:
372 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500373 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800374 if CONF.image_feature_enabled.api_v1:
375 resp = self.image_client.check_image(imageRef)
376 image = common_image.get_image_meta_from_headers(resp)
377 else:
378 image = self.image_client.show_image(imageRef)
379 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500380 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100381 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800382 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000383 kwargs.update({'name': name,
384 'snapshot_id': snapshot_id,
385 'imageRef': imageRef,
386 'volume_type': volume_type,
387 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100388
389 if CONF.compute.compute_volume_common_az:
390 kwargs.setdefault('availability_zone',
391 CONF.compute.compute_volume_common_az)
392
Milana Levye9a58a12023-02-21 12:55:20 +0000393 volume = client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700394
Milana Levye9a58a12023-02-21 12:55:20 +0000395 self.addCleanup(client.wait_for_resource_deletion,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100396 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100397 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Milana Levye9a58a12023-02-21 12:55:20 +0000398 client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300399 self.assertEqual(name, volume['name'])
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300400 if wait_until:
Milana Levye9a58a12023-02-21 12:55:20 +0000401 waiters.wait_for_volume_resource_status(client,
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300402 volume['id'], wait_until)
403 # The volume retrieved on creation has a non-up-to-date status.
404 # Retrieval after it becomes active ensures correct details.
Milana Levye9a58a12023-02-21 12:55:20 +0000405 volume = client.show_volume(volume['id'])['volume']
406
Andrea Frittoli247058f2014-07-16 16:09:22 +0100407 return volume
408
lkuchlane20e6a82018-05-08 11:28:46 +0300409 def create_backup(self, volume_id, name=None, description=None,
410 force=False, snapshot_id=None, incremental=False,
Martin Kopec4a140052020-10-16 16:26:55 +0000411 container=None, **kwargs):
412 """Creates a backup of the given volume_id or snapshot_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530413
Martin Kopec4a140052020-10-16 16:26:55 +0000414 This wrapper utility creates a backup and waits until it is in
415 'available' state.
416
417 :param volume_id: UUID of the volume to back up
418 :param name: backup name, '$classname-backup' by default
419 :param description: Description of the backup, None by default
420 :param force: boolean whether to backup even if the volume is attached
421 False by default
422 :param snapshot_id: UUID of the source snapshot to back up
423 None by default
424 :param incremental: boolean, False by default
425 :param container: a container name, None by default
426 :param **kwargs: additional parameters per the documentation:
427 https://docs.openstack.org/api-ref/block-storage/v3/
428 #create-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530429 """
lkuchlane20e6a82018-05-08 11:28:46 +0300430
431 name = name or data_utils.rand_name(
432 self.__class__.__name__ + "-backup")
Martin Kopec4a140052020-10-16 16:26:55 +0000433 args = {'name': name,
434 'description': description,
435 'force': force,
436 'snapshot_id': snapshot_id,
437 'incremental': incremental,
438 'container': container}
439 args.update(kwargs)
lkuchlane20e6a82018-05-08 11:28:46 +0300440 backup = self.backups_client.create_backup(volume_id=volume_id,
441 **kwargs)['backup']
442 self.addCleanup(self.backups_client.delete_backup, backup['id'])
443 waiters.wait_for_volume_resource_status(self.backups_client,
444 backup['id'], 'available')
445 return backup
446
Martin Kopec4a140052020-10-16 16:26:55 +0000447 def restore_backup(self, backup_id, **kwargs):
448 """Restores a backup given by the backup_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530449
Martin Kopec4a140052020-10-16 16:26:55 +0000450 This wrapper utility restores a backup and waits until it is in
451 'available' state.
452
453 :param backup_id: UUID of a backup to restore
454 :param **kwargs: additional parameters per the documentation:
455 https://docs.openstack.org/api-ref/block-storage/v3/
456 #restore-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530457 """
458
Martin Kopec4a140052020-10-16 16:26:55 +0000459 body = self.backups_client.restore_backup(backup_id, **kwargs)
460 restore = body['restore']
Sofia Enriquez404b55c2022-05-26 19:33:47 +0000461
462 using_pre_existing_volume = kwargs.get('volume_id', False)
463 if not using_pre_existing_volume:
464 self.addCleanup(self.volumes_client.delete_volume,
465 restore['volume_id'])
466
lkuchlane20e6a82018-05-08 11:28:46 +0300467 waiters.wait_for_volume_resource_status(self.backups_client,
468 backup_id, 'available')
469 waiters.wait_for_volume_resource_status(self.volumes_client,
470 restore['volume_id'],
471 'available')
472 self.assertEqual(backup_id, restore['backup_id'])
473 return restore
474
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000475 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
476 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530477 if image is None:
478 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530479 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
480 server_id, image, preserve_ephemeral)
481 self.servers_client.rebuild_server(
482 server_id=server_id,
483 image_ref=image,
484 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000485 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530486 if wait:
487 waiters.wait_for_server_status(self.servers_client,
488 server_id, 'ACTIVE')
489
lkuchlan73ed1f32017-07-06 16:22:12 +0300490 def create_volume_snapshot(self, volume_id, name=None, description=None,
Martin Kopeca17cca42020-10-17 16:57:51 +0000491 metadata=None, force=False, **kwargs):
492 """Creates volume's snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530493
Martin Kopeca17cca42020-10-17 16:57:51 +0000494 This wrapper utility creates volume snapshot and waits for it until
495 it is in 'available' state.
496
497 :param volume_id: UUID of a volume to create snapshot of
498 :param name: name of the snapshot, '$classname-snapshot' by default
499 :param description: description of the snapshot
500 :param metadata: metadata key and value pairs for the snapshot
501 :param force: whether snapshot even when the volume is attached
502 :param **kwargs: additional parameters per the doc
503 https://docs.openstack.org/api-ref/block-storage/v3/
504 #create-a-snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530505 """
506
lkuchlan73ed1f32017-07-06 16:22:12 +0300507 name = name or data_utils.rand_name(
508 self.__class__.__name__ + '-snapshot')
509 snapshot = self.snapshots_client.create_snapshot(
510 volume_id=volume_id,
511 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000512 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300513 description=description,
Martin Kopeca17cca42020-10-17 16:57:51 +0000514 metadata=metadata,
515 **kwargs)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530516
lkuchlan73ed1f32017-07-06 16:22:12 +0300517 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
518 snapshot['id'])
Benny Kopilovd4d49b02021-08-10 18:54:01 +0300519 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
520 self.snapshots_client.delete_snapshot, snapshot['id'])
lkuchlan73ed1f32017-07-06 16:22:12 +0300521 waiters.wait_for_volume_resource_status(self.snapshots_client,
522 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200523 snapshot = self.snapshots_client.show_snapshot(
524 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300525 return snapshot
526
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530527 def cleanup_volume_type(self, volume_type):
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100528 """Clean up a given volume type.
529
530 Ensuring all volumes associated to a type are first removed before
531 attempting to remove the type itself. This includes any image volume
532 cache volumes stored in a separate tenant to the original volumes
533 created from the type.
534 """
535 admin_volume_type_client = self.os_admin.volume_types_client_latest
536 admin_volumes_client = self.os_admin.volumes_client_latest
537 volumes = admin_volumes_client.list_volumes(
538 detail=True, params={'all_tenants': 1})['volumes']
539 type_name = volume_type['name']
540 for volume in [v for v in volumes if v['volume_type'] == type_name]:
541 test_utils.call_and_ignore_notfound_exc(
542 admin_volumes_client.delete_volume, volume['id'])
543 admin_volumes_client.wait_for_resource_deletion(volume['id'])
544 admin_volume_type_client.delete_volume_type(volume_type['id'])
545
Martin Kopec8e673a42020-10-18 17:33:02 +0000546 def create_volume_type(self, client=None, name=None, backend_name=None,
547 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530548 """Creates volume type
549
550 In a multiple-storage back-end configuration,
551 each back end has a name (volume_backend_name).
552 The name of the back end is declared as an extra-specification
553 of a volume type (such as, volume_backend_name=LVM).
554 When a volume is created, the scheduler chooses an
555 appropriate back end to handle the request, according
556 to the volume type specified by the user.
557 The scheduler uses volume types to explicitly create volumes on
558 specific back ends.
559
560 Before using volume type, a volume type has to be declared
561 to Block Storage. In addition to that, an extra-specification
562 has to be created to link the volume type to a back end name.
563 """
564
scottda61f68ac2016-06-07 12:07:55 -0600565 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000566 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000567 if not name:
568 class_name = self.__class__.__name__
569 name = data_utils.rand_name(class_name + '-volume-type')
570 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600571
572 LOG.debug("Creating a volume type: %s on backend %s",
573 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000574 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600575 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000576 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600577
Martin Kopec8e673a42020-10-18 17:33:02 +0000578 volume_type_resp = client.create_volume_type(
579 name=randomized_name, extra_specs=extra_specs, **kwargs)
580 volume_type = volume_type_resp['volume_type']
581
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530582 self.assertIn('id', volume_type)
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530583 self.addCleanup(self.cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600584 return volume_type
585
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500586 def create_security_group(self, security_group_rules_client=None,
587 project_id=None,
588 namestart='secgroup-smoke',
589 security_groups_client=None):
590 if security_group_rules_client is None:
591 security_group_rules_client = self.security_group_rules_client
592 if security_groups_client is None:
593 security_groups_client = self.security_groups_client
594 if project_id is None:
595 project_id = security_groups_client.project_id
596 secgroup = self.create_empty_security_group(
597 namestart=namestart, client=security_groups_client,
598 project_id=project_id)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100599
600 # Add rules to the security group
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500601 rules = self.create_loginable_secgroup_rule(
602 security_group_rules_client=security_group_rules_client,
603 secgroup=secgroup,
604 security_groups_client=security_groups_client)
605 for rule in rules:
606 self.assertEqual(project_id, rule['project_id'])
607 self.assertEqual(secgroup['id'], rule['security_group_id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100608 return secgroup
609
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500610 def create_empty_security_group(self, client=None, project_id=None,
611 namestart='secgroup-smoke'):
612 """Create a security group without rules.
613
614 Default rules will be created:
615 - IPv4 egress to any
616 - IPv6 egress to any
617 :param project_id: secgroup will be created in this project
618 :returns: the created security group
619 """
620
621 if client is None:
622 client = self.security_groups_client
623 if not project_id:
624 project_id = client.project_id
625 sg_name = data_utils.rand_name(namestart)
626 sg_desc = sg_name + " description"
627 sg_dict = dict(name=sg_name,
628 description=sg_desc)
629 sg_dict['project_id'] = project_id
630 result = client.create_security_group(**sg_dict)
631
632 secgroup = result['security_group']
633 self.assertEqual(secgroup['name'], sg_name)
634 self.assertEqual(project_id, secgroup['project_id'])
635 self.assertEqual(secgroup['description'], sg_desc)
636
637 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
638 client.delete_security_group, secgroup['id'])
639 return secgroup
640
641 def create_security_group_rule(self, secgroup=None,
642 sec_group_rules_client=None,
643 project_id=None,
644 security_groups_client=None, **kwargs):
645 """Create a rule from a dictionary of rule parameters.
646
647 Create a rule in a secgroup. if secgroup not defined will search for
648 default secgroup in project_id.
649 :param secgroup: the security group.
650 :param project_id: if secgroup not passed -- the tenant in which to
651 search for default secgroup
652 :param kwargs: a dictionary containing rule parameters:
653 for example, to allow incoming ssh:
654 rule = {
655 direction: 'ingress'
656 protocol:'tcp',
657 port_range_min: 22,
658 port_range_max: 22
659 }
660 """
661
662 if sec_group_rules_client is None:
663 sec_group_rules_client = self.security_group_rules_client
664 if security_groups_client is None:
665 security_groups_client = self.security_groups_client
666 if not project_id:
667 project_id = security_groups_client.project_id
668 if secgroup is None:
669 # Get default secgroup for project_id
670 default_secgroups = security_groups_client.list_security_groups(
671 name='default', project_id=project_id)['security_groups']
672 msg = "No default security group for project %s." % (project_id)
673 self.assertNotEmpty(default_secgroups, msg)
674 secgroup = default_secgroups[0]
675
676 ruleset = dict(security_group_id=secgroup['id'],
677 project_id=secgroup['project_id'])
678 ruleset.update(kwargs)
679
680 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
681 sg_rule = sg_rule['security_group_rule']
682
683 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
684 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
685
686 return sg_rule
687
688 def create_loginable_secgroup_rule(self, security_group_rules_client=None,
689 secgroup=None,
Roman Popelka3b0ccb02022-03-24 10:25:19 +0100690 security_groups_client=None,
691 rulesets=None):
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500692 """Create loginable security group rule by neutron clients by default.
693
694 This function will create:
695 1. egress and ingress tcp port 22 allow rule in order to allow ssh
696 access for ipv4.
697 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
698 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
699 """
700
701 if security_group_rules_client is None:
702 security_group_rules_client = self.security_group_rules_client
703 if security_groups_client is None:
704 security_groups_client = self.security_groups_client
Roman Popelka3b0ccb02022-03-24 10:25:19 +0100705 if rulesets is None:
706 rulesets = [
707 dict(
708 # ssh
709 protocol='tcp',
710 port_range_min=22,
711 port_range_max=22,
712 ),
713 dict(
714 # ping
715 protocol='icmp',
716 ),
717 dict(
718 # ipv6-icmp for ping6
719 protocol='icmp',
720 ethertype='IPv6',
721 )
722 ]
723
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500724 rules = []
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500725 sec_group_rules_client = security_group_rules_client
726 for ruleset in rulesets:
727 for r_direction in ['ingress', 'egress']:
728 ruleset['direction'] = r_direction
729 try:
730 sg_rule = self.create_security_group_rule(
731 sec_group_rules_client=sec_group_rules_client,
732 secgroup=secgroup,
733 security_groups_client=security_groups_client,
734 **ruleset)
735 except lib_exc.Conflict as ex:
736 # if rule already exist - skip rule and continue
737 msg = 'Security group rule already exists'
738 if msg not in ex._error_string:
739 raise ex
740 else:
741 self.assertEqual(r_direction, sg_rule['direction'])
742 rules.append(sg_rule)
743
744 return rules
745
zhuflf52c7592017-05-25 13:55:24 +0800746 def get_remote_client(self, ip_address, username=None, private_key=None,
747 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100748 """Get a SSH client to a remote server
749
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600750 :param ip_address: the server floating or fixed IP address to use
751 for ssh validation
752 :param username: name of the Linux account on the remote server
753 :param private_key: the SSH private key to use
754 :param server: server dict, used for debugging purposes
755 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100756 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700757
Andrea Frittoli247058f2014-07-16 16:09:22 +0100758 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800759 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800760 # Set this with 'keypair' or others to log in with keypair or
761 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000762 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800763 password = None
764 if private_key is None:
765 private_key = self.keypair['private_key']
766 else:
lanoux283273b2015-12-04 03:01:54 -0800767 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800768 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800769 linux_client = remote_client.RemoteClient(
770 ip_address, username, pkey=private_key, password=password,
771 server=server, servers_client=self.servers_client)
772 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100773 return linux_client
774
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000775 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000776 img_path = CONF.scenario.img_file
777 if not os.path.exists(img_path):
Martin Kopec008950e2020-09-29 08:12:39 +0000778 lib_exc.InvalidConfiguration(
Martin Kopec02af6a42020-03-03 12:39:12 +0000779 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
780 'a full path for the image. CONF.scenario.img_dir was '
781 'deprecated and will be removed in the next release. Till '
Martin Kopec008950e2020-09-29 08:12:39 +0000782 'Tempest 25.0.0, old behavior was maintained and kept working '
Martin Kopec02af6a42020-03-03 12:39:12 +0000783 'but starting Tempest 26.0.0, you need to specify the full '
784 'path in CONF.scenario.img_file config option.')
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300785 img_container_format = CONF.scenario.img_container_format
786 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000787 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400788 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000789 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100790 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000791 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530792 if img_properties is None:
793 img_properties = {}
794 name = data_utils.rand_name('%s-' % name)
795 params = {
796 'name': name,
797 'container_format': img_container_format,
798 'disk_format': img_disk_format or img_container_format,
799 }
800 if CONF.image_feature_enabled.api_v1:
801 params['is_public'] = 'False'
802 if img_properties:
803 params['properties'] = img_properties
804 params = {'headers': common_image.image_meta_to_headers(**params)}
805 else:
806 params['visibility'] = 'private'
807 # Additional properties are flattened out in the v2 API.
808 if img_properties:
809 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000810 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530811 body = self.image_client.create_image(**params)
812 image = body['image'] if 'image' in body else body
813 self.addCleanup(self.image_client.delete_image, image['id'])
814 self.assertEqual("queued", image['status'])
815 with open(img_path, 'rb') as image_file:
816 if CONF.image_feature_enabled.api_v1:
817 self.image_client.update_image(image['id'], data=image_file)
818 else:
819 self.image_client.store_image_file(image['id'], image_file)
820 LOG.debug("image:%s", image['id'])
821 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100822
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530823 def log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530824 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400825 if not CONF.compute_feature_enabled.console_output:
826 LOG.debug('Console output not supported, cannot log')
827 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700828 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100829 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700830 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100831 servers = servers['servers']
832 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100833 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700834 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000835 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100836 LOG.debug('Console output for %s\nbody=\n%s',
837 server['id'], console_output)
838 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100839 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100840 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100841
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000842 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530843 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300844 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000845 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000846
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000847 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530848 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000849 # Glance client
850 _image_client = self.image_client
851 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900852 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000853 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800854 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000855 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000856 image = _images_client.create_image(server['id'], name=name, **kwargs)
Benny Kopilov7d2edc22022-06-30 17:22:14 +0300857 # microversion 2.45 and above returns image_id
858 image_id = image.get('image_id') or image.response['location'].split(
859 'images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300860 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200861
862 self.addCleanup(_image_client.wait_for_resource_deletion,
863 image_id)
864 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
865 _image_client.delete_image, image_id)
866
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400867 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530868 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700869 resp = _image_client.check_image(image_id)
870 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400871 image_props = snapshot_image.get('properties', {})
872 else:
873 # In glance v2 the additional properties are flattened.
874 snapshot_image = _image_client.show_image(image_id)
875 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300876
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400877 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300878 if bdm:
879 bdm = json.loads(bdm)
880 if bdm and 'snapshot_id' in bdm[0]:
881 snapshot_id = bdm[0]['snapshot_id']
882 self.addCleanup(
883 self.snapshots_client.wait_for_resource_deletion,
884 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100885 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
886 self.snapshots_client.delete_snapshot,
887 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200888 waiters.wait_for_volume_resource_status(self.snapshots_client,
889 snapshot_id,
890 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000891 image_name = snapshot_image['name']
892 self.assertEqual(name, image_name)
893 LOG.debug("Created snapshot image %s for server %s",
894 image_name, server['name'])
895 return snapshot_image
896
Milana Levye9a58a12023-02-21 12:55:20 +0000897 def nova_volume_attach(self, server, volume_to_attach,
898 volumes_client=None, servers_client=None,
899 **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530900 """Compute volume attach
901
902 This utility attaches volume from compute and waits for the
903 volume status to be 'in-use' state.
904 """
Milana Levye9a58a12023-02-21 12:55:20 +0000905 if volumes_client is None:
906 volumes_client = self.volumes_client
907 if servers_client is None:
908 servers_client = self.servers_client
909
910 volume = servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000911 server['id'], volumeId=volume_to_attach['id'],
912 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200913 self.assertEqual(volume_to_attach['id'], volume['id'])
Milana Levye9a58a12023-02-21 12:55:20 +0000914 waiters.wait_for_volume_resource_status(volumes_client,
lkuchlan52d7b0d2016-11-07 20:53:19 +0200915 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000916 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Milana Levye9a58a12023-02-21 12:55:20 +0000917 self.nova_volume_detach, server, volume,
918 servers_client)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200919 # Return the updated volume after the attachment
Milana Levye9a58a12023-02-21 12:55:20 +0000920 return volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900921
Milana Levye9a58a12023-02-21 12:55:20 +0000922 def nova_volume_detach(self, server, volume, servers_client=None):
Soniya Vyasae631132020-08-28 13:37:12 +0530923 """Compute volume detach
924
Lee Yarwood5423c532020-12-17 11:24:46 +0000925 This utility detaches the volume from the server and checks whether the
926 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530927 """
Milana Levye9a58a12023-02-21 12:55:20 +0000928 if servers_client is None:
929 servers_client = self.servers_client
930
931 servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000932 waiters.wait_for_volume_attachment_remove_from_server(
Milana Levye9a58a12023-02-21 12:55:20 +0000933 servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200934
Steven Hardyda2a8352014-10-02 12:52:20 +0100935 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800936 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530937 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000938 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000939 cmd = ['ping', '-c1', '-w1']
940
941 if mtu:
942 cmd += [
943 # don't fragment
944 '-M', 'do',
945 # ping receives just the size of ICMP payload
946 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
947 ]
948 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700949
950 def ping():
951 proc = subprocess.Popen(cmd,
952 stdout=subprocess.PIPE,
953 stderr=subprocess.PIPE)
954 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000955
Aaron Rosena7df13b2014-09-23 09:45:45 -0700956 return (proc.returncode == 0) == should_succeed
957
Jordan Pittier9e227c52016-02-09 14:35:18 +0100958 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000959 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800960 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000961 'caller': caller, 'ip': ip_address, 'timeout': timeout,
962 'should_succeed':
963 'reachable' if should_succeed else 'unreachable'
964 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200965 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000966 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800967 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000968 'caller': caller, 'ip': ip_address, 'timeout': timeout,
969 'result': 'expected' if result else 'unexpected'
970 })
zhufl0ec74c42017-11-15 14:02:28 +0800971 if server:
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530972 self.log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000973 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700974
Yair Friedae0e73d2014-11-24 11:56:26 +0200975 def check_vm_connectivity(self, ip_address,
976 username=None,
977 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000978 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800979 extra_msg="",
980 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000981 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000982 """Check server connectivity
983
Yair Friedae0e73d2014-11-24 11:56:26 +0200984 :param ip_address: server to test against
985 :param username: server's ssh username
986 :param private_key: server's ssh private key to be used
987 :param should_connect: True/False indicates positive/negative test
988 positive - attempt ping and ssh
989 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800990 :param extra_msg: Message to help with debugging if ``ping_ip_address``
991 fails
992 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000993 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200994
995 :raises: AssertError if the result of the connectivity check does
996 not match the value of the should_connect param
997 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530998
zhufl0ec74c42017-11-15 14:02:28 +0800999 LOG.debug('checking network connections to IP %s with user: %s',
1000 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +02001001 if should_connect:
1002 msg = "Timed out waiting for %s to become reachable" % ip_address
1003 else:
1004 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +08001005 if extra_msg:
1006 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +02001007 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +00001008 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +08001009 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +02001010 msg=msg)
1011 if should_connect:
1012 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +08001013 try:
1014 self.get_remote_client(ip_address, username, private_key,
1015 server=server)
1016 except Exception:
1017 if not extra_msg:
1018 extra_msg = 'Failed to ssh to %s' % ip_address
1019 LOG.exception(extra_msg)
1020 raise
Yair Friedae0e73d2014-11-24 11:56:26 +02001021
Ghanshyam Mann64281392021-03-24 18:48:38 -05001022 def get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
Yair Friedae0e73d2014-11-24 11:56:26 +02001023
Ghanshyam Mann64281392021-03-24 18:48:38 -05001024 if ip_addr and not kwargs.get('fixed_ips'):
1025 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
1026 ports = self.os_admin.ports_client.list_ports(
1027 device_id=server['id'], **kwargs)['ports']
Lukas Piwowarskif759bc12020-11-05 10:51:29 +00001028
Ghanshyam Mann64281392021-03-24 18:48:38 -05001029 # A port can have more than one IP address in some cases.
1030 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1031 # with 2 subnets
1032
1033 def _is_active(port):
1034 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1035 # servers. Neutron does not bind ports for Ironic instances, as a
1036 # result the port remains in the DOWN state. This has been fixed
1037 # with the introduction of the networking-baremetal plugin but
1038 # it's not mandatory (and is not used on all stable branches).
1039 return (port['status'] == 'ACTIVE' or
1040 port.get('binding:vnic_type') == 'baremetal')
1041
1042 port_map = [(p["id"], fxip["ip_address"])
1043 for p in ports
1044 for fxip in p["fixed_ips"]
1045 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
1046 _is_active(p))]
1047 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1048 if inactive:
1049 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
1050
1051 self.assertNotEmpty(port_map,
1052 "No IPv4 addresses found in: %s" % ports)
1053 self.assertEqual(len(port_map), 1,
1054 "Found multiple IPv4 addresses: %s. "
1055 "Unable to determine which port to target."
1056 % port_map)
1057 return port_map[0]
1058
1059 def create_floating_ip(self, server, external_network_id=None,
1060 port_id=None, client=None, **kwargs):
1061 """Create a floating IP and associates to a resource/port on Neutron"""
1062
1063 if not external_network_id:
1064 external_network_id = CONF.network.public_network_id
1065 if not client:
1066 client = self.floating_ips_client
1067 if not port_id:
1068 port_id, ip4 = self.get_server_port_id_and_ip4(server)
1069 else:
1070 ip4 = None
1071
1072 floatingip_kwargs = {
1073 'floating_network_id': external_network_id,
1074 'port_id': port_id,
1075 'tenant_id': server.get('project_id') or server['tenant_id'],
1076 'fixed_ip_address': ip4,
1077 }
1078 if CONF.network.subnet_id:
1079 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1080
1081 floatingip_kwargs.update(kwargs)
1082 result = client.create_floatingip(**floatingip_kwargs)
1083 floating_ip = result['floatingip']
1084
Jordan Pittier9e227c52016-02-09 14:35:18 +01001085 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Ghanshyam Mann64281392021-03-24 18:48:38 -05001086 client.delete_floatingip,
Yair Friedae0e73d2014-11-24 11:56:26 +02001087 floating_ip['id'])
Ghanshyam Mann64281392021-03-24 18:48:38 -05001088 return floating_ip
1089
Dan Smith49c2b3b2023-04-26 15:52:22 -07001090 def get_floating_ip(self, server):
1091 """Attempt to get an existing floating ip or a server
1092
1093 If one exists, return it, else return None
1094 """
1095 port_id, ip4 = self.get_server_port_id_and_ip4(server)
1096 ips = self.floating_ips_client.list_floatingips(
1097 floating_network_id=CONF.network.public_network_id,
1098 port_id=port_id)
1099 try:
1100 return ips['floatingips'][0]['floating_ip_address']
1101 except (KeyError, IndexError):
1102 return None
1103
Ghanshyam Mann64281392021-03-24 18:48:38 -05001104 def associate_floating_ip(self, floating_ip, server):
1105 """Associate floating ip to server
1106
1107 This wrapper utility attaches the floating_ip for
1108 the respective port_id of server
1109 """
1110 port_id, _ = self.get_server_port_id_and_ip4(server)
1111 kwargs = dict(port_id=port_id)
1112 floating_ip = self.floating_ips_client.update_floatingip(
1113 floating_ip['id'], **kwargs)['floatingip']
1114 self.assertEqual(port_id, floating_ip['port_id'])
1115 return floating_ip
1116
1117 def disassociate_floating_ip(self, floating_ip):
1118 """Disassociates floating ip
1119
1120 This wrapper utility disassociates given floating ip.
1121 :param floating_ip: a dict which is a return value of
1122 floating_ips_client.create_floatingip method
1123 """
1124 kwargs = dict(port_id=None)
1125 floating_ip = self.floating_ips_client.update_floatingip(
1126 floating_ip['id'], **kwargs)['floatingip']
1127 self.assertIsNone(floating_ip['port_id'])
Yair Friedae0e73d2014-11-24 11:56:26 +02001128 return floating_ip
1129
Sean Dague20e98612016-01-06 14:33:28 -05001130 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +00001131 private_key=None, server=None, username=None,
1132 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301133 """Creates timestamp
1134
1135 This wrapper utility does ssh, creates timestamp and returns the
1136 created timestamp.
1137 """
Sean Dague20e98612016-01-06 14:33:28 -05001138 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +02001139 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +00001140 server=server,
1141 username=username)
1142
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001143 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +00001144 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001145 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
1146 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001147 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
1148 ssh_client.exec_command(cmd_timestamp)
1149 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
1150 % mount_path)
1151 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001152 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001153 return timestamp
1154
Sean Dague20e98612016-01-06 14:33:28 -05001155 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001156 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301157 """Returns timestamp
1158
1159 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001160
1161 :param ip_address: The floating IP or fixed IP of the remote server
1162 :param dev_name: Name of the device that stores the timestamp
1163 :param mount_path: Path which should be used as mount point for
1164 dev_name
1165 :param private_key: The SSH private key to use for authentication
1166 :param server: Server dict, used for debugging purposes
1167 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301168 """
1169
Sean Dague20e98612016-01-06 14:33:28 -05001170 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +02001171 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001172 server=server,
1173 username=username)
1174
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001175 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -07001176 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001177 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
1178 % mount_path)
1179 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001180 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001181 return timestamp
1182
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001183 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -05001184 """Get the server fixed or floating IP.
1185
1186 Based on the configuration we're in, return a correct ip
1187 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001188
1189 If CONF.validation.connect_method is floating, then
1190 a floating ip will be created passing kwargs as additional
1191 argument.
Sean Dague20e98612016-01-06 14:33:28 -05001192 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301193
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001194 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -05001195 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +08001196 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -05001197 # method is creating the floating IP there.
Dan Smith49c2b3b2023-04-26 15:52:22 -07001198 fip = self.get_floating_ip(server)
1199 if fip:
1200 # Already have a floating ip, so use it instead of creating
1201 # another
1202 return fip
1203 else:
1204 return self.create_floating_ip(
1205 server, **kwargs)['floating_ip_address']
Sean Dague20e98612016-01-06 14:33:28 -05001206 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -04001207 # Determine the network name to look for based on config or creds
1208 # provider network resources.
1209 if CONF.validation.network_for_ssh:
1210 addresses = server['addresses'][
1211 CONF.validation.network_for_ssh]
1212 else:
zhufl7b4a7202017-09-28 10:29:27 +08001213 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -04001214 addresses = (server['addresses'][network['name']]
1215 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -05001216 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001217 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
1218 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -05001219 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +08001220 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001221 else:
Matthew Treinish4217a702016-10-07 17:27:11 -04001222 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001223
zhufl7bc916d2018-08-22 14:47:39 +08001224 @classmethod
1225 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301226 """Gets host of server"""
1227
zhufl7bc916d2018-08-22 14:47:39 +08001228 server_details = cls.os_admin.servers_client.show_server(server_id)
1229 return server_details['server']['OS-EXT-SRV-ATTR:host']
1230
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001231 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
1232 bd_map_v2 = [{
1233 'uuid': source_id,
1234 'source_type': source_type,
1235 'destination_type': 'volume',
1236 'boot_index': 0,
1237 'delete_on_termination': delete_on_termination}]
1238 return {'block_device_mapping_v2': bd_map_v2}
1239
1240 def boot_instance_from_resource(self, source_id,
1241 source_type,
1242 keypair=None,
1243 security_group=None,
1244 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +00001245 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301246 """Boot instance from resource
1247
1248 This wrapper utility boots instance from resource with block device
1249 mapping with source info passed in arguments
1250 """
1251
Martin Kopecbee673e2020-11-04 09:40:52 +00001252 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001253 if keypair:
Dan Smith49c2b3b2023-04-26 15:52:22 -07001254 create_kwargs['keypair'] = keypair
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001255 if security_group:
1256 create_kwargs['security_groups'] = [
1257 {'name': security_group['name']}]
1258 create_kwargs.update(self._get_bdm(
1259 source_id,
1260 source_type,
1261 delete_on_termination=delete_on_termination))
1262 if name:
1263 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +00001264 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001265
Martin Kopecbee673e2020-11-04 09:40:52 +00001266 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001267
Martin Kopec0216b372020-11-04 09:32:05 +00001268 def create_volume_from_image(self, **kwargs):
1269 """Create volume from image.
1270
1271 :param image_id: ID of the image to create volume from,
1272 CONF.compute.image_ref by default
1273 :param name: name of the volume,
1274 '$classname-volume-origin' by default
1275 :param **kwargs: additional parameters
1276 """
1277 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
1278 name = kwargs.pop('name', None)
1279 if not name:
1280 namestart = self.__class__.__name__ + '-volume-origin'
1281 name = data_utils.rand_name(namestart)
1282 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001283
Andrea Frittoli2e733b52014-07-16 14:12:11 +01001284
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001285class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001286 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001287
Yair Fried1fc32a12014-08-04 09:11:30 +03001288 This class provide helpers for network scenario tests, using the neutron
1289 API. Helpers from ancestor which use the nova network API are overridden
1290 with the neutron API.
1291
1292 This Class also enforces using Neutron instead of novanetwork.
1293 Subclassed tests will be skipped if Neutron is not enabled
1294
1295 """
1296
1297 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001298 def skip_checks(cls):
1299 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001300 if not CONF.service_available.neutron:
1301 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001302
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301303 def create_network(self, networks_client=None,
1304 project_id=None,
1305 namestart='network-smoke-',
1306 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001307 if not networks_client:
1308 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001309 if not project_id:
1310 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001311 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001312 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001313 if net_dict:
1314 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001315 # Neutron disables port security by default so we have to check the
1316 # config before trying to create the network with port_security_enabled
1317 if CONF.network_feature_enabled.port_security:
1318 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001319 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001320 network = result['network']
1321
1322 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001323 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001324 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001325 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001326 return network
1327
zhufl5b0a52f2017-10-24 15:48:20 +08001328 def create_subnet(self, network, subnets_client=None,
1329 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001330 """Create a subnet for the given network
1331
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301332 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001333 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301334
1335 :param **kwargs:
1336 See extra parameters below
1337
1338 :Keyword Arguments:
1339
1340 * *ip_version = ip version of the given network,
Soniya Vyas795ef252020-12-10 19:07:23 +05301341 use_default_subnetpool = default subnetpool to
1342 manage IPv6 addresses range.
Yair Fried1fc32a12014-08-04 09:11:30 +03001343 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301344
John Warren3961acd2015-10-02 14:38:53 -04001345 if not subnets_client:
1346 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001347
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001348 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001349 """Check cidr existence
1350
yangjianfeng4ad346e2020-11-22 06:49:19 +00001351 :returns: True if subnet with cidr already exist in tenant or
1352 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001353 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001354 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001355 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001356 external_nets = self.os_admin.networks_client.list_networks(
1357 **{"router:external": True})['networks']
1358 external_subnets = []
1359 for ext_net in external_nets:
1360 external_subnets.extend(
1361 self.os_admin.subnets_client.list_subnets(
1362 network_id=ext_net['id'], cidr=cidr)['subnets'])
1363 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001364
Soniya Vyas795ef252020-12-10 19:07:23 +05301365 def _make_create_subnet_request(namestart, network,
1366 ip_version, subnets_client, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001367
1368 subnet = dict(
1369 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001370 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001371 project_id=network['project_id'],
Kirill Shileev14113572014-11-21 16:58:02 +03001372 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001373 **kwargs
1374 )
Soniya Vyas795ef252020-12-10 19:07:23 +05301375
1376 if ip_version == 6:
1377 subnet['ipv6_address_mode'] = 'slaac'
1378 subnet['ipv6_ra_mode'] = 'slaac'
1379
Yair Fried1fc32a12014-08-04 09:11:30 +03001380 try:
Soniya Vyas795ef252020-12-10 19:07:23 +05301381 return subnets_client.create_subnet(**subnet)
Masayuki Igawad9388762015-01-20 14:56:42 +09001382 except lib_exc.Conflict as e:
Soniya Vyas795ef252020-12-10 19:07:23 +05301383 if 'overlaps with another subnet' not in str(e):
Yair Fried1fc32a12014-08-04 09:11:30 +03001384 raise
Soniya Vyas795ef252020-12-10 19:07:23 +05301385
1386 result = None
1387 str_cidr = None
1388
1389 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
1390 ip_version = kwargs.pop('ip_version', 4)
1391
1392 if not use_default_subnetpool:
1393
1394 if ip_version == 6:
1395 tenant_cidr = netaddr.IPNetwork(
1396 CONF.network.project_network_v6_cidr)
1397 num_bits = CONF.network.project_network_v6_mask_bits
1398 else:
1399 tenant_cidr = netaddr.IPNetwork(
1400 CONF.network.project_network_cidr)
1401 num_bits = CONF.network.project_network_mask_bits
1402
1403 # Repeatedly attempt subnet creation with sequential cidr
1404 # blocks until an unallocated block is found.
1405 for subnet_cidr in tenant_cidr.subnet(num_bits):
1406 str_cidr = str(subnet_cidr)
1407 if cidr_in_use(str_cidr, project_id=network['project_id']):
1408 continue
1409 result = _make_create_subnet_request(
1410 namestart, network, ip_version, subnets_client,
1411 cidr=str_cidr, **kwargs)
1412
1413 if result is not None:
1414 break
1415
1416 else:
1417 result = _make_create_subnet_request(
1418 namestart, network, ip_version, subnets_client,
1419 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +03001420 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001421
1422 subnet = result['subnet']
Soniya Vyas795ef252020-12-10 19:07:23 +05301423 if str_cidr is not None:
1424 self.assertEqual(subnet['cidr'], str_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -05001425
1426 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1427 subnets_client.delete_subnet, subnet['id'])
1428
Yair Fried1fc32a12014-08-04 09:11:30 +03001429 return subnet
1430
Soniya Vyasc37410f2021-02-24 15:26:27 +05301431 def get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001432 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001433 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001434 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001435 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001436 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001437
Yair Fried45f92952014-06-26 05:19:19 +03001438 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001439 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001440
Steve Heyman33735f22016-05-24 09:28:08 -05001441 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001442 :param status: target status
1443 :raises: AssertionError if status doesn't match
1444 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301445
Steve Heyman33735f22016-05-24 09:28:08 -05001446 floatingip_id = floating_ip['id']
1447
Carl Baldwina754e2d2014-10-23 22:47:41 +00001448 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001449 floating_ip = (self.floating_ips_client.
1450 show_floatingip(floatingip_id)['floatingip'])
1451 if status == floating_ip['status']:
1452 LOG.info("FloatingIP: {fp} is at status: {st}"
1453 .format(fp=floating_ip, st=status))
1454 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001455
zhufl4dda94e2017-03-14 16:14:46 +08001456 if not test_utils.call_until_true(refresh,
1457 CONF.network.build_timeout,
1458 CONF.network.build_interval):
1459 floating_ip = self.floating_ips_client.show_floatingip(
1460 floatingip_id)['floatingip']
1461 self.assertEqual(status, floating_ip['status'],
1462 message="FloatingIP: {fp} is at status: {cst}. "
1463 "failed to reach status: {st}"
1464 .format(fp=floating_ip, cst=floating_ip['status'],
1465 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001466
zhufl420a0192017-09-28 11:04:50 +08001467 def check_tenant_network_connectivity(self, server,
1468 username,
1469 private_key,
1470 should_connect=True,
1471 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301472 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001473 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001474 msg = 'Tenant networks not configured to be reachable.'
1475 LOG.info(msg)
1476 return
1477 # The target login is assumed to have been configured for
1478 # key-based authentication by cloud-init.
1479 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001480 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001481 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001482 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001483 username,
1484 private_key,
1485 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001486 except Exception as e:
1487 LOG.exception('Tenant network connectivity check failed')
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301488 self.log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001489 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001490 raise
1491
zhufle9877c62017-10-13 09:38:19 +08001492 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001493 nic=None, protocol='icmp'):
1494 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001495
Claudiu Belu33c3e602014-08-28 16:38:01 +03001496 :param source: RemoteClient: an ssh connection from which to execute
1497 the check
1498 :param dest: an IP to check connectivity against
1499 :param should_succeed: boolean should connection succeed or not
1500 :param nic: specific network interface to test connectivity from
1501 :param protocol: the protocol used to test connectivity with.
1502 :returns: True, if the connection succeeded and it was expected to
1503 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001504 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301505
Claudiu Belu33c3e602014-08-28 16:38:01 +03001506 method_name = '%s_check' % protocol
1507 connectivity_checker = getattr(source, method_name)
1508
1509 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001510 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001511 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001512 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001513 LOG.warning('Failed to check %(protocol)s connectivity for '
1514 'IP %(dest)s via a ssh connection from: %(src)s.',
1515 dict(protocol=protocol, dest=dest,
1516 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001517 return not should_succeed
1518 return should_succeed
1519
Claudiu Belu33c3e602014-08-28 16:38:01 +03001520 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001521 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001522 if result:
1523 return
1524
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001525 source_host = source.ssh_client.host
1526 if should_succeed:
1527 msg = "Timed out waiting for %s to become reachable from %s" \
1528 % (dest, source_host)
1529 else:
1530 msg = "%s is reachable from %s" % (dest, source_host)
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301531 self.log_console_output()
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001532 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001533
Soniya Vyas73555df2021-03-04 19:05:42 +05301534 def get_router(self, client=None, project_id=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001535 """Retrieve a router for the given tenant id.
1536
1537 If a public router has been configured, it will be returned.
1538
1539 If a public router has not been configured, but a public
1540 network has, a tenant router will be created and returned that
1541 routes traffic to the public network.
1542 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301543
Yair Frieddb6c9e92014-08-06 08:53:13 +03001544 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001545 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001546 if not project_id:
1547 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001548 router_id = CONF.network.public_router_id
1549 network_id = CONF.network.public_network_id
1550 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001551 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001552 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001553 elif network_id:
Martin Kopec0090a102020-11-03 13:50:19 +00001554 name = kwargs.pop('name', None)
1555 if not name:
1556 namestart = self.__class__.__name__ + '-router'
1557 name = data_utils.rand_name(namestart)
1558
1559 ext_gw_info = kwargs.pop('external_gateway_info', None)
1560 if not ext_gw_info:
1561 ext_gw_info = dict(network_id=network_id)
zhufl3484f992017-10-10 16:18:29 +08001562 router = client.create_router(
Martin Kopec0090a102020-11-03 13:50:19 +00001563 name=name,
1564 admin_state_up=kwargs.get('admin_state_up', True),
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001565 project_id=project_id,
Martin Kopec0090a102020-11-03 13:50:19 +00001566 external_gateway_info=ext_gw_info,
1567 **kwargs)['router']
zhufl3484f992017-10-10 16:18:29 +08001568 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1569 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001570 return router
1571 else:
1572 raise Exception("Neither of 'public_router_id' or "
1573 "'public_network_id' has been defined.")
1574
Ghanshyam Mann071d1542021-03-24 19:10:47 -05001575 def setup_network_subnet_with_router(
1576 self, networks_client=None,
1577 routers_client=None, subnets_client=None,
1578 project_id=None, dns_nameservers=None,
1579 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001580 """Create a network with a subnet connected to a router.
1581
David Shrewsbury9bac3662014-08-07 15:07:01 -04001582 The baremetal driver is a special case since all nodes are
1583 on the same shared network.
1584
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001585 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001586 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001587 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001588 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001589 a form like this: {'provider:network_type': 'vlan',
1590 'provider:physical_network': 'foo',
1591 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001592 :returns: network, subnet, router
1593 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301594
Thiago Paiva66cded22016-08-15 14:55:58 -03001595 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001596 # NOTE(Shrews): This exception is for environments where tenant
1597 # credential isolation is available, but network separation is
1598 # not (the current baremetal case). Likely can be removed when
1599 # test account mgmt is reworked:
1600 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001601 if not CONF.compute.fixed_network_name:
1602 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001603 raise lib_exc.InvalidConfiguration(m)
Soniya Vyasc37410f2021-02-24 15:26:27 +05301604 network = self.get_network_by_name(
David Shrewsbury9bac3662014-08-07 15:07:01 -04001605 CONF.compute.fixed_network_name)
1606 router = None
1607 subnet = None
1608 else:
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301609 network = self.create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001610 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001611 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001612 port_security_enabled=port_security_enabled,
1613 **net_dict)
Soniya Vyas73555df2021-03-04 19:05:42 +05301614 router = self.get_router(client=routers_client,
1615 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001616 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001617 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001618 # use explicit check because empty list is a valid option
1619 if dns_nameservers is not None:
1620 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001621 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001622 if not routers_client:
1623 routers_client = self.routers_client
1624 router_id = router['id']
1625 routers_client.add_router_interface(router_id,
1626 subnet_id=subnet['id'])
1627
1628 # save a cleanup job to remove this association between
1629 # router and subnet
1630 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1631 routers_client.remove_router_interface, router_id,
1632 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001633 return network, subnet, router
1634
1635
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001636class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001637 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001638
David Kranz4cc852b2015-03-09 14:57:11 -04001639 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001640 def setup_clients(cls):
1641 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001642 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001643 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001644 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001645
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001646 def create_encryption_type(self, client=None, type_id=None, provider=None,
1647 key_size=None, cipher=None,
1648 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301649 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001650 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001651 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001652 if not type_id:
1653 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001654 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001655 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001656 client.create_encryption_type(
1657 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001658 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001659
lkuchlan3023e752017-06-08 12:53:13 +03001660 def create_encrypted_volume(self, encryption_provider, volume_type,
1661 key_size=256, cipher='aes-xts-plain64',
1662 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301663 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001664 volume_type = self.create_volume_type(name=volume_type)
1665 self.create_encryption_type(type_id=volume_type['id'],
1666 provider=encryption_provider,
1667 key_size=key_size,
1668 cipher=cipher,
1669 control_location=control_location)
1670 return self.create_volume(volume_type=volume_type['name'])
1671
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001672
Masayuki Igawa0870db52015-09-18 21:08:36 +09001673class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001674 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001675
1676 Subclasses implement the tests that use the methods provided by this
1677 class.
1678 """
1679
Ghanshyam Mann64281392021-03-24 18:48:38 -05001680 credentials = ['primary']
1681
Chris Dent0d494112014-08-26 13:48:30 +01001682 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001683 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001684 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001685 if not CONF.service_available.swift:
1686 skip_msg = ("%s skipped as swift is not available" %
1687 cls.__name__)
1688 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001689
1690 @classmethod
1691 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001692 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001693 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001694 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001695 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001696
1697 @classmethod
1698 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001699 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001700 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001701 cls.account_client = cls.os_operator.account_client
1702 cls.container_client = cls.os_operator.container_client
1703 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001704
Chris Dentde456a12014-09-10 12:41:15 +01001705 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301706 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001707 self.account_client.list_account_containers()
1708 LOG.debug('Swift status information obtained successfully')
1709
Chris Dentde456a12014-09-10 12:41:15 +01001710 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301711 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001712 name = container_name or data_utils.rand_name(
1713 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001714 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001715 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001716 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001717 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001718 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001719 self.container_client.delete_container,
1720 name)
Chris Dent0d494112014-08-26 13:48:30 +01001721 return name
1722
Chris Dentde456a12014-09-10 12:41:15 +01001723 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301724 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001725 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001726 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001727
Chris Dentde456a12014-09-10 12:41:15 +01001728 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301729 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001730 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001731 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001732 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001733 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001734 self.object_client.delete_object,
1735 container_name,
1736 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001737 return obj_name, obj_data
1738
Chris Dentde456a12014-09-10 12:41:15 +01001739 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301740 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001741 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001742 self.list_and_check_container_objects(container_name,
1743 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001744
Chris Dentde456a12014-09-10 12:41:15 +01001745 def list_and_check_container_objects(self, container_name,
1746 present_obj=None,
1747 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301748 """List and verify objects for a given container
1749
1750 This utility lists objects for a given container
1751 and asserts which are present and
1752 which are not
1753 """
1754
Ghanshyam2a180b82014-06-16 13:54:22 +09001755 if present_obj is None:
1756 present_obj = []
1757 if not_present_obj is None:
1758 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001759 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001760 container_name)
1761 if present_obj:
1762 for obj in present_obj:
1763 self.assertIn(obj, object_list)
1764 if not_present_obj:
1765 for obj in not_present_obj:
1766 self.assertNotIn(obj, object_list)
1767
Chris Dentde456a12014-09-10 12:41:15 +01001768 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301769 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001770 _, obj = self.object_client.get_object(container_name, obj_name)
1771 self.assertEqual(obj, expected_data)