blob: aa34fb4b89d55115eb1092d54af49065f8daf964 [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):
Martin Kopec9c874412020-12-17 20:43:26 +0000146 """Creates port for the respective network_id
147
148 :param network_id: the id of the network
149 :param client: the client to use, defaults to self.ports_client
150 :param kwargs: additional arguments such as:
151 - namestart - a string to generate a name for the port from
152 - default is self.__class__.__name__
153 - 'binding:vnic_type' - defaults to CONF.network.port_vnic_type
154 - 'binding:profile' - defaults to CONF.network.port_profile
155 """
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300156 if not client:
157 client = self.ports_client
Martin Kopec9c874412020-12-17 20:43:26 +0000158 name = data_utils.rand_name(
159 kwargs.pop('namestart', self.__class__.__name__))
Edan David408a97b2018-01-15 03:52:15 -0500160 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
161 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
162 if CONF.network.port_profile and 'binding:profile' not in kwargs:
163 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300164 result = client.create_port(
165 name=name,
166 network_id=network_id,
167 **kwargs)
Soniya Vyas0123f522020-09-24 17:43:26 +0530168 self.assertIsNotNone(result, 'Unable to allocate port')
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300169 port = result['port']
170 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
171 client.delete_port, port['id'])
172 return port
173
Martin Kopec30b4d532020-10-16 12:02:43 +0000174 def create_keypair(self, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530175 """Creates keypair
176
177 Keypair is a public key of OpenSSH key pair used for accessing
178 and create servers
179 Keypair can also be created by a private key for the same purpose
180 Here, the keys are randomly generated[public/private]
181 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300182 if not client:
183 client = self.keypairs_client
Martin Kopec30b4d532020-10-16 12:02:43 +0000184 if not kwargs.get('name'):
185 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100186 # We don't need to create a keypair by pubkey in scenario
Martin Kopec30b4d532020-10-16 12:02:43 +0000187 body = client.create_keypair(**kwargs)
188 self.addCleanup(client.delete_keypair, kwargs['name'])
ghanshyamdee01f22015-08-17 11:41:47 +0900189 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100190
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530191 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800192 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200193 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000194 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100195
lanoux5fc14522015-09-21 08:17:35 +0000196 This wrapper utility calls the common create test server and
197 returns a test server. The purpose of this wrapper is to minimize
198 the impact on the code of the tests already using this
199 function.
Noam Angel6e309952019-01-27 05:52:40 +0000200
201 :param **kwargs:
202 See extra parameters below
203
204 :Keyword Arguments:
205 * *vnic_type* (``string``) --
206 used when launching instances with pre-configured ports.
207 Examples:
208 normal: a traditional virtual port that is either attached
209 to a linux bridge or an openvswitch bridge on a
210 compute node.
211 direct: an SR-IOV port that is directly attached to a VM
212 macvtap: an SR-IOV port that is attached to a VM via a macvtap
213 device.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100214 direct-physical: an SR-IOV port that is directly attached to a
215 VM using physical instead of virtual
216 functions.
217 baremetal: a baremetal port directly attached to a baremetal
218 node.
219 virtio-forwarder: an SR-IOV port that is indirectly attached
220 to a VM using a low-latency vhost-user
221 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000222 Defaults to ``CONF.network.port_vnic_type``.
223 * *port_profile* (``dict``) --
224 This attribute is a dictionary that can be used (with admin
225 credentials) to supply information influencing the binding of
226 the port.
227 example: port_profile = "capabilities:[switchdev]"
228 Defaults to ``CONF.network.port_profile``.
Martin Kopec9c874412020-12-17 20:43:26 +0000229 * *create_port_body* (``dict``) --
230 This attribute is a dictionary of additional arguments to be
231 passed to create_port method.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100232 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100233
lanoux5fc14522015-09-21 08:17:35 +0000234 # NOTE(jlanoux): As a first step, ssh checks in the scenario
235 # tests need to be run regardless of the run_validation and
236 # validatable parameters and thus until the ssh validation job
237 # becomes voting in CI. The test resources management and IP
238 # association are taken care of in the scenario tests.
239 # Therefore, the validatable parameter is set to false in all
240 # those tests. In this way create_server just return a standard
241 # server and the scenario tests always perform ssh checks.
242
243 # Needed for the cross_tenant_traffic test:
244 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800245 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000246
zhufl24208c22016-10-25 15:23:48 +0800247 if name is None:
248 name = data_utils.rand_name(self.__class__.__name__ + "-server")
249
Noam Angel6e309952019-01-27 05:52:40 +0000250 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
251 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000252
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000253 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000254 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000255 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000256 ports = []
Martin Kopec9c874412020-12-17 20:43:26 +0000257 create_port_body = kwargs.pop('create_port_body', {})
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300258
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000259 if vnic_type:
260 create_port_body['binding:vnic_type'] = vnic_type
261
262 if profile:
263 create_port_body['binding:profile'] = profile
264
lanoux5fc14522015-09-21 08:17:35 +0000265 if kwargs:
266 # Convert security group names to security group ids
267 # to pass to create_port
268 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300269 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500270 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000271 ).get('security_groups')
272 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100273 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000274
275 sec_groups_names = [s['name'] for s in kwargs.pop(
276 'security_groups')]
277 security_groups_ids = [sec_dict[s]
278 for s in sec_groups_names]
279
280 if security_groups_ids:
281 create_port_body[
282 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300283 networks = kwargs.pop('networks', [])
284 else:
285 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000286
287 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300288 # for the project's private networks and create a port.
289 # The same behaviour as we would expect when passing
290 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000291 if not networks:
292 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300293 **{'router:external': False, 'fields': 'id'})['networks']
294
295 # It's net['uuid'] if networks come from kwargs
296 # and net['id'] if they come from
297 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000298 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000299 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300300 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800301 port = self.create_port(network_id=net_id,
302 client=clients.ports_client,
303 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300304 ports.append({'port': port['id']})
305 else:
306 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000307 if ports:
308 kwargs['networks'] = ports
309 self.ports = ports
310
311 tenant_network = self.get_tenant_network()
312
Marc Koderer979e4942016-12-08 10:07:59 +0100313 if CONF.compute.compute_volume_common_az:
314 kwargs.setdefault('availability_zone',
315 CONF.compute.compute_volume_common_az)
316
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200317 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000318 clients,
319 tenant_network=tenant_network,
320 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530321 name=name, flavor=flavor,
322 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000323
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200324 self.addCleanup(waiters.wait_for_server_termination,
325 clients.servers_client, body['id'])
326 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
327 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000328 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100329 return server
330
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100331 def create_volume(self, size=None, name=None, snapshot_id=None,
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000332 imageRef=None, volume_type=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530333 """Creates volume
334
335 This wrapper utility creates volume and waits for volume to be
336 in 'available' state.
337 This method returns the volume's full representation by GET request.
338 """
339
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700340 if size is None:
341 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500342 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800343 if CONF.image_feature_enabled.api_v1:
344 resp = self.image_client.check_image(imageRef)
345 image = common_image.get_image_meta_from_headers(resp)
346 else:
347 image = self.image_client.show_image(imageRef)
348 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500349 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100350 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800351 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000352 kwargs.update({'name': name,
353 'snapshot_id': snapshot_id,
354 'imageRef': imageRef,
355 'volume_type': volume_type,
356 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100357
358 if CONF.compute.compute_volume_common_az:
359 kwargs.setdefault('availability_zone',
360 CONF.compute.compute_volume_common_az)
361
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900362 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700363
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100364 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
365 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100366 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100367 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300368 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200369 waiters.wait_for_volume_resource_status(self.volumes_client,
370 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100371 # The volume retrieved on creation has a non-up-to-date status.
372 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000373 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100374 return volume
375
lkuchlane20e6a82018-05-08 11:28:46 +0300376 def create_backup(self, volume_id, name=None, description=None,
377 force=False, snapshot_id=None, incremental=False,
Martin Kopec4a140052020-10-16 16:26:55 +0000378 container=None, **kwargs):
379 """Creates a backup of the given volume_id or snapshot_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530380
Martin Kopec4a140052020-10-16 16:26:55 +0000381 This wrapper utility creates a backup and waits until it is in
382 'available' state.
383
384 :param volume_id: UUID of the volume to back up
385 :param name: backup name, '$classname-backup' by default
386 :param description: Description of the backup, None by default
387 :param force: boolean whether to backup even if the volume is attached
388 False by default
389 :param snapshot_id: UUID of the source snapshot to back up
390 None by default
391 :param incremental: boolean, False by default
392 :param container: a container name, None by default
393 :param **kwargs: additional parameters per the documentation:
394 https://docs.openstack.org/api-ref/block-storage/v3/
395 #create-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530396 """
lkuchlane20e6a82018-05-08 11:28:46 +0300397
398 name = name or data_utils.rand_name(
399 self.__class__.__name__ + "-backup")
Martin Kopec4a140052020-10-16 16:26:55 +0000400 args = {'name': name,
401 'description': description,
402 'force': force,
403 'snapshot_id': snapshot_id,
404 'incremental': incremental,
405 'container': container}
406 args.update(kwargs)
lkuchlane20e6a82018-05-08 11:28:46 +0300407 backup = self.backups_client.create_backup(volume_id=volume_id,
408 **kwargs)['backup']
409 self.addCleanup(self.backups_client.delete_backup, backup['id'])
410 waiters.wait_for_volume_resource_status(self.backups_client,
411 backup['id'], 'available')
412 return backup
413
Martin Kopec4a140052020-10-16 16:26:55 +0000414 def restore_backup(self, backup_id, **kwargs):
415 """Restores a backup given by the backup_id
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530416
Martin Kopec4a140052020-10-16 16:26:55 +0000417 This wrapper utility restores a backup and waits until it is in
418 'available' state.
419
420 :param backup_id: UUID of a backup to restore
421 :param **kwargs: additional parameters per the documentation:
422 https://docs.openstack.org/api-ref/block-storage/v3/
423 #restore-a-backup
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530424 """
425
Martin Kopec4a140052020-10-16 16:26:55 +0000426 body = self.backups_client.restore_backup(backup_id, **kwargs)
427 restore = body['restore']
lkuchlane20e6a82018-05-08 11:28:46 +0300428 self.addCleanup(self.volumes_client.delete_volume,
429 restore['volume_id'])
430 waiters.wait_for_volume_resource_status(self.backups_client,
431 backup_id, 'available')
432 waiters.wait_for_volume_resource_status(self.volumes_client,
433 restore['volume_id'],
434 'available')
435 self.assertEqual(backup_id, restore['backup_id'])
436 return restore
437
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000438 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
439 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530440 if image is None:
441 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530442 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
443 server_id, image, preserve_ephemeral)
444 self.servers_client.rebuild_server(
445 server_id=server_id,
446 image_ref=image,
447 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000448 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530449 if wait:
450 waiters.wait_for_server_status(self.servers_client,
451 server_id, 'ACTIVE')
452
lkuchlan73ed1f32017-07-06 16:22:12 +0300453 def create_volume_snapshot(self, volume_id, name=None, description=None,
Martin Kopeca17cca42020-10-17 16:57:51 +0000454 metadata=None, force=False, **kwargs):
455 """Creates volume's snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530456
Martin Kopeca17cca42020-10-17 16:57:51 +0000457 This wrapper utility creates volume snapshot and waits for it until
458 it is in 'available' state.
459
460 :param volume_id: UUID of a volume to create snapshot of
461 :param name: name of the snapshot, '$classname-snapshot' by default
462 :param description: description of the snapshot
463 :param metadata: metadata key and value pairs for the snapshot
464 :param force: whether snapshot even when the volume is attached
465 :param **kwargs: additional parameters per the doc
466 https://docs.openstack.org/api-ref/block-storage/v3/
467 #create-a-snapshot
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530468 """
469
lkuchlan73ed1f32017-07-06 16:22:12 +0300470 name = name or data_utils.rand_name(
471 self.__class__.__name__ + '-snapshot')
472 snapshot = self.snapshots_client.create_snapshot(
473 volume_id=volume_id,
474 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000475 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300476 description=description,
Martin Kopeca17cca42020-10-17 16:57:51 +0000477 metadata=metadata,
478 **kwargs)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530479
lkuchlan73ed1f32017-07-06 16:22:12 +0300480 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
481 snapshot['id'])
482 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
483 waiters.wait_for_volume_resource_status(self.snapshots_client,
484 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200485 snapshot = self.snapshots_client.show_snapshot(
486 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300487 return snapshot
488
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100489 def _cleanup_volume_type(self, volume_type):
490 """Clean up a given volume type.
491
492 Ensuring all volumes associated to a type are first removed before
493 attempting to remove the type itself. This includes any image volume
494 cache volumes stored in a separate tenant to the original volumes
495 created from the type.
496 """
497 admin_volume_type_client = self.os_admin.volume_types_client_latest
498 admin_volumes_client = self.os_admin.volumes_client_latest
499 volumes = admin_volumes_client.list_volumes(
500 detail=True, params={'all_tenants': 1})['volumes']
501 type_name = volume_type['name']
502 for volume in [v for v in volumes if v['volume_type'] == type_name]:
503 test_utils.call_and_ignore_notfound_exc(
504 admin_volumes_client.delete_volume, volume['id'])
505 admin_volumes_client.wait_for_resource_deletion(volume['id'])
506 admin_volume_type_client.delete_volume_type(volume_type['id'])
507
Martin Kopec8e673a42020-10-18 17:33:02 +0000508 def create_volume_type(self, client=None, name=None, backend_name=None,
509 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530510 """Creates volume type
511
512 In a multiple-storage back-end configuration,
513 each back end has a name (volume_backend_name).
514 The name of the back end is declared as an extra-specification
515 of a volume type (such as, volume_backend_name=LVM).
516 When a volume is created, the scheduler chooses an
517 appropriate back end to handle the request, according
518 to the volume type specified by the user.
519 The scheduler uses volume types to explicitly create volumes on
520 specific back ends.
521
522 Before using volume type, a volume type has to be declared
523 to Block Storage. In addition to that, an extra-specification
524 has to be created to link the volume type to a back end name.
525 """
526
scottda61f68ac2016-06-07 12:07:55 -0600527 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000528 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000529 if not name:
530 class_name = self.__class__.__name__
531 name = data_utils.rand_name(class_name + '-volume-type')
532 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600533
534 LOG.debug("Creating a volume type: %s on backend %s",
535 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000536 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600537 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000538 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600539
Martin Kopec8e673a42020-10-18 17:33:02 +0000540 volume_type_resp = client.create_volume_type(
541 name=randomized_name, extra_specs=extra_specs, **kwargs)
542 volume_type = volume_type_resp['volume_type']
543
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530544 self.assertIn('id', volume_type)
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100545 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600546 return volume_type
547
Soniya Vyascb95bba2021-02-04 19:35:10 +0530548 def create_loginable_secgroup_rule(self, secgroup_id=None, rulesets=None):
Martin Kopece1d873a2020-11-02 12:19:54 +0000549 """Create loginable security group rule by compute clients.
550
551 This function will create by default the following rules:
552 1. tcp port 22 allow rule in order to allow ssh access for ipv4
553 2. ipv4 icmp allow rule in order to allow icmpv4
554 """
555
John Warrenf2345512015-12-10 13:39:30 -0500556 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500557 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100558 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900559 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100560 for sg in sgs:
561 if sg['name'] == 'default':
562 secgroup_id = sg['id']
563
564 # These rules are intended to permit inbound ssh and icmp
565 # traffic from all sources, so no group_id is provided.
566 # Setting a group_id would only permit traffic from ports
567 # belonging to the same security group.
Martin Kopece1d873a2020-11-02 12:19:54 +0000568 if not rulesets:
569 rulesets = [
570 {
571 # ssh
572 'ip_protocol': 'tcp',
573 'from_port': 22,
574 'to_port': 22,
575 'cidr': '0.0.0.0/0',
576 },
577 {
578 # ping
579 'ip_protocol': 'icmp',
580 'from_port': -1,
581 'to_port': -1,
582 'cidr': '0.0.0.0/0',
583 }
584 ]
Andrea Frittoli247058f2014-07-16 16:09:22 +0100585 rules = list()
586 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000587 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900588 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100589 rules.append(sg_rule)
590 return rules
591
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530592 def _create_security_group(self, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530593 """Create security group and add rules to security group"""
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530594 if not kwargs.get('name'):
595 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
596 if not kwargs.get('description'):
597 kwargs['description'] = kwargs['name'] + " description"
John Warrenf2345512015-12-10 13:39:30 -0500598 secgroup = self.compute_security_groups_client.create_security_group(
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530599 **kwargs)['security_group']
600 self.assertEqual(secgroup['name'], kwargs['name'])
601 self.assertEqual(secgroup['description'], kwargs['description'])
John Warrenf2345512015-12-10 13:39:30 -0500602 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100603 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500604 self.compute_security_groups_client.delete_security_group,
605 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100606
607 # Add rules to the security group
Soniya Vyascb95bba2021-02-04 19:35:10 +0530608 self.create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100609 return secgroup
610
zhuflf52c7592017-05-25 13:55:24 +0800611 def get_remote_client(self, ip_address, username=None, private_key=None,
612 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100613 """Get a SSH client to a remote server
614
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600615 :param ip_address: the server floating or fixed IP address to use
616 for ssh validation
617 :param username: name of the Linux account on the remote server
618 :param private_key: the SSH private key to use
619 :param server: server dict, used for debugging purposes
620 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100621 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700622
Andrea Frittoli247058f2014-07-16 16:09:22 +0100623 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800624 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800625 # Set this with 'keypair' or others to log in with keypair or
626 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000627 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800628 password = None
629 if private_key is None:
630 private_key = self.keypair['private_key']
631 else:
lanoux283273b2015-12-04 03:01:54 -0800632 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800633 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800634 linux_client = remote_client.RemoteClient(
635 ip_address, username, pkey=private_key, password=password,
636 server=server, servers_client=self.servers_client)
637 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100638 return linux_client
639
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000640 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000641 img_path = CONF.scenario.img_file
642 if not os.path.exists(img_path):
643 # TODO(kopecmartin): replace LOG.warning for rasing
644 # InvalidConfiguration exception after tempest 25.0.0 is
645 # released - there will be one release which accepts both
646 # behaviors in order to avoid many failures across CIs and etc.
647 LOG.warning(
648 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
649 'a full path for the image. CONF.scenario.img_dir was '
650 'deprecated and will be removed in the next release. Till '
651 'Tempest 25.0.0, old behavior is maintained and keep working '
652 'but starting Tempest 26.0.0, you need to specify the full '
653 'path in CONF.scenario.img_file config option.')
654 img_path = os.path.join(CONF.scenario.img_dir, img_path)
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300655 img_container_format = CONF.scenario.img_container_format
656 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000657 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400658 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000659 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100660 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000661 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530662 if img_properties is None:
663 img_properties = {}
664 name = data_utils.rand_name('%s-' % name)
665 params = {
666 'name': name,
667 'container_format': img_container_format,
668 'disk_format': img_disk_format or img_container_format,
669 }
670 if CONF.image_feature_enabled.api_v1:
671 params['is_public'] = 'False'
672 if img_properties:
673 params['properties'] = img_properties
674 params = {'headers': common_image.image_meta_to_headers(**params)}
675 else:
676 params['visibility'] = 'private'
677 # Additional properties are flattened out in the v2 API.
678 if img_properties:
679 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000680 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530681 body = self.image_client.create_image(**params)
682 image = body['image'] if 'image' in body else body
683 self.addCleanup(self.image_client.delete_image, image['id'])
684 self.assertEqual("queued", image['status'])
685 with open(img_path, 'rb') as image_file:
686 if CONF.image_feature_enabled.api_v1:
687 self.image_client.update_image(image['id'], data=image_file)
688 else:
689 self.image_client.store_image_file(image['id'], image_file)
690 LOG.debug("image:%s", image['id'])
691 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100692
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530693 def log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530694 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400695 if not CONF.compute_feature_enabled.console_output:
696 LOG.debug('Console output not supported, cannot log')
697 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700698 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100699 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700700 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100701 servers = servers['servers']
702 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100703 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700704 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000705 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100706 LOG.debug('Console output for %s\nbody=\n%s',
707 server['id'], console_output)
708 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100709 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100710 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100711
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000712 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530713 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300714 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000715 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000716
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000717 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530718 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000719 # Glance client
720 _image_client = self.image_client
721 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900722 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000723 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800724 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000725 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000726 image = _images_client.create_image(server['id'], name=name, **kwargs)
David Kranza5299eb2015-01-15 17:24:05 -0500727 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300728 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200729
730 self.addCleanup(_image_client.wait_for_resource_deletion,
731 image_id)
732 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
733 _image_client.delete_image, image_id)
734
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400735 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530736 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700737 resp = _image_client.check_image(image_id)
738 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400739 image_props = snapshot_image.get('properties', {})
740 else:
741 # In glance v2 the additional properties are flattened.
742 snapshot_image = _image_client.show_image(image_id)
743 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300744
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400745 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300746 if bdm:
747 bdm = json.loads(bdm)
748 if bdm and 'snapshot_id' in bdm[0]:
749 snapshot_id = bdm[0]['snapshot_id']
750 self.addCleanup(
751 self.snapshots_client.wait_for_resource_deletion,
752 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100753 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
754 self.snapshots_client.delete_snapshot,
755 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200756 waiters.wait_for_volume_resource_status(self.snapshots_client,
757 snapshot_id,
758 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000759 image_name = snapshot_image['name']
760 self.assertEqual(name, image_name)
761 LOG.debug("Created snapshot image %s for server %s",
762 image_name, server['name'])
763 return snapshot_image
764
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000765 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530766 """Compute volume attach
767
768 This utility attaches volume from compute and waits for the
769 volume status to be 'in-use' state.
770 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000771 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000772 server['id'], volumeId=volume_to_attach['id'],
773 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200774 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200775 waiters.wait_for_volume_resource_status(self.volumes_client,
776 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000777 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
778 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200779 # Return the updated volume after the attachment
780 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900781
Jordan Pittier7cf64762015-10-14 15:01:12 +0200782 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530783 """Compute volume detach
784
Lee Yarwood5423c532020-12-17 11:24:46 +0000785 This utility detaches the volume from the server and checks whether the
786 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530787 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200788 self.servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000789 waiters.wait_for_volume_attachment_remove_from_server(
790 self.servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200791
Steven Hardyda2a8352014-10-02 12:52:20 +0100792 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800793 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530794 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000795 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000796 cmd = ['ping', '-c1', '-w1']
797
798 if mtu:
799 cmd += [
800 # don't fragment
801 '-M', 'do',
802 # ping receives just the size of ICMP payload
803 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
804 ]
805 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700806
807 def ping():
808 proc = subprocess.Popen(cmd,
809 stdout=subprocess.PIPE,
810 stderr=subprocess.PIPE)
811 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000812
Aaron Rosena7df13b2014-09-23 09:45:45 -0700813 return (proc.returncode == 0) == should_succeed
814
Jordan Pittier9e227c52016-02-09 14:35:18 +0100815 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000816 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800817 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000818 'caller': caller, 'ip': ip_address, 'timeout': timeout,
819 'should_succeed':
820 'reachable' if should_succeed else 'unreachable'
821 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200822 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000823 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800824 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000825 'caller': caller, 'ip': ip_address, 'timeout': timeout,
826 'result': 'expected' if result else 'unexpected'
827 })
zhufl0ec74c42017-11-15 14:02:28 +0800828 if server:
Soniya Vyas1b0cddc2021-01-29 17:28:19 +0530829 self.log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000830 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700831
Yair Friedae0e73d2014-11-24 11:56:26 +0200832 def check_vm_connectivity(self, ip_address,
833 username=None,
834 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000835 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800836 extra_msg="",
837 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000838 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000839 """Check server connectivity
840
Yair Friedae0e73d2014-11-24 11:56:26 +0200841 :param ip_address: server to test against
842 :param username: server's ssh username
843 :param private_key: server's ssh private key to be used
844 :param should_connect: True/False indicates positive/negative test
845 positive - attempt ping and ssh
846 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800847 :param extra_msg: Message to help with debugging if ``ping_ip_address``
848 fails
849 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000850 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200851
852 :raises: AssertError if the result of the connectivity check does
853 not match the value of the should_connect param
854 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530855
zhufl0ec74c42017-11-15 14:02:28 +0800856 LOG.debug('checking network connections to IP %s with user: %s',
857 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200858 if should_connect:
859 msg = "Timed out waiting for %s to become reachable" % ip_address
860 else:
861 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800862 if extra_msg:
863 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200864 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000865 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800866 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200867 msg=msg)
868 if should_connect:
869 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800870 try:
871 self.get_remote_client(ip_address, username, private_key,
872 server=server)
873 except Exception:
874 if not extra_msg:
875 extra_msg = 'Failed to ssh to %s' % ip_address
876 LOG.exception(extra_msg)
877 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200878
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000879 def create_floating_ip(self, server, pool_name=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000880 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200881
Marc Koderer3b57d802016-03-22 15:23:31 +0100882 if not pool_name:
883 pool_name = CONF.network.floating_network_name
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000884
John Warrene74890a2015-11-11 15:18:01 -0500885 floating_ip = (self.compute_floating_ips_client.
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000886 create_floating_ip(pool=pool_name,
887 **kwargs)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100888 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500889 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200890 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500891 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530892 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200893 return floating_ip
894
Sean Dague20e98612016-01-06 14:33:28 -0500895 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000896 private_key=None, server=None, username=None,
897 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530898 """Creates timestamp
899
900 This wrapper utility does ssh, creates timestamp and returns the
901 created timestamp.
902 """
Sean Dague20e98612016-01-06 14:33:28 -0500903 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200904 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000905 server=server,
906 username=username)
907
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300908 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000909 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800910 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
911 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300912 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
913 ssh_client.exec_command(cmd_timestamp)
914 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
915 % mount_path)
916 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800917 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300918 return timestamp
919
Sean Dague20e98612016-01-06 14:33:28 -0500920 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000921 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530922 """Returns timestamp
923
924 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000925
926 :param ip_address: The floating IP or fixed IP of the remote server
927 :param dev_name: Name of the device that stores the timestamp
928 :param mount_path: Path which should be used as mount point for
929 dev_name
930 :param private_key: The SSH private key to use for authentication
931 :param server: Server dict, used for debugging purposes
932 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530933 """
934
Sean Dague20e98612016-01-06 14:33:28 -0500935 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200936 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000937 server=server,
938 username=username)
939
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300940 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700941 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300942 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
943 % mount_path)
944 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800945 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300946 return timestamp
947
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000948 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -0500949 """Get the server fixed or floating IP.
950
951 Based on the configuration we're in, return a correct ip
952 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000953
954 If CONF.validation.connect_method is floating, then
955 a floating ip will be created passing kwargs as additional
956 argument.
Sean Dague20e98612016-01-06 14:33:28 -0500957 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530958
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200959 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500960 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800961 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500962 # method is creating the floating IP there.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000963 return self.create_floating_ip(server, **kwargs)['ip']
Sean Dague20e98612016-01-06 14:33:28 -0500964 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400965 # Determine the network name to look for based on config or creds
966 # provider network resources.
967 if CONF.validation.network_for_ssh:
968 addresses = server['addresses'][
969 CONF.validation.network_for_ssh]
970 else:
zhufl7b4a7202017-09-28 10:29:27 +0800971 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400972 addresses = (server['addresses'][network['name']]
973 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500974 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200975 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
976 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500977 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800978 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200979 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400980 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200981
zhufl7bc916d2018-08-22 14:47:39 +0800982 @classmethod
983 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530984 """Gets host of server"""
985
zhufl7bc916d2018-08-22 14:47:39 +0800986 server_details = cls.os_admin.servers_client.show_server(server_id)
987 return server_details['server']['OS-EXT-SRV-ATTR:host']
988
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000989 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
990 bd_map_v2 = [{
991 'uuid': source_id,
992 'source_type': source_type,
993 'destination_type': 'volume',
994 'boot_index': 0,
995 'delete_on_termination': delete_on_termination}]
996 return {'block_device_mapping_v2': bd_map_v2}
997
998 def boot_instance_from_resource(self, source_id,
999 source_type,
1000 keypair=None,
1001 security_group=None,
1002 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +00001003 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301004 """Boot instance from resource
1005
1006 This wrapper utility boots instance from resource with block device
1007 mapping with source info passed in arguments
1008 """
1009
Martin Kopecbee673e2020-11-04 09:40:52 +00001010 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001011 if keypair:
1012 create_kwargs['key_name'] = keypair['name']
1013 if security_group:
1014 create_kwargs['security_groups'] = [
1015 {'name': security_group['name']}]
1016 create_kwargs.update(self._get_bdm(
1017 source_id,
1018 source_type,
1019 delete_on_termination=delete_on_termination))
1020 if name:
1021 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +00001022 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001023
Martin Kopecbee673e2020-11-04 09:40:52 +00001024 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001025
Martin Kopec0216b372020-11-04 09:32:05 +00001026 def create_volume_from_image(self, **kwargs):
1027 """Create volume from image.
1028
1029 :param image_id: ID of the image to create volume from,
1030 CONF.compute.image_ref by default
1031 :param name: name of the volume,
1032 '$classname-volume-origin' by default
1033 :param **kwargs: additional parameters
1034 """
1035 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
1036 name = kwargs.pop('name', None)
1037 if not name:
1038 namestart = self.__class__.__name__ + '-volume-origin'
1039 name = data_utils.rand_name(namestart)
1040 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001041
Andrea Frittoli2e733b52014-07-16 14:12:11 +01001042
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001043class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001044 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001045
Yair Fried1fc32a12014-08-04 09:11:30 +03001046 This class provide helpers for network scenario tests, using the neutron
1047 API. Helpers from ancestor which use the nova network API are overridden
1048 with the neutron API.
1049
1050 This Class also enforces using Neutron instead of novanetwork.
1051 Subclassed tests will be skipped if Neutron is not enabled
1052
1053 """
1054
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001055 credentials = ['primary', 'admin']
1056
Yair Fried1fc32a12014-08-04 09:11:30 +03001057 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001058 def skip_checks(cls):
1059 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001060 if not CONF.service_available.neutron:
1061 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001062
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001063 def _create_network(self, networks_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001064 project_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001065 namestart='network-smoke-',
Lajos Katonac87a06b2019-01-04 13:21:48 +01001066 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001067 if not networks_client:
1068 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001069 if not project_id:
1070 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001071 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001072 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001073 if net_dict:
1074 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001075 # Neutron disables port security by default so we have to check the
1076 # config before trying to create the network with port_security_enabled
1077 if CONF.network_feature_enabled.port_security:
1078 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001079 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001080 network = result['network']
1081
1082 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001083 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001084 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001085 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001086 return network
1087
zhufl5b0a52f2017-10-24 15:48:20 +08001088 def create_subnet(self, network, subnets_client=None,
1089 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001090 """Create a subnet for the given network
1091
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301092 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001093 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301094
1095 :param **kwargs:
1096 See extra parameters below
1097
1098 :Keyword Arguments:
1099
1100 * *ip_version = ip version of the given network,
Yair Fried1fc32a12014-08-04 09:11:30 +03001101 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301102
John Warren3961acd2015-10-02 14:38:53 -04001103 if not subnets_client:
1104 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001105
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001106 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001107 """Check cidr existence
1108
yangjianfeng4ad346e2020-11-22 06:49:19 +00001109 :returns: True if subnet with cidr already exist in tenant or
1110 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001111 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001112 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001113 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001114 external_nets = self.os_admin.networks_client.list_networks(
1115 **{"router:external": True})['networks']
1116 external_subnets = []
1117 for ext_net in external_nets:
1118 external_subnets.extend(
1119 self.os_admin.subnets_client.list_subnets(
1120 network_id=ext_net['id'], cidr=cidr)['subnets'])
1121 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001122
Kirill Shileev14113572014-11-21 16:58:02 +03001123 ip_version = kwargs.pop('ip_version', 4)
1124
1125 if ip_version == 6:
1126 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -04001127 CONF.network.project_network_v6_cidr)
1128 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001129 else:
Sean Dagueed6e5862016-04-04 10:49:13 -04001130 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
1131 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001132
Yair Fried1fc32a12014-08-04 09:11:30 +03001133 result = None
Kirill Shileev14113572014-11-21 16:58:02 +03001134 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +03001135 # Repeatedly attempt subnet creation with sequential cidr
1136 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +03001137 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +03001138 str_cidr = str(subnet_cidr)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001139 if cidr_in_use(str_cidr, project_id=network['project_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +03001140 continue
1141
1142 subnet = dict(
1143 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001144 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001145 project_id=network['project_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +03001146 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +03001147 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001148 **kwargs
1149 )
1150 try:
John Warren3961acd2015-10-02 14:38:53 -04001151 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +03001152 break
Masayuki Igawad9388762015-01-20 14:56:42 +09001153 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +03001154 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1155 if not is_overlapping_cidr:
1156 raise
1157 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001158
1159 subnet = result['subnet']
1160 self.assertEqual(subnet['cidr'], str_cidr)
1161
1162 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1163 subnets_client.delete_subnet, subnet['id'])
1164
Yair Fried1fc32a12014-08-04 09:11:30 +03001165 return subnet
1166
Lukas Piwowarski9523d512020-10-30 09:37:58 +00001167 def _get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
1168
1169 if ip_addr and not kwargs.get('fixed_ips'):
1170 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
1171 ports = self.os_admin.ports_client.list_ports(
1172 device_id=server['id'], **kwargs)['ports']
1173
Kobi Samoray166500a2016-10-09 14:42:48 +03001174 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001175 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1176 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001177
1178 def _is_active(port):
1179 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1180 # servers. Neutron does not bind ports for Ironic instances, as a
1181 # result the port remains in the DOWN state. This has been fixed
1182 # with the introduction of the networking-baremetal plugin but
1183 # it's not mandatory (and is not used on all stable branches).
1184 return (port['status'] == 'ACTIVE' or
1185 port.get('binding:vnic_type') == 'baremetal')
1186
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001187 port_map = [(p["id"], fxip["ip_address"])
1188 for p in ports
1189 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001190 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001191 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001192 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1193 if inactive:
1194 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001195
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001196 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001197 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001198 self.assertEqual(len(port_map), 1,
1199 "Found multiple IPv4 addresses: %s. "
1200 "Unable to determine which port to target."
1201 % port_map)
1202 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001203
Soniya Vyasc37410f2021-02-24 15:26:27 +05301204 def get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001205 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001206 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001207 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001208 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001209 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001210
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301211 def create_floating_ip(self, server, external_network_id=None,
Martin Kopec5e761bf2020-11-03 16:11:08 +00001212 port_id=None, client=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001213 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301214
Yair Friedae0e73d2014-11-24 11:56:26 +02001215 if not external_network_id:
1216 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001217 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001218 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001219 if not port_id:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301220 port_id, ip4 = self._get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001221 else:
1222 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001223
Martin Kopec5e761bf2020-11-03 16:11:08 +00001224 floatingip_kwargs = {
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001225 'floating_network_id': external_network_id,
1226 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301227 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001228 'fixed_ip_address': ip4,
1229 }
1230 if CONF.network.subnet_id:
Martin Kopec5e761bf2020-11-03 16:11:08 +00001231 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1232
1233 floatingip_kwargs.update(kwargs)
1234 result = client.create_floatingip(**floatingip_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001235 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001236
Jordan Pittier9e227c52016-02-09 14:35:18 +01001237 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001238 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001239 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001240 return floating_ip
1241
Soniya Vyased664472020-09-23 18:40:25 +05301242 def associate_floating_ip(self, floating_ip, server):
1243 """Associate floating ip
1244
1245 This wrapper utility attaches the floating_ip for
1246 the respective port_id of server
1247 """
1248 port_id, _ = self._get_server_port_id_and_ip4(server)
1249 kwargs = dict(port_id=port_id)
1250 floating_ip = self.floating_ips_client.update_floatingip(
1251 floating_ip['id'], **kwargs)['floatingip']
1252 self.assertEqual(port_id, floating_ip['port_id'])
1253 return floating_ip
1254
1255 def disassociate_floating_ip(self, floating_ip):
1256 """Disassociates floating ip
1257
1258 This wrapper utility disassociates given floating ip.
1259 :param floating_ip: a dict which is a return value of
1260 floating_ips_client.create_floatingip method
1261 """
1262 kwargs = dict(port_id=None)
1263 floating_ip = self.floating_ips_client.update_floatingip(
1264 floating_ip['id'], **kwargs)['floatingip']
1265 self.assertIsNone(floating_ip['port_id'])
1266 return floating_ip
1267
Yair Fried45f92952014-06-26 05:19:19 +03001268 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001269 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001270
Steve Heyman33735f22016-05-24 09:28:08 -05001271 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001272 :param status: target status
1273 :raises: AssertionError if status doesn't match
1274 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301275
Steve Heyman33735f22016-05-24 09:28:08 -05001276 floatingip_id = floating_ip['id']
1277
Carl Baldwina754e2d2014-10-23 22:47:41 +00001278 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001279 floating_ip = (self.floating_ips_client.
1280 show_floatingip(floatingip_id)['floatingip'])
1281 if status == floating_ip['status']:
1282 LOG.info("FloatingIP: {fp} is at status: {st}"
1283 .format(fp=floating_ip, st=status))
1284 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001285
zhufl4dda94e2017-03-14 16:14:46 +08001286 if not test_utils.call_until_true(refresh,
1287 CONF.network.build_timeout,
1288 CONF.network.build_interval):
1289 floating_ip = self.floating_ips_client.show_floatingip(
1290 floatingip_id)['floatingip']
1291 self.assertEqual(status, floating_ip['status'],
1292 message="FloatingIP: {fp} is at status: {cst}. "
1293 "failed to reach status: {st}"
1294 .format(fp=floating_ip, cst=floating_ip['status'],
1295 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001296
zhufl420a0192017-09-28 11:04:50 +08001297 def check_tenant_network_connectivity(self, server,
1298 username,
1299 private_key,
1300 should_connect=True,
1301 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301302 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001303 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001304 msg = 'Tenant networks not configured to be reachable.'
1305 LOG.info(msg)
1306 return
1307 # The target login is assumed to have been configured for
1308 # key-based authentication by cloud-init.
1309 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001310 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001311 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001312 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001313 username,
1314 private_key,
1315 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001316 except Exception as e:
1317 LOG.exception('Tenant network connectivity check failed')
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301318 self.log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001319 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001320 raise
1321
zhufle9877c62017-10-13 09:38:19 +08001322 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001323 nic=None, protocol='icmp'):
1324 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001325
Claudiu Belu33c3e602014-08-28 16:38:01 +03001326 :param source: RemoteClient: an ssh connection from which to execute
1327 the check
1328 :param dest: an IP to check connectivity against
1329 :param should_succeed: boolean should connection succeed or not
1330 :param nic: specific network interface to test connectivity from
1331 :param protocol: the protocol used to test connectivity with.
1332 :returns: True, if the connection succeeded and it was expected to
1333 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001334 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301335
Claudiu Belu33c3e602014-08-28 16:38:01 +03001336 method_name = '%s_check' % protocol
1337 connectivity_checker = getattr(source, method_name)
1338
1339 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001340 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001341 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001342 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001343 LOG.warning('Failed to check %(protocol)s connectivity for '
1344 'IP %(dest)s via a ssh connection from: %(src)s.',
1345 dict(protocol=protocol, dest=dest,
1346 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001347 return not should_succeed
1348 return should_succeed
1349
Claudiu Belu33c3e602014-08-28 16:38:01 +03001350 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001351 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001352 if result:
1353 return
1354
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001355 source_host = source.ssh_client.host
1356 if should_succeed:
1357 msg = "Timed out waiting for %s to become reachable from %s" \
1358 % (dest, source_host)
1359 else:
1360 msg = "%s is reachable from %s" % (dest, source_host)
Soniya Vyas1b0cddc2021-01-29 17:28:19 +05301361 self.log_console_output()
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001362 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001363
John Warren456d9ae2016-01-12 15:36:33 -05001364 def _create_security_group(self, security_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001365 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001366 namestart='secgroup-smoke',
1367 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001368 if security_group_rules_client is None:
1369 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001370 if security_groups_client is None:
1371 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001372 if project_id is None:
1373 project_id = security_groups_client.project_id
John Warrenf9606e92015-12-10 12:12:42 -05001374 secgroup = self._create_empty_security_group(
1375 namestart=namestart, client=security_groups_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001376 project_id=project_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001377
1378 # Add rules to the security group
Soniya Vyascb95bba2021-02-04 19:35:10 +05301379 rules = self.create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001380 security_group_rules_client=security_group_rules_client,
1381 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001382 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001383 for rule in rules:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001384 self.assertEqual(project_id, rule['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001385 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001386 return secgroup
1387
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001388 def _create_empty_security_group(self, client=None, project_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001389 namestart='secgroup-smoke'):
1390 """Create a security group without rules.
1391
1392 Default rules will be created:
1393 - IPv4 egress to any
1394 - IPv6 egress to any
1395
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001396 :param project_id: secgroup will be created in this project
Steve Heyman33735f22016-05-24 09:28:08 -05001397 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001398 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301399
Yair Fried1fc32a12014-08-04 09:11:30 +03001400 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001401 client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001402 if not project_id:
1403 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001404 sg_name = data_utils.rand_name(namestart)
1405 sg_desc = sg_name + " description"
1406 sg_dict = dict(name=sg_name,
1407 description=sg_desc)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001408 sg_dict['project_id'] = project_id
David Kranz34e88122014-12-11 15:24:05 -05001409 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001410
1411 secgroup = result['security_group']
1412 self.assertEqual(secgroup['name'], sg_name)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001413 self.assertEqual(project_id, secgroup['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001414 self.assertEqual(secgroup['description'], sg_desc)
1415
Jordan Pittier9e227c52016-02-09 14:35:18 +01001416 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001417 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001418 return secgroup
1419
Soniya Vyas945f5a12021-02-02 23:49:12 +05301420 def create_security_group_rule(self, secgroup=None,
1421 sec_group_rules_client=None,
1422 project_id=None,
1423 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001424 """Create a rule from a dictionary of rule parameters.
1425
1426 Create a rule in a secgroup. if secgroup not defined will search for
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001427 default secgroup in project_id.
Yair Fried1fc32a12014-08-04 09:11:30 +03001428
Steve Heyman33735f22016-05-24 09:28:08 -05001429 :param secgroup: the security group.
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001430 :param project_id: if secgroup not passed -- the tenant in which to
Yair Fried1fc32a12014-08-04 09:11:30 +03001431 search for default secgroup
1432 :param kwargs: a dictionary containing rule parameters:
1433 for example, to allow incoming ssh:
1434 rule = {
1435 direction: 'ingress'
1436 protocol:'tcp',
1437 port_range_min: 22,
1438 port_range_max: 22
1439 }
1440 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301441
John Warren456d9ae2016-01-12 15:36:33 -05001442 if sec_group_rules_client is None:
1443 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001444 if security_groups_client is None:
1445 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001446 if not project_id:
1447 project_id = security_groups_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001448 if secgroup is None:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001449 # Get default secgroup for project_id
zhuflb0b272e2017-09-22 16:01:46 +08001450 default_secgroups = security_groups_client.list_security_groups(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001451 name='default', project_id=project_id)['security_groups']
1452 msg = "No default security group for project %s." % (project_id)
zhuflb0b272e2017-09-22 16:01:46 +08001453 self.assertNotEmpty(default_secgroups, msg)
1454 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001455
Steve Heyman33735f22016-05-24 09:28:08 -05001456 ruleset = dict(security_group_id=secgroup['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001457 project_id=secgroup['project_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001458 ruleset.update(kwargs)
1459
John Warren456d9ae2016-01-12 15:36:33 -05001460 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001461 sg_rule = sg_rule['security_group_rule']
1462
1463 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1464 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001465
1466 return sg_rule
1467
Soniya Vyascb95bba2021-02-04 19:35:10 +05301468 def create_loginable_secgroup_rule(self, security_group_rules_client=None,
1469 secgroup=None,
1470 security_groups_client=None):
Martin Kopece1d873a2020-11-02 12:19:54 +00001471 """Create loginable security group rule by neutron clients by default.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001472
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001473 This function will create:
1474 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1475 access for ipv4.
1476 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1477 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001478 """
1479
John Warren456d9ae2016-01-12 15:36:33 -05001480 if security_group_rules_client is None:
1481 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001482 if security_groups_client is None:
1483 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001484 rules = []
1485 rulesets = [
1486 dict(
1487 # ssh
1488 protocol='tcp',
1489 port_range_min=22,
1490 port_range_max=22,
1491 ),
1492 dict(
1493 # ping
1494 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001495 ),
1496 dict(
1497 # ipv6-icmp for ping6
1498 protocol='icmp',
1499 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001500 )
1501 ]
John Warren456d9ae2016-01-12 15:36:33 -05001502 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001503 for ruleset in rulesets:
1504 for r_direction in ['ingress', 'egress']:
1505 ruleset['direction'] = r_direction
1506 try:
Soniya Vyas945f5a12021-02-02 23:49:12 +05301507 sg_rule = self.create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001508 sec_group_rules_client=sec_group_rules_client,
1509 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001510 security_groups_client=security_groups_client,
1511 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001512 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001513 # if rule already exist - skip rule and continue
1514 msg = 'Security group rule already exists'
1515 if msg not in ex._error_string:
1516 raise ex
1517 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001518 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001519 rules.append(sg_rule)
1520
1521 return rules
1522
Martin Kopec0090a102020-11-03 13:50:19 +00001523 def _get_router(self, client=None, project_id=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001524 """Retrieve a router for the given tenant id.
1525
1526 If a public router has been configured, it will be returned.
1527
1528 If a public router has not been configured, but a public
1529 network has, a tenant router will be created and returned that
1530 routes traffic to the public network.
1531 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301532
Yair Frieddb6c9e92014-08-06 08:53:13 +03001533 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001534 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001535 if not project_id:
1536 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001537 router_id = CONF.network.public_router_id
1538 network_id = CONF.network.public_network_id
1539 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001540 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001541 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001542 elif network_id:
Martin Kopec0090a102020-11-03 13:50:19 +00001543 name = kwargs.pop('name', None)
1544 if not name:
1545 namestart = self.__class__.__name__ + '-router'
1546 name = data_utils.rand_name(namestart)
1547
1548 ext_gw_info = kwargs.pop('external_gateway_info', None)
1549 if not ext_gw_info:
1550 ext_gw_info = dict(network_id=network_id)
zhufl3484f992017-10-10 16:18:29 +08001551 router = client.create_router(
Martin Kopec0090a102020-11-03 13:50:19 +00001552 name=name,
1553 admin_state_up=kwargs.get('admin_state_up', True),
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001554 project_id=project_id,
Martin Kopec0090a102020-11-03 13:50:19 +00001555 external_gateway_info=ext_gw_info,
1556 **kwargs)['router']
zhufl3484f992017-10-10 16:18:29 +08001557 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1558 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001559 return router
1560 else:
1561 raise Exception("Neither of 'public_router_id' or "
1562 "'public_network_id' has been defined.")
1563
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001564 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001565 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001566 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001567 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001568 """Create a network with a subnet connected to a router.
1569
David Shrewsbury9bac3662014-08-07 15:07:01 -04001570 The baremetal driver is a special case since all nodes are
1571 on the same shared network.
1572
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001573 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001574 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001575 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001576 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001577 a form like this: {'provider:network_type': 'vlan',
1578 'provider:physical_network': 'foo',
1579 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001580 :returns: network, subnet, router
1581 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301582
Thiago Paiva66cded22016-08-15 14:55:58 -03001583 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001584 # NOTE(Shrews): This exception is for environments where tenant
1585 # credential isolation is available, but network separation is
1586 # not (the current baremetal case). Likely can be removed when
1587 # test account mgmt is reworked:
1588 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001589 if not CONF.compute.fixed_network_name:
1590 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001591 raise lib_exc.InvalidConfiguration(m)
Soniya Vyasc37410f2021-02-24 15:26:27 +05301592 network = self.get_network_by_name(
David Shrewsbury9bac3662014-08-07 15:07:01 -04001593 CONF.compute.fixed_network_name)
1594 router = None
1595 subnet = None
1596 else:
John Warren94d8faf2015-09-15 12:22:24 -04001597 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001598 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001599 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001600 port_security_enabled=port_security_enabled,
1601 **net_dict)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001602 router = self._get_router(client=routers_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001603 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001604 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001605 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001606 # use explicit check because empty list is a valid option
1607 if dns_nameservers is not None:
1608 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001609 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001610 if not routers_client:
1611 routers_client = self.routers_client
1612 router_id = router['id']
1613 routers_client.add_router_interface(router_id,
1614 subnet_id=subnet['id'])
1615
1616 # save a cleanup job to remove this association between
1617 # router and subnet
1618 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1619 routers_client.remove_router_interface, router_id,
1620 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001621 return network, subnet, router
1622
1623
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001624class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001625 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001626
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001627 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001628
1629 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001630 def setup_clients(cls):
1631 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001632 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001633 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001634 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001635
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001636 def create_encryption_type(self, client=None, type_id=None, provider=None,
1637 key_size=None, cipher=None,
1638 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301639 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001640 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001641 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001642 if not type_id:
1643 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001644 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001645 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001646 client.create_encryption_type(
1647 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001648 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001649
lkuchlan3023e752017-06-08 12:53:13 +03001650 def create_encrypted_volume(self, encryption_provider, volume_type,
1651 key_size=256, cipher='aes-xts-plain64',
1652 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301653 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001654 volume_type = self.create_volume_type(name=volume_type)
1655 self.create_encryption_type(type_id=volume_type['id'],
1656 provider=encryption_provider,
1657 key_size=key_size,
1658 cipher=cipher,
1659 control_location=control_location)
1660 return self.create_volume(volume_type=volume_type['name'])
1661
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001662
Masayuki Igawa0870db52015-09-18 21:08:36 +09001663class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001664 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001665
1666 Subclasses implement the tests that use the methods provided by this
1667 class.
1668 """
1669
1670 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001671 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001672 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001673 if not CONF.service_available.swift:
1674 skip_msg = ("%s skipped as swift is not available" %
1675 cls.__name__)
1676 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001677
1678 @classmethod
1679 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001680 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001681 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001682 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001683 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001684
1685 @classmethod
1686 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001687 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001688 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001689 cls.account_client = cls.os_operator.account_client
1690 cls.container_client = cls.os_operator.container_client
1691 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001692
Chris Dentde456a12014-09-10 12:41:15 +01001693 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301694 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001695 self.account_client.list_account_containers()
1696 LOG.debug('Swift status information obtained successfully')
1697
Chris Dentde456a12014-09-10 12:41:15 +01001698 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301699 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001700 name = container_name or data_utils.rand_name(
1701 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001702 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001703 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001704 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001705 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001706 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001707 self.container_client.delete_container,
1708 name)
Chris Dent0d494112014-08-26 13:48:30 +01001709 return name
1710
Chris Dentde456a12014-09-10 12:41:15 +01001711 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301712 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001713 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001714 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001715
Chris Dentde456a12014-09-10 12:41:15 +01001716 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301717 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001718 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001719 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001720 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001721 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001722 self.object_client.delete_object,
1723 container_name,
1724 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001725 return obj_name, obj_data
1726
Chris Dentde456a12014-09-10 12:41:15 +01001727 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301728 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001729 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001730 self.list_and_check_container_objects(container_name,
1731 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001732
Chris Dentde456a12014-09-10 12:41:15 +01001733 def list_and_check_container_objects(self, container_name,
1734 present_obj=None,
1735 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301736 """List and verify objects for a given container
1737
1738 This utility lists objects for a given container
1739 and asserts which are present and
1740 which are not
1741 """
1742
Ghanshyam2a180b82014-06-16 13:54:22 +09001743 if present_obj is None:
1744 present_obj = []
1745 if not_present_obj is None:
1746 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001747 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001748 container_name)
1749 if present_obj:
1750 for obj in present_obj:
1751 self.assertIn(obj, object_list)
1752 if not_present_obj:
1753 for obj in not_present_obj:
1754 self.assertNotIn(obj, object_list)
1755
Chris Dentde456a12014-09-10 12:41:15 +01001756 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301757 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001758 _, obj = self.object_client.get_object(container_name, obj_name)
1759 self.assertEqual(obj, expected_data)