blob: 1388418c5023b2069c60ffad786c8076b5d0d1a1 [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
Doug Hellmann583ce2c2015-03-11 14:55:46 +000021from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030022from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053023from oslo_utils import netutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040024
lanoux5fc14522015-09-21 08:17:35 +000025from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070026from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090027from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000028from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000029from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000030from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020031from tempest import exceptions
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000032from tempest.lib.common import api_microversion_fixture
33from tempest.lib.common import api_version_utils
Ken'ichi Ohmichibe4fb502017-03-10 10:04:48 -080034from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010035from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050036from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040037import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Matthew Treinish6c072292014-01-29 19:15:52 +000039CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040040
Attila Fazekasfb7552a2013-08-27 13:02:26 +020041LOG = log.getLogger(__name__)
42
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000043LATEST_MICROVERSION = 'latest'
44
Sean Dague6dbc6da2013-05-08 17:49:46 -040045
Andrea Frittoli2e733b52014-07-16 14:12:11 +010046class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010047 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010048
Andrea Frittolib21de6c2015-02-06 20:12:38 +000049 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000050
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000051 compute_min_microversion = None
52 compute_max_microversion = LATEST_MICROVERSION
53 volume_min_microversion = None
54 volume_max_microversion = LATEST_MICROVERSION
55 placement_min_microversion = None
56 placement_max_microversion = LATEST_MICROVERSION
57
58 @classmethod
59 def skip_checks(cls):
60 super(ScenarioTest, cls).skip_checks()
61 api_version_utils.check_skip_with_microversion(
62 cls.compute_min_microversion, cls.compute_max_microversion,
63 CONF.compute.min_microversion, CONF.compute.max_microversion)
64 api_version_utils.check_skip_with_microversion(
65 cls.volume_min_microversion, cls.volume_max_microversion,
66 CONF.volume.min_microversion, CONF.volume.max_microversion)
67 api_version_utils.check_skip_with_microversion(
68 cls.placement_min_microversion, cls.placement_max_microversion,
69 CONF.placement.min_microversion, CONF.placement.max_microversion)
70
71 @classmethod
72 def resource_setup(cls):
73 super(ScenarioTest, cls).resource_setup()
74 cls.compute_request_microversion = (
75 api_version_utils.select_request_microversion(
76 cls.compute_min_microversion,
77 CONF.compute.min_microversion))
78 cls.volume_request_microversion = (
79 api_version_utils.select_request_microversion(
80 cls.volume_min_microversion,
81 CONF.volume.min_microversion))
82 cls.placement_request_microversion = (
83 api_version_utils.select_request_microversion(
84 cls.placement_min_microversion,
85 CONF.placement.min_microversion))
86
87 def setUp(self):
88 super(ScenarioTest, self).setUp()
89 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
90 compute_microversion=self.compute_request_microversion,
91 volume_microversion=self.volume_request_microversion,
92 placement_microversion=self.placement_request_microversion))
93
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053094 def setup_compute_client(cls):
95 """Compute and Compute security groups client"""
96 cls.compute_images_client = cls.os_primary.compute_images_client
97 cls.keypairs_client = cls.os_primary.keypairs_client
98 cls.compute_security_groups_client = (
99 cls.os_primary.compute_security_groups_client)
100 cls.compute_security_group_rules_client = (
101 cls.os_primary.compute_security_group_rules_client)
102 cls.servers_client = cls.os_primary.servers_client
103 cls.interface_client = cls.os_primary.interfaces_client
104
105 def setup_network_client(cls):
106 """Neutron network client"""
107 cls.networks_client = cls.os_primary.networks_client
108 cls.ports_client = cls.os_primary.ports_client
109 cls.routers_client = cls.os_primary.routers_client
110 cls.subnets_client = cls.os_primary.subnets_client
111 cls.floating_ips_client = cls.os_primary.floating_ips_client
112 cls.security_groups_client = cls.os_primary.security_groups_client
113 cls.security_group_rules_client = (
114 cls.os_primary.security_group_rules_client)
115
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000116 @classmethod
117 def setup_clients(cls):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530118 """This setup the service clients for the tests"""
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000119 super(ScenarioTest, cls).setup_clients()
jeremy.zhang0343be52017-05-25 21:29:57 +0800120 cls.flavors_client = cls.os_primary.flavors_client
John Warrene74890a2015-11-11 15:18:01 -0500121 cls.compute_floating_ips_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +0800122 cls.os_primary.compute_floating_ips_client)
Jordan Pittier1d2e40f2016-01-05 18:49:14 +0100123 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400124 # Check if glance v1 is available to determine which client to use.
125 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +0800126 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400127 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +0800128 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400129 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400130 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400131 'Either api_v1 or api_v2 must be True in '
132 '[image-feature-enabled].')
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530133
134 cls.setup_compute_client(cls)
135 cls.setup_network_client(cls)
Andrea Frittolia6b30152017-08-04 10:46:10 +0100136 if CONF.service_available.cinder:
137 cls.volumes_client = cls.os_primary.volumes_client_latest
138 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +0300139 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300140
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200141 # ## Test functions library
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200142 # The create_[resource] functions only return body and discard the
143 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +0100144
zhufl1e446b52017-10-16 16:54:57 +0800145 def create_port(self, network_id, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530146 """Creates port"""
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300147 if not client:
148 client = self.ports_client
zhufl1e446b52017-10-16 16:54:57 +0800149 name = data_utils.rand_name(self.__class__.__name__)
Edan David408a97b2018-01-15 03:52:15 -0500150 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
151 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
152 if CONF.network.port_profile and 'binding:profile' not in kwargs:
153 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300154 result = client.create_port(
155 name=name,
156 network_id=network_id,
157 **kwargs)
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300158 port = result['port']
159 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
160 client.delete_port, port['id'])
161 return port
162
Yair Frieddb6c9e92014-08-06 08:53:13 +0300163 def create_keypair(self, client=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530164 """Creates keypair
165
166 Keypair is a public key of OpenSSH key pair used for accessing
167 and create servers
168 Keypair can also be created by a private key for the same purpose
169 Here, the keys are randomly generated[public/private]
170 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300171 if not client:
172 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100173 name = data_utils.rand_name(self.__class__.__name__)
174 # We don't need to create a keypair by pubkey in scenario
Ken'ichi Ohmichie364bce2015-07-17 10:27:59 +0000175 body = client.create_keypair(name=name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300176 self.addCleanup(client.delete_keypair, name)
ghanshyamdee01f22015-08-17 11:41:47 +0900177 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100178
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530179 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800180 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200181 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000182 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100183
lanoux5fc14522015-09-21 08:17:35 +0000184 This wrapper utility calls the common create test server and
185 returns a test server. The purpose of this wrapper is to minimize
186 the impact on the code of the tests already using this
187 function.
Noam Angel6e309952019-01-27 05:52:40 +0000188
189 :param **kwargs:
190 See extra parameters below
191
192 :Keyword Arguments:
193 * *vnic_type* (``string``) --
194 used when launching instances with pre-configured ports.
195 Examples:
196 normal: a traditional virtual port that is either attached
197 to a linux bridge or an openvswitch bridge on a
198 compute node.
199 direct: an SR-IOV port that is directly attached to a VM
200 macvtap: an SR-IOV port that is attached to a VM via a macvtap
201 device.
202 Defaults to ``CONF.network.port_vnic_type``.
203 * *port_profile* (``dict``) --
204 This attribute is a dictionary that can be used (with admin
205 credentials) to supply information influencing the binding of
206 the port.
207 example: port_profile = "capabilities:[switchdev]"
208 Defaults to ``CONF.network.port_profile``.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100209 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100210
lanoux5fc14522015-09-21 08:17:35 +0000211 # NOTE(jlanoux): As a first step, ssh checks in the scenario
212 # tests need to be run regardless of the run_validation and
213 # validatable parameters and thus until the ssh validation job
214 # becomes voting in CI. The test resources management and IP
215 # association are taken care of in the scenario tests.
216 # Therefore, the validatable parameter is set to false in all
217 # those tests. In this way create_server just return a standard
218 # server and the scenario tests always perform ssh checks.
219
220 # Needed for the cross_tenant_traffic test:
221 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800222 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000223
zhufl24208c22016-10-25 15:23:48 +0800224 if name is None:
225 name = data_utils.rand_name(self.__class__.__name__ + "-server")
226
Noam Angel6e309952019-01-27 05:52:40 +0000227 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
228 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000229
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000230 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000231 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000232 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000233 ports = []
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000234 create_port_body = {}
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300235
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000236 if vnic_type:
237 create_port_body['binding:vnic_type'] = vnic_type
238
239 if profile:
240 create_port_body['binding:profile'] = profile
241
lanoux5fc14522015-09-21 08:17:35 +0000242 if kwargs:
243 # Convert security group names to security group ids
244 # to pass to create_port
245 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300246 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500247 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000248 ).get('security_groups')
249 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100250 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000251
252 sec_groups_names = [s['name'] for s in kwargs.pop(
253 'security_groups')]
254 security_groups_ids = [sec_dict[s]
255 for s in sec_groups_names]
256
257 if security_groups_ids:
258 create_port_body[
259 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300260 networks = kwargs.pop('networks', [])
261 else:
262 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000263
264 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300265 # for the project's private networks and create a port.
266 # The same behaviour as we would expect when passing
267 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000268 if not networks:
269 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300270 **{'router:external': False, 'fields': 'id'})['networks']
271
272 # It's net['uuid'] if networks come from kwargs
273 # and net['id'] if they come from
274 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000275 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000276 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300277 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800278 port = self.create_port(network_id=net_id,
279 client=clients.ports_client,
280 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300281 ports.append({'port': port['id']})
282 else:
283 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000284 if ports:
285 kwargs['networks'] = ports
286 self.ports = ports
287
288 tenant_network = self.get_tenant_network()
289
Marc Koderer979e4942016-12-08 10:07:59 +0100290 if CONF.compute.compute_volume_common_az:
291 kwargs.setdefault('availability_zone',
292 CONF.compute.compute_volume_common_az)
293
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200294 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000295 clients,
296 tenant_network=tenant_network,
297 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530298 name=name, flavor=flavor,
299 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000300
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200301 self.addCleanup(waiters.wait_for_server_termination,
302 clients.servers_client, body['id'])
303 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
304 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000305 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100306 return server
307
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100308 def create_volume(self, size=None, name=None, snapshot_id=None,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100309 imageRef=None, volume_type=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530310 """Creates volume
311
312 This wrapper utility creates volume and waits for volume to be
313 in 'available' state.
314 This method returns the volume's full representation by GET request.
315 """
316
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700317 if size is None:
318 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500319 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800320 if CONF.image_feature_enabled.api_v1:
321 resp = self.image_client.check_image(imageRef)
322 image = common_image.get_image_meta_from_headers(resp)
323 else:
324 image = self.image_client.show_image(imageRef)
325 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500326 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100327 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800328 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900329 kwargs = {'display_name': name,
330 'snapshot_id': snapshot_id,
331 'imageRef': imageRef,
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700332 'volume_type': volume_type,
333 'size': size}
Marc Koderer979e4942016-12-08 10:07:59 +0100334
335 if CONF.compute.compute_volume_common_az:
336 kwargs.setdefault('availability_zone',
337 CONF.compute.compute_volume_common_az)
338
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900339 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700340
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100341 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
342 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100343 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100344 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300345 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200346 waiters.wait_for_volume_resource_status(self.volumes_client,
347 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100348 # The volume retrieved on creation has a non-up-to-date status.
349 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000350 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100351 return volume
352
lkuchlane20e6a82018-05-08 11:28:46 +0300353 def create_backup(self, volume_id, name=None, description=None,
354 force=False, snapshot_id=None, incremental=False,
355 container=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530356 """Creates backup
357
358 This wrapper utility creates backup and waits for backup to be
359 in 'available' state.
360 """
lkuchlane20e6a82018-05-08 11:28:46 +0300361
362 name = name or data_utils.rand_name(
363 self.__class__.__name__ + "-backup")
364 kwargs = {'name': name,
365 'description': description,
366 'force': force,
367 'snapshot_id': snapshot_id,
368 'incremental': incremental,
369 'container': container}
370 backup = self.backups_client.create_backup(volume_id=volume_id,
371 **kwargs)['backup']
372 self.addCleanup(self.backups_client.delete_backup, backup['id'])
373 waiters.wait_for_volume_resource_status(self.backups_client,
374 backup['id'], 'available')
375 return backup
376
377 def restore_backup(self, backup_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530378 """Restore backup
379
380 This wrapper utility restores backup and waits for backup to be
381 in 'available' state.
382 """
383
lkuchlane20e6a82018-05-08 11:28:46 +0300384 restore = self.backups_client.restore_backup(backup_id)['restore']
385 self.addCleanup(self.volumes_client.delete_volume,
386 restore['volume_id'])
387 waiters.wait_for_volume_resource_status(self.backups_client,
388 backup_id, 'available')
389 waiters.wait_for_volume_resource_status(self.volumes_client,
390 restore['volume_id'],
391 'available')
392 self.assertEqual(backup_id, restore['backup_id'])
393 return restore
394
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530395 def rebuild_server(self, server_id, image=None,
396 preserve_ephemeral=False, wait=True,
397 rebuild_kwargs=None):
398 if image is None:
399 image = CONF.compute.image_ref
400 rebuild_kwargs = rebuild_kwargs or {}
401 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
402 server_id, image, preserve_ephemeral)
403 self.servers_client.rebuild_server(
404 server_id=server_id,
405 image_ref=image,
406 preserve_ephemeral=preserve_ephemeral,
407 **rebuild_kwargs)
408 if wait:
409 waiters.wait_for_server_status(self.servers_client,
410 server_id, 'ACTIVE')
411
lkuchlan73ed1f32017-07-06 16:22:12 +0300412 def create_volume_snapshot(self, volume_id, name=None, description=None,
413 metadata=None, force=False):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530414 """Creates volume
415
416 This wrapper utility creates volume snapshot and waits for backup
417 to be in 'available' state.
418 """
419
lkuchlan73ed1f32017-07-06 16:22:12 +0300420 name = name or data_utils.rand_name(
421 self.__class__.__name__ + '-snapshot')
422 snapshot = self.snapshots_client.create_snapshot(
423 volume_id=volume_id,
424 force=force,
425 display_name=name,
426 description=description,
427 metadata=metadata)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530428
lkuchlan73ed1f32017-07-06 16:22:12 +0300429 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
430 snapshot['id'])
431 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
432 waiters.wait_for_volume_resource_status(self.snapshots_client,
433 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200434 snapshot = self.snapshots_client.show_snapshot(
435 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300436 return snapshot
437
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100438 def _cleanup_volume_type(self, volume_type):
439 """Clean up a given volume type.
440
441 Ensuring all volumes associated to a type are first removed before
442 attempting to remove the type itself. This includes any image volume
443 cache volumes stored in a separate tenant to the original volumes
444 created from the type.
445 """
446 admin_volume_type_client = self.os_admin.volume_types_client_latest
447 admin_volumes_client = self.os_admin.volumes_client_latest
448 volumes = admin_volumes_client.list_volumes(
449 detail=True, params={'all_tenants': 1})['volumes']
450 type_name = volume_type['name']
451 for volume in [v for v in volumes if v['volume_type'] == type_name]:
452 test_utils.call_and_ignore_notfound_exc(
453 admin_volumes_client.delete_volume, volume['id'])
454 admin_volumes_client.wait_for_resource_deletion(volume['id'])
455 admin_volume_type_client.delete_volume_type(volume_type['id'])
456
scottda61f68ac2016-06-07 12:07:55 -0600457 def create_volume_type(self, client=None, name=None, backend_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530458 """Creates volume type
459
460 In a multiple-storage back-end configuration,
461 each back end has a name (volume_backend_name).
462 The name of the back end is declared as an extra-specification
463 of a volume type (such as, volume_backend_name=LVM).
464 When a volume is created, the scheduler chooses an
465 appropriate back end to handle the request, according
466 to the volume type specified by the user.
467 The scheduler uses volume types to explicitly create volumes on
468 specific back ends.
469
470 Before using volume type, a volume type has to be declared
471 to Block Storage. In addition to that, an extra-specification
472 has to be created to link the volume type to a back end name.
473 """
474
scottda61f68ac2016-06-07 12:07:55 -0600475 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000476 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000477 if not name:
478 class_name = self.__class__.__name__
479 name = data_utils.rand_name(class_name + '-volume-type')
480 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600481
482 LOG.debug("Creating a volume type: %s on backend %s",
483 randomized_name, backend_name)
484 extra_specs = {}
485 if backend_name:
486 extra_specs = {"volume_backend_name": backend_name}
487
lkuchlanbbabe542017-09-26 10:47:23 +0300488 volume_type = client.create_volume_type(
489 name=randomized_name, extra_specs=extra_specs)['volume_type']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530490 self.assertIn('id', volume_type)
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100491 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600492 return volume_type
493
Yair Fried1fc32a12014-08-04 09:11:30 +0300494 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500495 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500496 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100497 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900498 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100499 for sg in sgs:
500 if sg['name'] == 'default':
501 secgroup_id = sg['id']
502
503 # These rules are intended to permit inbound ssh and icmp
504 # traffic from all sources, so no group_id is provided.
505 # Setting a group_id would only permit traffic from ports
506 # belonging to the same security group.
507 rulesets = [
508 {
509 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000510 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100511 'from_port': 22,
512 'to_port': 22,
513 'cidr': '0.0.0.0/0',
514 },
515 {
516 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000517 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100518 'from_port': -1,
519 'to_port': -1,
520 'cidr': '0.0.0.0/0',
521 }
522 ]
523 rules = list()
524 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000525 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900526 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100527 rules.append(sg_rule)
528 return rules
529
Yair Fried1fc32a12014-08-04 09:11:30 +0300530 def _create_security_group(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530531 """Create security group and add rules to security group"""
Andrea Frittoli247058f2014-07-16 16:09:22 +0100532 sg_name = data_utils.rand_name(self.__class__.__name__)
533 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500534 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900535 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100536 self.assertEqual(secgroup['name'], sg_name)
537 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500538 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100539 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500540 self.compute_security_groups_client.delete_security_group,
541 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100542
543 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300544 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100545 return secgroup
546
zhuflf52c7592017-05-25 13:55:24 +0800547 def get_remote_client(self, ip_address, username=None, private_key=None,
548 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100549 """Get a SSH client to a remote server
550
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600551 :param ip_address: the server floating or fixed IP address to use
552 for ssh validation
553 :param username: name of the Linux account on the remote server
554 :param private_key: the SSH private key to use
555 :param server: server dict, used for debugging purposes
556 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100557 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700558
Andrea Frittoli247058f2014-07-16 16:09:22 +0100559 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800560 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800561 # Set this with 'keypair' or others to log in with keypair or
562 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000563 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800564 password = None
565 if private_key is None:
566 private_key = self.keypair['private_key']
567 else:
lanoux283273b2015-12-04 03:01:54 -0800568 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800569 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800570 linux_client = remote_client.RemoteClient(
571 ip_address, username, pkey=private_key, password=password,
572 server=server, servers_client=self.servers_client)
573 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100574 return linux_client
575
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530576 def image_create(self, name='scenario-img'):
Martin Kopec02af6a42020-03-03 12:39:12 +0000577 img_path = CONF.scenario.img_file
578 if not os.path.exists(img_path):
579 # TODO(kopecmartin): replace LOG.warning for rasing
580 # InvalidConfiguration exception after tempest 25.0.0 is
581 # released - there will be one release which accepts both
582 # behaviors in order to avoid many failures across CIs and etc.
583 LOG.warning(
584 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
585 'a full path for the image. CONF.scenario.img_dir was '
586 'deprecated and will be removed in the next release. Till '
587 'Tempest 25.0.0, old behavior is maintained and keep working '
588 'but starting Tempest 26.0.0, you need to specify the full '
589 'path in CONF.scenario.img_file config option.')
590 img_path = os.path.join(CONF.scenario.img_dir, img_path)
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300591 img_container_format = CONF.scenario.img_container_format
592 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000593 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400594 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000595 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100596 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000597 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530598 if img_properties is None:
599 img_properties = {}
600 name = data_utils.rand_name('%s-' % name)
601 params = {
602 'name': name,
603 'container_format': img_container_format,
604 'disk_format': img_disk_format or img_container_format,
605 }
606 if CONF.image_feature_enabled.api_v1:
607 params['is_public'] = 'False'
608 if img_properties:
609 params['properties'] = img_properties
610 params = {'headers': common_image.image_meta_to_headers(**params)}
611 else:
612 params['visibility'] = 'private'
613 # Additional properties are flattened out in the v2 API.
614 if img_properties:
615 params.update(img_properties)
616 body = self.image_client.create_image(**params)
617 image = body['image'] if 'image' in body else body
618 self.addCleanup(self.image_client.delete_image, image['id'])
619 self.assertEqual("queued", image['status'])
620 with open(img_path, 'rb') as image_file:
621 if CONF.image_feature_enabled.api_v1:
622 self.image_client.update_image(image['id'], data=image_file)
623 else:
624 self.image_client.store_image_file(image['id'], image_file)
625 LOG.debug("image:%s", image['id'])
626 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100627
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700628 def _log_console_output(self, servers=None, client=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530629 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400630 if not CONF.compute_feature_enabled.console_output:
631 LOG.debug('Console output not supported, cannot log')
632 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700633 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100634 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700635 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100636 servers = servers['servers']
637 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100638 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700639 console_output = client.get_console_output(
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100640 server['id'])['output']
641 LOG.debug('Console output for %s\nbody=\n%s',
642 server['id'], console_output)
643 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100644 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100645 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100646
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000647 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530648 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300649 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000650 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000651
nithya-ganesan882595e2014-07-29 18:51:07 +0000652 def create_server_snapshot(self, server, name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530653 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000654 # Glance client
655 _image_client = self.image_client
656 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900657 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000658 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800659 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000660 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000661 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500662 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300663 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200664
665 self.addCleanup(_image_client.wait_for_resource_deletion,
666 image_id)
667 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
668 _image_client.delete_image, image_id)
669
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400670 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530671 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700672 resp = _image_client.check_image(image_id)
673 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400674 image_props = snapshot_image.get('properties', {})
675 else:
676 # In glance v2 the additional properties are flattened.
677 snapshot_image = _image_client.show_image(image_id)
678 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300679
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400680 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300681 if bdm:
682 bdm = json.loads(bdm)
683 if bdm and 'snapshot_id' in bdm[0]:
684 snapshot_id = bdm[0]['snapshot_id']
685 self.addCleanup(
686 self.snapshots_client.wait_for_resource_deletion,
687 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100688 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
689 self.snapshots_client.delete_snapshot,
690 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200691 waiters.wait_for_volume_resource_status(self.snapshots_client,
692 snapshot_id,
693 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000694 image_name = snapshot_image['name']
695 self.assertEqual(name, image_name)
696 LOG.debug("Created snapshot image %s for server %s",
697 image_name, server['name'])
698 return snapshot_image
699
Jordan Pittier7cf64762015-10-14 15:01:12 +0200700 def nova_volume_attach(self, server, volume_to_attach):
Soniya Vyasae631132020-08-28 13:37:12 +0530701 """Compute volume attach
702
703 This utility attaches volume from compute and waits for the
704 volume status to be 'in-use' state.
705 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000706 volume = self.servers_client.attach_volume(
Paras Babbar4b45f9e2019-12-11 16:51:57 -0500707 server['id'], volumeId=volume_to_attach['id'])['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200708 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200709 waiters.wait_for_volume_resource_status(self.volumes_client,
710 volume['id'], 'in-use')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200711 # Return the updated volume after the attachment
712 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900713
Jordan Pittier7cf64762015-10-14 15:01:12 +0200714 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530715 """Compute volume detach
716
717 This utility detaches volume from compute and check whether the
718 volume status is 'available' state, and if not, an exception
719 will be thrown.
720 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200721 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200722 waiters.wait_for_volume_resource_status(self.volumes_client,
723 volume['id'], 'available')
Soniya Vyasae631132020-08-28 13:37:12 +0530724 volume = self.volumes_client.show_volume(volume['id'])['volume']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200725
Steven Hardyda2a8352014-10-02 12:52:20 +0100726 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800727 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530728 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000729 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000730 cmd = ['ping', '-c1', '-w1']
731
732 if mtu:
733 cmd += [
734 # don't fragment
735 '-M', 'do',
736 # ping receives just the size of ICMP payload
737 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
738 ]
739 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700740
741 def ping():
742 proc = subprocess.Popen(cmd,
743 stdout=subprocess.PIPE,
744 stderr=subprocess.PIPE)
745 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000746
Aaron Rosena7df13b2014-09-23 09:45:45 -0700747 return (proc.returncode == 0) == should_succeed
748
Jordan Pittier9e227c52016-02-09 14:35:18 +0100749 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000750 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800751 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000752 'caller': caller, 'ip': ip_address, 'timeout': timeout,
753 'should_succeed':
754 'reachable' if should_succeed else 'unreachable'
755 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200756 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000757 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800758 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000759 'caller': caller, 'ip': ip_address, 'timeout': timeout,
760 'result': 'expected' if result else 'unexpected'
761 })
zhufl0ec74c42017-11-15 14:02:28 +0800762 if server:
763 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000764 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700765
Yair Friedae0e73d2014-11-24 11:56:26 +0200766 def check_vm_connectivity(self, ip_address,
767 username=None,
768 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000769 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800770 extra_msg="",
771 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000772 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000773 """Check server connectivity
774
Yair Friedae0e73d2014-11-24 11:56:26 +0200775 :param ip_address: server to test against
776 :param username: server's ssh username
777 :param private_key: server's ssh private key to be used
778 :param should_connect: True/False indicates positive/negative test
779 positive - attempt ping and ssh
780 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800781 :param extra_msg: Message to help with debugging if ``ping_ip_address``
782 fails
783 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000784 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200785
786 :raises: AssertError if the result of the connectivity check does
787 not match the value of the should_connect param
788 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530789
zhufl0ec74c42017-11-15 14:02:28 +0800790 LOG.debug('checking network connections to IP %s with user: %s',
791 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200792 if should_connect:
793 msg = "Timed out waiting for %s to become reachable" % ip_address
794 else:
795 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800796 if extra_msg:
797 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200798 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000799 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800800 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200801 msg=msg)
802 if should_connect:
803 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800804 try:
805 self.get_remote_client(ip_address, username, private_key,
806 server=server)
807 except Exception:
808 if not extra_msg:
809 extra_msg = 'Failed to ssh to %s' % ip_address
810 LOG.exception(extra_msg)
811 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200812
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530813 def create_floating_ip(self, server, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000814 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200815
Marc Koderer3b57d802016-03-22 15:23:31 +0100816 if not pool_name:
817 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500818 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000819 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100820 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500821 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200822 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500823 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530824 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200825 return floating_ip
826
Sean Dague20e98612016-01-06 14:33:28 -0500827 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200828 private_key=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530829 """Creates timestamp
830
831 This wrapper utility does ssh, creates timestamp and returns the
832 created timestamp.
833 """
834
Sean Dague20e98612016-01-06 14:33:28 -0500835 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200836 private_key=private_key,
837 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300838 if dev_name is not None:
839 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800840 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
841 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300842 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
843 ssh_client.exec_command(cmd_timestamp)
844 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
845 % mount_path)
846 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800847 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300848 return timestamp
849
Sean Dague20e98612016-01-06 14:33:28 -0500850 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200851 private_key=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530852 """Returns timestamp
853
854 This wrapper utility does ssh and returns the timestamp.
855 """
856
Sean Dague20e98612016-01-06 14:33:28 -0500857 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200858 private_key=private_key,
859 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300860 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700861 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300862 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
863 % mount_path)
864 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800865 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300866 return timestamp
867
Sean Dague20e98612016-01-06 14:33:28 -0500868 def get_server_ip(self, server):
869 """Get the server fixed or floating IP.
870
871 Based on the configuration we're in, return a correct ip
872 address for validating that a guest is up.
873 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530874
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200875 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500876 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800877 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500878 # method is creating the floating IP there.
879 return self.create_floating_ip(server)['ip']
880 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400881 # Determine the network name to look for based on config or creds
882 # provider network resources.
883 if CONF.validation.network_for_ssh:
884 addresses = server['addresses'][
885 CONF.validation.network_for_ssh]
886 else:
zhufl7b4a7202017-09-28 10:29:27 +0800887 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400888 addresses = (server['addresses'][network['name']]
889 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500890 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200891 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
892 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500893 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800894 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200895 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400896 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200897
zhufl7bc916d2018-08-22 14:47:39 +0800898 @classmethod
899 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530900 """Gets host of server"""
901
zhufl7bc916d2018-08-22 14:47:39 +0800902 server_details = cls.os_admin.servers_client.show_server(server_id)
903 return server_details['server']['OS-EXT-SRV-ATTR:host']
904
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000905 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
906 bd_map_v2 = [{
907 'uuid': source_id,
908 'source_type': source_type,
909 'destination_type': 'volume',
910 'boot_index': 0,
911 'delete_on_termination': delete_on_termination}]
912 return {'block_device_mapping_v2': bd_map_v2}
913
914 def boot_instance_from_resource(self, source_id,
915 source_type,
916 keypair=None,
917 security_group=None,
918 delete_on_termination=False,
919 name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530920 """Boot instance from resource
921
922 This wrapper utility boots instance from resource with block device
923 mapping with source info passed in arguments
924 """
925
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000926 create_kwargs = dict()
927 if keypair:
928 create_kwargs['key_name'] = keypair['name']
929 if security_group:
930 create_kwargs['security_groups'] = [
931 {'name': security_group['name']}]
932 create_kwargs.update(self._get_bdm(
933 source_id,
934 source_type,
935 delete_on_termination=delete_on_termination))
936 if name:
937 create_kwargs['name'] = name
938
939 return self.create_server(image_id='', **create_kwargs)
940
941 def create_volume_from_image(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530942 """Create volume from image"""
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000943 img_uuid = CONF.compute.image_ref
944 vol_name = data_utils.rand_name(
945 self.__class__.__name__ + '-volume-origin')
946 return self.create_volume(name=vol_name, imageRef=img_uuid)
947
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100948
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100949class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300950 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000951
Yair Fried1fc32a12014-08-04 09:11:30 +0300952 This class provide helpers for network scenario tests, using the neutron
953 API. Helpers from ancestor which use the nova network API are overridden
954 with the neutron API.
955
956 This Class also enforces using Neutron instead of novanetwork.
957 Subclassed tests will be skipped if Neutron is not enabled
958
959 """
960
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000961 credentials = ['primary', 'admin']
962
Yair Fried1fc32a12014-08-04 09:11:30 +0300963 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000964 def skip_checks(cls):
965 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100966 if not CONF.service_available.neutron:
967 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300968
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700969 def _create_network(self, networks_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +0000970 project_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200971 namestart='network-smoke-',
Lajos Katonac87a06b2019-01-04 13:21:48 +0100972 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -0400973 if not networks_client:
974 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +0000975 if not project_id:
976 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300977 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +0000978 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +0100979 if net_dict:
980 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400981 # Neutron disables port security by default so we have to check the
982 # config before trying to create the network with port_security_enabled
983 if CONF.network_feature_enabled.port_security:
984 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200985 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500986 network = result['network']
987
988 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100989 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800990 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -0500991 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300992 return network
993
zhufl5b0a52f2017-10-24 15:48:20 +0800994 def create_subnet(self, network, subnets_client=None,
995 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000996 """Create a subnet for the given network
997
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530998 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000999 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301000
1001 :param **kwargs:
1002 See extra parameters below
1003
1004 :Keyword Arguments:
1005
1006 * *ip_version = ip version of the given network,
Yair Fried1fc32a12014-08-04 09:11:30 +03001007 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301008
John Warren3961acd2015-10-02 14:38:53 -04001009 if not subnets_client:
1010 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001011
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001012 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001013 """Check cidr existence
1014
lei zhangdd552b22015-11-25 20:41:48 +08001015 :returns: True if subnet with cidr already exist in tenant
1016 False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001017 """
jeremy.zhang5870ff12017-05-25 11:24:23 +08001018 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001019 project_id=project_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +03001020 return len(cidr_in_use) != 0
1021
Kirill Shileev14113572014-11-21 16:58:02 +03001022 ip_version = kwargs.pop('ip_version', 4)
1023
1024 if ip_version == 6:
1025 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -04001026 CONF.network.project_network_v6_cidr)
1027 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001028 else:
Sean Dagueed6e5862016-04-04 10:49:13 -04001029 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
1030 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001031
Yair Fried1fc32a12014-08-04 09:11:30 +03001032 result = None
Kirill Shileev14113572014-11-21 16:58:02 +03001033 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +03001034 # Repeatedly attempt subnet creation with sequential cidr
1035 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +03001036 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +03001037 str_cidr = str(subnet_cidr)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001038 if cidr_in_use(str_cidr, project_id=network['project_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +03001039 continue
1040
1041 subnet = dict(
1042 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001043 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001044 project_id=network['project_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +03001045 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +03001046 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001047 **kwargs
1048 )
1049 try:
John Warren3961acd2015-10-02 14:38:53 -04001050 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +03001051 break
Masayuki Igawad9388762015-01-20 14:56:42 +09001052 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +03001053 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1054 if not is_overlapping_cidr:
1055 raise
1056 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001057
1058 subnet = result['subnet']
1059 self.assertEqual(subnet['cidr'], str_cidr)
1060
1061 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1062 subnets_client.delete_subnet, subnet['id'])
1063
Yair Fried1fc32a12014-08-04 09:11:30 +03001064 return subnet
1065
Kirill Shileev14113572014-11-21 16:58:02 +03001066 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu95a31692018-06-13 23:17:54 +00001067 if ip_addr:
1068 ports = self.os_admin.ports_client.list_ports(
1069 device_id=server['id'],
1070 fixed_ips='ip_address=%s' % ip_addr)['ports']
1071 else:
1072 ports = self.os_admin.ports_client.list_ports(
1073 device_id=server['id'])['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +03001074 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001075 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1076 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001077
1078 def _is_active(port):
1079 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1080 # servers. Neutron does not bind ports for Ironic instances, as a
1081 # result the port remains in the DOWN state. This has been fixed
1082 # with the introduction of the networking-baremetal plugin but
1083 # it's not mandatory (and is not used on all stable branches).
1084 return (port['status'] == 'ACTIVE' or
1085 port.get('binding:vnic_type') == 'baremetal')
1086
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001087 port_map = [(p["id"], fxip["ip_address"])
1088 for p in ports
1089 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001090 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001091 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001092 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1093 if inactive:
1094 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001095
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001096 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001097 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001098 self.assertEqual(len(port_map), 1,
1099 "Found multiple IPv4 addresses: %s. "
1100 "Unable to determine which port to target."
1101 % port_map)
1102 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001103
David Shrewsbury9bac3662014-08-07 15:07:01 -04001104 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001105 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001106 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001107 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001108 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001109 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001110
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301111 def create_floating_ip(self, server, external_network_id=None,
Yair Friedae0e73d2014-11-24 11:56:26 +02001112 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001113 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301114
Yair Friedae0e73d2014-11-24 11:56:26 +02001115 if not external_network_id:
1116 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001117 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001118 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001119 if not port_id:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301120 port_id, ip4 = self._get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001121 else:
1122 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001123
1124 kwargs = {
1125 'floating_network_id': external_network_id,
1126 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301127 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001128 'fixed_ip_address': ip4,
1129 }
1130 if CONF.network.subnet_id:
1131 kwargs['subnet_id'] = CONF.network.subnet_id
1132 result = client.create_floatingip(**kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001133 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001134
Jordan Pittier9e227c52016-02-09 14:35:18 +01001135 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001136 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001137 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001138 return floating_ip
1139
Yair Fried45f92952014-06-26 05:19:19 +03001140 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001141 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001142
Steve Heyman33735f22016-05-24 09:28:08 -05001143 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001144 :param status: target status
1145 :raises: AssertionError if status doesn't match
1146 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301147
Steve Heyman33735f22016-05-24 09:28:08 -05001148 floatingip_id = floating_ip['id']
1149
Carl Baldwina754e2d2014-10-23 22:47:41 +00001150 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001151 floating_ip = (self.floating_ips_client.
1152 show_floatingip(floatingip_id)['floatingip'])
1153 if status == floating_ip['status']:
1154 LOG.info("FloatingIP: {fp} is at status: {st}"
1155 .format(fp=floating_ip, st=status))
1156 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001157
zhufl4dda94e2017-03-14 16:14:46 +08001158 if not test_utils.call_until_true(refresh,
1159 CONF.network.build_timeout,
1160 CONF.network.build_interval):
1161 floating_ip = self.floating_ips_client.show_floatingip(
1162 floatingip_id)['floatingip']
1163 self.assertEqual(status, floating_ip['status'],
1164 message="FloatingIP: {fp} is at status: {cst}. "
1165 "failed to reach status: {st}"
1166 .format(fp=floating_ip, cst=floating_ip['status'],
1167 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001168
zhufl420a0192017-09-28 11:04:50 +08001169 def check_tenant_network_connectivity(self, server,
1170 username,
1171 private_key,
1172 should_connect=True,
1173 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301174 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001175 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001176 msg = 'Tenant networks not configured to be reachable.'
1177 LOG.info(msg)
1178 return
1179 # The target login is assumed to have been configured for
1180 # key-based authentication by cloud-init.
1181 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001182 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001183 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001184 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001185 username,
1186 private_key,
1187 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001188 except Exception as e:
1189 LOG.exception('Tenant network connectivity check failed')
1190 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001191 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001192 raise
1193
zhufle9877c62017-10-13 09:38:19 +08001194 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001195 nic=None, protocol='icmp'):
1196 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001197
Claudiu Belu33c3e602014-08-28 16:38:01 +03001198 :param source: RemoteClient: an ssh connection from which to execute
1199 the check
1200 :param dest: an IP to check connectivity against
1201 :param should_succeed: boolean should connection succeed or not
1202 :param nic: specific network interface to test connectivity from
1203 :param protocol: the protocol used to test connectivity with.
1204 :returns: True, if the connection succeeded and it was expected to
1205 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001206 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301207
Claudiu Belu33c3e602014-08-28 16:38:01 +03001208 method_name = '%s_check' % protocol
1209 connectivity_checker = getattr(source, method_name)
1210
1211 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001212 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001213 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001214 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001215 LOG.warning('Failed to check %(protocol)s connectivity for '
1216 'IP %(dest)s via a ssh connection from: %(src)s.',
1217 dict(protocol=protocol, dest=dest,
1218 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001219 return not should_succeed
1220 return should_succeed
1221
Claudiu Belu33c3e602014-08-28 16:38:01 +03001222 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001223 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001224 if result:
1225 return
1226
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001227 source_host = source.ssh_client.host
1228 if should_succeed:
1229 msg = "Timed out waiting for %s to become reachable from %s" \
1230 % (dest, source_host)
1231 else:
1232 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001233 self._log_console_output()
1234 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001235
John Warren456d9ae2016-01-12 15:36:33 -05001236 def _create_security_group(self, security_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001237 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001238 namestart='secgroup-smoke',
1239 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001240 if security_group_rules_client is None:
1241 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001242 if security_groups_client is None:
1243 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001244 if project_id is None:
1245 project_id = security_groups_client.project_id
John Warrenf9606e92015-12-10 12:12:42 -05001246 secgroup = self._create_empty_security_group(
1247 namestart=namestart, client=security_groups_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001248 project_id=project_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001249
1250 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -05001251 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001252 security_group_rules_client=security_group_rules_client,
1253 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001254 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001255 for rule in rules:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001256 self.assertEqual(project_id, rule['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001257 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001258 return secgroup
1259
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001260 def _create_empty_security_group(self, client=None, project_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001261 namestart='secgroup-smoke'):
1262 """Create a security group without rules.
1263
1264 Default rules will be created:
1265 - IPv4 egress to any
1266 - IPv6 egress to any
1267
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001268 :param project_id: secgroup will be created in this project
Steve Heyman33735f22016-05-24 09:28:08 -05001269 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001270 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301271
Yair Fried1fc32a12014-08-04 09:11:30 +03001272 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001273 client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001274 if not project_id:
1275 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001276 sg_name = data_utils.rand_name(namestart)
1277 sg_desc = sg_name + " description"
1278 sg_dict = dict(name=sg_name,
1279 description=sg_desc)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001280 sg_dict['project_id'] = project_id
David Kranz34e88122014-12-11 15:24:05 -05001281 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001282
1283 secgroup = result['security_group']
1284 self.assertEqual(secgroup['name'], sg_name)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001285 self.assertEqual(project_id, secgroup['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001286 self.assertEqual(secgroup['description'], sg_desc)
1287
Jordan Pittier9e227c52016-02-09 14:35:18 +01001288 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001289 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001290 return secgroup
1291
John Warren456d9ae2016-01-12 15:36:33 -05001292 def _create_security_group_rule(self, secgroup=None,
1293 sec_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001294 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001295 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001296 """Create a rule from a dictionary of rule parameters.
1297
1298 Create a rule in a secgroup. if secgroup not defined will search for
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001299 default secgroup in project_id.
Yair Fried1fc32a12014-08-04 09:11:30 +03001300
Steve Heyman33735f22016-05-24 09:28:08 -05001301 :param secgroup: the security group.
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001302 :param project_id: if secgroup not passed -- the tenant in which to
Yair Fried1fc32a12014-08-04 09:11:30 +03001303 search for default secgroup
1304 :param kwargs: a dictionary containing rule parameters:
1305 for example, to allow incoming ssh:
1306 rule = {
1307 direction: 'ingress'
1308 protocol:'tcp',
1309 port_range_min: 22,
1310 port_range_max: 22
1311 }
1312 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301313
John Warren456d9ae2016-01-12 15:36:33 -05001314 if sec_group_rules_client is None:
1315 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001316 if security_groups_client is None:
1317 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001318 if not project_id:
1319 project_id = security_groups_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001320 if secgroup is None:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001321 # Get default secgroup for project_id
zhuflb0b272e2017-09-22 16:01:46 +08001322 default_secgroups = security_groups_client.list_security_groups(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001323 name='default', project_id=project_id)['security_groups']
1324 msg = "No default security group for project %s." % (project_id)
zhuflb0b272e2017-09-22 16:01:46 +08001325 self.assertNotEmpty(default_secgroups, msg)
1326 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001327
Steve Heyman33735f22016-05-24 09:28:08 -05001328 ruleset = dict(security_group_id=secgroup['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001329 project_id=secgroup['project_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001330 ruleset.update(kwargs)
1331
John Warren456d9ae2016-01-12 15:36:33 -05001332 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001333 sg_rule = sg_rule['security_group_rule']
1334
1335 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1336 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001337
1338 return sg_rule
1339
John Warren456d9ae2016-01-12 15:36:33 -05001340 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1341 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001342 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001343 """Create loginable security group rule
1344
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001345 This function will create:
1346 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1347 access for ipv4.
1348 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1349 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001350 """
1351
John Warren456d9ae2016-01-12 15:36:33 -05001352 if security_group_rules_client is None:
1353 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001354 if security_groups_client is None:
1355 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001356 rules = []
1357 rulesets = [
1358 dict(
1359 # ssh
1360 protocol='tcp',
1361 port_range_min=22,
1362 port_range_max=22,
1363 ),
1364 dict(
1365 # ping
1366 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001367 ),
1368 dict(
1369 # ipv6-icmp for ping6
1370 protocol='icmp',
1371 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001372 )
1373 ]
John Warren456d9ae2016-01-12 15:36:33 -05001374 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001375 for ruleset in rulesets:
1376 for r_direction in ['ingress', 'egress']:
1377 ruleset['direction'] = r_direction
1378 try:
1379 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001380 sec_group_rules_client=sec_group_rules_client,
1381 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001382 security_groups_client=security_groups_client,
1383 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001384 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001385 # if rule already exist - skip rule and continue
1386 msg = 'Security group rule already exists'
1387 if msg not in ex._error_string:
1388 raise ex
1389 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001390 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001391 rules.append(sg_rule)
1392
1393 return rules
1394
Martin Kopec0090a102020-11-03 13:50:19 +00001395 def _get_router(self, client=None, project_id=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001396 """Retrieve a router for the given tenant id.
1397
1398 If a public router has been configured, it will be returned.
1399
1400 If a public router has not been configured, but a public
1401 network has, a tenant router will be created and returned that
1402 routes traffic to the public network.
1403 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301404
Yair Frieddb6c9e92014-08-06 08:53:13 +03001405 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001406 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001407 if not project_id:
1408 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001409 router_id = CONF.network.public_router_id
1410 network_id = CONF.network.public_network_id
1411 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001412 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001413 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001414 elif network_id:
Martin Kopec0090a102020-11-03 13:50:19 +00001415 name = kwargs.pop('name', None)
1416 if not name:
1417 namestart = self.__class__.__name__ + '-router'
1418 name = data_utils.rand_name(namestart)
1419
1420 ext_gw_info = kwargs.pop('external_gateway_info', None)
1421 if not ext_gw_info:
1422 ext_gw_info = dict(network_id=network_id)
zhufl3484f992017-10-10 16:18:29 +08001423 router = client.create_router(
Martin Kopec0090a102020-11-03 13:50:19 +00001424 name=name,
1425 admin_state_up=kwargs.get('admin_state_up', True),
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001426 project_id=project_id,
Martin Kopec0090a102020-11-03 13:50:19 +00001427 external_gateway_info=ext_gw_info,
1428 **kwargs)['router']
zhufl3484f992017-10-10 16:18:29 +08001429 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1430 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001431 return router
1432 else:
1433 raise Exception("Neither of 'public_router_id' or "
1434 "'public_network_id' has been defined.")
1435
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001436 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001437 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001438 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001439 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001440 """Create a network with a subnet connected to a router.
1441
David Shrewsbury9bac3662014-08-07 15:07:01 -04001442 The baremetal driver is a special case since all nodes are
1443 on the same shared network.
1444
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001445 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001446 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001447 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001448 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001449 a form like this: {'provider:network_type': 'vlan',
1450 'provider:physical_network': 'foo',
1451 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001452 :returns: network, subnet, router
1453 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301454
Thiago Paiva66cded22016-08-15 14:55:58 -03001455 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001456 # NOTE(Shrews): This exception is for environments where tenant
1457 # credential isolation is available, but network separation is
1458 # not (the current baremetal case). Likely can be removed when
1459 # test account mgmt is reworked:
1460 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001461 if not CONF.compute.fixed_network_name:
1462 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001463 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001464 network = self._get_network_by_name(
1465 CONF.compute.fixed_network_name)
1466 router = None
1467 subnet = None
1468 else:
John Warren94d8faf2015-09-15 12:22:24 -04001469 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001470 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001471 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001472 port_security_enabled=port_security_enabled,
1473 **net_dict)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001474 router = self._get_router(client=routers_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001475 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001476 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001477 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001478 # use explicit check because empty list is a valid option
1479 if dns_nameservers is not None:
1480 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001481 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001482 if not routers_client:
1483 routers_client = self.routers_client
1484 router_id = router['id']
1485 routers_client.add_router_interface(router_id,
1486 subnet_id=subnet['id'])
1487
1488 # save a cleanup job to remove this association between
1489 # router and subnet
1490 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1491 routers_client.remove_router_interface, router_id,
1492 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001493 return network, subnet, router
1494
1495
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001496class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001497 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001498
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001499 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001500
1501 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001502 def setup_clients(cls):
1503 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001504 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001505 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001506 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001507
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001508 def create_encryption_type(self, client=None, type_id=None, provider=None,
1509 key_size=None, cipher=None,
1510 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301511 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001512 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001513 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001514 if not type_id:
1515 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001516 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001517 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001518 client.create_encryption_type(
1519 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001520 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001521
lkuchlan3023e752017-06-08 12:53:13 +03001522 def create_encrypted_volume(self, encryption_provider, volume_type,
1523 key_size=256, cipher='aes-xts-plain64',
1524 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301525 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001526 volume_type = self.create_volume_type(name=volume_type)
1527 self.create_encryption_type(type_id=volume_type['id'],
1528 provider=encryption_provider,
1529 key_size=key_size,
1530 cipher=cipher,
1531 control_location=control_location)
1532 return self.create_volume(volume_type=volume_type['name'])
1533
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001534
Masayuki Igawa0870db52015-09-18 21:08:36 +09001535class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001536 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001537
1538 Subclasses implement the tests that use the methods provided by this
1539 class.
1540 """
1541
1542 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001543 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001544 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001545 if not CONF.service_available.swift:
1546 skip_msg = ("%s skipped as swift is not available" %
1547 cls.__name__)
1548 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001549
1550 @classmethod
1551 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001552 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001553 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001554 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001555 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001556
1557 @classmethod
1558 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001559 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001560 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001561 cls.account_client = cls.os_operator.account_client
1562 cls.container_client = cls.os_operator.container_client
1563 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001564
Chris Dentde456a12014-09-10 12:41:15 +01001565 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301566 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001567 self.account_client.list_account_containers()
1568 LOG.debug('Swift status information obtained successfully')
1569
Chris Dentde456a12014-09-10 12:41:15 +01001570 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301571 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001572 name = container_name or data_utils.rand_name(
1573 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001574 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001575 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001576 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001577 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001578 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001579 self.container_client.delete_container,
1580 name)
Chris Dent0d494112014-08-26 13:48:30 +01001581 return name
1582
Chris Dentde456a12014-09-10 12:41:15 +01001583 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301584 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001585 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001586 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001587
Chris Dentde456a12014-09-10 12:41:15 +01001588 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301589 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001590 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001591 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001592 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001593 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001594 self.object_client.delete_object,
1595 container_name,
1596 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001597 return obj_name, obj_data
1598
Chris Dentde456a12014-09-10 12:41:15 +01001599 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301600 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001601 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001602 self.list_and_check_container_objects(container_name,
1603 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001604
Chris Dentde456a12014-09-10 12:41:15 +01001605 def list_and_check_container_objects(self, container_name,
1606 present_obj=None,
1607 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301608 """List and verify objects for a given container
1609
1610 This utility lists objects for a given container
1611 and asserts which are present and
1612 which are not
1613 """
1614
Ghanshyam2a180b82014-06-16 13:54:22 +09001615 if present_obj is None:
1616 present_obj = []
1617 if not_present_obj is None:
1618 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001619 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001620 container_name)
1621 if present_obj:
1622 for obj in present_obj:
1623 self.assertIn(obj, object_list)
1624 if not_present_obj:
1625 for obj in not_present_obj:
1626 self.assertNotIn(obj, object_list)
1627
Chris Dentde456a12014-09-10 12:41:15 +01001628 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301629 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001630 _, obj = self.object_client.get_object(container_name, obj_name)
1631 self.assertEqual(obj, expected_data)