blob: bf3f62fa707473e6e9dd914c0567c6c98106424a [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Martin Kopec02af6a42020-03-03 12:39:12 +000017import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040018import subprocess
19
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import netaddr
Soniya Vyas795ef252020-12-10 19:07:23 +053021
Doug Hellmann583ce2c2015-03-11 14:55:46 +000022from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030023from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053024from oslo_utils import netutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
lanoux5fc14522015-09-21 08:17:35 +000026from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070027from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090028from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000029from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000030from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000031from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020032from tempest import exceptions
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000033from tempest.lib.common import api_version_utils
Ken'ichi Ohmichibe4fb502017-03-10 10:04:48 -080034from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010035from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050036from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040037import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Matthew Treinish6c072292014-01-29 19:15:52 +000039CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040040
Attila Fazekasfb7552a2013-08-27 13:02:26 +020041LOG = log.getLogger(__name__)
42
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000043LATEST_MICROVERSION = 'latest'
44
Sean Dague6dbc6da2013-05-08 17:49:46 -040045
Andrea Frittoli2e733b52014-07-16 14:12:11 +010046class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010047 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010048
Ghanshyam Mann64281392021-03-24 18:48:38 -050049 credentials = ['primary', 'admin']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000050
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000051 compute_min_microversion = None
52 compute_max_microversion = LATEST_MICROVERSION
53 volume_min_microversion = None
54 volume_max_microversion = LATEST_MICROVERSION
55 placement_min_microversion = None
56 placement_max_microversion = LATEST_MICROVERSION
57
58 @classmethod
59 def skip_checks(cls):
60 super(ScenarioTest, cls).skip_checks()
61 api_version_utils.check_skip_with_microversion(
62 cls.compute_min_microversion, cls.compute_max_microversion,
63 CONF.compute.min_microversion, CONF.compute.max_microversion)
64 api_version_utils.check_skip_with_microversion(
65 cls.volume_min_microversion, cls.volume_max_microversion,
66 CONF.volume.min_microversion, CONF.volume.max_microversion)
67 api_version_utils.check_skip_with_microversion(
68 cls.placement_min_microversion, cls.placement_max_microversion,
69 CONF.placement.min_microversion, CONF.placement.max_microversion)
70
71 @classmethod
72 def resource_setup(cls):
73 super(ScenarioTest, cls).resource_setup()
74 cls.compute_request_microversion = (
75 api_version_utils.select_request_microversion(
76 cls.compute_min_microversion,
77 CONF.compute.min_microversion))
78 cls.volume_request_microversion = (
79 api_version_utils.select_request_microversion(
80 cls.volume_min_microversion,
81 CONF.volume.min_microversion))
82 cls.placement_request_microversion = (
83 api_version_utils.select_request_microversion(
84 cls.placement_min_microversion,
85 CONF.placement.min_microversion))
86
Ghanshyam Mann18b45d72021-12-07 12:37:29 -060087 cls.setup_api_microversion_fixture(
88 compute_microversion=cls.compute_request_microversion,
89 volume_microversion=cls.volume_request_microversion,
90 placement_microversion=cls.placement_request_microversion)
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000091
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053092 def setup_compute_client(cls):
Ghanshyam Mann1072f502021-03-24 19:15:22 -050093 """Compute client"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053094 cls.compute_images_client = cls.os_primary.compute_images_client
95 cls.keypairs_client = cls.os_primary.keypairs_client
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053096 cls.servers_client = cls.os_primary.servers_client
97 cls.interface_client = cls.os_primary.interfaces_client
Ghanshyam Mann1072f502021-03-24 19:15:22 -050098 cls.flavors_client = cls.os_primary.flavors_client
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053099
100 def setup_network_client(cls):
101 """Neutron network client"""
102 cls.networks_client = cls.os_primary.networks_client
103 cls.ports_client = cls.os_primary.ports_client
104 cls.routers_client = cls.os_primary.routers_client
105 cls.subnets_client = cls.os_primary.subnets_client
106 cls.floating_ips_client = cls.os_primary.floating_ips_client
107 cls.security_groups_client = cls.os_primary.security_groups_client
108 cls.security_group_rules_client = (
109 cls.os_primary.security_group_rules_client)
110
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000111 @classmethod
112 def setup_clients(cls):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530113 """This setup the service clients for the tests"""
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000114 super(ScenarioTest, cls).setup_clients()
Jordan Pittier1d2e40f2016-01-05 18:49:14 +0100115 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400116 # Check if glance v1 is available to determine which client to use.
117 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +0800118 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400119 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +0800120 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400121 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400122 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400123 'Either api_v1 or api_v2 must be True in '
124 '[image-feature-enabled].')
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530125
126 cls.setup_compute_client(cls)
127 cls.setup_network_client(cls)
Andrea Frittolia6b30152017-08-04 10:46:10 +0100128 if CONF.service_available.cinder:
129 cls.volumes_client = cls.os_primary.volumes_client_latest
130 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +0300131 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300132
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200133 # ## Test functions library
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200134 # The create_[resource] functions only return body and discard the
135 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +0100136
zhufl1e446b52017-10-16 16:54:57 +0800137 def create_port(self, network_id, client=None, **kwargs):
Martin Kopec9c874412020-12-17 20:43:26 +0000138 """Creates port for the respective network_id
139
140 :param network_id: the id of the network
141 :param client: the client to use, defaults to self.ports_client
142 :param kwargs: additional arguments such as:
143 - namestart - a string to generate a name for the port from
144 - default is self.__class__.__name__
145 - 'binding:vnic_type' - defaults to CONF.network.port_vnic_type
146 - 'binding:profile' - defaults to CONF.network.port_profile
147 """
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000148
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300149 if not client:
150 client = self.ports_client
Martin Kopec9c874412020-12-17 20:43:26 +0000151 name = data_utils.rand_name(
152 kwargs.pop('namestart', self.__class__.__name__))
Edan David408a97b2018-01-15 03:52:15 -0500153 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
154 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
155 if CONF.network.port_profile and 'binding:profile' not in kwargs:
156 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300157 result = client.create_port(
158 name=name,
159 network_id=network_id,
160 **kwargs)
Soniya Vyas0123f522020-09-24 17:43:26 +0530161 self.assertIsNotNone(result, 'Unable to allocate port')
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000162 port_id = result['port']['id']
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300163 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000164 client.delete_port, port_id)
165 port = waiters.wait_for_port_status(
166 client=client, port_id=port_id, status="DOWN")
167 return port["port"]
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300168
Martin Kopec30b4d532020-10-16 12:02:43 +0000169 def create_keypair(self, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530170 """Creates keypair
171
172 Keypair is a public key of OpenSSH key pair used for accessing
173 and create servers
174 Keypair can also be created by a private key for the same purpose
175 Here, the keys are randomly generated[public/private]
176 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300177 if not client:
178 client = self.keypairs_client
Martin Kopec30b4d532020-10-16 12:02:43 +0000179 if not kwargs.get('name'):
180 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100181 # We don't need to create a keypair by pubkey in scenario
Martin Kopec30b4d532020-10-16 12:02:43 +0000182 body = client.create_keypair(**kwargs)
183 self.addCleanup(client.delete_keypair, kwargs['name'])
ghanshyamdee01f22015-08-17 11:41:47 +0900184 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100185
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530186 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800187 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200188 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000189 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100190
lanoux5fc14522015-09-21 08:17:35 +0000191 This wrapper utility calls the common create test server and
192 returns a test server. The purpose of this wrapper is to minimize
193 the impact on the code of the tests already using this
194 function.
Noam Angel6e309952019-01-27 05:52:40 +0000195
196 :param **kwargs:
197 See extra parameters below
198
199 :Keyword Arguments:
200 * *vnic_type* (``string``) --
201 used when launching instances with pre-configured ports.
202 Examples:
203 normal: a traditional virtual port that is either attached
204 to a linux bridge or an openvswitch bridge on a
205 compute node.
206 direct: an SR-IOV port that is directly attached to a VM
207 macvtap: an SR-IOV port that is attached to a VM via a macvtap
208 device.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100209 direct-physical: an SR-IOV port that is directly attached to a
210 VM using physical instead of virtual
211 functions.
212 baremetal: a baremetal port directly attached to a baremetal
213 node.
214 virtio-forwarder: an SR-IOV port that is indirectly attached
215 to a VM using a low-latency vhost-user
216 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000217 Defaults to ``CONF.network.port_vnic_type``.
218 * *port_profile* (``dict``) --
219 This attribute is a dictionary that can be used (with admin
220 credentials) to supply information influencing the binding of
221 the port.
222 example: port_profile = "capabilities:[switchdev]"
223 Defaults to ``CONF.network.port_profile``.
Martin Kopec9c874412020-12-17 20:43:26 +0000224 * *create_port_body* (``dict``) --
225 This attribute is a dictionary of additional arguments to be
226 passed to create_port method.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100227 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100228
lanoux5fc14522015-09-21 08:17:35 +0000229 # NOTE(jlanoux): As a first step, ssh checks in the scenario
230 # tests need to be run regardless of the run_validation and
231 # validatable parameters and thus until the ssh validation job
232 # becomes voting in CI. The test resources management and IP
233 # association are taken care of in the scenario tests.
234 # Therefore, the validatable parameter is set to false in all
235 # those tests. In this way create_server just return a standard
236 # server and the scenario tests always perform ssh checks.
237
238 # Needed for the cross_tenant_traffic test:
239 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800240 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000241
zhufl24208c22016-10-25 15:23:48 +0800242 if name is None:
243 name = data_utils.rand_name(self.__class__.__name__ + "-server")
244
Noam Angel6e309952019-01-27 05:52:40 +0000245 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
246 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000247
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000248 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000249 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000250 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000251 ports = []
Martin Kopec9c874412020-12-17 20:43:26 +0000252 create_port_body = kwargs.pop('create_port_body', {})
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300253
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000254 if vnic_type:
255 create_port_body['binding:vnic_type'] = vnic_type
256
257 if profile:
258 create_port_body['binding:profile'] = profile
259
lanoux5fc14522015-09-21 08:17:35 +0000260 if kwargs:
261 # Convert security group names to security group ids
262 # to pass to create_port
263 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300264 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500265 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000266 ).get('security_groups')
267 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100268 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000269
270 sec_groups_names = [s['name'] for s in kwargs.pop(
271 'security_groups')]
272 security_groups_ids = [sec_dict[s]
273 for s in sec_groups_names]
274
275 if security_groups_ids:
276 create_port_body[
277 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300278 networks = kwargs.pop('networks', [])
279 else:
280 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000281
282 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300283 # for the project's private networks and create a port.
284 # The same behaviour as we would expect when passing
285 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000286 if not networks:
287 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300288 **{'router:external': False, 'fields': 'id'})['networks']
289
290 # It's net['uuid'] if networks come from kwargs
291 # and net['id'] if they come from
292 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000293 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000294 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300295 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800296 port = self.create_port(network_id=net_id,
297 client=clients.ports_client,
298 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300299 ports.append({'port': port['id']})
300 else:
301 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000302 if ports:
303 kwargs['networks'] = ports
304 self.ports = ports
305
306 tenant_network = self.get_tenant_network()
307
Marc Koderer979e4942016-12-08 10:07:59 +0100308 if CONF.compute.compute_volume_common_az:
309 kwargs.setdefault('availability_zone',
310 CONF.compute.compute_volume_common_az)
311
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200312 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000313 clients,
314 tenant_network=tenant_network,
315 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530316 name=name, flavor=flavor,
317 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000318
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200319 self.addCleanup(waiters.wait_for_server_termination,
320 clients.servers_client, body['id'])
321 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
322 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000323 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100324 return server
325
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100326 def create_volume(self, size=None, name=None, snapshot_id=None,
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300327 imageRef=None, volume_type=None, wait_until='available',
328 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530329 """Creates volume
330
331 This wrapper utility creates volume and waits for volume to be
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300332 in 'available' state by default. If wait_until is None, means no wait.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530333 This method returns the volume's full representation by GET request.
334 """
335
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700336 if size is None:
337 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500338 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800339 if CONF.image_feature_enabled.api_v1:
340 resp = self.image_client.check_image(imageRef)
341 image = common_image.get_image_meta_from_headers(resp)
342 else:
343 image = self.image_client.show_image(imageRef)
344 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500345 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100346 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800347 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000348 kwargs.update({'name': name,
349 'snapshot_id': snapshot_id,
350 'imageRef': imageRef,
351 'volume_type': volume_type,
352 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100353
354 if CONF.compute.compute_volume_common_az:
355 kwargs.setdefault('availability_zone',
356 CONF.compute.compute_volume_common_az)
357
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900358 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700359
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100360 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
361 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100362 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100363 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300364 self.assertEqual(name, volume['name'])
Benny Kopilov7beb2d02022-04-12 20:33:53 +0300365 if wait_until:
366 waiters.wait_for_volume_resource_status(self.volumes_client,
367 volume['id'], wait_until)
368 # The volume retrieved on creation has a non-up-to-date status.
369 # Retrieval after it becomes active ensures correct details.
370 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100371 return volume
372
lkuchlane20e6a82018-05-08 11:28:46 +0300373 def create_backup(self, volume_id, name=None, description=None,
374 force=False, snapshot_id=None, incremental=False,
Martin Kopec4a140052020-10-16 16:26:55 +0000375 container=None, **kwargs):
376 """Creates a backup of the given volume_id or snapshot_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530377
Martin Kopec4a140052020-10-16 16:26:55 +0000378 This wrapper utility creates a backup and waits until it is in
379 'available' state.
380
381 :param volume_id: UUID of the volume to back up
382 :param name: backup name, '$classname-backup' by default
383 :param description: Description of the backup, None by default
384 :param force: boolean whether to backup even if the volume is attached
385 False by default
386 :param snapshot_id: UUID of the source snapshot to back up
387 None by default
388 :param incremental: boolean, False by default
389 :param container: a container name, None by default
390 :param **kwargs: additional parameters per the documentation:
391 https://docs.openstack.org/api-ref/block-storage/v3/
392 #create-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530393 """
lkuchlane20e6a82018-05-08 11:28:46 +0300394
395 name = name or data_utils.rand_name(
396 self.__class__.__name__ + "-backup")
Martin Kopec4a140052020-10-16 16:26:55 +0000397 args = {'name': name,
398 'description': description,
399 'force': force,
400 'snapshot_id': snapshot_id,
401 'incremental': incremental,
402 'container': container}
403 args.update(kwargs)
lkuchlane20e6a82018-05-08 11:28:46 +0300404 backup = self.backups_client.create_backup(volume_id=volume_id,
405 **kwargs)['backup']
406 self.addCleanup(self.backups_client.delete_backup, backup['id'])
407 waiters.wait_for_volume_resource_status(self.backups_client,
408 backup['id'], 'available')
409 return backup
410
Martin Kopec4a140052020-10-16 16:26:55 +0000411 def restore_backup(self, backup_id, **kwargs):
412 """Restores a backup given by the backup_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530413
Martin Kopec4a140052020-10-16 16:26:55 +0000414 This wrapper utility restores a backup and waits until it is in
415 'available' state.
416
417 :param backup_id: UUID of a backup to restore
418 :param **kwargs: additional parameters per the documentation:
419 https://docs.openstack.org/api-ref/block-storage/v3/
420 #restore-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530421 """
422
Martin Kopec4a140052020-10-16 16:26:55 +0000423 body = self.backups_client.restore_backup(backup_id, **kwargs)
424 restore = body['restore']
Sofia Enriquez404b55c2022-05-26 19:33:47 +0000425
426 using_pre_existing_volume = kwargs.get('volume_id', False)
427 if not using_pre_existing_volume:
428 self.addCleanup(self.volumes_client.delete_volume,
429 restore['volume_id'])
430
lkuchlane20e6a82018-05-08 11:28:46 +0300431 waiters.wait_for_volume_resource_status(self.backups_client,
432 backup_id, 'available')
433 waiters.wait_for_volume_resource_status(self.volumes_client,
434 restore['volume_id'],
435 'available')
436 self.assertEqual(backup_id, restore['backup_id'])
437 return restore
438
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000439 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
440 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530441 if image is None:
442 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530443 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
444 server_id, image, preserve_ephemeral)
445 self.servers_client.rebuild_server(
446 server_id=server_id,
447 image_ref=image,
448 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000449 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530450 if wait:
451 waiters.wait_for_server_status(self.servers_client,
452 server_id, 'ACTIVE')
453
lkuchlan73ed1f32017-07-06 16:22:12 +0300454 def create_volume_snapshot(self, volume_id, name=None, description=None,
Martin Kopeca17cca42020-10-17 16:57:51 +0000455 metadata=None, force=False, **kwargs):
456 """Creates volume's snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530457
Martin Kopeca17cca42020-10-17 16:57:51 +0000458 This wrapper utility creates volume snapshot and waits for it until
459 it is in 'available' state.
460
461 :param volume_id: UUID of a volume to create snapshot of
462 :param name: name of the snapshot, '$classname-snapshot' by default
463 :param description: description of the snapshot
464 :param metadata: metadata key and value pairs for the snapshot
465 :param force: whether snapshot even when the volume is attached
466 :param **kwargs: additional parameters per the doc
467 https://docs.openstack.org/api-ref/block-storage/v3/
468 #create-a-snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530469 """
470
lkuchlan73ed1f32017-07-06 16:22:12 +0300471 name = name or data_utils.rand_name(
472 self.__class__.__name__ + '-snapshot')
473 snapshot = self.snapshots_client.create_snapshot(
474 volume_id=volume_id,
475 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000476 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300477 description=description,
Martin Kopeca17cca42020-10-17 16:57:51 +0000478 metadata=metadata,
479 **kwargs)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530480
lkuchlan73ed1f32017-07-06 16:22:12 +0300481 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
482 snapshot['id'])
Benny Kopilovd4d49b02021-08-10 18:54:01 +0300483 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
484 self.snapshots_client.delete_snapshot, snapshot['id'])
lkuchlan73ed1f32017-07-06 16:22:12 +0300485 waiters.wait_for_volume_resource_status(self.snapshots_client,
486 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200487 snapshot = self.snapshots_client.show_snapshot(
488 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300489 return snapshot
490
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530491 def cleanup_volume_type(self, volume_type):
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100492 """Clean up a given volume type.
493
494 Ensuring all volumes associated to a type are first removed before
495 attempting to remove the type itself. This includes any image volume
496 cache volumes stored in a separate tenant to the original volumes
497 created from the type.
498 """
499 admin_volume_type_client = self.os_admin.volume_types_client_latest
500 admin_volumes_client = self.os_admin.volumes_client_latest
501 volumes = admin_volumes_client.list_volumes(
502 detail=True, params={'all_tenants': 1})['volumes']
503 type_name = volume_type['name']
504 for volume in [v for v in volumes if v['volume_type'] == type_name]:
505 test_utils.call_and_ignore_notfound_exc(
506 admin_volumes_client.delete_volume, volume['id'])
507 admin_volumes_client.wait_for_resource_deletion(volume['id'])
508 admin_volume_type_client.delete_volume_type(volume_type['id'])
509
Martin Kopec8e673a42020-10-18 17:33:02 +0000510 def create_volume_type(self, client=None, name=None, backend_name=None,
511 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530512 """Creates volume type
513
514 In a multiple-storage back-end configuration,
515 each back end has a name (volume_backend_name).
516 The name of the back end is declared as an extra-specification
517 of a volume type (such as, volume_backend_name=LVM).
518 When a volume is created, the scheduler chooses an
519 appropriate back end to handle the request, according
520 to the volume type specified by the user.
521 The scheduler uses volume types to explicitly create volumes on
522 specific back ends.
523
524 Before using volume type, a volume type has to be declared
525 to Block Storage. In addition to that, an extra-specification
526 has to be created to link the volume type to a back end name.
527 """
528
scottda61f68ac2016-06-07 12:07:55 -0600529 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000530 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000531 if not name:
532 class_name = self.__class__.__name__
533 name = data_utils.rand_name(class_name + '-volume-type')
534 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600535
536 LOG.debug("Creating a volume type: %s on backend %s",
537 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000538 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600539 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000540 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600541
Martin Kopec8e673a42020-10-18 17:33:02 +0000542 volume_type_resp = client.create_volume_type(
543 name=randomized_name, extra_specs=extra_specs, **kwargs)
544 volume_type = volume_type_resp['volume_type']
545
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530546 self.assertIn('id', volume_type)
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530547 self.addCleanup(self.cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600548 return volume_type
549
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500550 def create_security_group(self, security_group_rules_client=None,
551 project_id=None,
552 namestart='secgroup-smoke',
553 security_groups_client=None):
554 if security_group_rules_client is None:
555 security_group_rules_client = self.security_group_rules_client
556 if security_groups_client is None:
557 security_groups_client = self.security_groups_client
558 if project_id is None:
559 project_id = security_groups_client.project_id
560 secgroup = self.create_empty_security_group(
561 namestart=namestart, client=security_groups_client,
562 project_id=project_id)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100563
564 # Add rules to the security group
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500565 rules = self.create_loginable_secgroup_rule(
566 security_group_rules_client=security_group_rules_client,
567 secgroup=secgroup,
568 security_groups_client=security_groups_client)
569 for rule in rules:
570 self.assertEqual(project_id, rule['project_id'])
571 self.assertEqual(secgroup['id'], rule['security_group_id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100572 return secgroup
573
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500574 def create_empty_security_group(self, client=None, project_id=None,
575 namestart='secgroup-smoke'):
576 """Create a security group without rules.
577
578 Default rules will be created:
579 - IPv4 egress to any
580 - IPv6 egress to any
581 :param project_id: secgroup will be created in this project
582 :returns: the created security group
583 """
584
585 if client is None:
586 client = self.security_groups_client
587 if not project_id:
588 project_id = client.project_id
589 sg_name = data_utils.rand_name(namestart)
590 sg_desc = sg_name + " description"
591 sg_dict = dict(name=sg_name,
592 description=sg_desc)
593 sg_dict['project_id'] = project_id
594 result = client.create_security_group(**sg_dict)
595
596 secgroup = result['security_group']
597 self.assertEqual(secgroup['name'], sg_name)
598 self.assertEqual(project_id, secgroup['project_id'])
599 self.assertEqual(secgroup['description'], sg_desc)
600
601 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
602 client.delete_security_group, secgroup['id'])
603 return secgroup
604
605 def create_security_group_rule(self, secgroup=None,
606 sec_group_rules_client=None,
607 project_id=None,
608 security_groups_client=None, **kwargs):
609 """Create a rule from a dictionary of rule parameters.
610
611 Create a rule in a secgroup. if secgroup not defined will search for
612 default secgroup in project_id.
613 :param secgroup: the security group.
614 :param project_id: if secgroup not passed -- the tenant in which to
615 search for default secgroup
616 :param kwargs: a dictionary containing rule parameters:
617 for example, to allow incoming ssh:
618 rule = {
619 direction: 'ingress'
620 protocol:'tcp',
621 port_range_min: 22,
622 port_range_max: 22
623 }
624 """
625
626 if sec_group_rules_client is None:
627 sec_group_rules_client = self.security_group_rules_client
628 if security_groups_client is None:
629 security_groups_client = self.security_groups_client
630 if not project_id:
631 project_id = security_groups_client.project_id
632 if secgroup is None:
633 # Get default secgroup for project_id
634 default_secgroups = security_groups_client.list_security_groups(
635 name='default', project_id=project_id)['security_groups']
636 msg = "No default security group for project %s." % (project_id)
637 self.assertNotEmpty(default_secgroups, msg)
638 secgroup = default_secgroups[0]
639
640 ruleset = dict(security_group_id=secgroup['id'],
641 project_id=secgroup['project_id'])
642 ruleset.update(kwargs)
643
644 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
645 sg_rule = sg_rule['security_group_rule']
646
647 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
648 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
649
650 return sg_rule
651
652 def create_loginable_secgroup_rule(self, security_group_rules_client=None,
653 secgroup=None,
Roman Popelka3b0ccb02022-03-24 10:25:19 +0100654 security_groups_client=None,
655 rulesets=None):
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500656 """Create loginable security group rule by neutron clients by default.
657
658 This function will create:
659 1. egress and ingress tcp port 22 allow rule in order to allow ssh
660 access for ipv4.
661 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
662 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
663 """
664
665 if security_group_rules_client is None:
666 security_group_rules_client = self.security_group_rules_client
667 if security_groups_client is None:
668 security_groups_client = self.security_groups_client
Roman Popelka3b0ccb02022-03-24 10:25:19 +0100669 if rulesets is None:
670 rulesets = [
671 dict(
672 # ssh
673 protocol='tcp',
674 port_range_min=22,
675 port_range_max=22,
676 ),
677 dict(
678 # ping
679 protocol='icmp',
680 ),
681 dict(
682 # ipv6-icmp for ping6
683 protocol='icmp',
684 ethertype='IPv6',
685 )
686 ]
687
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500688 rules = []
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500689 sec_group_rules_client = security_group_rules_client
690 for ruleset in rulesets:
691 for r_direction in ['ingress', 'egress']:
692 ruleset['direction'] = r_direction
693 try:
694 sg_rule = self.create_security_group_rule(
695 sec_group_rules_client=sec_group_rules_client,
696 secgroup=secgroup,
697 security_groups_client=security_groups_client,
698 **ruleset)
699 except lib_exc.Conflict as ex:
700 # if rule already exist - skip rule and continue
701 msg = 'Security group rule already exists'
702 if msg not in ex._error_string:
703 raise ex
704 else:
705 self.assertEqual(r_direction, sg_rule['direction'])
706 rules.append(sg_rule)
707
708 return rules
709
zhuflf52c7592017-05-25 13:55:24 +0800710 def get_remote_client(self, ip_address, username=None, private_key=None,
711 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100712 """Get a SSH client to a remote server
713
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600714 :param ip_address: the server floating or fixed IP address to use
715 for ssh validation
716 :param username: name of the Linux account on the remote server
717 :param private_key: the SSH private key to use
718 :param server: server dict, used for debugging purposes
719 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100720 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700721
Andrea Frittoli247058f2014-07-16 16:09:22 +0100722 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800723 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800724 # Set this with 'keypair' or others to log in with keypair or
725 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000726 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800727 password = None
728 if private_key is None:
729 private_key = self.keypair['private_key']
730 else:
lanoux283273b2015-12-04 03:01:54 -0800731 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800732 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800733 linux_client = remote_client.RemoteClient(
734 ip_address, username, pkey=private_key, password=password,
735 server=server, servers_client=self.servers_client)
736 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100737 return linux_client
738
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000739 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000740 img_path = CONF.scenario.img_file
741 if not os.path.exists(img_path):
Martin Kopec008950e2020-09-29 08:12:39 +0000742 lib_exc.InvalidConfiguration(
Martin Kopec02af6a42020-03-03 12:39:12 +0000743 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
744 'a full path for the image. CONF.scenario.img_dir was '
745 'deprecated and will be removed in the next release. Till '
Martin Kopec008950e2020-09-29 08:12:39 +0000746 'Tempest 25.0.0, old behavior was maintained and kept working '
Martin Kopec02af6a42020-03-03 12:39:12 +0000747 'but starting Tempest 26.0.0, you need to specify the full '
748 'path in CONF.scenario.img_file config option.')
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300749 img_container_format = CONF.scenario.img_container_format
750 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000751 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400752 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000753 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100754 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000755 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530756 if img_properties is None:
757 img_properties = {}
758 name = data_utils.rand_name('%s-' % name)
759 params = {
760 'name': name,
761 'container_format': img_container_format,
762 'disk_format': img_disk_format or img_container_format,
763 }
764 if CONF.image_feature_enabled.api_v1:
765 params['is_public'] = 'False'
766 if img_properties:
767 params['properties'] = img_properties
768 params = {'headers': common_image.image_meta_to_headers(**params)}
769 else:
770 params['visibility'] = 'private'
771 # Additional properties are flattened out in the v2 API.
772 if img_properties:
773 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000774 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530775 body = self.image_client.create_image(**params)
776 image = body['image'] if 'image' in body else body
777 self.addCleanup(self.image_client.delete_image, image['id'])
778 self.assertEqual("queued", image['status'])
779 with open(img_path, 'rb') as image_file:
780 if CONF.image_feature_enabled.api_v1:
781 self.image_client.update_image(image['id'], data=image_file)
782 else:
783 self.image_client.store_image_file(image['id'], image_file)
784 LOG.debug("image:%s", image['id'])
785 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100786
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530787 def log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530788 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400789 if not CONF.compute_feature_enabled.console_output:
790 LOG.debug('Console output not supported, cannot log')
791 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700792 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100793 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700794 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100795 servers = servers['servers']
796 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100797 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700798 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000799 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100800 LOG.debug('Console output for %s\nbody=\n%s',
801 server['id'], console_output)
802 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100803 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100804 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100805
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000806 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530807 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300808 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000809 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000810
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000811 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530812 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000813 # Glance client
814 _image_client = self.image_client
815 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900816 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000817 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800818 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000819 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000820 image = _images_client.create_image(server['id'], name=name, **kwargs)
Benny Kopilov7d2edc22022-06-30 17:22:14 +0300821 # microversion 2.45 and above returns image_id
822 image_id = image.get('image_id') or image.response['location'].split(
823 'images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300824 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200825
826 self.addCleanup(_image_client.wait_for_resource_deletion,
827 image_id)
828 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
829 _image_client.delete_image, image_id)
830
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400831 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530832 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700833 resp = _image_client.check_image(image_id)
834 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400835 image_props = snapshot_image.get('properties', {})
836 else:
837 # In glance v2 the additional properties are flattened.
838 snapshot_image = _image_client.show_image(image_id)
839 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300840
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400841 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300842 if bdm:
843 bdm = json.loads(bdm)
844 if bdm and 'snapshot_id' in bdm[0]:
845 snapshot_id = bdm[0]['snapshot_id']
846 self.addCleanup(
847 self.snapshots_client.wait_for_resource_deletion,
848 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100849 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
850 self.snapshots_client.delete_snapshot,
851 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200852 waiters.wait_for_volume_resource_status(self.snapshots_client,
853 snapshot_id,
854 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000855 image_name = snapshot_image['name']
856 self.assertEqual(name, image_name)
857 LOG.debug("Created snapshot image %s for server %s",
858 image_name, server['name'])
859 return snapshot_image
860
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000861 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530862 """Compute volume attach
863
864 This utility attaches volume from compute and waits for the
865 volume status to be 'in-use' state.
866 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000867 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000868 server['id'], volumeId=volume_to_attach['id'],
869 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200870 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200871 waiters.wait_for_volume_resource_status(self.volumes_client,
872 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000873 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
874 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200875 # Return the updated volume after the attachment
876 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900877
Jordan Pittier7cf64762015-10-14 15:01:12 +0200878 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530879 """Compute volume detach
880
Lee Yarwood5423c532020-12-17 11:24:46 +0000881 This utility detaches the volume from the server and checks whether the
882 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530883 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200884 self.servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000885 waiters.wait_for_volume_attachment_remove_from_server(
886 self.servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200887
Steven Hardyda2a8352014-10-02 12:52:20 +0100888 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800889 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530890 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000891 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000892 cmd = ['ping', '-c1', '-w1']
893
894 if mtu:
895 cmd += [
896 # don't fragment
897 '-M', 'do',
898 # ping receives just the size of ICMP payload
899 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
900 ]
901 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700902
903 def ping():
904 proc = subprocess.Popen(cmd,
905 stdout=subprocess.PIPE,
906 stderr=subprocess.PIPE)
907 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000908
Aaron Rosena7df13b2014-09-23 09:45:45 -0700909 return (proc.returncode == 0) == should_succeed
910
Jordan Pittier9e227c52016-02-09 14:35:18 +0100911 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000912 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800913 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000914 'caller': caller, 'ip': ip_address, 'timeout': timeout,
915 'should_succeed':
916 'reachable' if should_succeed else 'unreachable'
917 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200918 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000919 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800920 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000921 'caller': caller, 'ip': ip_address, 'timeout': timeout,
922 'result': 'expected' if result else 'unexpected'
923 })
zhufl0ec74c42017-11-15 14:02:28 +0800924 if server:
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530925 self.log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000926 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700927
Yair Friedae0e73d2014-11-24 11:56:26 +0200928 def check_vm_connectivity(self, ip_address,
929 username=None,
930 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000931 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800932 extra_msg="",
933 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000934 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000935 """Check server connectivity
936
Yair Friedae0e73d2014-11-24 11:56:26 +0200937 :param ip_address: server to test against
938 :param username: server's ssh username
939 :param private_key: server's ssh private key to be used
940 :param should_connect: True/False indicates positive/negative test
941 positive - attempt ping and ssh
942 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800943 :param extra_msg: Message to help with debugging if ``ping_ip_address``
944 fails
945 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000946 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200947
948 :raises: AssertError if the result of the connectivity check does
949 not match the value of the should_connect param
950 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530951
zhufl0ec74c42017-11-15 14:02:28 +0800952 LOG.debug('checking network connections to IP %s with user: %s',
953 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200954 if should_connect:
955 msg = "Timed out waiting for %s to become reachable" % ip_address
956 else:
957 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800958 if extra_msg:
959 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200960 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000961 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800962 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200963 msg=msg)
964 if should_connect:
965 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800966 try:
967 self.get_remote_client(ip_address, username, private_key,
968 server=server)
969 except Exception:
970 if not extra_msg:
971 extra_msg = 'Failed to ssh to %s' % ip_address
972 LOG.exception(extra_msg)
973 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200974
Ghanshyam Mann64281392021-03-24 18:48:38 -0500975 def get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
Yair Friedae0e73d2014-11-24 11:56:26 +0200976
Ghanshyam Mann64281392021-03-24 18:48:38 -0500977 if ip_addr and not kwargs.get('fixed_ips'):
978 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
979 ports = self.os_admin.ports_client.list_ports(
980 device_id=server['id'], **kwargs)['ports']
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000981
Ghanshyam Mann64281392021-03-24 18:48:38 -0500982 # A port can have more than one IP address in some cases.
983 # If the network is dual-stack (IPv4 + IPv6), this port is associated
984 # with 2 subnets
985
986 def _is_active(port):
987 # NOTE(vsaienko) With Ironic, instances live on separate hardware
988 # servers. Neutron does not bind ports for Ironic instances, as a
989 # result the port remains in the DOWN state. This has been fixed
990 # with the introduction of the networking-baremetal plugin but
991 # it's not mandatory (and is not used on all stable branches).
992 return (port['status'] == 'ACTIVE' or
993 port.get('binding:vnic_type') == 'baremetal')
994
995 port_map = [(p["id"], fxip["ip_address"])
996 for p in ports
997 for fxip in p["fixed_ips"]
998 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
999 _is_active(p))]
1000 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1001 if inactive:
1002 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
1003
1004 self.assertNotEmpty(port_map,
1005 "No IPv4 addresses found in: %s" % ports)
1006 self.assertEqual(len(port_map), 1,
1007 "Found multiple IPv4 addresses: %s. "
1008 "Unable to determine which port to target."
1009 % port_map)
1010 return port_map[0]
1011
1012 def create_floating_ip(self, server, external_network_id=None,
1013 port_id=None, client=None, **kwargs):
1014 """Create a floating IP and associates to a resource/port on Neutron"""
1015
1016 if not external_network_id:
1017 external_network_id = CONF.network.public_network_id
1018 if not client:
1019 client = self.floating_ips_client
1020 if not port_id:
1021 port_id, ip4 = self.get_server_port_id_and_ip4(server)
1022 else:
1023 ip4 = None
1024
1025 floatingip_kwargs = {
1026 'floating_network_id': external_network_id,
1027 'port_id': port_id,
1028 'tenant_id': server.get('project_id') or server['tenant_id'],
1029 'fixed_ip_address': ip4,
1030 }
1031 if CONF.network.subnet_id:
1032 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1033
1034 floatingip_kwargs.update(kwargs)
1035 result = client.create_floatingip(**floatingip_kwargs)
1036 floating_ip = result['floatingip']
1037
Jordan Pittier9e227c52016-02-09 14:35:18 +01001038 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Ghanshyam Mann64281392021-03-24 18:48:38 -05001039 client.delete_floatingip,
Yair Friedae0e73d2014-11-24 11:56:26 +02001040 floating_ip['id'])
Ghanshyam Mann64281392021-03-24 18:48:38 -05001041 return floating_ip
1042
1043 def associate_floating_ip(self, floating_ip, server):
1044 """Associate floating ip to server
1045
1046 This wrapper utility attaches the floating_ip for
1047 the respective port_id of server
1048 """
1049 port_id, _ = self.get_server_port_id_and_ip4(server)
1050 kwargs = dict(port_id=port_id)
1051 floating_ip = self.floating_ips_client.update_floatingip(
1052 floating_ip['id'], **kwargs)['floatingip']
1053 self.assertEqual(port_id, floating_ip['port_id'])
1054 return floating_ip
1055
1056 def disassociate_floating_ip(self, floating_ip):
1057 """Disassociates floating ip
1058
1059 This wrapper utility disassociates given floating ip.
1060 :param floating_ip: a dict which is a return value of
1061 floating_ips_client.create_floatingip method
1062 """
1063 kwargs = dict(port_id=None)
1064 floating_ip = self.floating_ips_client.update_floatingip(
1065 floating_ip['id'], **kwargs)['floatingip']
1066 self.assertIsNone(floating_ip['port_id'])
Yair Friedae0e73d2014-11-24 11:56:26 +02001067 return floating_ip
1068
Sean Dague20e98612016-01-06 14:33:28 -05001069 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +00001070 private_key=None, server=None, username=None,
1071 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301072 """Creates timestamp
1073
1074 This wrapper utility does ssh, creates timestamp and returns the
1075 created timestamp.
1076 """
Sean Dague20e98612016-01-06 14:33:28 -05001077 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +02001078 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +00001079 server=server,
1080 username=username)
1081
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001082 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +00001083 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001084 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
1085 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001086 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
1087 ssh_client.exec_command(cmd_timestamp)
1088 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
1089 % mount_path)
1090 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001091 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001092 return timestamp
1093
Sean Dague20e98612016-01-06 14:33:28 -05001094 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001095 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301096 """Returns timestamp
1097
1098 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001099
1100 :param ip_address: The floating IP or fixed IP of the remote server
1101 :param dev_name: Name of the device that stores the timestamp
1102 :param mount_path: Path which should be used as mount point for
1103 dev_name
1104 :param private_key: The SSH private key to use for authentication
1105 :param server: Server dict, used for debugging purposes
1106 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301107 """
1108
Sean Dague20e98612016-01-06 14:33:28 -05001109 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +02001110 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001111 server=server,
1112 username=username)
1113
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001114 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -07001115 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001116 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
1117 % mount_path)
1118 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001119 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001120 return timestamp
1121
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001122 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -05001123 """Get the server fixed or floating IP.
1124
1125 Based on the configuration we're in, return a correct ip
1126 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001127
1128 If CONF.validation.connect_method is floating, then
1129 a floating ip will be created passing kwargs as additional
1130 argument.
Sean Dague20e98612016-01-06 14:33:28 -05001131 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301132
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001133 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -05001134 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +08001135 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -05001136 # method is creating the floating IP there.
Ghanshyam Mann64281392021-03-24 18:48:38 -05001137 return self.create_floating_ip(
1138 server, **kwargs)['floating_ip_address']
Sean Dague20e98612016-01-06 14:33:28 -05001139 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -04001140 # Determine the network name to look for based on config or creds
1141 # provider network resources.
1142 if CONF.validation.network_for_ssh:
1143 addresses = server['addresses'][
1144 CONF.validation.network_for_ssh]
1145 else:
zhufl7b4a7202017-09-28 10:29:27 +08001146 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -04001147 addresses = (server['addresses'][network['name']]
1148 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -05001149 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001150 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
1151 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -05001152 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +08001153 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001154 else:
Matthew Treinish4217a702016-10-07 17:27:11 -04001155 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001156
zhufl7bc916d2018-08-22 14:47:39 +08001157 @classmethod
1158 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301159 """Gets host of server"""
1160
zhufl7bc916d2018-08-22 14:47:39 +08001161 server_details = cls.os_admin.servers_client.show_server(server_id)
1162 return server_details['server']['OS-EXT-SRV-ATTR:host']
1163
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001164 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
1165 bd_map_v2 = [{
1166 'uuid': source_id,
1167 'source_type': source_type,
1168 'destination_type': 'volume',
1169 'boot_index': 0,
1170 'delete_on_termination': delete_on_termination}]
1171 return {'block_device_mapping_v2': bd_map_v2}
1172
1173 def boot_instance_from_resource(self, source_id,
1174 source_type,
1175 keypair=None,
1176 security_group=None,
1177 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +00001178 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301179 """Boot instance from resource
1180
1181 This wrapper utility boots instance from resource with block device
1182 mapping with source info passed in arguments
1183 """
1184
Martin Kopecbee673e2020-11-04 09:40:52 +00001185 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001186 if keypair:
1187 create_kwargs['key_name'] = keypair['name']
1188 if security_group:
1189 create_kwargs['security_groups'] = [
1190 {'name': security_group['name']}]
1191 create_kwargs.update(self._get_bdm(
1192 source_id,
1193 source_type,
1194 delete_on_termination=delete_on_termination))
1195 if name:
1196 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +00001197 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001198
Martin Kopecbee673e2020-11-04 09:40:52 +00001199 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001200
Martin Kopec0216b372020-11-04 09:32:05 +00001201 def create_volume_from_image(self, **kwargs):
1202 """Create volume from image.
1203
1204 :param image_id: ID of the image to create volume from,
1205 CONF.compute.image_ref by default
1206 :param name: name of the volume,
1207 '$classname-volume-origin' by default
1208 :param **kwargs: additional parameters
1209 """
1210 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
1211 name = kwargs.pop('name', None)
1212 if not name:
1213 namestart = self.__class__.__name__ + '-volume-origin'
1214 name = data_utils.rand_name(namestart)
1215 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001216
Andrea Frittoli2e733b52014-07-16 14:12:11 +01001217
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001218class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001219 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001220
Yair Fried1fc32a12014-08-04 09:11:30 +03001221 This class provide helpers for network scenario tests, using the neutron
1222 API. Helpers from ancestor which use the nova network API are overridden
1223 with the neutron API.
1224
1225 This Class also enforces using Neutron instead of novanetwork.
1226 Subclassed tests will be skipped if Neutron is not enabled
1227
1228 """
1229
1230 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001231 def skip_checks(cls):
1232 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001233 if not CONF.service_available.neutron:
1234 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001235
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301236 def create_network(self, networks_client=None,
1237 project_id=None,
1238 namestart='network-smoke-',
1239 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001240 if not networks_client:
1241 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001242 if not project_id:
1243 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001244 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001245 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001246 if net_dict:
1247 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001248 # Neutron disables port security by default so we have to check the
1249 # config before trying to create the network with port_security_enabled
1250 if CONF.network_feature_enabled.port_security:
1251 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001252 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001253 network = result['network']
1254
1255 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001256 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001257 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001258 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001259 return network
1260
zhufl5b0a52f2017-10-24 15:48:20 +08001261 def create_subnet(self, network, subnets_client=None,
1262 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001263 """Create a subnet for the given network
1264
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301265 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001266 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301267
1268 :param **kwargs:
1269 See extra parameters below
1270
1271 :Keyword Arguments:
1272
1273 * *ip_version = ip version of the given network,
Soniya Vyas795ef252020-12-10 19:07:23 +05301274 use_default_subnetpool = default subnetpool to
1275 manage IPv6 addresses range.
Yair Fried1fc32a12014-08-04 09:11:30 +03001276 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301277
John Warren3961acd2015-10-02 14:38:53 -04001278 if not subnets_client:
1279 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001280
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001281 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001282 """Check cidr existence
1283
yangjianfeng4ad346e2020-11-22 06:49:19 +00001284 :returns: True if subnet with cidr already exist in tenant or
1285 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001286 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001287 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001288 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001289 external_nets = self.os_admin.networks_client.list_networks(
1290 **{"router:external": True})['networks']
1291 external_subnets = []
1292 for ext_net in external_nets:
1293 external_subnets.extend(
1294 self.os_admin.subnets_client.list_subnets(
1295 network_id=ext_net['id'], cidr=cidr)['subnets'])
1296 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001297
Soniya Vyas795ef252020-12-10 19:07:23 +05301298 def _make_create_subnet_request(namestart, network,
1299 ip_version, subnets_client, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001300
1301 subnet = dict(
1302 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001303 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001304 project_id=network['project_id'],
Kirill Shileev14113572014-11-21 16:58:02 +03001305 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001306 **kwargs
1307 )
Soniya Vyas795ef252020-12-10 19:07:23 +05301308
1309 if ip_version == 6:
1310 subnet['ipv6_address_mode'] = 'slaac'
1311 subnet['ipv6_ra_mode'] = 'slaac'
1312
Yair Fried1fc32a12014-08-04 09:11:30 +03001313 try:
Soniya Vyas795ef252020-12-10 19:07:23 +05301314 return subnets_client.create_subnet(**subnet)
Masayuki Igawad9388762015-01-20 14:56:42 +09001315 except lib_exc.Conflict as e:
Soniya Vyas795ef252020-12-10 19:07:23 +05301316 if 'overlaps with another subnet' not in str(e):
Yair Fried1fc32a12014-08-04 09:11:30 +03001317 raise
Soniya Vyas795ef252020-12-10 19:07:23 +05301318
1319 result = None
1320 str_cidr = None
1321
1322 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
1323 ip_version = kwargs.pop('ip_version', 4)
1324
1325 if not use_default_subnetpool:
1326
1327 if ip_version == 6:
1328 tenant_cidr = netaddr.IPNetwork(
1329 CONF.network.project_network_v6_cidr)
1330 num_bits = CONF.network.project_network_v6_mask_bits
1331 else:
1332 tenant_cidr = netaddr.IPNetwork(
1333 CONF.network.project_network_cidr)
1334 num_bits = CONF.network.project_network_mask_bits
1335
1336 # Repeatedly attempt subnet creation with sequential cidr
1337 # blocks until an unallocated block is found.
1338 for subnet_cidr in tenant_cidr.subnet(num_bits):
1339 str_cidr = str(subnet_cidr)
1340 if cidr_in_use(str_cidr, project_id=network['project_id']):
1341 continue
1342 result = _make_create_subnet_request(
1343 namestart, network, ip_version, subnets_client,
1344 cidr=str_cidr, **kwargs)
1345
1346 if result is not None:
1347 break
1348
1349 else:
1350 result = _make_create_subnet_request(
1351 namestart, network, ip_version, subnets_client,
1352 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +03001353 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001354
1355 subnet = result['subnet']
Soniya Vyas795ef252020-12-10 19:07:23 +05301356 if str_cidr is not None:
1357 self.assertEqual(subnet['cidr'], str_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -05001358
1359 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1360 subnets_client.delete_subnet, subnet['id'])
1361
Yair Fried1fc32a12014-08-04 09:11:30 +03001362 return subnet
1363
Soniya Vyasc37410f2021-02-24 15:26:27 +05301364 def get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001365 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001366 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001367 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001368 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001369 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001370
Yair Fried45f92952014-06-26 05:19:19 +03001371 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001372 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001373
Steve Heyman33735f22016-05-24 09:28:08 -05001374 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001375 :param status: target status
1376 :raises: AssertionError if status doesn't match
1377 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301378
Steve Heyman33735f22016-05-24 09:28:08 -05001379 floatingip_id = floating_ip['id']
1380
Carl Baldwina754e2d2014-10-23 22:47:41 +00001381 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001382 floating_ip = (self.floating_ips_client.
1383 show_floatingip(floatingip_id)['floatingip'])
1384 if status == floating_ip['status']:
1385 LOG.info("FloatingIP: {fp} is at status: {st}"
1386 .format(fp=floating_ip, st=status))
1387 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001388
zhufl4dda94e2017-03-14 16:14:46 +08001389 if not test_utils.call_until_true(refresh,
1390 CONF.network.build_timeout,
1391 CONF.network.build_interval):
1392 floating_ip = self.floating_ips_client.show_floatingip(
1393 floatingip_id)['floatingip']
1394 self.assertEqual(status, floating_ip['status'],
1395 message="FloatingIP: {fp} is at status: {cst}. "
1396 "failed to reach status: {st}"
1397 .format(fp=floating_ip, cst=floating_ip['status'],
1398 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001399
zhufl420a0192017-09-28 11:04:50 +08001400 def check_tenant_network_connectivity(self, server,
1401 username,
1402 private_key,
1403 should_connect=True,
1404 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301405 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001406 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001407 msg = 'Tenant networks not configured to be reachable.'
1408 LOG.info(msg)
1409 return
1410 # The target login is assumed to have been configured for
1411 # key-based authentication by cloud-init.
1412 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001413 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001414 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001415 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001416 username,
1417 private_key,
1418 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001419 except Exception as e:
1420 LOG.exception('Tenant network connectivity check failed')
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301421 self.log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001422 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001423 raise
1424
zhufle9877c62017-10-13 09:38:19 +08001425 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001426 nic=None, protocol='icmp'):
1427 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001428
Claudiu Belu33c3e602014-08-28 16:38:01 +03001429 :param source: RemoteClient: an ssh connection from which to execute
1430 the check
1431 :param dest: an IP to check connectivity against
1432 :param should_succeed: boolean should connection succeed or not
1433 :param nic: specific network interface to test connectivity from
1434 :param protocol: the protocol used to test connectivity with.
1435 :returns: True, if the connection succeeded and it was expected to
1436 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001437 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301438
Claudiu Belu33c3e602014-08-28 16:38:01 +03001439 method_name = '%s_check' % protocol
1440 connectivity_checker = getattr(source, method_name)
1441
1442 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001443 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001444 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001445 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001446 LOG.warning('Failed to check %(protocol)s connectivity for '
1447 'IP %(dest)s via a ssh connection from: %(src)s.',
1448 dict(protocol=protocol, dest=dest,
1449 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001450 return not should_succeed
1451 return should_succeed
1452
Claudiu Belu33c3e602014-08-28 16:38:01 +03001453 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001454 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001455 if result:
1456 return
1457
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001458 source_host = source.ssh_client.host
1459 if should_succeed:
1460 msg = "Timed out waiting for %s to become reachable from %s" \
1461 % (dest, source_host)
1462 else:
1463 msg = "%s is reachable from %s" % (dest, source_host)
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301464 self.log_console_output()
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001465 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001466
Soniya Vyas73555df2021-03-04 19:05:42 +05301467 def get_router(self, client=None, project_id=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001468 """Retrieve a router for the given tenant id.
1469
1470 If a public router has been configured, it will be returned.
1471
1472 If a public router has not been configured, but a public
1473 network has, a tenant router will be created and returned that
1474 routes traffic to the public network.
1475 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301476
Yair Frieddb6c9e92014-08-06 08:53:13 +03001477 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001478 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001479 if not project_id:
1480 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001481 router_id = CONF.network.public_router_id
1482 network_id = CONF.network.public_network_id
1483 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001484 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001485 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001486 elif network_id:
Martin Kopec0090a102020-11-03 13:50:19 +00001487 name = kwargs.pop('name', None)
1488 if not name:
1489 namestart = self.__class__.__name__ + '-router'
1490 name = data_utils.rand_name(namestart)
1491
1492 ext_gw_info = kwargs.pop('external_gateway_info', None)
1493 if not ext_gw_info:
1494 ext_gw_info = dict(network_id=network_id)
zhufl3484f992017-10-10 16:18:29 +08001495 router = client.create_router(
Martin Kopec0090a102020-11-03 13:50:19 +00001496 name=name,
1497 admin_state_up=kwargs.get('admin_state_up', True),
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001498 project_id=project_id,
Martin Kopec0090a102020-11-03 13:50:19 +00001499 external_gateway_info=ext_gw_info,
1500 **kwargs)['router']
zhufl3484f992017-10-10 16:18:29 +08001501 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1502 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001503 return router
1504 else:
1505 raise Exception("Neither of 'public_router_id' or "
1506 "'public_network_id' has been defined.")
1507
Ghanshyam Mann071d1542021-03-24 19:10:47 -05001508 def setup_network_subnet_with_router(
1509 self, networks_client=None,
1510 routers_client=None, subnets_client=None,
1511 project_id=None, dns_nameservers=None,
1512 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001513 """Create a network with a subnet connected to a router.
1514
David Shrewsbury9bac3662014-08-07 15:07:01 -04001515 The baremetal driver is a special case since all nodes are
1516 on the same shared network.
1517
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001518 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001519 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001520 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001521 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001522 a form like this: {'provider:network_type': 'vlan',
1523 'provider:physical_network': 'foo',
1524 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001525 :returns: network, subnet, router
1526 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301527
Thiago Paiva66cded22016-08-15 14:55:58 -03001528 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001529 # NOTE(Shrews): This exception is for environments where tenant
1530 # credential isolation is available, but network separation is
1531 # not (the current baremetal case). Likely can be removed when
1532 # test account mgmt is reworked:
1533 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001534 if not CONF.compute.fixed_network_name:
1535 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001536 raise lib_exc.InvalidConfiguration(m)
Soniya Vyasc37410f2021-02-24 15:26:27 +05301537 network = self.get_network_by_name(
David Shrewsbury9bac3662014-08-07 15:07:01 -04001538 CONF.compute.fixed_network_name)
1539 router = None
1540 subnet = None
1541 else:
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301542 network = self.create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001543 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001544 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001545 port_security_enabled=port_security_enabled,
1546 **net_dict)
Soniya Vyas73555df2021-03-04 19:05:42 +05301547 router = self.get_router(client=routers_client,
1548 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001549 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001550 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001551 # use explicit check because empty list is a valid option
1552 if dns_nameservers is not None:
1553 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001554 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001555 if not routers_client:
1556 routers_client = self.routers_client
1557 router_id = router['id']
1558 routers_client.add_router_interface(router_id,
1559 subnet_id=subnet['id'])
1560
1561 # save a cleanup job to remove this association between
1562 # router and subnet
1563 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1564 routers_client.remove_router_interface, router_id,
1565 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001566 return network, subnet, router
1567
1568
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001569class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001570 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001571
David Kranz4cc852b2015-03-09 14:57:11 -04001572 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001573 def setup_clients(cls):
1574 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001575 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001576 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001577 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001578
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001579 def create_encryption_type(self, client=None, type_id=None, provider=None,
1580 key_size=None, cipher=None,
1581 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301582 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001583 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001584 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001585 if not type_id:
1586 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001587 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001588 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001589 client.create_encryption_type(
1590 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001591 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001592
lkuchlan3023e752017-06-08 12:53:13 +03001593 def create_encrypted_volume(self, encryption_provider, volume_type,
1594 key_size=256, cipher='aes-xts-plain64',
1595 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301596 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001597 volume_type = self.create_volume_type(name=volume_type)
1598 self.create_encryption_type(type_id=volume_type['id'],
1599 provider=encryption_provider,
1600 key_size=key_size,
1601 cipher=cipher,
1602 control_location=control_location)
1603 return self.create_volume(volume_type=volume_type['name'])
1604
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001605
Masayuki Igawa0870db52015-09-18 21:08:36 +09001606class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001607 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001608
1609 Subclasses implement the tests that use the methods provided by this
1610 class.
1611 """
1612
Ghanshyam Mann64281392021-03-24 18:48:38 -05001613 credentials = ['primary']
1614
Chris Dent0d494112014-08-26 13:48:30 +01001615 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001616 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001617 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001618 if not CONF.service_available.swift:
1619 skip_msg = ("%s skipped as swift is not available" %
1620 cls.__name__)
1621 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001622
1623 @classmethod
1624 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001625 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001626 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001627 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001628 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001629
1630 @classmethod
1631 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001632 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001633 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001634 cls.account_client = cls.os_operator.account_client
1635 cls.container_client = cls.os_operator.container_client
1636 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001637
Chris Dentde456a12014-09-10 12:41:15 +01001638 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301639 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001640 self.account_client.list_account_containers()
1641 LOG.debug('Swift status information obtained successfully')
1642
Chris Dentde456a12014-09-10 12:41:15 +01001643 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301644 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001645 name = container_name or data_utils.rand_name(
1646 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001647 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001648 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001649 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001650 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001651 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001652 self.container_client.delete_container,
1653 name)
Chris Dent0d494112014-08-26 13:48:30 +01001654 return name
1655
Chris Dentde456a12014-09-10 12:41:15 +01001656 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301657 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001658 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001659 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001660
Chris Dentde456a12014-09-10 12:41:15 +01001661 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301662 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001663 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001664 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001665 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001666 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001667 self.object_client.delete_object,
1668 container_name,
1669 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001670 return obj_name, obj_data
1671
Chris Dentde456a12014-09-10 12:41:15 +01001672 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301673 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001674 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001675 self.list_and_check_container_objects(container_name,
1676 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001677
Chris Dentde456a12014-09-10 12:41:15 +01001678 def list_and_check_container_objects(self, container_name,
1679 present_obj=None,
1680 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301681 """List and verify objects for a given container
1682
1683 This utility lists objects for a given container
1684 and asserts which are present and
1685 which are not
1686 """
1687
Ghanshyam2a180b82014-06-16 13:54:22 +09001688 if present_obj is None:
1689 present_obj = []
1690 if not_present_obj is None:
1691 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001692 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001693 container_name)
1694 if present_obj:
1695 for obj in present_obj:
1696 self.assertIn(obj, object_list)
1697 if not_present_obj:
1698 for obj in not_present_obj:
1699 self.assertNotIn(obj, object_list)
1700
Chris Dentde456a12014-09-10 12:41:15 +01001701 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301702 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001703 _, obj = self.object_client.get_object(container_name, obj_name)
1704 self.assertEqual(obj, expected_data)