blob: acc563acb72adaacf2f4db5f54ecfd1371c95d7a [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,
378 container=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530379 """Creates backup
380
381 This wrapper utility creates backup and waits for backup to be
382 in 'available' state.
383 """
lkuchlane20e6a82018-05-08 11:28:46 +0300384
385 name = name or data_utils.rand_name(
386 self.__class__.__name__ + "-backup")
387 kwargs = {'name': name,
388 'description': description,
389 'force': force,
390 'snapshot_id': snapshot_id,
391 'incremental': incremental,
392 'container': container}
393 backup = self.backups_client.create_backup(volume_id=volume_id,
394 **kwargs)['backup']
395 self.addCleanup(self.backups_client.delete_backup, backup['id'])
396 waiters.wait_for_volume_resource_status(self.backups_client,
397 backup['id'], 'available')
398 return backup
399
400 def restore_backup(self, backup_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530401 """Restore backup
402
403 This wrapper utility restores backup and waits for backup to be
404 in 'available' state.
405 """
406
lkuchlane20e6a82018-05-08 11:28:46 +0300407 restore = self.backups_client.restore_backup(backup_id)['restore']
408 self.addCleanup(self.volumes_client.delete_volume,
409 restore['volume_id'])
410 waiters.wait_for_volume_resource_status(self.backups_client,
411 backup_id, 'available')
412 waiters.wait_for_volume_resource_status(self.volumes_client,
413 restore['volume_id'],
414 'available')
415 self.assertEqual(backup_id, restore['backup_id'])
416 return restore
417
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000418 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
419 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530420 if image is None:
421 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530422 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
423 server_id, image, preserve_ephemeral)
424 self.servers_client.rebuild_server(
425 server_id=server_id,
426 image_ref=image,
427 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000428 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530429 if wait:
430 waiters.wait_for_server_status(self.servers_client,
431 server_id, 'ACTIVE')
432
lkuchlan73ed1f32017-07-06 16:22:12 +0300433 def create_volume_snapshot(self, volume_id, name=None, description=None,
434 metadata=None, force=False):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530435 """Creates volume
436
437 This wrapper utility creates volume snapshot and waits for backup
438 to be in 'available' state.
439 """
440
lkuchlan73ed1f32017-07-06 16:22:12 +0300441 name = name or data_utils.rand_name(
442 self.__class__.__name__ + '-snapshot')
443 snapshot = self.snapshots_client.create_snapshot(
444 volume_id=volume_id,
445 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000446 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300447 description=description,
448 metadata=metadata)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530449
lkuchlan73ed1f32017-07-06 16:22:12 +0300450 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
451 snapshot['id'])
452 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
453 waiters.wait_for_volume_resource_status(self.snapshots_client,
454 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200455 snapshot = self.snapshots_client.show_snapshot(
456 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300457 return snapshot
458
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100459 def _cleanup_volume_type(self, volume_type):
460 """Clean up a given volume type.
461
462 Ensuring all volumes associated to a type are first removed before
463 attempting to remove the type itself. This includes any image volume
464 cache volumes stored in a separate tenant to the original volumes
465 created from the type.
466 """
467 admin_volume_type_client = self.os_admin.volume_types_client_latest
468 admin_volumes_client = self.os_admin.volumes_client_latest
469 volumes = admin_volumes_client.list_volumes(
470 detail=True, params={'all_tenants': 1})['volumes']
471 type_name = volume_type['name']
472 for volume in [v for v in volumes if v['volume_type'] == type_name]:
473 test_utils.call_and_ignore_notfound_exc(
474 admin_volumes_client.delete_volume, volume['id'])
475 admin_volumes_client.wait_for_resource_deletion(volume['id'])
476 admin_volume_type_client.delete_volume_type(volume_type['id'])
477
Martin Kopec8e673a42020-10-18 17:33:02 +0000478 def create_volume_type(self, client=None, name=None, backend_name=None,
479 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530480 """Creates volume type
481
482 In a multiple-storage back-end configuration,
483 each back end has a name (volume_backend_name).
484 The name of the back end is declared as an extra-specification
485 of a volume type (such as, volume_backend_name=LVM).
486 When a volume is created, the scheduler chooses an
487 appropriate back end to handle the request, according
488 to the volume type specified by the user.
489 The scheduler uses volume types to explicitly create volumes on
490 specific back ends.
491
492 Before using volume type, a volume type has to be declared
493 to Block Storage. In addition to that, an extra-specification
494 has to be created to link the volume type to a back end name.
495 """
496
scottda61f68ac2016-06-07 12:07:55 -0600497 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000498 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000499 if not name:
500 class_name = self.__class__.__name__
501 name = data_utils.rand_name(class_name + '-volume-type')
502 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600503
504 LOG.debug("Creating a volume type: %s on backend %s",
505 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000506 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600507 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000508 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600509
Martin Kopec8e673a42020-10-18 17:33:02 +0000510 volume_type_resp = client.create_volume_type(
511 name=randomized_name, extra_specs=extra_specs, **kwargs)
512 volume_type = volume_type_resp['volume_type']
513
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530514 self.assertIn('id', volume_type)
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100515 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600516 return volume_type
517
Martin Kopece1d873a2020-11-02 12:19:54 +0000518 def _create_loginable_secgroup_rule(self, secgroup_id=None, rulesets=None):
519 """Create loginable security group rule by compute clients.
520
521 This function will create by default the following rules:
522 1. tcp port 22 allow rule in order to allow ssh access for ipv4
523 2. ipv4 icmp allow rule in order to allow icmpv4
524 """
525
John Warrenf2345512015-12-10 13:39:30 -0500526 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500527 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100528 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900529 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100530 for sg in sgs:
531 if sg['name'] == 'default':
532 secgroup_id = sg['id']
533
534 # These rules are intended to permit inbound ssh and icmp
535 # traffic from all sources, so no group_id is provided.
536 # Setting a group_id would only permit traffic from ports
537 # belonging to the same security group.
Martin Kopece1d873a2020-11-02 12:19:54 +0000538 if not rulesets:
539 rulesets = [
540 {
541 # ssh
542 'ip_protocol': 'tcp',
543 'from_port': 22,
544 'to_port': 22,
545 'cidr': '0.0.0.0/0',
546 },
547 {
548 # ping
549 'ip_protocol': 'icmp',
550 'from_port': -1,
551 'to_port': -1,
552 'cidr': '0.0.0.0/0',
553 }
554 ]
Andrea Frittoli247058f2014-07-16 16:09:22 +0100555 rules = list()
556 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000557 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900558 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100559 rules.append(sg_rule)
560 return rules
561
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530562 def _create_security_group(self, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530563 """Create security group and add rules to security group"""
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530564 if not kwargs.get('name'):
565 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
566 if not kwargs.get('description'):
567 kwargs['description'] = kwargs['name'] + " description"
John Warrenf2345512015-12-10 13:39:30 -0500568 secgroup = self.compute_security_groups_client.create_security_group(
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530569 **kwargs)['security_group']
570 self.assertEqual(secgroup['name'], kwargs['name'])
571 self.assertEqual(secgroup['description'], kwargs['description'])
John Warrenf2345512015-12-10 13:39:30 -0500572 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100573 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500574 self.compute_security_groups_client.delete_security_group,
575 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100576
577 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300578 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100579 return secgroup
580
zhuflf52c7592017-05-25 13:55:24 +0800581 def get_remote_client(self, ip_address, username=None, private_key=None,
582 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100583 """Get a SSH client to a remote server
584
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600585 :param ip_address: the server floating or fixed IP address to use
586 for ssh validation
587 :param username: name of the Linux account on the remote server
588 :param private_key: the SSH private key to use
589 :param server: server dict, used for debugging purposes
590 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100591 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700592
Andrea Frittoli247058f2014-07-16 16:09:22 +0100593 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800594 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800595 # Set this with 'keypair' or others to log in with keypair or
596 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000597 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800598 password = None
599 if private_key is None:
600 private_key = self.keypair['private_key']
601 else:
lanoux283273b2015-12-04 03:01:54 -0800602 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800603 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800604 linux_client = remote_client.RemoteClient(
605 ip_address, username, pkey=private_key, password=password,
606 server=server, servers_client=self.servers_client)
607 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100608 return linux_client
609
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000610 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000611 img_path = CONF.scenario.img_file
612 if not os.path.exists(img_path):
613 # TODO(kopecmartin): replace LOG.warning for rasing
614 # InvalidConfiguration exception after tempest 25.0.0 is
615 # released - there will be one release which accepts both
616 # behaviors in order to avoid many failures across CIs and etc.
617 LOG.warning(
618 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
619 'a full path for the image. CONF.scenario.img_dir was '
620 'deprecated and will be removed in the next release. Till '
621 'Tempest 25.0.0, old behavior is maintained and keep working '
622 'but starting Tempest 26.0.0, you need to specify the full '
623 'path in CONF.scenario.img_file config option.')
624 img_path = os.path.join(CONF.scenario.img_dir, img_path)
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300625 img_container_format = CONF.scenario.img_container_format
626 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000627 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400628 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000629 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100630 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000631 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530632 if img_properties is None:
633 img_properties = {}
634 name = data_utils.rand_name('%s-' % name)
635 params = {
636 'name': name,
637 'container_format': img_container_format,
638 'disk_format': img_disk_format or img_container_format,
639 }
640 if CONF.image_feature_enabled.api_v1:
641 params['is_public'] = 'False'
642 if img_properties:
643 params['properties'] = img_properties
644 params = {'headers': common_image.image_meta_to_headers(**params)}
645 else:
646 params['visibility'] = 'private'
647 # Additional properties are flattened out in the v2 API.
648 if img_properties:
649 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000650 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530651 body = self.image_client.create_image(**params)
652 image = body['image'] if 'image' in body else body
653 self.addCleanup(self.image_client.delete_image, image['id'])
654 self.assertEqual("queued", image['status'])
655 with open(img_path, 'rb') as image_file:
656 if CONF.image_feature_enabled.api_v1:
657 self.image_client.update_image(image['id'], data=image_file)
658 else:
659 self.image_client.store_image_file(image['id'], image_file)
660 LOG.debug("image:%s", image['id'])
661 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100662
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000663 def _log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530664 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400665 if not CONF.compute_feature_enabled.console_output:
666 LOG.debug('Console output not supported, cannot log')
667 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700668 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100669 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700670 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100671 servers = servers['servers']
672 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100673 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700674 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000675 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100676 LOG.debug('Console output for %s\nbody=\n%s',
677 server['id'], console_output)
678 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100679 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100680 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100681
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000682 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530683 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300684 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000685 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000686
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000687 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530688 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000689 # Glance client
690 _image_client = self.image_client
691 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900692 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000693 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800694 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000695 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000696 image = _images_client.create_image(server['id'], name=name, **kwargs)
David Kranza5299eb2015-01-15 17:24:05 -0500697 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300698 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200699
700 self.addCleanup(_image_client.wait_for_resource_deletion,
701 image_id)
702 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
703 _image_client.delete_image, image_id)
704
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400705 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530706 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700707 resp = _image_client.check_image(image_id)
708 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400709 image_props = snapshot_image.get('properties', {})
710 else:
711 # In glance v2 the additional properties are flattened.
712 snapshot_image = _image_client.show_image(image_id)
713 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300714
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400715 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300716 if bdm:
717 bdm = json.loads(bdm)
718 if bdm and 'snapshot_id' in bdm[0]:
719 snapshot_id = bdm[0]['snapshot_id']
720 self.addCleanup(
721 self.snapshots_client.wait_for_resource_deletion,
722 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100723 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
724 self.snapshots_client.delete_snapshot,
725 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200726 waiters.wait_for_volume_resource_status(self.snapshots_client,
727 snapshot_id,
728 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000729 image_name = snapshot_image['name']
730 self.assertEqual(name, image_name)
731 LOG.debug("Created snapshot image %s for server %s",
732 image_name, server['name'])
733 return snapshot_image
734
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000735 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530736 """Compute volume attach
737
738 This utility attaches volume from compute and waits for the
739 volume status to be 'in-use' state.
740 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000741 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000742 server['id'], volumeId=volume_to_attach['id'],
743 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200744 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200745 waiters.wait_for_volume_resource_status(self.volumes_client,
746 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000747 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
748 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200749 # Return the updated volume after the attachment
750 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900751
Jordan Pittier7cf64762015-10-14 15:01:12 +0200752 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530753 """Compute volume detach
754
Lee Yarwood5423c532020-12-17 11:24:46 +0000755 This utility detaches the volume from the server and checks whether the
756 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530757 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200758 self.servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000759 waiters.wait_for_volume_attachment_remove_from_server(
760 self.servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200761
Steven Hardyda2a8352014-10-02 12:52:20 +0100762 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800763 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530764 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000765 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000766 cmd = ['ping', '-c1', '-w1']
767
768 if mtu:
769 cmd += [
770 # don't fragment
771 '-M', 'do',
772 # ping receives just the size of ICMP payload
773 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
774 ]
775 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700776
777 def ping():
778 proc = subprocess.Popen(cmd,
779 stdout=subprocess.PIPE,
780 stderr=subprocess.PIPE)
781 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000782
Aaron Rosena7df13b2014-09-23 09:45:45 -0700783 return (proc.returncode == 0) == should_succeed
784
Jordan Pittier9e227c52016-02-09 14:35:18 +0100785 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000786 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800787 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000788 'caller': caller, 'ip': ip_address, 'timeout': timeout,
789 'should_succeed':
790 'reachable' if should_succeed else 'unreachable'
791 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200792 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000793 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800794 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000795 'caller': caller, 'ip': ip_address, 'timeout': timeout,
796 'result': 'expected' if result else 'unexpected'
797 })
zhufl0ec74c42017-11-15 14:02:28 +0800798 if server:
799 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000800 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700801
Yair Friedae0e73d2014-11-24 11:56:26 +0200802 def check_vm_connectivity(self, ip_address,
803 username=None,
804 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000805 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800806 extra_msg="",
807 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000808 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000809 """Check server connectivity
810
Yair Friedae0e73d2014-11-24 11:56:26 +0200811 :param ip_address: server to test against
812 :param username: server's ssh username
813 :param private_key: server's ssh private key to be used
814 :param should_connect: True/False indicates positive/negative test
815 positive - attempt ping and ssh
816 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800817 :param extra_msg: Message to help with debugging if ``ping_ip_address``
818 fails
819 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000820 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200821
822 :raises: AssertError if the result of the connectivity check does
823 not match the value of the should_connect param
824 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530825
zhufl0ec74c42017-11-15 14:02:28 +0800826 LOG.debug('checking network connections to IP %s with user: %s',
827 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200828 if should_connect:
829 msg = "Timed out waiting for %s to become reachable" % ip_address
830 else:
831 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800832 if extra_msg:
833 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200834 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000835 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800836 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200837 msg=msg)
838 if should_connect:
839 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800840 try:
841 self.get_remote_client(ip_address, username, private_key,
842 server=server)
843 except Exception:
844 if not extra_msg:
845 extra_msg = 'Failed to ssh to %s' % ip_address
846 LOG.exception(extra_msg)
847 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200848
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000849 def create_floating_ip(self, server, pool_name=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000850 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200851
Marc Koderer3b57d802016-03-22 15:23:31 +0100852 if not pool_name:
853 pool_name = CONF.network.floating_network_name
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000854
John Warrene74890a2015-11-11 15:18:01 -0500855 floating_ip = (self.compute_floating_ips_client.
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000856 create_floating_ip(pool=pool_name,
857 **kwargs)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100858 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500859 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200860 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500861 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530862 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200863 return floating_ip
864
Sean Dague20e98612016-01-06 14:33:28 -0500865 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000866 private_key=None, server=None, username=None,
867 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530868 """Creates timestamp
869
870 This wrapper utility does ssh, creates timestamp and returns the
871 created timestamp.
872 """
Sean Dague20e98612016-01-06 14:33:28 -0500873 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200874 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000875 server=server,
876 username=username)
877
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300878 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000879 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800880 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
881 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300882 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
883 ssh_client.exec_command(cmd_timestamp)
884 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
885 % mount_path)
886 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800887 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300888 return timestamp
889
Sean Dague20e98612016-01-06 14:33:28 -0500890 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000891 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530892 """Returns timestamp
893
894 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000895
896 :param ip_address: The floating IP or fixed IP of the remote server
897 :param dev_name: Name of the device that stores the timestamp
898 :param mount_path: Path which should be used as mount point for
899 dev_name
900 :param private_key: The SSH private key to use for authentication
901 :param server: Server dict, used for debugging purposes
902 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530903 """
904
Sean Dague20e98612016-01-06 14:33:28 -0500905 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200906 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000907 server=server,
908 username=username)
909
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300910 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700911 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300912 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
913 % mount_path)
914 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800915 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300916 return timestamp
917
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000918 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -0500919 """Get the server fixed or floating IP.
920
921 Based on the configuration we're in, return a correct ip
922 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000923
924 If CONF.validation.connect_method is floating, then
925 a floating ip will be created passing kwargs as additional
926 argument.
Sean Dague20e98612016-01-06 14:33:28 -0500927 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530928
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200929 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500930 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800931 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500932 # method is creating the floating IP there.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000933 return self.create_floating_ip(server, **kwargs)['ip']
Sean Dague20e98612016-01-06 14:33:28 -0500934 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400935 # Determine the network name to look for based on config or creds
936 # provider network resources.
937 if CONF.validation.network_for_ssh:
938 addresses = server['addresses'][
939 CONF.validation.network_for_ssh]
940 else:
zhufl7b4a7202017-09-28 10:29:27 +0800941 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400942 addresses = (server['addresses'][network['name']]
943 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500944 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200945 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
946 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500947 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800948 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200949 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400950 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200951
zhufl7bc916d2018-08-22 14:47:39 +0800952 @classmethod
953 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530954 """Gets host of server"""
955
zhufl7bc916d2018-08-22 14:47:39 +0800956 server_details = cls.os_admin.servers_client.show_server(server_id)
957 return server_details['server']['OS-EXT-SRV-ATTR:host']
958
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000959 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
960 bd_map_v2 = [{
961 'uuid': source_id,
962 'source_type': source_type,
963 'destination_type': 'volume',
964 'boot_index': 0,
965 'delete_on_termination': delete_on_termination}]
966 return {'block_device_mapping_v2': bd_map_v2}
967
968 def boot_instance_from_resource(self, source_id,
969 source_type,
970 keypair=None,
971 security_group=None,
972 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +0000973 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530974 """Boot instance from resource
975
976 This wrapper utility boots instance from resource with block device
977 mapping with source info passed in arguments
978 """
979
Martin Kopecbee673e2020-11-04 09:40:52 +0000980 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000981 if keypair:
982 create_kwargs['key_name'] = keypair['name']
983 if security_group:
984 create_kwargs['security_groups'] = [
985 {'name': security_group['name']}]
986 create_kwargs.update(self._get_bdm(
987 source_id,
988 source_type,
989 delete_on_termination=delete_on_termination))
990 if name:
991 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +0000992 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000993
Martin Kopecbee673e2020-11-04 09:40:52 +0000994 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000995
Martin Kopec0216b372020-11-04 09:32:05 +0000996 def create_volume_from_image(self, **kwargs):
997 """Create volume from image.
998
999 :param image_id: ID of the image to create volume from,
1000 CONF.compute.image_ref by default
1001 :param name: name of the volume,
1002 '$classname-volume-origin' by default
1003 :param **kwargs: additional parameters
1004 """
1005 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
1006 name = kwargs.pop('name', None)
1007 if not name:
1008 namestart = self.__class__.__name__ + '-volume-origin'
1009 name = data_utils.rand_name(namestart)
1010 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +00001011
Andrea Frittoli2e733b52014-07-16 14:12:11 +01001012
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001013class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001014 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001015
Yair Fried1fc32a12014-08-04 09:11:30 +03001016 This class provide helpers for network scenario tests, using the neutron
1017 API. Helpers from ancestor which use the nova network API are overridden
1018 with the neutron API.
1019
1020 This Class also enforces using Neutron instead of novanetwork.
1021 Subclassed tests will be skipped if Neutron is not enabled
1022
1023 """
1024
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001025 credentials = ['primary', 'admin']
1026
Yair Fried1fc32a12014-08-04 09:11:30 +03001027 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001028 def skip_checks(cls):
1029 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001030 if not CONF.service_available.neutron:
1031 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001032
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001033 def _create_network(self, networks_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001034 project_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001035 namestart='network-smoke-',
Lajos Katonac87a06b2019-01-04 13:21:48 +01001036 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001037 if not networks_client:
1038 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001039 if not project_id:
1040 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001041 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001042 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001043 if net_dict:
1044 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001045 # Neutron disables port security by default so we have to check the
1046 # config before trying to create the network with port_security_enabled
1047 if CONF.network_feature_enabled.port_security:
1048 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001049 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001050 network = result['network']
1051
1052 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001053 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001054 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001055 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001056 return network
1057
zhufl5b0a52f2017-10-24 15:48:20 +08001058 def create_subnet(self, network, subnets_client=None,
1059 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001060 """Create a subnet for the given network
1061
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301062 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001063 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301064
1065 :param **kwargs:
1066 See extra parameters below
1067
1068 :Keyword Arguments:
1069
1070 * *ip_version = ip version of the given network,
Yair Fried1fc32a12014-08-04 09:11:30 +03001071 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301072
John Warren3961acd2015-10-02 14:38:53 -04001073 if not subnets_client:
1074 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001075
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001076 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001077 """Check cidr existence
1078
yangjianfeng4ad346e2020-11-22 06:49:19 +00001079 :returns: True if subnet with cidr already exist in tenant or
1080 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001081 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001082 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001083 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001084 external_nets = self.os_admin.networks_client.list_networks(
1085 **{"router:external": True})['networks']
1086 external_subnets = []
1087 for ext_net in external_nets:
1088 external_subnets.extend(
1089 self.os_admin.subnets_client.list_subnets(
1090 network_id=ext_net['id'], cidr=cidr)['subnets'])
1091 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001092
Kirill Shileev14113572014-11-21 16:58:02 +03001093 ip_version = kwargs.pop('ip_version', 4)
1094
1095 if ip_version == 6:
1096 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -04001097 CONF.network.project_network_v6_cidr)
1098 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001099 else:
Sean Dagueed6e5862016-04-04 10:49:13 -04001100 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
1101 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001102
Yair Fried1fc32a12014-08-04 09:11:30 +03001103 result = None
Kirill Shileev14113572014-11-21 16:58:02 +03001104 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +03001105 # Repeatedly attempt subnet creation with sequential cidr
1106 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +03001107 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +03001108 str_cidr = str(subnet_cidr)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001109 if cidr_in_use(str_cidr, project_id=network['project_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +03001110 continue
1111
1112 subnet = dict(
1113 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001114 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001115 project_id=network['project_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +03001116 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +03001117 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001118 **kwargs
1119 )
1120 try:
John Warren3961acd2015-10-02 14:38:53 -04001121 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +03001122 break
Masayuki Igawad9388762015-01-20 14:56:42 +09001123 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +03001124 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1125 if not is_overlapping_cidr:
1126 raise
1127 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001128
1129 subnet = result['subnet']
1130 self.assertEqual(subnet['cidr'], str_cidr)
1131
1132 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1133 subnets_client.delete_subnet, subnet['id'])
1134
Yair Fried1fc32a12014-08-04 09:11:30 +03001135 return subnet
1136
Lukas Piwowarski9523d512020-10-30 09:37:58 +00001137 def _get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
1138
1139 if ip_addr and not kwargs.get('fixed_ips'):
1140 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
1141 ports = self.os_admin.ports_client.list_ports(
1142 device_id=server['id'], **kwargs)['ports']
1143
Kobi Samoray166500a2016-10-09 14:42:48 +03001144 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001145 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1146 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001147
1148 def _is_active(port):
1149 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1150 # servers. Neutron does not bind ports for Ironic instances, as a
1151 # result the port remains in the DOWN state. This has been fixed
1152 # with the introduction of the networking-baremetal plugin but
1153 # it's not mandatory (and is not used on all stable branches).
1154 return (port['status'] == 'ACTIVE' or
1155 port.get('binding:vnic_type') == 'baremetal')
1156
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001157 port_map = [(p["id"], fxip["ip_address"])
1158 for p in ports
1159 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001160 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001161 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001162 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1163 if inactive:
1164 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001165
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001166 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001167 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001168 self.assertEqual(len(port_map), 1,
1169 "Found multiple IPv4 addresses: %s. "
1170 "Unable to determine which port to target."
1171 % port_map)
1172 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001173
David Shrewsbury9bac3662014-08-07 15:07:01 -04001174 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001175 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001176 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001177 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001178 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001179 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001180
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301181 def create_floating_ip(self, server, external_network_id=None,
Martin Kopec5e761bf2020-11-03 16:11:08 +00001182 port_id=None, client=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001183 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301184
Yair Friedae0e73d2014-11-24 11:56:26 +02001185 if not external_network_id:
1186 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001187 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001188 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001189 if not port_id:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301190 port_id, ip4 = self._get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001191 else:
1192 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001193
Martin Kopec5e761bf2020-11-03 16:11:08 +00001194 floatingip_kwargs = {
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001195 'floating_network_id': external_network_id,
1196 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301197 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001198 'fixed_ip_address': ip4,
1199 }
1200 if CONF.network.subnet_id:
Martin Kopec5e761bf2020-11-03 16:11:08 +00001201 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1202
1203 floatingip_kwargs.update(kwargs)
1204 result = client.create_floatingip(**floatingip_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001205 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001206
Jordan Pittier9e227c52016-02-09 14:35:18 +01001207 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001208 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001209 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001210 return floating_ip
1211
Soniya Vyased664472020-09-23 18:40:25 +05301212 def associate_floating_ip(self, floating_ip, server):
1213 """Associate floating ip
1214
1215 This wrapper utility attaches the floating_ip for
1216 the respective port_id of server
1217 """
1218 port_id, _ = self._get_server_port_id_and_ip4(server)
1219 kwargs = dict(port_id=port_id)
1220 floating_ip = self.floating_ips_client.update_floatingip(
1221 floating_ip['id'], **kwargs)['floatingip']
1222 self.assertEqual(port_id, floating_ip['port_id'])
1223 return floating_ip
1224
1225 def disassociate_floating_ip(self, floating_ip):
1226 """Disassociates floating ip
1227
1228 This wrapper utility disassociates given floating ip.
1229 :param floating_ip: a dict which is a return value of
1230 floating_ips_client.create_floatingip method
1231 """
1232 kwargs = dict(port_id=None)
1233 floating_ip = self.floating_ips_client.update_floatingip(
1234 floating_ip['id'], **kwargs)['floatingip']
1235 self.assertIsNone(floating_ip['port_id'])
1236 return floating_ip
1237
Yair Fried45f92952014-06-26 05:19:19 +03001238 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001239 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001240
Steve Heyman33735f22016-05-24 09:28:08 -05001241 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001242 :param status: target status
1243 :raises: AssertionError if status doesn't match
1244 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301245
Steve Heyman33735f22016-05-24 09:28:08 -05001246 floatingip_id = floating_ip['id']
1247
Carl Baldwina754e2d2014-10-23 22:47:41 +00001248 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001249 floating_ip = (self.floating_ips_client.
1250 show_floatingip(floatingip_id)['floatingip'])
1251 if status == floating_ip['status']:
1252 LOG.info("FloatingIP: {fp} is at status: {st}"
1253 .format(fp=floating_ip, st=status))
1254 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001255
zhufl4dda94e2017-03-14 16:14:46 +08001256 if not test_utils.call_until_true(refresh,
1257 CONF.network.build_timeout,
1258 CONF.network.build_interval):
1259 floating_ip = self.floating_ips_client.show_floatingip(
1260 floatingip_id)['floatingip']
1261 self.assertEqual(status, floating_ip['status'],
1262 message="FloatingIP: {fp} is at status: {cst}. "
1263 "failed to reach status: {st}"
1264 .format(fp=floating_ip, cst=floating_ip['status'],
1265 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001266
zhufl420a0192017-09-28 11:04:50 +08001267 def check_tenant_network_connectivity(self, server,
1268 username,
1269 private_key,
1270 should_connect=True,
1271 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301272 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001273 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001274 msg = 'Tenant networks not configured to be reachable.'
1275 LOG.info(msg)
1276 return
1277 # The target login is assumed to have been configured for
1278 # key-based authentication by cloud-init.
1279 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001280 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001281 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001282 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001283 username,
1284 private_key,
1285 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001286 except Exception as e:
1287 LOG.exception('Tenant network connectivity check failed')
1288 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001289 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001290 raise
1291
zhufle9877c62017-10-13 09:38:19 +08001292 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001293 nic=None, protocol='icmp'):
1294 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001295
Claudiu Belu33c3e602014-08-28 16:38:01 +03001296 :param source: RemoteClient: an ssh connection from which to execute
1297 the check
1298 :param dest: an IP to check connectivity against
1299 :param should_succeed: boolean should connection succeed or not
1300 :param nic: specific network interface to test connectivity from
1301 :param protocol: the protocol used to test connectivity with.
1302 :returns: True, if the connection succeeded and it was expected to
1303 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001304 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301305
Claudiu Belu33c3e602014-08-28 16:38:01 +03001306 method_name = '%s_check' % protocol
1307 connectivity_checker = getattr(source, method_name)
1308
1309 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001310 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001311 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001312 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001313 LOG.warning('Failed to check %(protocol)s connectivity for '
1314 'IP %(dest)s via a ssh connection from: %(src)s.',
1315 dict(protocol=protocol, dest=dest,
1316 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001317 return not should_succeed
1318 return should_succeed
1319
Claudiu Belu33c3e602014-08-28 16:38:01 +03001320 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001321 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001322 if result:
1323 return
1324
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001325 source_host = source.ssh_client.host
1326 if should_succeed:
1327 msg = "Timed out waiting for %s to become reachable from %s" \
1328 % (dest, source_host)
1329 else:
1330 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001331 self._log_console_output()
1332 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001333
John Warren456d9ae2016-01-12 15:36:33 -05001334 def _create_security_group(self, security_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001335 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001336 namestart='secgroup-smoke',
1337 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001338 if security_group_rules_client is None:
1339 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001340 if security_groups_client is None:
1341 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001342 if project_id is None:
1343 project_id = security_groups_client.project_id
John Warrenf9606e92015-12-10 12:12:42 -05001344 secgroup = self._create_empty_security_group(
1345 namestart=namestart, client=security_groups_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001346 project_id=project_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001347
1348 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -05001349 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001350 security_group_rules_client=security_group_rules_client,
1351 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001352 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001353 for rule in rules:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001354 self.assertEqual(project_id, rule['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001355 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001356 return secgroup
1357
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001358 def _create_empty_security_group(self, client=None, project_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001359 namestart='secgroup-smoke'):
1360 """Create a security group without rules.
1361
1362 Default rules will be created:
1363 - IPv4 egress to any
1364 - IPv6 egress to any
1365
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001366 :param project_id: secgroup will be created in this project
Steve Heyman33735f22016-05-24 09:28:08 -05001367 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001368 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301369
Yair Fried1fc32a12014-08-04 09:11:30 +03001370 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001371 client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001372 if not project_id:
1373 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001374 sg_name = data_utils.rand_name(namestart)
1375 sg_desc = sg_name + " description"
1376 sg_dict = dict(name=sg_name,
1377 description=sg_desc)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001378 sg_dict['project_id'] = project_id
David Kranz34e88122014-12-11 15:24:05 -05001379 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001380
1381 secgroup = result['security_group']
1382 self.assertEqual(secgroup['name'], sg_name)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001383 self.assertEqual(project_id, secgroup['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001384 self.assertEqual(secgroup['description'], sg_desc)
1385
Jordan Pittier9e227c52016-02-09 14:35:18 +01001386 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001387 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001388 return secgroup
1389
John Warren456d9ae2016-01-12 15:36:33 -05001390 def _create_security_group_rule(self, secgroup=None,
1391 sec_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001392 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001393 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001394 """Create a rule from a dictionary of rule parameters.
1395
1396 Create a rule in a secgroup. if secgroup not defined will search for
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001397 default secgroup in project_id.
Yair Fried1fc32a12014-08-04 09:11:30 +03001398
Steve Heyman33735f22016-05-24 09:28:08 -05001399 :param secgroup: the security group.
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001400 :param project_id: if secgroup not passed -- the tenant in which to
Yair Fried1fc32a12014-08-04 09:11:30 +03001401 search for default secgroup
1402 :param kwargs: a dictionary containing rule parameters:
1403 for example, to allow incoming ssh:
1404 rule = {
1405 direction: 'ingress'
1406 protocol:'tcp',
1407 port_range_min: 22,
1408 port_range_max: 22
1409 }
1410 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301411
John Warren456d9ae2016-01-12 15:36:33 -05001412 if sec_group_rules_client is None:
1413 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001414 if security_groups_client is None:
1415 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001416 if not project_id:
1417 project_id = security_groups_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001418 if secgroup is None:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001419 # Get default secgroup for project_id
zhuflb0b272e2017-09-22 16:01:46 +08001420 default_secgroups = security_groups_client.list_security_groups(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001421 name='default', project_id=project_id)['security_groups']
1422 msg = "No default security group for project %s." % (project_id)
zhuflb0b272e2017-09-22 16:01:46 +08001423 self.assertNotEmpty(default_secgroups, msg)
1424 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001425
Steve Heyman33735f22016-05-24 09:28:08 -05001426 ruleset = dict(security_group_id=secgroup['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001427 project_id=secgroup['project_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001428 ruleset.update(kwargs)
1429
John Warren456d9ae2016-01-12 15:36:33 -05001430 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001431 sg_rule = sg_rule['security_group_rule']
1432
1433 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1434 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001435
1436 return sg_rule
1437
John Warren456d9ae2016-01-12 15:36:33 -05001438 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1439 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001440 security_groups_client=None):
Martin Kopece1d873a2020-11-02 12:19:54 +00001441 """Create loginable security group rule by neutron clients by default.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001442
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001443 This function will create:
1444 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1445 access for ipv4.
1446 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1447 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001448 """
1449
John Warren456d9ae2016-01-12 15:36:33 -05001450 if security_group_rules_client is None:
1451 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001452 if security_groups_client is None:
1453 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001454 rules = []
1455 rulesets = [
1456 dict(
1457 # ssh
1458 protocol='tcp',
1459 port_range_min=22,
1460 port_range_max=22,
1461 ),
1462 dict(
1463 # ping
1464 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001465 ),
1466 dict(
1467 # ipv6-icmp for ping6
1468 protocol='icmp',
1469 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001470 )
1471 ]
John Warren456d9ae2016-01-12 15:36:33 -05001472 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001473 for ruleset in rulesets:
1474 for r_direction in ['ingress', 'egress']:
1475 ruleset['direction'] = r_direction
1476 try:
1477 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001478 sec_group_rules_client=sec_group_rules_client,
1479 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001480 security_groups_client=security_groups_client,
1481 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001482 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001483 # if rule already exist - skip rule and continue
1484 msg = 'Security group rule already exists'
1485 if msg not in ex._error_string:
1486 raise ex
1487 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001488 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001489 rules.append(sg_rule)
1490
1491 return rules
1492
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001493 def _get_router(self, client=None, project_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001494 """Retrieve a router for the given tenant id.
1495
1496 If a public router has been configured, it will be returned.
1497
1498 If a public router has not been configured, but a public
1499 network has, a tenant router will be created and returned that
1500 routes traffic to the public network.
1501 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301502
Yair Frieddb6c9e92014-08-06 08:53:13 +03001503 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001504 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001505 if not project_id:
1506 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001507 router_id = CONF.network.public_router_id
1508 network_id = CONF.network.public_network_id
1509 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001510 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001511 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001512 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001513 router = client.create_router(
1514 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1515 admin_state_up=True,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001516 project_id=project_id,
zhufl3484f992017-10-10 16:18:29 +08001517 external_gateway_info=dict(network_id=network_id))['router']
1518 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1519 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001520 return router
1521 else:
1522 raise Exception("Neither of 'public_router_id' or "
1523 "'public_network_id' has been defined.")
1524
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001525 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001526 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001527 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001528 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001529 """Create a network with a subnet connected to a router.
1530
David Shrewsbury9bac3662014-08-07 15:07:01 -04001531 The baremetal driver is a special case since all nodes are
1532 on the same shared network.
1533
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001534 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001535 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001536 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001537 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001538 a form like this: {'provider:network_type': 'vlan',
1539 'provider:physical_network': 'foo',
1540 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001541 :returns: network, subnet, router
1542 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301543
Thiago Paiva66cded22016-08-15 14:55:58 -03001544 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001545 # NOTE(Shrews): This exception is for environments where tenant
1546 # credential isolation is available, but network separation is
1547 # not (the current baremetal case). Likely can be removed when
1548 # test account mgmt is reworked:
1549 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001550 if not CONF.compute.fixed_network_name:
1551 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001552 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001553 network = self._get_network_by_name(
1554 CONF.compute.fixed_network_name)
1555 router = None
1556 subnet = None
1557 else:
John Warren94d8faf2015-09-15 12:22:24 -04001558 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001559 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001560 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001561 port_security_enabled=port_security_enabled,
1562 **net_dict)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001563 router = self._get_router(client=routers_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001564 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001565 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001566 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001567 # use explicit check because empty list is a valid option
1568 if dns_nameservers is not None:
1569 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001570 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001571 if not routers_client:
1572 routers_client = self.routers_client
1573 router_id = router['id']
1574 routers_client.add_router_interface(router_id,
1575 subnet_id=subnet['id'])
1576
1577 # save a cleanup job to remove this association between
1578 # router and subnet
1579 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1580 routers_client.remove_router_interface, router_id,
1581 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001582 return network, subnet, router
1583
1584
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001585class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001586 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001587
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001588 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001589
1590 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001591 def setup_clients(cls):
1592 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001593 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001594 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001595 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001596
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001597 def create_encryption_type(self, client=None, type_id=None, provider=None,
1598 key_size=None, cipher=None,
1599 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301600 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001601 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001602 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001603 if not type_id:
1604 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001605 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001606 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001607 client.create_encryption_type(
1608 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001609 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001610
lkuchlan3023e752017-06-08 12:53:13 +03001611 def create_encrypted_volume(self, encryption_provider, volume_type,
1612 key_size=256, cipher='aes-xts-plain64',
1613 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301614 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001615 volume_type = self.create_volume_type(name=volume_type)
1616 self.create_encryption_type(type_id=volume_type['id'],
1617 provider=encryption_provider,
1618 key_size=key_size,
1619 cipher=cipher,
1620 control_location=control_location)
1621 return self.create_volume(volume_type=volume_type['name'])
1622
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001623
Masayuki Igawa0870db52015-09-18 21:08:36 +09001624class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001625 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001626
1627 Subclasses implement the tests that use the methods provided by this
1628 class.
1629 """
1630
1631 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001632 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001633 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001634 if not CONF.service_available.swift:
1635 skip_msg = ("%s skipped as swift is not available" %
1636 cls.__name__)
1637 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001638
1639 @classmethod
1640 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001641 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001642 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001643 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001644 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001645
1646 @classmethod
1647 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001648 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001649 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001650 cls.account_client = cls.os_operator.account_client
1651 cls.container_client = cls.os_operator.container_client
1652 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001653
Chris Dentde456a12014-09-10 12:41:15 +01001654 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301655 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001656 self.account_client.list_account_containers()
1657 LOG.debug('Swift status information obtained successfully')
1658
Chris Dentde456a12014-09-10 12:41:15 +01001659 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301660 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001661 name = container_name or data_utils.rand_name(
1662 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001663 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001664 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001665 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001666 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001667 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001668 self.container_client.delete_container,
1669 name)
Chris Dent0d494112014-08-26 13:48:30 +01001670 return name
1671
Chris Dentde456a12014-09-10 12:41:15 +01001672 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301673 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001674 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001675 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001676
Chris Dentde456a12014-09-10 12:41:15 +01001677 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301678 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001679 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001680 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001681 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001682 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001683 self.object_client.delete_object,
1684 container_name,
1685 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001686 return obj_name, obj_data
1687
Chris Dentde456a12014-09-10 12:41:15 +01001688 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301689 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001690 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001691 self.list_and_check_container_objects(container_name,
1692 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001693
Chris Dentde456a12014-09-10 12:41:15 +01001694 def list_and_check_container_objects(self, container_name,
1695 present_obj=None,
1696 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301697 """List and verify objects for a given container
1698
1699 This utility lists objects for a given container
1700 and asserts which are present and
1701 which are not
1702 """
1703
Ghanshyam2a180b82014-06-16 13:54:22 +09001704 if present_obj is None:
1705 present_obj = []
1706 if not_present_obj is None:
1707 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001708 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001709 container_name)
1710 if present_obj:
1711 for obj in present_obj:
1712 self.assertIn(obj, object_list)
1713 if not_present_obj:
1714 for obj in not_present_obj:
1715 self.assertNotIn(obj, object_list)
1716
Chris Dentde456a12014-09-10 12:41:15 +01001717 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301718 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001719 _, obj = self.object_client.get_object(container_name, obj_name)
1720 self.assertEqual(obj, expected_data)