blob: 5188191cc777084d8cac139f1593bbf45c143820 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Martin Kopec02af6a42020-03-03 12:39:12 +000017import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040018import subprocess
19
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import netaddr
Soniya Vyas795ef252020-12-10 19:07:23 +053021
Doug Hellmann583ce2c2015-03-11 14:55:46 +000022from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030023from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053024from oslo_utils import netutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
lanoux5fc14522015-09-21 08:17:35 +000026from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070027from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090028from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000029from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000030from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000031from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020032from tempest import exceptions
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000033from tempest.lib.common import api_microversion_fixture
34from tempest.lib.common import api_version_utils
Ken'ichi Ohmichibe4fb502017-03-10 10:04:48 -080035from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010036from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050037from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040038import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040039
Matthew Treinish6c072292014-01-29 19:15:52 +000040CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040041
Attila Fazekasfb7552a2013-08-27 13:02:26 +020042LOG = log.getLogger(__name__)
43
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000044LATEST_MICROVERSION = 'latest'
45
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
Andrea Frittoli2e733b52014-07-16 14:12:11 +010047class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010048 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010049
Andrea Frittolib21de6c2015-02-06 20:12:38 +000050 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000051
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000052 compute_min_microversion = None
53 compute_max_microversion = LATEST_MICROVERSION
54 volume_min_microversion = None
55 volume_max_microversion = LATEST_MICROVERSION
56 placement_min_microversion = None
57 placement_max_microversion = LATEST_MICROVERSION
58
59 @classmethod
60 def skip_checks(cls):
61 super(ScenarioTest, cls).skip_checks()
62 api_version_utils.check_skip_with_microversion(
63 cls.compute_min_microversion, cls.compute_max_microversion,
64 CONF.compute.min_microversion, CONF.compute.max_microversion)
65 api_version_utils.check_skip_with_microversion(
66 cls.volume_min_microversion, cls.volume_max_microversion,
67 CONF.volume.min_microversion, CONF.volume.max_microversion)
68 api_version_utils.check_skip_with_microversion(
69 cls.placement_min_microversion, cls.placement_max_microversion,
70 CONF.placement.min_microversion, CONF.placement.max_microversion)
71
72 @classmethod
73 def resource_setup(cls):
74 super(ScenarioTest, cls).resource_setup()
75 cls.compute_request_microversion = (
76 api_version_utils.select_request_microversion(
77 cls.compute_min_microversion,
78 CONF.compute.min_microversion))
79 cls.volume_request_microversion = (
80 api_version_utils.select_request_microversion(
81 cls.volume_min_microversion,
82 CONF.volume.min_microversion))
83 cls.placement_request_microversion = (
84 api_version_utils.select_request_microversion(
85 cls.placement_min_microversion,
86 CONF.placement.min_microversion))
87
88 def setUp(self):
89 super(ScenarioTest, self).setUp()
90 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
91 compute_microversion=self.compute_request_microversion,
92 volume_microversion=self.volume_request_microversion,
93 placement_microversion=self.placement_request_microversion))
94
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053095 def setup_compute_client(cls):
96 """Compute and Compute security groups client"""
97 cls.compute_images_client = cls.os_primary.compute_images_client
98 cls.keypairs_client = cls.os_primary.keypairs_client
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053099 cls.servers_client = cls.os_primary.servers_client
100 cls.interface_client = cls.os_primary.interfaces_client
101
102 def setup_network_client(cls):
103 """Neutron network client"""
104 cls.networks_client = cls.os_primary.networks_client
105 cls.ports_client = cls.os_primary.ports_client
106 cls.routers_client = cls.os_primary.routers_client
107 cls.subnets_client = cls.os_primary.subnets_client
108 cls.floating_ips_client = cls.os_primary.floating_ips_client
109 cls.security_groups_client = cls.os_primary.security_groups_client
110 cls.security_group_rules_client = (
111 cls.os_primary.security_group_rules_client)
112
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000113 @classmethod
114 def setup_clients(cls):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530115 """This setup the service clients for the tests"""
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000116 super(ScenarioTest, cls).setup_clients()
jeremy.zhang0343be52017-05-25 21:29:57 +0800117 cls.flavors_client = cls.os_primary.flavors_client
John Warrene74890a2015-11-11 15:18:01 -0500118 cls.compute_floating_ips_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +0800119 cls.os_primary.compute_floating_ips_client)
Jordan Pittier1d2e40f2016-01-05 18:49:14 +0100120 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400121 # Check if glance v1 is available to determine which client to use.
122 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +0800123 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400124 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +0800125 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400126 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400127 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400128 'Either api_v1 or api_v2 must be True in '
129 '[image-feature-enabled].')
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530130
131 cls.setup_compute_client(cls)
132 cls.setup_network_client(cls)
Andrea Frittolia6b30152017-08-04 10:46:10 +0100133 if CONF.service_available.cinder:
134 cls.volumes_client = cls.os_primary.volumes_client_latest
135 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +0300136 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300137
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200138 # ## Test functions library
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200139 # The create_[resource] functions only return body and discard the
140 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +0100141
zhufl1e446b52017-10-16 16:54:57 +0800142 def create_port(self, network_id, client=None, **kwargs):
Martin Kopec9c874412020-12-17 20:43:26 +0000143 """Creates port for the respective network_id
144
145 :param network_id: the id of the network
146 :param client: the client to use, defaults to self.ports_client
147 :param kwargs: additional arguments such as:
148 - namestart - a string to generate a name for the port from
149 - default is self.__class__.__name__
150 - 'binding:vnic_type' - defaults to CONF.network.port_vnic_type
151 - 'binding:profile' - defaults to CONF.network.port_profile
152 """
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300153 if not client:
154 client = self.ports_client
Martin Kopec9c874412020-12-17 20:43:26 +0000155 name = data_utils.rand_name(
156 kwargs.pop('namestart', self.__class__.__name__))
Edan David408a97b2018-01-15 03:52:15 -0500157 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
158 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
159 if CONF.network.port_profile and 'binding:profile' not in kwargs:
160 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300161 result = client.create_port(
162 name=name,
163 network_id=network_id,
164 **kwargs)
Soniya Vyas0123f522020-09-24 17:43:26 +0530165 self.assertIsNotNone(result, 'Unable to allocate port')
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300166 port = result['port']
167 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
168 client.delete_port, port['id'])
169 return port
170
Martin Kopec30b4d532020-10-16 12:02:43 +0000171 def create_keypair(self, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530172 """Creates keypair
173
174 Keypair is a public key of OpenSSH key pair used for accessing
175 and create servers
176 Keypair can also be created by a private key for the same purpose
177 Here, the keys are randomly generated[public/private]
178 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300179 if not client:
180 client = self.keypairs_client
Martin Kopec30b4d532020-10-16 12:02:43 +0000181 if not kwargs.get('name'):
182 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100183 # We don't need to create a keypair by pubkey in scenario
Martin Kopec30b4d532020-10-16 12:02:43 +0000184 body = client.create_keypair(**kwargs)
185 self.addCleanup(client.delete_keypair, kwargs['name'])
ghanshyamdee01f22015-08-17 11:41:47 +0900186 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100187
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530188 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800189 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200190 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000191 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100192
lanoux5fc14522015-09-21 08:17:35 +0000193 This wrapper utility calls the common create test server and
194 returns a test server. The purpose of this wrapper is to minimize
195 the impact on the code of the tests already using this
196 function.
Noam Angel6e309952019-01-27 05:52:40 +0000197
198 :param **kwargs:
199 See extra parameters below
200
201 :Keyword Arguments:
202 * *vnic_type* (``string``) --
203 used when launching instances with pre-configured ports.
204 Examples:
205 normal: a traditional virtual port that is either attached
206 to a linux bridge or an openvswitch bridge on a
207 compute node.
208 direct: an SR-IOV port that is directly attached to a VM
209 macvtap: an SR-IOV port that is attached to a VM via a macvtap
210 device.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100211 direct-physical: an SR-IOV port that is directly attached to a
212 VM using physical instead of virtual
213 functions.
214 baremetal: a baremetal port directly attached to a baremetal
215 node.
216 virtio-forwarder: an SR-IOV port that is indirectly attached
217 to a VM using a low-latency vhost-user
218 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000219 Defaults to ``CONF.network.port_vnic_type``.
220 * *port_profile* (``dict``) --
221 This attribute is a dictionary that can be used (with admin
222 credentials) to supply information influencing the binding of
223 the port.
224 example: port_profile = "capabilities:[switchdev]"
225 Defaults to ``CONF.network.port_profile``.
Martin Kopec9c874412020-12-17 20:43:26 +0000226 * *create_port_body* (``dict``) --
227 This attribute is a dictionary of additional arguments to be
228 passed to create_port method.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100229 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100230
lanoux5fc14522015-09-21 08:17:35 +0000231 # NOTE(jlanoux): As a first step, ssh checks in the scenario
232 # tests need to be run regardless of the run_validation and
233 # validatable parameters and thus until the ssh validation job
234 # becomes voting in CI. The test resources management and IP
235 # association are taken care of in the scenario tests.
236 # Therefore, the validatable parameter is set to false in all
237 # those tests. In this way create_server just return a standard
238 # server and the scenario tests always perform ssh checks.
239
240 # Needed for the cross_tenant_traffic test:
241 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800242 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000243
zhufl24208c22016-10-25 15:23:48 +0800244 if name is None:
245 name = data_utils.rand_name(self.__class__.__name__ + "-server")
246
Noam Angel6e309952019-01-27 05:52:40 +0000247 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
248 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000249
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000250 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000251 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000252 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000253 ports = []
Martin Kopec9c874412020-12-17 20:43:26 +0000254 create_port_body = kwargs.pop('create_port_body', {})
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300255
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000256 if vnic_type:
257 create_port_body['binding:vnic_type'] = vnic_type
258
259 if profile:
260 create_port_body['binding:profile'] = profile
261
lanoux5fc14522015-09-21 08:17:35 +0000262 if kwargs:
263 # Convert security group names to security group ids
264 # to pass to create_port
265 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300266 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500267 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000268 ).get('security_groups')
269 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100270 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000271
272 sec_groups_names = [s['name'] for s in kwargs.pop(
273 'security_groups')]
274 security_groups_ids = [sec_dict[s]
275 for s in sec_groups_names]
276
277 if security_groups_ids:
278 create_port_body[
279 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300280 networks = kwargs.pop('networks', [])
281 else:
282 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000283
284 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300285 # for the project's private networks and create a port.
286 # The same behaviour as we would expect when passing
287 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000288 if not networks:
289 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300290 **{'router:external': False, 'fields': 'id'})['networks']
291
292 # It's net['uuid'] if networks come from kwargs
293 # and net['id'] if they come from
294 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000295 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000296 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300297 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800298 port = self.create_port(network_id=net_id,
299 client=clients.ports_client,
300 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300301 ports.append({'port': port['id']})
302 else:
303 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000304 if ports:
305 kwargs['networks'] = ports
306 self.ports = ports
307
308 tenant_network = self.get_tenant_network()
309
Marc Koderer979e4942016-12-08 10:07:59 +0100310 if CONF.compute.compute_volume_common_az:
311 kwargs.setdefault('availability_zone',
312 CONF.compute.compute_volume_common_az)
313
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200314 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000315 clients,
316 tenant_network=tenant_network,
317 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530318 name=name, flavor=flavor,
319 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000320
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200321 self.addCleanup(waiters.wait_for_server_termination,
322 clients.servers_client, body['id'])
323 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
324 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000325 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100326 return server
327
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100328 def create_volume(self, size=None, name=None, snapshot_id=None,
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000329 imageRef=None, volume_type=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530330 """Creates volume
331
332 This wrapper utility creates volume and waits for volume to be
333 in 'available' state.
334 This method returns the volume's full representation by GET request.
335 """
336
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700337 if size is None:
338 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500339 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800340 if CONF.image_feature_enabled.api_v1:
341 resp = self.image_client.check_image(imageRef)
342 image = common_image.get_image_meta_from_headers(resp)
343 else:
344 image = self.image_client.show_image(imageRef)
345 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500346 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100347 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800348 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000349 kwargs.update({'name': name,
350 'snapshot_id': snapshot_id,
351 'imageRef': imageRef,
352 'volume_type': volume_type,
353 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100354
355 if CONF.compute.compute_volume_common_az:
356 kwargs.setdefault('availability_zone',
357 CONF.compute.compute_volume_common_az)
358
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900359 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700360
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100361 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
362 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100363 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100364 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300365 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200366 waiters.wait_for_volume_resource_status(self.volumes_client,
367 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100368 # The volume retrieved on creation has a non-up-to-date status.
369 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000370 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']
lkuchlane20e6a82018-05-08 11:28:46 +0300425 self.addCleanup(self.volumes_client.delete_volume,
426 restore['volume_id'])
427 waiters.wait_for_volume_resource_status(self.backups_client,
428 backup_id, 'available')
429 waiters.wait_for_volume_resource_status(self.volumes_client,
430 restore['volume_id'],
431 'available')
432 self.assertEqual(backup_id, restore['backup_id'])
433 return restore
434
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000435 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
436 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530437 if image is None:
438 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530439 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
440 server_id, image, preserve_ephemeral)
441 self.servers_client.rebuild_server(
442 server_id=server_id,
443 image_ref=image,
444 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000445 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530446 if wait:
447 waiters.wait_for_server_status(self.servers_client,
448 server_id, 'ACTIVE')
449
lkuchlan73ed1f32017-07-06 16:22:12 +0300450 def create_volume_snapshot(self, volume_id, name=None, description=None,
Martin Kopeca17cca42020-10-17 16:57:51 +0000451 metadata=None, force=False, **kwargs):
452 """Creates volume's snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530453
Martin Kopeca17cca42020-10-17 16:57:51 +0000454 This wrapper utility creates volume snapshot and waits for it until
455 it is in 'available' state.
456
457 :param volume_id: UUID of a volume to create snapshot of
458 :param name: name of the snapshot, '$classname-snapshot' by default
459 :param description: description of the snapshot
460 :param metadata: metadata key and value pairs for the snapshot
461 :param force: whether snapshot even when the volume is attached
462 :param **kwargs: additional parameters per the doc
463 https://docs.openstack.org/api-ref/block-storage/v3/
464 #create-a-snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530465 """
466
lkuchlan73ed1f32017-07-06 16:22:12 +0300467 name = name or data_utils.rand_name(
468 self.__class__.__name__ + '-snapshot')
469 snapshot = self.snapshots_client.create_snapshot(
470 volume_id=volume_id,
471 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000472 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300473 description=description,
Martin Kopeca17cca42020-10-17 16:57:51 +0000474 metadata=metadata,
475 **kwargs)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530476
lkuchlan73ed1f32017-07-06 16:22:12 +0300477 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
478 snapshot['id'])
479 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
480 waiters.wait_for_volume_resource_status(self.snapshots_client,
481 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200482 snapshot = self.snapshots_client.show_snapshot(
483 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300484 return snapshot
485
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530486 def cleanup_volume_type(self, volume_type):
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100487 """Clean up a given volume type.
488
489 Ensuring all volumes associated to a type are first removed before
490 attempting to remove the type itself. This includes any image volume
491 cache volumes stored in a separate tenant to the original volumes
492 created from the type.
493 """
494 admin_volume_type_client = self.os_admin.volume_types_client_latest
495 admin_volumes_client = self.os_admin.volumes_client_latest
496 volumes = admin_volumes_client.list_volumes(
497 detail=True, params={'all_tenants': 1})['volumes']
498 type_name = volume_type['name']
499 for volume in [v for v in volumes if v['volume_type'] == type_name]:
500 test_utils.call_and_ignore_notfound_exc(
501 admin_volumes_client.delete_volume, volume['id'])
502 admin_volumes_client.wait_for_resource_deletion(volume['id'])
503 admin_volume_type_client.delete_volume_type(volume_type['id'])
504
Martin Kopec8e673a42020-10-18 17:33:02 +0000505 def create_volume_type(self, client=None, name=None, backend_name=None,
506 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530507 """Creates volume type
508
509 In a multiple-storage back-end configuration,
510 each back end has a name (volume_backend_name).
511 The name of the back end is declared as an extra-specification
512 of a volume type (such as, volume_backend_name=LVM).
513 When a volume is created, the scheduler chooses an
514 appropriate back end to handle the request, according
515 to the volume type specified by the user.
516 The scheduler uses volume types to explicitly create volumes on
517 specific back ends.
518
519 Before using volume type, a volume type has to be declared
520 to Block Storage. In addition to that, an extra-specification
521 has to be created to link the volume type to a back end name.
522 """
523
scottda61f68ac2016-06-07 12:07:55 -0600524 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000525 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000526 if not name:
527 class_name = self.__class__.__name__
528 name = data_utils.rand_name(class_name + '-volume-type')
529 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600530
531 LOG.debug("Creating a volume type: %s on backend %s",
532 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000533 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600534 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000535 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600536
Martin Kopec8e673a42020-10-18 17:33:02 +0000537 volume_type_resp = client.create_volume_type(
538 name=randomized_name, extra_specs=extra_specs, **kwargs)
539 volume_type = volume_type_resp['volume_type']
540
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530541 self.assertIn('id', volume_type)
Soniya Vyasfd4dcf92021-02-17 18:12:43 +0530542 self.addCleanup(self.cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600543 return volume_type
544
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500545 def create_security_group(self, security_group_rules_client=None,
546 project_id=None,
547 namestart='secgroup-smoke',
548 security_groups_client=None):
549 if security_group_rules_client is None:
550 security_group_rules_client = self.security_group_rules_client
551 if security_groups_client is None:
552 security_groups_client = self.security_groups_client
553 if project_id is None:
554 project_id = security_groups_client.project_id
555 secgroup = self.create_empty_security_group(
556 namestart=namestart, client=security_groups_client,
557 project_id=project_id)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100558
559 # Add rules to the security group
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500560 rules = self.create_loginable_secgroup_rule(
561 security_group_rules_client=security_group_rules_client,
562 secgroup=secgroup,
563 security_groups_client=security_groups_client)
564 for rule in rules:
565 self.assertEqual(project_id, rule['project_id'])
566 self.assertEqual(secgroup['id'], rule['security_group_id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100567 return secgroup
568
Soniya Vyasbbc9dd32021-03-23 16:06:29 -0500569 def create_empty_security_group(self, client=None, project_id=None,
570 namestart='secgroup-smoke'):
571 """Create a security group without rules.
572
573 Default rules will be created:
574 - IPv4 egress to any
575 - IPv6 egress to any
576 :param project_id: secgroup will be created in this project
577 :returns: the created security group
578 """
579
580 if client is None:
581 client = self.security_groups_client
582 if not project_id:
583 project_id = client.project_id
584 sg_name = data_utils.rand_name(namestart)
585 sg_desc = sg_name + " description"
586 sg_dict = dict(name=sg_name,
587 description=sg_desc)
588 sg_dict['project_id'] = project_id
589 result = client.create_security_group(**sg_dict)
590
591 secgroup = result['security_group']
592 self.assertEqual(secgroup['name'], sg_name)
593 self.assertEqual(project_id, secgroup['project_id'])
594 self.assertEqual(secgroup['description'], sg_desc)
595
596 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
597 client.delete_security_group, secgroup['id'])
598 return secgroup
599
600 def create_security_group_rule(self, secgroup=None,
601 sec_group_rules_client=None,
602 project_id=None,
603 security_groups_client=None, **kwargs):
604 """Create a rule from a dictionary of rule parameters.
605
606 Create a rule in a secgroup. if secgroup not defined will search for
607 default secgroup in project_id.
608 :param secgroup: the security group.
609 :param project_id: if secgroup not passed -- the tenant in which to
610 search for default secgroup
611 :param kwargs: a dictionary containing rule parameters:
612 for example, to allow incoming ssh:
613 rule = {
614 direction: 'ingress'
615 protocol:'tcp',
616 port_range_min: 22,
617 port_range_max: 22
618 }
619 """
620
621 if sec_group_rules_client is None:
622 sec_group_rules_client = self.security_group_rules_client
623 if security_groups_client is None:
624 security_groups_client = self.security_groups_client
625 if not project_id:
626 project_id = security_groups_client.project_id
627 if secgroup is None:
628 # Get default secgroup for project_id
629 default_secgroups = security_groups_client.list_security_groups(
630 name='default', project_id=project_id)['security_groups']
631 msg = "No default security group for project %s." % (project_id)
632 self.assertNotEmpty(default_secgroups, msg)
633 secgroup = default_secgroups[0]
634
635 ruleset = dict(security_group_id=secgroup['id'],
636 project_id=secgroup['project_id'])
637 ruleset.update(kwargs)
638
639 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
640 sg_rule = sg_rule['security_group_rule']
641
642 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
643 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
644
645 return sg_rule
646
647 def create_loginable_secgroup_rule(self, security_group_rules_client=None,
648 secgroup=None,
649 security_groups_client=None):
650 """Create loginable security group rule by neutron clients by default.
651
652 This function will create:
653 1. egress and ingress tcp port 22 allow rule in order to allow ssh
654 access for ipv4.
655 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
656 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
657 """
658
659 if security_group_rules_client is None:
660 security_group_rules_client = self.security_group_rules_client
661 if security_groups_client is None:
662 security_groups_client = self.security_groups_client
663 rules = []
664 rulesets = [
665 dict(
666 # ssh
667 protocol='tcp',
668 port_range_min=22,
669 port_range_max=22,
670 ),
671 dict(
672 # ping
673 protocol='icmp',
674 ),
675 dict(
676 # ipv6-icmp for ping6
677 protocol='icmp',
678 ethertype='IPv6',
679 )
680 ]
681 sec_group_rules_client = security_group_rules_client
682 for ruleset in rulesets:
683 for r_direction in ['ingress', 'egress']:
684 ruleset['direction'] = r_direction
685 try:
686 sg_rule = self.create_security_group_rule(
687 sec_group_rules_client=sec_group_rules_client,
688 secgroup=secgroup,
689 security_groups_client=security_groups_client,
690 **ruleset)
691 except lib_exc.Conflict as ex:
692 # if rule already exist - skip rule and continue
693 msg = 'Security group rule already exists'
694 if msg not in ex._error_string:
695 raise ex
696 else:
697 self.assertEqual(r_direction, sg_rule['direction'])
698 rules.append(sg_rule)
699
700 return rules
701
zhuflf52c7592017-05-25 13:55:24 +0800702 def get_remote_client(self, ip_address, username=None, private_key=None,
703 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100704 """Get a SSH client to a remote server
705
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600706 :param ip_address: the server floating or fixed IP address to use
707 for ssh validation
708 :param username: name of the Linux account on the remote server
709 :param private_key: the SSH private key to use
710 :param server: server dict, used for debugging purposes
711 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100712 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700713
Andrea Frittoli247058f2014-07-16 16:09:22 +0100714 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800715 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800716 # Set this with 'keypair' or others to log in with keypair or
717 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000718 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800719 password = None
720 if private_key is None:
721 private_key = self.keypair['private_key']
722 else:
lanoux283273b2015-12-04 03:01:54 -0800723 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800724 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800725 linux_client = remote_client.RemoteClient(
726 ip_address, username, pkey=private_key, password=password,
727 server=server, servers_client=self.servers_client)
728 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100729 return linux_client
730
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000731 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000732 img_path = CONF.scenario.img_file
733 if not os.path.exists(img_path):
Martin Kopec008950e2020-09-29 08:12:39 +0000734 lib_exc.InvalidConfiguration(
Martin Kopec02af6a42020-03-03 12:39:12 +0000735 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
736 'a full path for the image. CONF.scenario.img_dir was '
737 'deprecated and will be removed in the next release. Till '
Martin Kopec008950e2020-09-29 08:12:39 +0000738 'Tempest 25.0.0, old behavior was maintained and kept working '
Martin Kopec02af6a42020-03-03 12:39:12 +0000739 'but starting Tempest 26.0.0, you need to specify the full '
740 'path in CONF.scenario.img_file config option.')
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300741 img_container_format = CONF.scenario.img_container_format
742 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000743 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400744 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000745 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100746 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000747 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530748 if img_properties is None:
749 img_properties = {}
750 name = data_utils.rand_name('%s-' % name)
751 params = {
752 'name': name,
753 'container_format': img_container_format,
754 'disk_format': img_disk_format or img_container_format,
755 }
756 if CONF.image_feature_enabled.api_v1:
757 params['is_public'] = 'False'
758 if img_properties:
759 params['properties'] = img_properties
760 params = {'headers': common_image.image_meta_to_headers(**params)}
761 else:
762 params['visibility'] = 'private'
763 # Additional properties are flattened out in the v2 API.
764 if img_properties:
765 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000766 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530767 body = self.image_client.create_image(**params)
768 image = body['image'] if 'image' in body else body
769 self.addCleanup(self.image_client.delete_image, image['id'])
770 self.assertEqual("queued", image['status'])
771 with open(img_path, 'rb') as image_file:
772 if CONF.image_feature_enabled.api_v1:
773 self.image_client.update_image(image['id'], data=image_file)
774 else:
775 self.image_client.store_image_file(image['id'], image_file)
776 LOG.debug("image:%s", image['id'])
777 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100778
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530779 def log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530780 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400781 if not CONF.compute_feature_enabled.console_output:
782 LOG.debug('Console output not supported, cannot log')
783 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700784 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100785 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700786 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100787 servers = servers['servers']
788 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100789 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700790 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000791 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100792 LOG.debug('Console output for %s\nbody=\n%s',
793 server['id'], console_output)
794 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100795 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100796 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100797
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000798 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530799 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300800 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000801 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000802
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000803 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530804 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000805 # Glance client
806 _image_client = self.image_client
807 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900808 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000809 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800810 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000811 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000812 image = _images_client.create_image(server['id'], name=name, **kwargs)
David Kranza5299eb2015-01-15 17:24:05 -0500813 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300814 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200815
816 self.addCleanup(_image_client.wait_for_resource_deletion,
817 image_id)
818 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
819 _image_client.delete_image, image_id)
820
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400821 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530822 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700823 resp = _image_client.check_image(image_id)
824 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400825 image_props = snapshot_image.get('properties', {})
826 else:
827 # In glance v2 the additional properties are flattened.
828 snapshot_image = _image_client.show_image(image_id)
829 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300830
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400831 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300832 if bdm:
833 bdm = json.loads(bdm)
834 if bdm and 'snapshot_id' in bdm[0]:
835 snapshot_id = bdm[0]['snapshot_id']
836 self.addCleanup(
837 self.snapshots_client.wait_for_resource_deletion,
838 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100839 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
840 self.snapshots_client.delete_snapshot,
841 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200842 waiters.wait_for_volume_resource_status(self.snapshots_client,
843 snapshot_id,
844 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000845 image_name = snapshot_image['name']
846 self.assertEqual(name, image_name)
847 LOG.debug("Created snapshot image %s for server %s",
848 image_name, server['name'])
849 return snapshot_image
850
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000851 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530852 """Compute volume attach
853
854 This utility attaches volume from compute and waits for the
855 volume status to be 'in-use' state.
856 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000857 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000858 server['id'], volumeId=volume_to_attach['id'],
859 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200860 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200861 waiters.wait_for_volume_resource_status(self.volumes_client,
862 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000863 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
864 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200865 # Return the updated volume after the attachment
866 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900867
Jordan Pittier7cf64762015-10-14 15:01:12 +0200868 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530869 """Compute volume detach
870
Lee Yarwood5423c532020-12-17 11:24:46 +0000871 This utility detaches the volume from the server and checks whether the
872 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530873 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200874 self.servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000875 waiters.wait_for_volume_attachment_remove_from_server(
876 self.servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200877
Steven Hardyda2a8352014-10-02 12:52:20 +0100878 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800879 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530880 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000881 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000882 cmd = ['ping', '-c1', '-w1']
883
884 if mtu:
885 cmd += [
886 # don't fragment
887 '-M', 'do',
888 # ping receives just the size of ICMP payload
889 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
890 ]
891 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700892
893 def ping():
894 proc = subprocess.Popen(cmd,
895 stdout=subprocess.PIPE,
896 stderr=subprocess.PIPE)
897 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000898
Aaron Rosena7df13b2014-09-23 09:45:45 -0700899 return (proc.returncode == 0) == should_succeed
900
Jordan Pittier9e227c52016-02-09 14:35:18 +0100901 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000902 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800903 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000904 'caller': caller, 'ip': ip_address, 'timeout': timeout,
905 'should_succeed':
906 'reachable' if should_succeed else 'unreachable'
907 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200908 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000909 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800910 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000911 'caller': caller, 'ip': ip_address, 'timeout': timeout,
912 'result': 'expected' if result else 'unexpected'
913 })
zhufl0ec74c42017-11-15 14:02:28 +0800914 if server:
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530915 self.log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000916 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700917
Yair Friedae0e73d2014-11-24 11:56:26 +0200918 def check_vm_connectivity(self, ip_address,
919 username=None,
920 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000921 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800922 extra_msg="",
923 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000924 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000925 """Check server connectivity
926
Yair Friedae0e73d2014-11-24 11:56:26 +0200927 :param ip_address: server to test against
928 :param username: server's ssh username
929 :param private_key: server's ssh private key to be used
930 :param should_connect: True/False indicates positive/negative test
931 positive - attempt ping and ssh
932 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800933 :param extra_msg: Message to help with debugging if ``ping_ip_address``
934 fails
935 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000936 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200937
938 :raises: AssertError if the result of the connectivity check does
939 not match the value of the should_connect param
940 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530941
zhufl0ec74c42017-11-15 14:02:28 +0800942 LOG.debug('checking network connections to IP %s with user: %s',
943 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200944 if should_connect:
945 msg = "Timed out waiting for %s to become reachable" % ip_address
946 else:
947 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800948 if extra_msg:
949 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200950 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000951 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800952 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200953 msg=msg)
954 if should_connect:
955 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800956 try:
957 self.get_remote_client(ip_address, username, private_key,
958 server=server)
959 except Exception:
960 if not extra_msg:
961 extra_msg = 'Failed to ssh to %s' % ip_address
962 LOG.exception(extra_msg)
963 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200964
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000965 def create_floating_ip(self, server, pool_name=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000966 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200967
Marc Koderer3b57d802016-03-22 15:23:31 +0100968 if not pool_name:
969 pool_name = CONF.network.floating_network_name
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000970
John Warrene74890a2015-11-11 15:18:01 -0500971 floating_ip = (self.compute_floating_ips_client.
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000972 create_floating_ip(pool=pool_name,
973 **kwargs)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100974 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500975 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200976 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500977 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530978 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200979 return floating_ip
980
Sean Dague20e98612016-01-06 14:33:28 -0500981 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000982 private_key=None, server=None, username=None,
983 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530984 """Creates timestamp
985
986 This wrapper utility does ssh, creates timestamp and returns the
987 created timestamp.
988 """
Sean Dague20e98612016-01-06 14:33:28 -0500989 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200990 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000991 server=server,
992 username=username)
993
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300994 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000995 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800996 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
997 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300998 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
999 ssh_client.exec_command(cmd_timestamp)
1000 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
1001 % mount_path)
1002 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001003 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001004 return timestamp
1005
Sean Dague20e98612016-01-06 14:33:28 -05001006 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001007 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301008 """Returns timestamp
1009
1010 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001011
1012 :param ip_address: The floating IP or fixed IP of the remote server
1013 :param dev_name: Name of the device that stores the timestamp
1014 :param mount_path: Path which should be used as mount point for
1015 dev_name
1016 :param private_key: The SSH private key to use for authentication
1017 :param server: Server dict, used for debugging purposes
1018 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301019 """
1020
Sean Dague20e98612016-01-06 14:33:28 -05001021 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +02001022 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +00001023 server=server,
1024 username=username)
1025
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001026 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -07001027 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001028 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
1029 % mount_path)
1030 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -08001031 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +03001032 return timestamp
1033
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001034 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -05001035 """Get the server fixed or floating IP.
1036
1037 Based on the configuration we're in, return a correct ip
1038 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001039
1040 If CONF.validation.connect_method is floating, then
1041 a floating ip will be created passing kwargs as additional
1042 argument.
Sean Dague20e98612016-01-06 14:33:28 -05001043 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301044
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001045 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -05001046 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +08001047 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -05001048 # method is creating the floating IP there.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +00001049 return self.create_floating_ip(server, **kwargs)['ip']
Sean Dague20e98612016-01-06 14:33:28 -05001050 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -04001051 # Determine the network name to look for based on config or creds
1052 # provider network resources.
1053 if CONF.validation.network_for_ssh:
1054 addresses = server['addresses'][
1055 CONF.validation.network_for_ssh]
1056 else:
zhufl7b4a7202017-09-28 10:29:27 +08001057 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -04001058 addresses = (server['addresses'][network['name']]
1059 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -05001060 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001061 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
1062 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -05001063 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +08001064 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001065 else:
Matthew Treinish4217a702016-10-07 17:27:11 -04001066 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +02001067
zhufl7bc916d2018-08-22 14:47:39 +08001068 @classmethod
1069 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301070 """Gets host of server"""
1071
zhufl7bc916d2018-08-22 14:47:39 +08001072 server_details = cls.os_admin.servers_client.show_server(server_id)
1073 return server_details['server']['OS-EXT-SRV-ATTR:host']
1074
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001075 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
1076 bd_map_v2 = [{
1077 'uuid': source_id,
1078 'source_type': source_type,
1079 'destination_type': 'volume',
1080 'boot_index': 0,
1081 'delete_on_termination': delete_on_termination}]
1082 return {'block_device_mapping_v2': bd_map_v2}
1083
1084 def boot_instance_from_resource(self, source_id,
1085 source_type,
1086 keypair=None,
1087 security_group=None,
1088 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +00001089 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301090 """Boot instance from resource
1091
1092 This wrapper utility boots instance from resource with block device
1093 mapping with source info passed in arguments
1094 """
1095
Martin Kopecbee673e2020-11-04 09:40:52 +00001096 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001097 if keypair:
1098 create_kwargs['key_name'] = keypair['name']
1099 if security_group:
1100 create_kwargs['security_groups'] = [
1101 {'name': security_group['name']}]
1102 create_kwargs.update(self._get_bdm(
1103 source_id,
1104 source_type,
1105 delete_on_termination=delete_on_termination))
1106 if name:
1107 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +00001108 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001109
Martin Kopecbee673e2020-11-04 09:40:52 +00001110 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001111
Martin Kopec0216b372020-11-04 09:32:05 +00001112 def create_volume_from_image(self, **kwargs):
1113 """Create volume from image.
1114
1115 :param image_id: ID of the image to create volume from,
1116 CONF.compute.image_ref by default
1117 :param name: name of the volume,
1118 '$classname-volume-origin' by default
1119 :param **kwargs: additional parameters
1120 """
1121 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
1122 name = kwargs.pop('name', None)
1123 if not name:
1124 namestart = self.__class__.__name__ + '-volume-origin'
1125 name = data_utils.rand_name(namestart)
1126 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001127
Andrea Frittoli2e733b52014-07-16 14:12:11 +01001128
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001129class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001130 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001131
Yair Fried1fc32a12014-08-04 09:11:30 +03001132 This class provide helpers for network scenario tests, using the neutron
1133 API. Helpers from ancestor which use the nova network API are overridden
1134 with the neutron API.
1135
1136 This Class also enforces using Neutron instead of novanetwork.
1137 Subclassed tests will be skipped if Neutron is not enabled
1138
1139 """
1140
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001141 credentials = ['primary', 'admin']
1142
Yair Fried1fc32a12014-08-04 09:11:30 +03001143 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001144 def skip_checks(cls):
1145 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001146 if not CONF.service_available.neutron:
1147 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001148
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301149 def create_network(self, networks_client=None,
1150 project_id=None,
1151 namestart='network-smoke-',
1152 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001153 if not networks_client:
1154 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001155 if not project_id:
1156 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001157 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001158 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001159 if net_dict:
1160 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001161 # Neutron disables port security by default so we have to check the
1162 # config before trying to create the network with port_security_enabled
1163 if CONF.network_feature_enabled.port_security:
1164 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001165 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001166 network = result['network']
1167
1168 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001169 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001170 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001171 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001172 return network
1173
zhufl5b0a52f2017-10-24 15:48:20 +08001174 def create_subnet(self, network, subnets_client=None,
1175 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001176 """Create a subnet for the given network
1177
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301178 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001179 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301180
1181 :param **kwargs:
1182 See extra parameters below
1183
1184 :Keyword Arguments:
1185
1186 * *ip_version = ip version of the given network,
Soniya Vyas795ef252020-12-10 19:07:23 +05301187 use_default_subnetpool = default subnetpool to
1188 manage IPv6 addresses range.
Yair Fried1fc32a12014-08-04 09:11:30 +03001189 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301190
John Warren3961acd2015-10-02 14:38:53 -04001191 if not subnets_client:
1192 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001193
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001194 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001195 """Check cidr existence
1196
yangjianfeng4ad346e2020-11-22 06:49:19 +00001197 :returns: True if subnet with cidr already exist in tenant or
1198 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001199 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001200 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001201 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001202 external_nets = self.os_admin.networks_client.list_networks(
1203 **{"router:external": True})['networks']
1204 external_subnets = []
1205 for ext_net in external_nets:
1206 external_subnets.extend(
1207 self.os_admin.subnets_client.list_subnets(
1208 network_id=ext_net['id'], cidr=cidr)['subnets'])
1209 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001210
Soniya Vyas795ef252020-12-10 19:07:23 +05301211 def _make_create_subnet_request(namestart, network,
1212 ip_version, subnets_client, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001213
1214 subnet = dict(
1215 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001216 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001217 project_id=network['project_id'],
Kirill Shileev14113572014-11-21 16:58:02 +03001218 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001219 **kwargs
1220 )
Soniya Vyas795ef252020-12-10 19:07:23 +05301221
1222 if ip_version == 6:
1223 subnet['ipv6_address_mode'] = 'slaac'
1224 subnet['ipv6_ra_mode'] = 'slaac'
1225
Yair Fried1fc32a12014-08-04 09:11:30 +03001226 try:
Soniya Vyas795ef252020-12-10 19:07:23 +05301227 return subnets_client.create_subnet(**subnet)
Masayuki Igawad9388762015-01-20 14:56:42 +09001228 except lib_exc.Conflict as e:
Soniya Vyas795ef252020-12-10 19:07:23 +05301229 if 'overlaps with another subnet' not in str(e):
Yair Fried1fc32a12014-08-04 09:11:30 +03001230 raise
Soniya Vyas795ef252020-12-10 19:07:23 +05301231
1232 result = None
1233 str_cidr = None
1234
1235 use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
1236 ip_version = kwargs.pop('ip_version', 4)
1237
1238 if not use_default_subnetpool:
1239
1240 if ip_version == 6:
1241 tenant_cidr = netaddr.IPNetwork(
1242 CONF.network.project_network_v6_cidr)
1243 num_bits = CONF.network.project_network_v6_mask_bits
1244 else:
1245 tenant_cidr = netaddr.IPNetwork(
1246 CONF.network.project_network_cidr)
1247 num_bits = CONF.network.project_network_mask_bits
1248
1249 # Repeatedly attempt subnet creation with sequential cidr
1250 # blocks until an unallocated block is found.
1251 for subnet_cidr in tenant_cidr.subnet(num_bits):
1252 str_cidr = str(subnet_cidr)
1253 if cidr_in_use(str_cidr, project_id=network['project_id']):
1254 continue
1255 result = _make_create_subnet_request(
1256 namestart, network, ip_version, subnets_client,
1257 cidr=str_cidr, **kwargs)
1258
1259 if result is not None:
1260 break
1261
1262 else:
1263 result = _make_create_subnet_request(
1264 namestart, network, ip_version, subnets_client,
1265 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +03001266 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001267
1268 subnet = result['subnet']
Soniya Vyas795ef252020-12-10 19:07:23 +05301269 if str_cidr is not None:
1270 self.assertEqual(subnet['cidr'], str_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -05001271
1272 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1273 subnets_client.delete_subnet, subnet['id'])
1274
Yair Fried1fc32a12014-08-04 09:11:30 +03001275 return subnet
1276
Soniya Vyasa446d702021-02-23 15:58:53 +05301277 def get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
Lukas Piwowarski9523d512020-10-30 09:37:58 +00001278
1279 if ip_addr and not kwargs.get('fixed_ips'):
1280 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
1281 ports = self.os_admin.ports_client.list_ports(
1282 device_id=server['id'], **kwargs)['ports']
1283
Kobi Samoray166500a2016-10-09 14:42:48 +03001284 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001285 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1286 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001287
1288 def _is_active(port):
1289 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1290 # servers. Neutron does not bind ports for Ironic instances, as a
1291 # result the port remains in the DOWN state. This has been fixed
1292 # with the introduction of the networking-baremetal plugin but
1293 # it's not mandatory (and is not used on all stable branches).
1294 return (port['status'] == 'ACTIVE' or
1295 port.get('binding:vnic_type') == 'baremetal')
1296
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001297 port_map = [(p["id"], fxip["ip_address"])
1298 for p in ports
1299 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001300 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001301 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001302 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1303 if inactive:
1304 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001305
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001306 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001307 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001308 self.assertEqual(len(port_map), 1,
1309 "Found multiple IPv4 addresses: %s. "
1310 "Unable to determine which port to target."
1311 % port_map)
1312 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001313
Soniya Vyasc37410f2021-02-24 15:26:27 +05301314 def get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001315 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001316 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001317 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001318 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001319 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001320
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301321 def create_floating_ip(self, server, external_network_id=None,
Martin Kopec5e761bf2020-11-03 16:11:08 +00001322 port_id=None, client=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001323 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301324
Yair Friedae0e73d2014-11-24 11:56:26 +02001325 if not external_network_id:
1326 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001327 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001328 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001329 if not port_id:
Soniya Vyasa446d702021-02-23 15:58:53 +05301330 port_id, ip4 = self.get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001331 else:
1332 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001333
Martin Kopec5e761bf2020-11-03 16:11:08 +00001334 floatingip_kwargs = {
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001335 'floating_network_id': external_network_id,
1336 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301337 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001338 'fixed_ip_address': ip4,
1339 }
1340 if CONF.network.subnet_id:
Martin Kopec5e761bf2020-11-03 16:11:08 +00001341 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1342
1343 floatingip_kwargs.update(kwargs)
1344 result = client.create_floatingip(**floatingip_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001345 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001346
Jordan Pittier9e227c52016-02-09 14:35:18 +01001347 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001348 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001349 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001350 return floating_ip
1351
Soniya Vyased664472020-09-23 18:40:25 +05301352 def associate_floating_ip(self, floating_ip, server):
1353 """Associate floating ip
1354
1355 This wrapper utility attaches the floating_ip for
1356 the respective port_id of server
1357 """
Soniya Vyasa446d702021-02-23 15:58:53 +05301358 port_id, _ = self.get_server_port_id_and_ip4(server)
Soniya Vyased664472020-09-23 18:40:25 +05301359 kwargs = dict(port_id=port_id)
1360 floating_ip = self.floating_ips_client.update_floatingip(
1361 floating_ip['id'], **kwargs)['floatingip']
1362 self.assertEqual(port_id, floating_ip['port_id'])
1363 return floating_ip
1364
1365 def disassociate_floating_ip(self, floating_ip):
1366 """Disassociates floating ip
1367
1368 This wrapper utility disassociates given floating ip.
1369 :param floating_ip: a dict which is a return value of
1370 floating_ips_client.create_floatingip method
1371 """
1372 kwargs = dict(port_id=None)
1373 floating_ip = self.floating_ips_client.update_floatingip(
1374 floating_ip['id'], **kwargs)['floatingip']
1375 self.assertIsNone(floating_ip['port_id'])
1376 return floating_ip
1377
Yair Fried45f92952014-06-26 05:19:19 +03001378 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001379 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001380
Steve Heyman33735f22016-05-24 09:28:08 -05001381 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001382 :param status: target status
1383 :raises: AssertionError if status doesn't match
1384 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301385
Steve Heyman33735f22016-05-24 09:28:08 -05001386 floatingip_id = floating_ip['id']
1387
Carl Baldwina754e2d2014-10-23 22:47:41 +00001388 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001389 floating_ip = (self.floating_ips_client.
1390 show_floatingip(floatingip_id)['floatingip'])
1391 if status == floating_ip['status']:
1392 LOG.info("FloatingIP: {fp} is at status: {st}"
1393 .format(fp=floating_ip, st=status))
1394 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001395
zhufl4dda94e2017-03-14 16:14:46 +08001396 if not test_utils.call_until_true(refresh,
1397 CONF.network.build_timeout,
1398 CONF.network.build_interval):
1399 floating_ip = self.floating_ips_client.show_floatingip(
1400 floatingip_id)['floatingip']
1401 self.assertEqual(status, floating_ip['status'],
1402 message="FloatingIP: {fp} is at status: {cst}. "
1403 "failed to reach status: {st}"
1404 .format(fp=floating_ip, cst=floating_ip['status'],
1405 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001406
zhufl420a0192017-09-28 11:04:50 +08001407 def check_tenant_network_connectivity(self, server,
1408 username,
1409 private_key,
1410 should_connect=True,
1411 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301412 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001413 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001414 msg = 'Tenant networks not configured to be reachable.'
1415 LOG.info(msg)
1416 return
1417 # The target login is assumed to have been configured for
1418 # key-based authentication by cloud-init.
1419 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001420 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001421 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001422 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001423 username,
1424 private_key,
1425 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001426 except Exception as e:
1427 LOG.exception('Tenant network connectivity check failed')
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301428 self.log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001429 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001430 raise
1431
zhufle9877c62017-10-13 09:38:19 +08001432 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001433 nic=None, protocol='icmp'):
1434 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001435
Claudiu Belu33c3e602014-08-28 16:38:01 +03001436 :param source: RemoteClient: an ssh connection from which to execute
1437 the check
1438 :param dest: an IP to check connectivity against
1439 :param should_succeed: boolean should connection succeed or not
1440 :param nic: specific network interface to test connectivity from
1441 :param protocol: the protocol used to test connectivity with.
1442 :returns: True, if the connection succeeded and it was expected to
1443 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001444 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301445
Claudiu Belu33c3e602014-08-28 16:38:01 +03001446 method_name = '%s_check' % protocol
1447 connectivity_checker = getattr(source, method_name)
1448
1449 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001450 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001451 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001452 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001453 LOG.warning('Failed to check %(protocol)s connectivity for '
1454 'IP %(dest)s via a ssh connection from: %(src)s.',
1455 dict(protocol=protocol, dest=dest,
1456 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001457 return not should_succeed
1458 return should_succeed
1459
Claudiu Belu33c3e602014-08-28 16:38:01 +03001460 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001461 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001462 if result:
1463 return
1464
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001465 source_host = source.ssh_client.host
1466 if should_succeed:
1467 msg = "Timed out waiting for %s to become reachable from %s" \
1468 % (dest, source_host)
1469 else:
1470 msg = "%s is reachable from %s" % (dest, source_host)
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301471 self.log_console_output()
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001472 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001473
Soniya Vyas73555df2021-03-04 19:05:42 +05301474 def get_router(self, client=None, project_id=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001475 """Retrieve a router for the given tenant id.
1476
1477 If a public router has been configured, it will be returned.
1478
1479 If a public router has not been configured, but a public
1480 network has, a tenant router will be created and returned that
1481 routes traffic to the public network.
1482 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301483
Yair Frieddb6c9e92014-08-06 08:53:13 +03001484 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001485 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001486 if not project_id:
1487 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001488 router_id = CONF.network.public_router_id
1489 network_id = CONF.network.public_network_id
1490 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001491 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001492 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001493 elif network_id:
Martin Kopec0090a102020-11-03 13:50:19 +00001494 name = kwargs.pop('name', None)
1495 if not name:
1496 namestart = self.__class__.__name__ + '-router'
1497 name = data_utils.rand_name(namestart)
1498
1499 ext_gw_info = kwargs.pop('external_gateway_info', None)
1500 if not ext_gw_info:
1501 ext_gw_info = dict(network_id=network_id)
zhufl3484f992017-10-10 16:18:29 +08001502 router = client.create_router(
Martin Kopec0090a102020-11-03 13:50:19 +00001503 name=name,
1504 admin_state_up=kwargs.get('admin_state_up', True),
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001505 project_id=project_id,
Martin Kopec0090a102020-11-03 13:50:19 +00001506 external_gateway_info=ext_gw_info,
1507 **kwargs)['router']
zhufl3484f992017-10-10 16:18:29 +08001508 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1509 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001510 return router
1511 else:
1512 raise Exception("Neither of 'public_router_id' or "
1513 "'public_network_id' has been defined.")
1514
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001515 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001516 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001517 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001518 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001519 """Create a network with a subnet connected to a router.
1520
David Shrewsbury9bac3662014-08-07 15:07:01 -04001521 The baremetal driver is a special case since all nodes are
1522 on the same shared network.
1523
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001524 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001525 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001526 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001527 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001528 a form like this: {'provider:network_type': 'vlan',
1529 'provider:physical_network': 'foo',
1530 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001531 :returns: network, subnet, router
1532 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301533
Thiago Paiva66cded22016-08-15 14:55:58 -03001534 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001535 # NOTE(Shrews): This exception is for environments where tenant
1536 # credential isolation is available, but network separation is
1537 # not (the current baremetal case). Likely can be removed when
1538 # test account mgmt is reworked:
1539 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001540 if not CONF.compute.fixed_network_name:
1541 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001542 raise lib_exc.InvalidConfiguration(m)
Soniya Vyasc37410f2021-02-24 15:26:27 +05301543 network = self.get_network_by_name(
David Shrewsbury9bac3662014-08-07 15:07:01 -04001544 CONF.compute.fixed_network_name)
1545 router = None
1546 subnet = None
1547 else:
Soniya Vyas3bdafd82021-02-22 18:59:27 +05301548 network = self.create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001549 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001550 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001551 port_security_enabled=port_security_enabled,
1552 **net_dict)
Soniya Vyas73555df2021-03-04 19:05:42 +05301553 router = self.get_router(client=routers_client,
1554 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001555 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001556 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001557 # use explicit check because empty list is a valid option
1558 if dns_nameservers is not None:
1559 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001560 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001561 if not routers_client:
1562 routers_client = self.routers_client
1563 router_id = router['id']
1564 routers_client.add_router_interface(router_id,
1565 subnet_id=subnet['id'])
1566
1567 # save a cleanup job to remove this association between
1568 # router and subnet
1569 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1570 routers_client.remove_router_interface, router_id,
1571 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001572 return network, subnet, router
1573
1574
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001575class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001576 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001577
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001578 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001579
1580 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001581 def setup_clients(cls):
1582 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001583 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001584 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001585 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001586
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001587 def create_encryption_type(self, client=None, type_id=None, provider=None,
1588 key_size=None, cipher=None,
1589 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301590 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001591 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001592 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001593 if not type_id:
1594 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001595 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001596 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001597 client.create_encryption_type(
1598 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001599 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001600
lkuchlan3023e752017-06-08 12:53:13 +03001601 def create_encrypted_volume(self, encryption_provider, volume_type,
1602 key_size=256, cipher='aes-xts-plain64',
1603 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301604 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001605 volume_type = self.create_volume_type(name=volume_type)
1606 self.create_encryption_type(type_id=volume_type['id'],
1607 provider=encryption_provider,
1608 key_size=key_size,
1609 cipher=cipher,
1610 control_location=control_location)
1611 return self.create_volume(volume_type=volume_type['name'])
1612
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001613
Masayuki Igawa0870db52015-09-18 21:08:36 +09001614class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001615 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001616
1617 Subclasses implement the tests that use the methods provided by this
1618 class.
1619 """
1620
1621 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001622 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001623 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001624 if not CONF.service_available.swift:
1625 skip_msg = ("%s skipped as swift is not available" %
1626 cls.__name__)
1627 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001628
1629 @classmethod
1630 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001631 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001632 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001633 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001634 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001635
1636 @classmethod
1637 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001638 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001639 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001640 cls.account_client = cls.os_operator.account_client
1641 cls.container_client = cls.os_operator.container_client
1642 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001643
Chris Dentde456a12014-09-10 12:41:15 +01001644 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301645 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001646 self.account_client.list_account_containers()
1647 LOG.debug('Swift status information obtained successfully')
1648
Chris Dentde456a12014-09-10 12:41:15 +01001649 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301650 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001651 name = container_name or data_utils.rand_name(
1652 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001653 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001654 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001655 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001656 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001657 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001658 self.container_client.delete_container,
1659 name)
Chris Dent0d494112014-08-26 13:48:30 +01001660 return name
1661
Chris Dentde456a12014-09-10 12:41:15 +01001662 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301663 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001664 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001665 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001666
Chris Dentde456a12014-09-10 12:41:15 +01001667 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301668 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001669 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001670 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001671 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001672 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001673 self.object_client.delete_object,
1674 container_name,
1675 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001676 return obj_name, obj_data
1677
Chris Dentde456a12014-09-10 12:41:15 +01001678 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301679 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001680 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001681 self.list_and_check_container_objects(container_name,
1682 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001683
Chris Dentde456a12014-09-10 12:41:15 +01001684 def list_and_check_container_objects(self, container_name,
1685 present_obj=None,
1686 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301687 """List and verify objects for a given container
1688
1689 This utility lists objects for a given container
1690 and asserts which are present and
1691 which are not
1692 """
1693
Ghanshyam2a180b82014-06-16 13:54:22 +09001694 if present_obj is None:
1695 present_obj = []
1696 if not_present_obj is None:
1697 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001698 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001699 container_name)
1700 if present_obj:
1701 for obj in present_obj:
1702 self.assertIn(obj, object_list)
1703 if not_present_obj:
1704 for obj in not_present_obj:
1705 self.assertNotIn(obj, object_list)
1706
Chris Dentde456a12014-09-10 12:41:15 +01001707 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301708 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001709 _, obj = self.object_client.get_object(container_name, obj_name)
1710 self.assertEqual(obj, expected_data)