blob: 9ebc460beda78cfeb8f6ebf87d8dceff499359f8 [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.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100202 direct-physical: an SR-IOV port that is directly attached to a
203 VM using physical instead of virtual
204 functions.
205 baremetal: a baremetal port directly attached to a baremetal
206 node.
207 virtio-forwarder: an SR-IOV port that is indirectly attached
208 to a VM using a low-latency vhost-user
209 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000210 Defaults to ``CONF.network.port_vnic_type``.
211 * *port_profile* (``dict``) --
212 This attribute is a dictionary that can be used (with admin
213 credentials) to supply information influencing the binding of
214 the port.
215 example: port_profile = "capabilities:[switchdev]"
216 Defaults to ``CONF.network.port_profile``.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100217 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100218
lanoux5fc14522015-09-21 08:17:35 +0000219 # NOTE(jlanoux): As a first step, ssh checks in the scenario
220 # tests need to be run regardless of the run_validation and
221 # validatable parameters and thus until the ssh validation job
222 # becomes voting in CI. The test resources management and IP
223 # association are taken care of in the scenario tests.
224 # Therefore, the validatable parameter is set to false in all
225 # those tests. In this way create_server just return a standard
226 # server and the scenario tests always perform ssh checks.
227
228 # Needed for the cross_tenant_traffic test:
229 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800230 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000231
zhufl24208c22016-10-25 15:23:48 +0800232 if name is None:
233 name = data_utils.rand_name(self.__class__.__name__ + "-server")
234
Noam Angel6e309952019-01-27 05:52:40 +0000235 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
236 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000237
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000238 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000239 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000240 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000241 ports = []
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000242 create_port_body = {}
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300243
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000244 if vnic_type:
245 create_port_body['binding:vnic_type'] = vnic_type
246
247 if profile:
248 create_port_body['binding:profile'] = profile
249
lanoux5fc14522015-09-21 08:17:35 +0000250 if kwargs:
251 # Convert security group names to security group ids
252 # to pass to create_port
253 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300254 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500255 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000256 ).get('security_groups')
257 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100258 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000259
260 sec_groups_names = [s['name'] for s in kwargs.pop(
261 'security_groups')]
262 security_groups_ids = [sec_dict[s]
263 for s in sec_groups_names]
264
265 if security_groups_ids:
266 create_port_body[
267 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300268 networks = kwargs.pop('networks', [])
269 else:
270 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000271
272 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300273 # for the project's private networks and create a port.
274 # The same behaviour as we would expect when passing
275 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000276 if not networks:
277 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300278 **{'router:external': False, 'fields': 'id'})['networks']
279
280 # It's net['uuid'] if networks come from kwargs
281 # and net['id'] if they come from
282 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000283 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000284 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300285 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800286 port = self.create_port(network_id=net_id,
287 client=clients.ports_client,
288 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300289 ports.append({'port': port['id']})
290 else:
291 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000292 if ports:
293 kwargs['networks'] = ports
294 self.ports = ports
295
296 tenant_network = self.get_tenant_network()
297
Marc Koderer979e4942016-12-08 10:07:59 +0100298 if CONF.compute.compute_volume_common_az:
299 kwargs.setdefault('availability_zone',
300 CONF.compute.compute_volume_common_az)
301
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200302 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000303 clients,
304 tenant_network=tenant_network,
305 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530306 name=name, flavor=flavor,
307 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000308
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200309 self.addCleanup(waiters.wait_for_server_termination,
310 clients.servers_client, body['id'])
311 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
312 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000313 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100314 return server
315
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100316 def create_volume(self, size=None, name=None, snapshot_id=None,
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000317 imageRef=None, volume_type=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530318 """Creates volume
319
320 This wrapper utility creates volume and waits for volume to be
321 in 'available' state.
322 This method returns the volume's full representation by GET request.
323 """
324
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700325 if size is None:
326 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500327 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800328 if CONF.image_feature_enabled.api_v1:
329 resp = self.image_client.check_image(imageRef)
330 image = common_image.get_image_meta_from_headers(resp)
331 else:
332 image = self.image_client.show_image(imageRef)
333 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500334 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100335 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800336 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000337 kwargs.update({'name': name,
338 'snapshot_id': snapshot_id,
339 'imageRef': imageRef,
340 'volume_type': volume_type,
341 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100342
343 if CONF.compute.compute_volume_common_az:
344 kwargs.setdefault('availability_zone',
345 CONF.compute.compute_volume_common_az)
346
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900347 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700348
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100349 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
350 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100351 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100352 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300353 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200354 waiters.wait_for_volume_resource_status(self.volumes_client,
355 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100356 # The volume retrieved on creation has a non-up-to-date status.
357 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000358 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100359 return volume
360
lkuchlane20e6a82018-05-08 11:28:46 +0300361 def create_backup(self, volume_id, name=None, description=None,
362 force=False, snapshot_id=None, incremental=False,
363 container=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530364 """Creates backup
365
366 This wrapper utility creates backup and waits for backup to be
367 in 'available' state.
368 """
lkuchlane20e6a82018-05-08 11:28:46 +0300369
370 name = name or data_utils.rand_name(
371 self.__class__.__name__ + "-backup")
372 kwargs = {'name': name,
373 'description': description,
374 'force': force,
375 'snapshot_id': snapshot_id,
376 'incremental': incremental,
377 'container': container}
378 backup = self.backups_client.create_backup(volume_id=volume_id,
379 **kwargs)['backup']
380 self.addCleanup(self.backups_client.delete_backup, backup['id'])
381 waiters.wait_for_volume_resource_status(self.backups_client,
382 backup['id'], 'available')
383 return backup
384
385 def restore_backup(self, backup_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530386 """Restore backup
387
388 This wrapper utility restores backup and waits for backup to be
389 in 'available' state.
390 """
391
lkuchlane20e6a82018-05-08 11:28:46 +0300392 restore = self.backups_client.restore_backup(backup_id)['restore']
393 self.addCleanup(self.volumes_client.delete_volume,
394 restore['volume_id'])
395 waiters.wait_for_volume_resource_status(self.backups_client,
396 backup_id, 'available')
397 waiters.wait_for_volume_resource_status(self.volumes_client,
398 restore['volume_id'],
399 'available')
400 self.assertEqual(backup_id, restore['backup_id'])
401 return restore
402
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530403 def rebuild_server(self, server_id, image=None,
404 preserve_ephemeral=False, wait=True,
405 rebuild_kwargs=None):
406 if image is None:
407 image = CONF.compute.image_ref
408 rebuild_kwargs = rebuild_kwargs or {}
409 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
410 server_id, image, preserve_ephemeral)
411 self.servers_client.rebuild_server(
412 server_id=server_id,
413 image_ref=image,
414 preserve_ephemeral=preserve_ephemeral,
415 **rebuild_kwargs)
416 if wait:
417 waiters.wait_for_server_status(self.servers_client,
418 server_id, 'ACTIVE')
419
lkuchlan73ed1f32017-07-06 16:22:12 +0300420 def create_volume_snapshot(self, volume_id, name=None, description=None,
421 metadata=None, force=False):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530422 """Creates volume
423
424 This wrapper utility creates volume snapshot and waits for backup
425 to be in 'available' state.
426 """
427
lkuchlan73ed1f32017-07-06 16:22:12 +0300428 name = name or data_utils.rand_name(
429 self.__class__.__name__ + '-snapshot')
430 snapshot = self.snapshots_client.create_snapshot(
431 volume_id=volume_id,
432 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000433 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300434 description=description,
435 metadata=metadata)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530436
lkuchlan73ed1f32017-07-06 16:22:12 +0300437 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
438 snapshot['id'])
439 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
440 waiters.wait_for_volume_resource_status(self.snapshots_client,
441 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200442 snapshot = self.snapshots_client.show_snapshot(
443 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300444 return snapshot
445
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100446 def _cleanup_volume_type(self, volume_type):
447 """Clean up a given volume type.
448
449 Ensuring all volumes associated to a type are first removed before
450 attempting to remove the type itself. This includes any image volume
451 cache volumes stored in a separate tenant to the original volumes
452 created from the type.
453 """
454 admin_volume_type_client = self.os_admin.volume_types_client_latest
455 admin_volumes_client = self.os_admin.volumes_client_latest
456 volumes = admin_volumes_client.list_volumes(
457 detail=True, params={'all_tenants': 1})['volumes']
458 type_name = volume_type['name']
459 for volume in [v for v in volumes if v['volume_type'] == type_name]:
460 test_utils.call_and_ignore_notfound_exc(
461 admin_volumes_client.delete_volume, volume['id'])
462 admin_volumes_client.wait_for_resource_deletion(volume['id'])
463 admin_volume_type_client.delete_volume_type(volume_type['id'])
464
scottda61f68ac2016-06-07 12:07:55 -0600465 def create_volume_type(self, client=None, name=None, backend_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530466 """Creates volume type
467
468 In a multiple-storage back-end configuration,
469 each back end has a name (volume_backend_name).
470 The name of the back end is declared as an extra-specification
471 of a volume type (such as, volume_backend_name=LVM).
472 When a volume is created, the scheduler chooses an
473 appropriate back end to handle the request, according
474 to the volume type specified by the user.
475 The scheduler uses volume types to explicitly create volumes on
476 specific back ends.
477
478 Before using volume type, a volume type has to be declared
479 to Block Storage. In addition to that, an extra-specification
480 has to be created to link the volume type to a back end name.
481 """
482
scottda61f68ac2016-06-07 12:07:55 -0600483 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000484 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000485 if not name:
486 class_name = self.__class__.__name__
487 name = data_utils.rand_name(class_name + '-volume-type')
488 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600489
490 LOG.debug("Creating a volume type: %s on backend %s",
491 randomized_name, backend_name)
492 extra_specs = {}
493 if backend_name:
494 extra_specs = {"volume_backend_name": backend_name}
495
lkuchlanbbabe542017-09-26 10:47:23 +0300496 volume_type = client.create_volume_type(
497 name=randomized_name, extra_specs=extra_specs)['volume_type']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530498 self.assertIn('id', volume_type)
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100499 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600500 return volume_type
501
Yair Fried1fc32a12014-08-04 09:11:30 +0300502 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500503 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500504 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100505 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900506 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100507 for sg in sgs:
508 if sg['name'] == 'default':
509 secgroup_id = sg['id']
510
511 # These rules are intended to permit inbound ssh and icmp
512 # traffic from all sources, so no group_id is provided.
513 # Setting a group_id would only permit traffic from ports
514 # belonging to the same security group.
515 rulesets = [
516 {
517 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000518 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100519 'from_port': 22,
520 'to_port': 22,
521 'cidr': '0.0.0.0/0',
522 },
523 {
524 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000525 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100526 'from_port': -1,
527 'to_port': -1,
528 'cidr': '0.0.0.0/0',
529 }
530 ]
531 rules = list()
532 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000533 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900534 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100535 rules.append(sg_rule)
536 return rules
537
Yair Fried1fc32a12014-08-04 09:11:30 +0300538 def _create_security_group(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530539 """Create security group and add rules to security group"""
Andrea Frittoli247058f2014-07-16 16:09:22 +0100540 sg_name = data_utils.rand_name(self.__class__.__name__)
541 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500542 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900543 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100544 self.assertEqual(secgroup['name'], sg_name)
545 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500546 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100547 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500548 self.compute_security_groups_client.delete_security_group,
549 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100550
551 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300552 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100553 return secgroup
554
zhuflf52c7592017-05-25 13:55:24 +0800555 def get_remote_client(self, ip_address, username=None, private_key=None,
556 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100557 """Get a SSH client to a remote server
558
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600559 :param ip_address: the server floating or fixed IP address to use
560 for ssh validation
561 :param username: name of the Linux account on the remote server
562 :param private_key: the SSH private key to use
563 :param server: server dict, used for debugging purposes
564 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100565 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700566
Andrea Frittoli247058f2014-07-16 16:09:22 +0100567 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800568 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800569 # Set this with 'keypair' or others to log in with keypair or
570 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000571 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800572 password = None
573 if private_key is None:
574 private_key = self.keypair['private_key']
575 else:
lanoux283273b2015-12-04 03:01:54 -0800576 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800577 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800578 linux_client = remote_client.RemoteClient(
579 ip_address, username, pkey=private_key, password=password,
580 server=server, servers_client=self.servers_client)
581 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100582 return linux_client
583
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530584 def image_create(self, name='scenario-img'):
Martin Kopec02af6a42020-03-03 12:39:12 +0000585 img_path = CONF.scenario.img_file
586 if not os.path.exists(img_path):
587 # TODO(kopecmartin): replace LOG.warning for rasing
588 # InvalidConfiguration exception after tempest 25.0.0 is
589 # released - there will be one release which accepts both
590 # behaviors in order to avoid many failures across CIs and etc.
591 LOG.warning(
592 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
593 'a full path for the image. CONF.scenario.img_dir was '
594 'deprecated and will be removed in the next release. Till '
595 'Tempest 25.0.0, old behavior is maintained and keep working '
596 'but starting Tempest 26.0.0, you need to specify the full '
597 'path in CONF.scenario.img_file config option.')
598 img_path = os.path.join(CONF.scenario.img_dir, img_path)
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300599 img_container_format = CONF.scenario.img_container_format
600 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000601 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400602 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000603 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100604 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000605 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530606 if img_properties is None:
607 img_properties = {}
608 name = data_utils.rand_name('%s-' % name)
609 params = {
610 'name': name,
611 'container_format': img_container_format,
612 'disk_format': img_disk_format or img_container_format,
613 }
614 if CONF.image_feature_enabled.api_v1:
615 params['is_public'] = 'False'
616 if img_properties:
617 params['properties'] = img_properties
618 params = {'headers': common_image.image_meta_to_headers(**params)}
619 else:
620 params['visibility'] = 'private'
621 # Additional properties are flattened out in the v2 API.
622 if img_properties:
623 params.update(img_properties)
624 body = self.image_client.create_image(**params)
625 image = body['image'] if 'image' in body else body
626 self.addCleanup(self.image_client.delete_image, image['id'])
627 self.assertEqual("queued", image['status'])
628 with open(img_path, 'rb') as image_file:
629 if CONF.image_feature_enabled.api_v1:
630 self.image_client.update_image(image['id'], data=image_file)
631 else:
632 self.image_client.store_image_file(image['id'], image_file)
633 LOG.debug("image:%s", image['id'])
634 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100635
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000636 def _log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530637 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400638 if not CONF.compute_feature_enabled.console_output:
639 LOG.debug('Console output not supported, cannot log')
640 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700641 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100642 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700643 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100644 servers = servers['servers']
645 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100646 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700647 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000648 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100649 LOG.debug('Console output for %s\nbody=\n%s',
650 server['id'], console_output)
651 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100652 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100653 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100654
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000655 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530656 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300657 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000658 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000659
nithya-ganesan882595e2014-07-29 18:51:07 +0000660 def create_server_snapshot(self, server, name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530661 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000662 # Glance client
663 _image_client = self.image_client
664 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900665 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000666 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800667 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000668 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000669 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500670 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300671 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200672
673 self.addCleanup(_image_client.wait_for_resource_deletion,
674 image_id)
675 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
676 _image_client.delete_image, image_id)
677
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400678 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530679 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700680 resp = _image_client.check_image(image_id)
681 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400682 image_props = snapshot_image.get('properties', {})
683 else:
684 # In glance v2 the additional properties are flattened.
685 snapshot_image = _image_client.show_image(image_id)
686 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300687
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400688 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300689 if bdm:
690 bdm = json.loads(bdm)
691 if bdm and 'snapshot_id' in bdm[0]:
692 snapshot_id = bdm[0]['snapshot_id']
693 self.addCleanup(
694 self.snapshots_client.wait_for_resource_deletion,
695 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100696 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
697 self.snapshots_client.delete_snapshot,
698 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200699 waiters.wait_for_volume_resource_status(self.snapshots_client,
700 snapshot_id,
701 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000702 image_name = snapshot_image['name']
703 self.assertEqual(name, image_name)
704 LOG.debug("Created snapshot image %s for server %s",
705 image_name, server['name'])
706 return snapshot_image
707
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000708 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530709 """Compute volume attach
710
711 This utility attaches volume from compute and waits for the
712 volume status to be 'in-use' state.
713 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000714 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000715 server['id'], volumeId=volume_to_attach['id'],
716 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200717 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200718 waiters.wait_for_volume_resource_status(self.volumes_client,
719 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000720 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
721 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200722 # Return the updated volume after the attachment
723 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900724
Jordan Pittier7cf64762015-10-14 15:01:12 +0200725 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530726 """Compute volume detach
727
728 This utility detaches volume from compute and check whether the
729 volume status is 'available' state, and if not, an exception
730 will be thrown.
731 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200732 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200733 waiters.wait_for_volume_resource_status(self.volumes_client,
734 volume['id'], 'available')
Soniya Vyasae631132020-08-28 13:37:12 +0530735 volume = self.volumes_client.show_volume(volume['id'])['volume']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200736
Steven Hardyda2a8352014-10-02 12:52:20 +0100737 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800738 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530739 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000740 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000741 cmd = ['ping', '-c1', '-w1']
742
743 if mtu:
744 cmd += [
745 # don't fragment
746 '-M', 'do',
747 # ping receives just the size of ICMP payload
748 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
749 ]
750 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700751
752 def ping():
753 proc = subprocess.Popen(cmd,
754 stdout=subprocess.PIPE,
755 stderr=subprocess.PIPE)
756 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000757
Aaron Rosena7df13b2014-09-23 09:45:45 -0700758 return (proc.returncode == 0) == should_succeed
759
Jordan Pittier9e227c52016-02-09 14:35:18 +0100760 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000761 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800762 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000763 'caller': caller, 'ip': ip_address, 'timeout': timeout,
764 'should_succeed':
765 'reachable' if should_succeed else 'unreachable'
766 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200767 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000768 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800769 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000770 'caller': caller, 'ip': ip_address, 'timeout': timeout,
771 'result': 'expected' if result else 'unexpected'
772 })
zhufl0ec74c42017-11-15 14:02:28 +0800773 if server:
774 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000775 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700776
Yair Friedae0e73d2014-11-24 11:56:26 +0200777 def check_vm_connectivity(self, ip_address,
778 username=None,
779 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000780 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800781 extra_msg="",
782 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000783 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000784 """Check server connectivity
785
Yair Friedae0e73d2014-11-24 11:56:26 +0200786 :param ip_address: server to test against
787 :param username: server's ssh username
788 :param private_key: server's ssh private key to be used
789 :param should_connect: True/False indicates positive/negative test
790 positive - attempt ping and ssh
791 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800792 :param extra_msg: Message to help with debugging if ``ping_ip_address``
793 fails
794 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000795 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200796
797 :raises: AssertError if the result of the connectivity check does
798 not match the value of the should_connect param
799 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530800
zhufl0ec74c42017-11-15 14:02:28 +0800801 LOG.debug('checking network connections to IP %s with user: %s',
802 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200803 if should_connect:
804 msg = "Timed out waiting for %s to become reachable" % ip_address
805 else:
806 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800807 if extra_msg:
808 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200809 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000810 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800811 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200812 msg=msg)
813 if should_connect:
814 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800815 try:
816 self.get_remote_client(ip_address, username, private_key,
817 server=server)
818 except Exception:
819 if not extra_msg:
820 extra_msg = 'Failed to ssh to %s' % ip_address
821 LOG.exception(extra_msg)
822 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200823
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000824 def create_floating_ip(self, server, pool_name=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000825 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200826
Marc Koderer3b57d802016-03-22 15:23:31 +0100827 if not pool_name:
828 pool_name = CONF.network.floating_network_name
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000829
John Warrene74890a2015-11-11 15:18:01 -0500830 floating_ip = (self.compute_floating_ips_client.
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000831 create_floating_ip(pool=pool_name,
832 **kwargs)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100833 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500834 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200835 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500836 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530837 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200838 return floating_ip
839
Sean Dague20e98612016-01-06 14:33:28 -0500840 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200841 private_key=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530842 """Creates timestamp
843
844 This wrapper utility does ssh, creates timestamp and returns the
845 created timestamp.
846 """
847
Sean Dague20e98612016-01-06 14:33:28 -0500848 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200849 private_key=private_key,
850 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300851 if dev_name is not None:
852 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800853 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
854 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300855 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
856 ssh_client.exec_command(cmd_timestamp)
857 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
858 % mount_path)
859 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800860 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300861 return timestamp
862
Sean Dague20e98612016-01-06 14:33:28 -0500863 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200864 private_key=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530865 """Returns timestamp
866
867 This wrapper utility does ssh and returns the timestamp.
868 """
869
Sean Dague20e98612016-01-06 14:33:28 -0500870 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200871 private_key=private_key,
872 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300873 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700874 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300875 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
876 % mount_path)
877 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800878 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300879 return timestamp
880
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000881 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -0500882 """Get the server fixed or floating IP.
883
884 Based on the configuration we're in, return a correct ip
885 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000886
887 If CONF.validation.connect_method is floating, then
888 a floating ip will be created passing kwargs as additional
889 argument.
Sean Dague20e98612016-01-06 14:33:28 -0500890 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530891
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200892 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500893 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800894 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500895 # method is creating the floating IP there.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000896 return self.create_floating_ip(server, **kwargs)['ip']
Sean Dague20e98612016-01-06 14:33:28 -0500897 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400898 # Determine the network name to look for based on config or creds
899 # provider network resources.
900 if CONF.validation.network_for_ssh:
901 addresses = server['addresses'][
902 CONF.validation.network_for_ssh]
903 else:
zhufl7b4a7202017-09-28 10:29:27 +0800904 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400905 addresses = (server['addresses'][network['name']]
906 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500907 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200908 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
909 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500910 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800911 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200912 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400913 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200914
zhufl7bc916d2018-08-22 14:47:39 +0800915 @classmethod
916 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530917 """Gets host of server"""
918
zhufl7bc916d2018-08-22 14:47:39 +0800919 server_details = cls.os_admin.servers_client.show_server(server_id)
920 return server_details['server']['OS-EXT-SRV-ATTR:host']
921
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000922 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
923 bd_map_v2 = [{
924 'uuid': source_id,
925 'source_type': source_type,
926 'destination_type': 'volume',
927 'boot_index': 0,
928 'delete_on_termination': delete_on_termination}]
929 return {'block_device_mapping_v2': bd_map_v2}
930
931 def boot_instance_from_resource(self, source_id,
932 source_type,
933 keypair=None,
934 security_group=None,
935 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +0000936 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530937 """Boot instance from resource
938
939 This wrapper utility boots instance from resource with block device
940 mapping with source info passed in arguments
941 """
942
Martin Kopecbee673e2020-11-04 09:40:52 +0000943 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000944 if keypair:
945 create_kwargs['key_name'] = keypair['name']
946 if security_group:
947 create_kwargs['security_groups'] = [
948 {'name': security_group['name']}]
949 create_kwargs.update(self._get_bdm(
950 source_id,
951 source_type,
952 delete_on_termination=delete_on_termination))
953 if name:
954 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +0000955 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000956
Martin Kopecbee673e2020-11-04 09:40:52 +0000957 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000958
959 def create_volume_from_image(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530960 """Create volume from image"""
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000961 img_uuid = CONF.compute.image_ref
962 vol_name = data_utils.rand_name(
963 self.__class__.__name__ + '-volume-origin')
964 return self.create_volume(name=vol_name, imageRef=img_uuid)
965
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100966
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100967class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300968 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000969
Yair Fried1fc32a12014-08-04 09:11:30 +0300970 This class provide helpers for network scenario tests, using the neutron
971 API. Helpers from ancestor which use the nova network API are overridden
972 with the neutron API.
973
974 This Class also enforces using Neutron instead of novanetwork.
975 Subclassed tests will be skipped if Neutron is not enabled
976
977 """
978
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000979 credentials = ['primary', 'admin']
980
Yair Fried1fc32a12014-08-04 09:11:30 +0300981 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000982 def skip_checks(cls):
983 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100984 if not CONF.service_available.neutron:
985 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300986
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700987 def _create_network(self, networks_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +0000988 project_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200989 namestart='network-smoke-',
Lajos Katonac87a06b2019-01-04 13:21:48 +0100990 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -0400991 if not networks_client:
992 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +0000993 if not project_id:
994 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300995 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +0000996 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +0100997 if net_dict:
998 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400999 # Neutron disables port security by default so we have to check the
1000 # config before trying to create the network with port_security_enabled
1001 if CONF.network_feature_enabled.port_security:
1002 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001003 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001004 network = result['network']
1005
1006 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001007 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001008 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001009 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001010 return network
1011
zhufl5b0a52f2017-10-24 15:48:20 +08001012 def create_subnet(self, network, subnets_client=None,
1013 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001014 """Create a subnet for the given network
1015
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301016 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001017 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301018
1019 :param **kwargs:
1020 See extra parameters below
1021
1022 :Keyword Arguments:
1023
1024 * *ip_version = ip version of the given network,
Yair Fried1fc32a12014-08-04 09:11:30 +03001025 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301026
John Warren3961acd2015-10-02 14:38:53 -04001027 if not subnets_client:
1028 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001029
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001030 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001031 """Check cidr existence
1032
lei zhangdd552b22015-11-25 20:41:48 +08001033 :returns: True if subnet with cidr already exist in tenant
1034 False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001035 """
jeremy.zhang5870ff12017-05-25 11:24:23 +08001036 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001037 project_id=project_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +03001038 return len(cidr_in_use) != 0
1039
Kirill Shileev14113572014-11-21 16:58:02 +03001040 ip_version = kwargs.pop('ip_version', 4)
1041
1042 if ip_version == 6:
1043 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -04001044 CONF.network.project_network_v6_cidr)
1045 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001046 else:
Sean Dagueed6e5862016-04-04 10:49:13 -04001047 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
1048 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001049
Yair Fried1fc32a12014-08-04 09:11:30 +03001050 result = None
Kirill Shileev14113572014-11-21 16:58:02 +03001051 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +03001052 # Repeatedly attempt subnet creation with sequential cidr
1053 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +03001054 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +03001055 str_cidr = str(subnet_cidr)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001056 if cidr_in_use(str_cidr, project_id=network['project_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +03001057 continue
1058
1059 subnet = dict(
1060 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001061 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001062 project_id=network['project_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +03001063 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +03001064 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001065 **kwargs
1066 )
1067 try:
John Warren3961acd2015-10-02 14:38:53 -04001068 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +03001069 break
Masayuki Igawad9388762015-01-20 14:56:42 +09001070 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +03001071 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1072 if not is_overlapping_cidr:
1073 raise
1074 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001075
1076 subnet = result['subnet']
1077 self.assertEqual(subnet['cidr'], str_cidr)
1078
1079 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1080 subnets_client.delete_subnet, subnet['id'])
1081
Yair Fried1fc32a12014-08-04 09:11:30 +03001082 return subnet
1083
Kirill Shileev14113572014-11-21 16:58:02 +03001084 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu95a31692018-06-13 23:17:54 +00001085 if ip_addr:
1086 ports = self.os_admin.ports_client.list_ports(
1087 device_id=server['id'],
1088 fixed_ips='ip_address=%s' % ip_addr)['ports']
1089 else:
1090 ports = self.os_admin.ports_client.list_ports(
1091 device_id=server['id'])['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +03001092 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001093 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1094 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001095
1096 def _is_active(port):
1097 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1098 # servers. Neutron does not bind ports for Ironic instances, as a
1099 # result the port remains in the DOWN state. This has been fixed
1100 # with the introduction of the networking-baremetal plugin but
1101 # it's not mandatory (and is not used on all stable branches).
1102 return (port['status'] == 'ACTIVE' or
1103 port.get('binding:vnic_type') == 'baremetal')
1104
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001105 port_map = [(p["id"], fxip["ip_address"])
1106 for p in ports
1107 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001108 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001109 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001110 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1111 if inactive:
1112 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001113
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001114 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001115 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001116 self.assertEqual(len(port_map), 1,
1117 "Found multiple IPv4 addresses: %s. "
1118 "Unable to determine which port to target."
1119 % port_map)
1120 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001121
David Shrewsbury9bac3662014-08-07 15:07:01 -04001122 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001123 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001124 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001125 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001126 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001127 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001128
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301129 def create_floating_ip(self, server, external_network_id=None,
Yair Friedae0e73d2014-11-24 11:56:26 +02001130 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001131 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301132
Yair Friedae0e73d2014-11-24 11:56:26 +02001133 if not external_network_id:
1134 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001135 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001136 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001137 if not port_id:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301138 port_id, ip4 = self._get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001139 else:
1140 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001141
1142 kwargs = {
1143 'floating_network_id': external_network_id,
1144 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301145 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001146 'fixed_ip_address': ip4,
1147 }
1148 if CONF.network.subnet_id:
1149 kwargs['subnet_id'] = CONF.network.subnet_id
1150 result = client.create_floatingip(**kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001151 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001152
Jordan Pittier9e227c52016-02-09 14:35:18 +01001153 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001154 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001155 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001156 return floating_ip
1157
Yair Fried45f92952014-06-26 05:19:19 +03001158 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001159 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001160
Steve Heyman33735f22016-05-24 09:28:08 -05001161 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001162 :param status: target status
1163 :raises: AssertionError if status doesn't match
1164 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301165
Steve Heyman33735f22016-05-24 09:28:08 -05001166 floatingip_id = floating_ip['id']
1167
Carl Baldwina754e2d2014-10-23 22:47:41 +00001168 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001169 floating_ip = (self.floating_ips_client.
1170 show_floatingip(floatingip_id)['floatingip'])
1171 if status == floating_ip['status']:
1172 LOG.info("FloatingIP: {fp} is at status: {st}"
1173 .format(fp=floating_ip, st=status))
1174 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001175
zhufl4dda94e2017-03-14 16:14:46 +08001176 if not test_utils.call_until_true(refresh,
1177 CONF.network.build_timeout,
1178 CONF.network.build_interval):
1179 floating_ip = self.floating_ips_client.show_floatingip(
1180 floatingip_id)['floatingip']
1181 self.assertEqual(status, floating_ip['status'],
1182 message="FloatingIP: {fp} is at status: {cst}. "
1183 "failed to reach status: {st}"
1184 .format(fp=floating_ip, cst=floating_ip['status'],
1185 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001186
zhufl420a0192017-09-28 11:04:50 +08001187 def check_tenant_network_connectivity(self, server,
1188 username,
1189 private_key,
1190 should_connect=True,
1191 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301192 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001193 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001194 msg = 'Tenant networks not configured to be reachable.'
1195 LOG.info(msg)
1196 return
1197 # The target login is assumed to have been configured for
1198 # key-based authentication by cloud-init.
1199 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001200 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001201 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001202 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001203 username,
1204 private_key,
1205 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001206 except Exception as e:
1207 LOG.exception('Tenant network connectivity check failed')
1208 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001209 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001210 raise
1211
zhufle9877c62017-10-13 09:38:19 +08001212 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001213 nic=None, protocol='icmp'):
1214 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001215
Claudiu Belu33c3e602014-08-28 16:38:01 +03001216 :param source: RemoteClient: an ssh connection from which to execute
1217 the check
1218 :param dest: an IP to check connectivity against
1219 :param should_succeed: boolean should connection succeed or not
1220 :param nic: specific network interface to test connectivity from
1221 :param protocol: the protocol used to test connectivity with.
1222 :returns: True, if the connection succeeded and it was expected to
1223 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001224 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301225
Claudiu Belu33c3e602014-08-28 16:38:01 +03001226 method_name = '%s_check' % protocol
1227 connectivity_checker = getattr(source, method_name)
1228
1229 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001230 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001231 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001232 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001233 LOG.warning('Failed to check %(protocol)s connectivity for '
1234 'IP %(dest)s via a ssh connection from: %(src)s.',
1235 dict(protocol=protocol, dest=dest,
1236 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001237 return not should_succeed
1238 return should_succeed
1239
Claudiu Belu33c3e602014-08-28 16:38:01 +03001240 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001241 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001242 if result:
1243 return
1244
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001245 source_host = source.ssh_client.host
1246 if should_succeed:
1247 msg = "Timed out waiting for %s to become reachable from %s" \
1248 % (dest, source_host)
1249 else:
1250 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001251 self._log_console_output()
1252 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001253
John Warren456d9ae2016-01-12 15:36:33 -05001254 def _create_security_group(self, security_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001255 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001256 namestart='secgroup-smoke',
1257 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001258 if security_group_rules_client is None:
1259 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001260 if security_groups_client is None:
1261 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001262 if project_id is None:
1263 project_id = security_groups_client.project_id
John Warrenf9606e92015-12-10 12:12:42 -05001264 secgroup = self._create_empty_security_group(
1265 namestart=namestart, client=security_groups_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001266 project_id=project_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001267
1268 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -05001269 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001270 security_group_rules_client=security_group_rules_client,
1271 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001272 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001273 for rule in rules:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001274 self.assertEqual(project_id, rule['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001275 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001276 return secgroup
1277
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001278 def _create_empty_security_group(self, client=None, project_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001279 namestart='secgroup-smoke'):
1280 """Create a security group without rules.
1281
1282 Default rules will be created:
1283 - IPv4 egress to any
1284 - IPv6 egress to any
1285
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001286 :param project_id: secgroup will be created in this project
Steve Heyman33735f22016-05-24 09:28:08 -05001287 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001288 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301289
Yair Fried1fc32a12014-08-04 09:11:30 +03001290 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001291 client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001292 if not project_id:
1293 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001294 sg_name = data_utils.rand_name(namestart)
1295 sg_desc = sg_name + " description"
1296 sg_dict = dict(name=sg_name,
1297 description=sg_desc)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001298 sg_dict['project_id'] = project_id
David Kranz34e88122014-12-11 15:24:05 -05001299 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001300
1301 secgroup = result['security_group']
1302 self.assertEqual(secgroup['name'], sg_name)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001303 self.assertEqual(project_id, secgroup['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001304 self.assertEqual(secgroup['description'], sg_desc)
1305
Jordan Pittier9e227c52016-02-09 14:35:18 +01001306 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001307 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001308 return secgroup
1309
John Warren456d9ae2016-01-12 15:36:33 -05001310 def _create_security_group_rule(self, secgroup=None,
1311 sec_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001312 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001313 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001314 """Create a rule from a dictionary of rule parameters.
1315
1316 Create a rule in a secgroup. if secgroup not defined will search for
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001317 default secgroup in project_id.
Yair Fried1fc32a12014-08-04 09:11:30 +03001318
Steve Heyman33735f22016-05-24 09:28:08 -05001319 :param secgroup: the security group.
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001320 :param project_id: if secgroup not passed -- the tenant in which to
Yair Fried1fc32a12014-08-04 09:11:30 +03001321 search for default secgroup
1322 :param kwargs: a dictionary containing rule parameters:
1323 for example, to allow incoming ssh:
1324 rule = {
1325 direction: 'ingress'
1326 protocol:'tcp',
1327 port_range_min: 22,
1328 port_range_max: 22
1329 }
1330 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301331
John Warren456d9ae2016-01-12 15:36:33 -05001332 if sec_group_rules_client is None:
1333 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001334 if security_groups_client is None:
1335 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001336 if not project_id:
1337 project_id = security_groups_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001338 if secgroup is None:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001339 # Get default secgroup for project_id
zhuflb0b272e2017-09-22 16:01:46 +08001340 default_secgroups = security_groups_client.list_security_groups(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001341 name='default', project_id=project_id)['security_groups']
1342 msg = "No default security group for project %s." % (project_id)
zhuflb0b272e2017-09-22 16:01:46 +08001343 self.assertNotEmpty(default_secgroups, msg)
1344 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001345
Steve Heyman33735f22016-05-24 09:28:08 -05001346 ruleset = dict(security_group_id=secgroup['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001347 project_id=secgroup['project_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001348 ruleset.update(kwargs)
1349
John Warren456d9ae2016-01-12 15:36:33 -05001350 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001351 sg_rule = sg_rule['security_group_rule']
1352
1353 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1354 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001355
1356 return sg_rule
1357
John Warren456d9ae2016-01-12 15:36:33 -05001358 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1359 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001360 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001361 """Create loginable security group rule
1362
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001363 This function will create:
1364 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1365 access for ipv4.
1366 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1367 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001368 """
1369
John Warren456d9ae2016-01-12 15:36:33 -05001370 if security_group_rules_client is None:
1371 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001372 if security_groups_client is None:
1373 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001374 rules = []
1375 rulesets = [
1376 dict(
1377 # ssh
1378 protocol='tcp',
1379 port_range_min=22,
1380 port_range_max=22,
1381 ),
1382 dict(
1383 # ping
1384 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001385 ),
1386 dict(
1387 # ipv6-icmp for ping6
1388 protocol='icmp',
1389 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001390 )
1391 ]
John Warren456d9ae2016-01-12 15:36:33 -05001392 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001393 for ruleset in rulesets:
1394 for r_direction in ['ingress', 'egress']:
1395 ruleset['direction'] = r_direction
1396 try:
1397 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001398 sec_group_rules_client=sec_group_rules_client,
1399 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001400 security_groups_client=security_groups_client,
1401 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001402 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001403 # if rule already exist - skip rule and continue
1404 msg = 'Security group rule already exists'
1405 if msg not in ex._error_string:
1406 raise ex
1407 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001408 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001409 rules.append(sg_rule)
1410
1411 return rules
1412
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001413 def _get_router(self, client=None, project_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001414 """Retrieve a router for the given tenant id.
1415
1416 If a public router has been configured, it will be returned.
1417
1418 If a public router has not been configured, but a public
1419 network has, a tenant router will be created and returned that
1420 routes traffic to the public network.
1421 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301422
Yair Frieddb6c9e92014-08-06 08:53:13 +03001423 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001424 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001425 if not project_id:
1426 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001427 router_id = CONF.network.public_router_id
1428 network_id = CONF.network.public_network_id
1429 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001430 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001431 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001432 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001433 router = client.create_router(
1434 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1435 admin_state_up=True,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001436 project_id=project_id,
zhufl3484f992017-10-10 16:18:29 +08001437 external_gateway_info=dict(network_id=network_id))['router']
1438 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1439 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001440 return router
1441 else:
1442 raise Exception("Neither of 'public_router_id' or "
1443 "'public_network_id' has been defined.")
1444
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001445 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001446 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001447 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001448 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001449 """Create a network with a subnet connected to a router.
1450
David Shrewsbury9bac3662014-08-07 15:07:01 -04001451 The baremetal driver is a special case since all nodes are
1452 on the same shared network.
1453
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001454 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001455 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001456 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001457 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001458 a form like this: {'provider:network_type': 'vlan',
1459 'provider:physical_network': 'foo',
1460 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001461 :returns: network, subnet, router
1462 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301463
Thiago Paiva66cded22016-08-15 14:55:58 -03001464 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001465 # NOTE(Shrews): This exception is for environments where tenant
1466 # credential isolation is available, but network separation is
1467 # not (the current baremetal case). Likely can be removed when
1468 # test account mgmt is reworked:
1469 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001470 if not CONF.compute.fixed_network_name:
1471 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001472 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001473 network = self._get_network_by_name(
1474 CONF.compute.fixed_network_name)
1475 router = None
1476 subnet = None
1477 else:
John Warren94d8faf2015-09-15 12:22:24 -04001478 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001479 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001480 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001481 port_security_enabled=port_security_enabled,
1482 **net_dict)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001483 router = self._get_router(client=routers_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001484 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001485 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001486 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001487 # use explicit check because empty list is a valid option
1488 if dns_nameservers is not None:
1489 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001490 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001491 if not routers_client:
1492 routers_client = self.routers_client
1493 router_id = router['id']
1494 routers_client.add_router_interface(router_id,
1495 subnet_id=subnet['id'])
1496
1497 # save a cleanup job to remove this association between
1498 # router and subnet
1499 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1500 routers_client.remove_router_interface, router_id,
1501 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001502 return network, subnet, router
1503
1504
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001505class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001506 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001507
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001508 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001509
1510 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001511 def setup_clients(cls):
1512 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001513 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001514 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001515 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001516
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001517 def create_encryption_type(self, client=None, type_id=None, provider=None,
1518 key_size=None, cipher=None,
1519 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301520 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001521 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001522 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001523 if not type_id:
1524 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001525 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001526 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001527 client.create_encryption_type(
1528 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001529 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001530
lkuchlan3023e752017-06-08 12:53:13 +03001531 def create_encrypted_volume(self, encryption_provider, volume_type,
1532 key_size=256, cipher='aes-xts-plain64',
1533 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301534 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001535 volume_type = self.create_volume_type(name=volume_type)
1536 self.create_encryption_type(type_id=volume_type['id'],
1537 provider=encryption_provider,
1538 key_size=key_size,
1539 cipher=cipher,
1540 control_location=control_location)
1541 return self.create_volume(volume_type=volume_type['name'])
1542
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001543
Masayuki Igawa0870db52015-09-18 21:08:36 +09001544class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001545 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001546
1547 Subclasses implement the tests that use the methods provided by this
1548 class.
1549 """
1550
1551 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001552 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001553 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001554 if not CONF.service_available.swift:
1555 skip_msg = ("%s skipped as swift is not available" %
1556 cls.__name__)
1557 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001558
1559 @classmethod
1560 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001561 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001562 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001563 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001564 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001565
1566 @classmethod
1567 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001568 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001569 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001570 cls.account_client = cls.os_operator.account_client
1571 cls.container_client = cls.os_operator.container_client
1572 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001573
Chris Dentde456a12014-09-10 12:41:15 +01001574 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301575 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001576 self.account_client.list_account_containers()
1577 LOG.debug('Swift status information obtained successfully')
1578
Chris Dentde456a12014-09-10 12:41:15 +01001579 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301580 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001581 name = container_name or data_utils.rand_name(
1582 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001583 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001584 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001585 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001586 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001587 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001588 self.container_client.delete_container,
1589 name)
Chris Dent0d494112014-08-26 13:48:30 +01001590 return name
1591
Chris Dentde456a12014-09-10 12:41:15 +01001592 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301593 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001594 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001595 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001596
Chris Dentde456a12014-09-10 12:41:15 +01001597 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301598 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001599 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001600 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001601 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001602 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001603 self.object_client.delete_object,
1604 container_name,
1605 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001606 return obj_name, obj_data
1607
Chris Dentde456a12014-09-10 12:41:15 +01001608 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301609 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001610 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001611 self.list_and_check_container_objects(container_name,
1612 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001613
Chris Dentde456a12014-09-10 12:41:15 +01001614 def list_and_check_container_objects(self, container_name,
1615 present_obj=None,
1616 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301617 """List and verify objects for a given container
1618
1619 This utility lists objects for a given container
1620 and asserts which are present and
1621 which are not
1622 """
1623
Ghanshyam2a180b82014-06-16 13:54:22 +09001624 if present_obj is None:
1625 present_obj = []
1626 if not_present_obj is None:
1627 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001628 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001629 container_name)
1630 if present_obj:
1631 for obj in present_obj:
1632 self.assertIn(obj, object_list)
1633 if not_present_obj:
1634 for obj in not_present_obj:
1635 self.assertNotIn(obj, object_list)
1636
Chris Dentde456a12014-09-10 12:41:15 +01001637 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301638 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001639 _, obj = self.object_client.get_object(container_name, obj_name)
1640 self.assertEqual(obj, expected_data)