blob: a3938250d37df42913aab9fa5cad85dbc177ecf4 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Martin Kopec02af6a42020-03-03 12:39:12 +000017import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040018import subprocess
19
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import netaddr
Doug Hellmann583ce2c2015-03-11 14:55:46 +000021from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030022from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053023from oslo_utils import netutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040024
lanoux5fc14522015-09-21 08:17:35 +000025from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070026from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090027from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000028from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000029from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000030from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020031from tempest import exceptions
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000032from tempest.lib.common import api_microversion_fixture
33from tempest.lib.common import api_version_utils
Ken'ichi Ohmichibe4fb502017-03-10 10:04:48 -080034from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010035from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050036from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040037import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Matthew Treinish6c072292014-01-29 19:15:52 +000039CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040040
Attila Fazekasfb7552a2013-08-27 13:02:26 +020041LOG = log.getLogger(__name__)
42
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000043LATEST_MICROVERSION = 'latest'
44
Sean Dague6dbc6da2013-05-08 17:49:46 -040045
Andrea Frittoli2e733b52014-07-16 14:12:11 +010046class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010047 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010048
Andrea Frittolib21de6c2015-02-06 20:12:38 +000049 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000050
Ghanshyam Mann09c4eb92019-06-04 13:07:12 +000051 compute_min_microversion = None
52 compute_max_microversion = LATEST_MICROVERSION
53 volume_min_microversion = None
54 volume_max_microversion = LATEST_MICROVERSION
55 placement_min_microversion = None
56 placement_max_microversion = LATEST_MICROVERSION
57
58 @classmethod
59 def skip_checks(cls):
60 super(ScenarioTest, cls).skip_checks()
61 api_version_utils.check_skip_with_microversion(
62 cls.compute_min_microversion, cls.compute_max_microversion,
63 CONF.compute.min_microversion, CONF.compute.max_microversion)
64 api_version_utils.check_skip_with_microversion(
65 cls.volume_min_microversion, cls.volume_max_microversion,
66 CONF.volume.min_microversion, CONF.volume.max_microversion)
67 api_version_utils.check_skip_with_microversion(
68 cls.placement_min_microversion, cls.placement_max_microversion,
69 CONF.placement.min_microversion, CONF.placement.max_microversion)
70
71 @classmethod
72 def resource_setup(cls):
73 super(ScenarioTest, cls).resource_setup()
74 cls.compute_request_microversion = (
75 api_version_utils.select_request_microversion(
76 cls.compute_min_microversion,
77 CONF.compute.min_microversion))
78 cls.volume_request_microversion = (
79 api_version_utils.select_request_microversion(
80 cls.volume_min_microversion,
81 CONF.volume.min_microversion))
82 cls.placement_request_microversion = (
83 api_version_utils.select_request_microversion(
84 cls.placement_min_microversion,
85 CONF.placement.min_microversion))
86
87 def setUp(self):
88 super(ScenarioTest, self).setUp()
89 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
90 compute_microversion=self.compute_request_microversion,
91 volume_microversion=self.volume_request_microversion,
92 placement_microversion=self.placement_request_microversion))
93
Soniya Vyas0c84f3e2020-07-15 15:20:59 +053094 def setup_compute_client(cls):
95 """Compute and Compute security groups client"""
96 cls.compute_images_client = cls.os_primary.compute_images_client
97 cls.keypairs_client = cls.os_primary.keypairs_client
98 cls.compute_security_groups_client = (
99 cls.os_primary.compute_security_groups_client)
100 cls.compute_security_group_rules_client = (
101 cls.os_primary.compute_security_group_rules_client)
102 cls.servers_client = cls.os_primary.servers_client
103 cls.interface_client = cls.os_primary.interfaces_client
104
105 def setup_network_client(cls):
106 """Neutron network client"""
107 cls.networks_client = cls.os_primary.networks_client
108 cls.ports_client = cls.os_primary.ports_client
109 cls.routers_client = cls.os_primary.routers_client
110 cls.subnets_client = cls.os_primary.subnets_client
111 cls.floating_ips_client = cls.os_primary.floating_ips_client
112 cls.security_groups_client = cls.os_primary.security_groups_client
113 cls.security_group_rules_client = (
114 cls.os_primary.security_group_rules_client)
115
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000116 @classmethod
117 def setup_clients(cls):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530118 """This setup the service clients for the tests"""
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000119 super(ScenarioTest, cls).setup_clients()
jeremy.zhang0343be52017-05-25 21:29:57 +0800120 cls.flavors_client = cls.os_primary.flavors_client
John Warrene74890a2015-11-11 15:18:01 -0500121 cls.compute_floating_ips_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +0800122 cls.os_primary.compute_floating_ips_client)
Jordan Pittier1d2e40f2016-01-05 18:49:14 +0100123 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400124 # Check if glance v1 is available to determine which client to use.
125 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +0800126 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400127 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +0800128 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400129 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400130 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400131 'Either api_v1 or api_v2 must be True in '
132 '[image-feature-enabled].')
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530133
134 cls.setup_compute_client(cls)
135 cls.setup_network_client(cls)
Andrea Frittolia6b30152017-08-04 10:46:10 +0100136 if CONF.service_available.cinder:
137 cls.volumes_client = cls.os_primary.volumes_client_latest
138 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +0300139 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300140
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200141 # ## Test functions library
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200142 # The create_[resource] functions only return body and discard the
143 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +0100144
zhufl1e446b52017-10-16 16:54:57 +0800145 def create_port(self, network_id, client=None, **kwargs):
Soniya Vyas0123f522020-09-24 17:43:26 +0530146 """Creates port for the respective network_id"""
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300147 if not client:
148 client = self.ports_client
Soniya Vyas0123f522020-09-24 17:43:26 +0530149 name = kwargs.pop('namestart', self.__class__.__name__)
Edan David408a97b2018-01-15 03:52:15 -0500150 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
151 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
152 if CONF.network.port_profile and 'binding:profile' not in kwargs:
153 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300154 result = client.create_port(
155 name=name,
156 network_id=network_id,
157 **kwargs)
Soniya Vyas0123f522020-09-24 17:43:26 +0530158 self.assertIsNotNone(result, 'Unable to allocate port')
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300159 port = result['port']
160 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
161 client.delete_port, port['id'])
162 return port
163
Martin Kopec30b4d532020-10-16 12:02:43 +0000164 def create_keypair(self, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530165 """Creates keypair
166
167 Keypair is a public key of OpenSSH key pair used for accessing
168 and create servers
169 Keypair can also be created by a private key for the same purpose
170 Here, the keys are randomly generated[public/private]
171 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300172 if not client:
173 client = self.keypairs_client
Martin Kopec30b4d532020-10-16 12:02:43 +0000174 if not kwargs.get('name'):
175 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100176 # We don't need to create a keypair by pubkey in scenario
Martin Kopec30b4d532020-10-16 12:02:43 +0000177 body = client.create_keypair(**kwargs)
178 self.addCleanup(client.delete_keypair, kwargs['name'])
ghanshyamdee01f22015-08-17 11:41:47 +0900179 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100180
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530181 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800182 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200183 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000184 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100185
lanoux5fc14522015-09-21 08:17:35 +0000186 This wrapper utility calls the common create test server and
187 returns a test server. The purpose of this wrapper is to minimize
188 the impact on the code of the tests already using this
189 function.
Noam Angel6e309952019-01-27 05:52:40 +0000190
191 :param **kwargs:
192 See extra parameters below
193
194 :Keyword Arguments:
195 * *vnic_type* (``string``) --
196 used when launching instances with pre-configured ports.
197 Examples:
198 normal: a traditional virtual port that is either attached
199 to a linux bridge or an openvswitch bridge on a
200 compute node.
201 direct: an SR-IOV port that is directly attached to a VM
202 macvtap: an SR-IOV port that is attached to a VM via a macvtap
203 device.
Tom Stappaerts27fd5cb2020-11-26 12:07:47 +0100204 direct-physical: an SR-IOV port that is directly attached to a
205 VM using physical instead of virtual
206 functions.
207 baremetal: a baremetal port directly attached to a baremetal
208 node.
209 virtio-forwarder: an SR-IOV port that is indirectly attached
210 to a VM using a low-latency vhost-user
211 forwarding process.
Noam Angel6e309952019-01-27 05:52:40 +0000212 Defaults to ``CONF.network.port_vnic_type``.
213 * *port_profile* (``dict``) --
214 This attribute is a dictionary that can be used (with admin
215 credentials) to supply information influencing the binding of
216 the port.
217 example: port_profile = "capabilities:[switchdev]"
218 Defaults to ``CONF.network.port_profile``.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100219 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100220
lanoux5fc14522015-09-21 08:17:35 +0000221 # NOTE(jlanoux): As a first step, ssh checks in the scenario
222 # tests need to be run regardless of the run_validation and
223 # validatable parameters and thus until the ssh validation job
224 # becomes voting in CI. The test resources management and IP
225 # association are taken care of in the scenario tests.
226 # Therefore, the validatable parameter is set to false in all
227 # those tests. In this way create_server just return a standard
228 # server and the scenario tests always perform ssh checks.
229
230 # Needed for the cross_tenant_traffic test:
231 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800232 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000233
zhufl24208c22016-10-25 15:23:48 +0800234 if name is None:
235 name = data_utils.rand_name(self.__class__.__name__ + "-server")
236
Noam Angel6e309952019-01-27 05:52:40 +0000237 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
238 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000239
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000240 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000241 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000242 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000243 ports = []
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000244 create_port_body = {}
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300245
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000246 if vnic_type:
247 create_port_body['binding:vnic_type'] = vnic_type
248
249 if profile:
250 create_port_body['binding:profile'] = profile
251
lanoux5fc14522015-09-21 08:17:35 +0000252 if kwargs:
253 # Convert security group names to security group ids
254 # to pass to create_port
255 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300256 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500257 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000258 ).get('security_groups')
259 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100260 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000261
262 sec_groups_names = [s['name'] for s in kwargs.pop(
263 'security_groups')]
264 security_groups_ids = [sec_dict[s]
265 for s in sec_groups_names]
266
267 if security_groups_ids:
268 create_port_body[
269 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300270 networks = kwargs.pop('networks', [])
271 else:
272 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000273
274 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300275 # for the project's private networks and create a port.
276 # The same behaviour as we would expect when passing
277 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000278 if not networks:
279 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300280 **{'router:external': False, 'fields': 'id'})['networks']
281
282 # It's net['uuid'] if networks come from kwargs
283 # and net['id'] if they come from
284 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000285 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000286 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300287 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800288 port = self.create_port(network_id=net_id,
289 client=clients.ports_client,
290 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300291 ports.append({'port': port['id']})
292 else:
293 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000294 if ports:
295 kwargs['networks'] = ports
296 self.ports = ports
297
298 tenant_network = self.get_tenant_network()
299
Marc Koderer979e4942016-12-08 10:07:59 +0100300 if CONF.compute.compute_volume_common_az:
301 kwargs.setdefault('availability_zone',
302 CONF.compute.compute_volume_common_az)
303
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200304 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000305 clients,
306 tenant_network=tenant_network,
307 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530308 name=name, flavor=flavor,
309 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000310
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200311 self.addCleanup(waiters.wait_for_server_termination,
312 clients.servers_client, body['id'])
313 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
314 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000315 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100316 return server
317
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100318 def create_volume(self, size=None, name=None, snapshot_id=None,
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000319 imageRef=None, volume_type=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530320 """Creates volume
321
322 This wrapper utility creates volume and waits for volume to be
323 in 'available' state.
324 This method returns the volume's full representation by GET request.
325 """
326
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700327 if size is None:
328 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500329 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800330 if CONF.image_feature_enabled.api_v1:
331 resp = self.image_client.check_image(imageRef)
332 image = common_image.get_image_meta_from_headers(resp)
333 else:
334 image = self.image_client.show_image(imageRef)
335 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500336 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100337 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800338 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Martin Kopecd3ad5e92020-10-16 14:45:09 +0000339 kwargs.update({'name': name,
340 'snapshot_id': snapshot_id,
341 'imageRef': imageRef,
342 'volume_type': volume_type,
343 'size': size})
Marc Koderer979e4942016-12-08 10:07:59 +0100344
345 if CONF.compute.compute_volume_common_az:
346 kwargs.setdefault('availability_zone',
347 CONF.compute.compute_volume_common_az)
348
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900349 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700350
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100351 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
352 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100353 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100354 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300355 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200356 waiters.wait_for_volume_resource_status(self.volumes_client,
357 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100358 # The volume retrieved on creation has a non-up-to-date status.
359 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000360 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100361 return volume
362
lkuchlane20e6a82018-05-08 11:28:46 +0300363 def create_backup(self, volume_id, name=None, description=None,
364 force=False, snapshot_id=None, incremental=False,
365 container=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530366 """Creates backup
367
368 This wrapper utility creates backup and waits for backup to be
369 in 'available' state.
370 """
lkuchlane20e6a82018-05-08 11:28:46 +0300371
372 name = name or data_utils.rand_name(
373 self.__class__.__name__ + "-backup")
374 kwargs = {'name': name,
375 'description': description,
376 'force': force,
377 'snapshot_id': snapshot_id,
378 'incremental': incremental,
379 'container': container}
380 backup = self.backups_client.create_backup(volume_id=volume_id,
381 **kwargs)['backup']
382 self.addCleanup(self.backups_client.delete_backup, backup['id'])
383 waiters.wait_for_volume_resource_status(self.backups_client,
384 backup['id'], 'available')
385 return backup
386
387 def restore_backup(self, backup_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530388 """Restore backup
389
390 This wrapper utility restores backup and waits for backup to be
391 in 'available' state.
392 """
393
lkuchlane20e6a82018-05-08 11:28:46 +0300394 restore = self.backups_client.restore_backup(backup_id)['restore']
395 self.addCleanup(self.volumes_client.delete_volume,
396 restore['volume_id'])
397 waiters.wait_for_volume_resource_status(self.backups_client,
398 backup_id, 'available')
399 waiters.wait_for_volume_resource_status(self.volumes_client,
400 restore['volume_id'],
401 'available')
402 self.assertEqual(backup_id, restore['backup_id'])
403 return restore
404
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000405 def rebuild_server(self, server_id, image=None, preserve_ephemeral=False,
406 wait=True, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530407 if image is None:
408 image = CONF.compute.image_ref
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530409 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
410 server_id, image, preserve_ephemeral)
411 self.servers_client.rebuild_server(
412 server_id=server_id,
413 image_ref=image,
414 preserve_ephemeral=preserve_ephemeral,
Martin Kopecbe8ba2c2020-12-17 21:33:32 +0000415 **kwargs)
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530416 if wait:
417 waiters.wait_for_server_status(self.servers_client,
418 server_id, 'ACTIVE')
419
lkuchlan73ed1f32017-07-06 16:22:12 +0300420 def create_volume_snapshot(self, volume_id, name=None, description=None,
421 metadata=None, force=False):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530422 """Creates volume
423
424 This wrapper utility creates volume snapshot and waits for backup
425 to be in 'available' state.
426 """
427
lkuchlan73ed1f32017-07-06 16:22:12 +0300428 name = name or data_utils.rand_name(
429 self.__class__.__name__ + '-snapshot')
430 snapshot = self.snapshots_client.create_snapshot(
431 volume_id=volume_id,
432 force=force,
Martin Kopec20c87c72020-10-17 11:42:29 +0000433 name=name,
lkuchlan73ed1f32017-07-06 16:22:12 +0300434 description=description,
435 metadata=metadata)['snapshot']
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530436
lkuchlan73ed1f32017-07-06 16:22:12 +0300437 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
438 snapshot['id'])
439 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
440 waiters.wait_for_volume_resource_status(self.snapshots_client,
441 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200442 snapshot = self.snapshots_client.show_snapshot(
443 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300444 return snapshot
445
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100446 def _cleanup_volume_type(self, volume_type):
447 """Clean up a given volume type.
448
449 Ensuring all volumes associated to a type are first removed before
450 attempting to remove the type itself. This includes any image volume
451 cache volumes stored in a separate tenant to the original volumes
452 created from the type.
453 """
454 admin_volume_type_client = self.os_admin.volume_types_client_latest
455 admin_volumes_client = self.os_admin.volumes_client_latest
456 volumes = admin_volumes_client.list_volumes(
457 detail=True, params={'all_tenants': 1})['volumes']
458 type_name = volume_type['name']
459 for volume in [v for v in volumes if v['volume_type'] == type_name]:
460 test_utils.call_and_ignore_notfound_exc(
461 admin_volumes_client.delete_volume, volume['id'])
462 admin_volumes_client.wait_for_resource_deletion(volume['id'])
463 admin_volume_type_client.delete_volume_type(volume_type['id'])
464
Martin Kopec8e673a42020-10-18 17:33:02 +0000465 def create_volume_type(self, client=None, name=None, backend_name=None,
466 **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530467 """Creates volume type
468
469 In a multiple-storage back-end configuration,
470 each back end has a name (volume_backend_name).
471 The name of the back end is declared as an extra-specification
472 of a volume type (such as, volume_backend_name=LVM).
473 When a volume is created, the scheduler chooses an
474 appropriate back end to handle the request, according
475 to the volume type specified by the user.
476 The scheduler uses volume types to explicitly create volumes on
477 specific back ends.
478
479 Before using volume type, a volume type has to be declared
480 to Block Storage. In addition to that, an extra-specification
481 has to be created to link the volume type to a back end name.
482 """
483
scottda61f68ac2016-06-07 12:07:55 -0600484 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000485 client = self.os_admin.volume_types_client_latest
Matt Riedemann514495b2019-05-04 17:34:12 +0000486 if not name:
487 class_name = self.__class__.__name__
488 name = data_utils.rand_name(class_name + '-volume-type')
489 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600490
491 LOG.debug("Creating a volume type: %s on backend %s",
492 randomized_name, backend_name)
Martin Kopec8e673a42020-10-18 17:33:02 +0000493 extra_specs = kwargs.pop("extra_specs", {})
scottda61f68ac2016-06-07 12:07:55 -0600494 if backend_name:
Martin Kopec8e673a42020-10-18 17:33:02 +0000495 extra_specs.update({"volume_backend_name": backend_name})
scottda61f68ac2016-06-07 12:07:55 -0600496
Martin Kopec8e673a42020-10-18 17:33:02 +0000497 volume_type_resp = client.create_volume_type(
498 name=randomized_name, extra_specs=extra_specs, **kwargs)
499 volume_type = volume_type_resp['volume_type']
500
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530501 self.assertIn('id', volume_type)
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100502 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600503 return volume_type
504
Martin Kopece1d873a2020-11-02 12:19:54 +0000505 def _create_loginable_secgroup_rule(self, secgroup_id=None, rulesets=None):
506 """Create loginable security group rule by compute clients.
507
508 This function will create by default the following rules:
509 1. tcp port 22 allow rule in order to allow ssh access for ipv4
510 2. ipv4 icmp allow rule in order to allow icmpv4
511 """
512
John Warrenf2345512015-12-10 13:39:30 -0500513 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500514 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100515 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900516 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100517 for sg in sgs:
518 if sg['name'] == 'default':
519 secgroup_id = sg['id']
520
521 # These rules are intended to permit inbound ssh and icmp
522 # traffic from all sources, so no group_id is provided.
523 # Setting a group_id would only permit traffic from ports
524 # belonging to the same security group.
Martin Kopece1d873a2020-11-02 12:19:54 +0000525 if not rulesets:
526 rulesets = [
527 {
528 # ssh
529 'ip_protocol': 'tcp',
530 'from_port': 22,
531 'to_port': 22,
532 'cidr': '0.0.0.0/0',
533 },
534 {
535 # ping
536 'ip_protocol': 'icmp',
537 'from_port': -1,
538 'to_port': -1,
539 'cidr': '0.0.0.0/0',
540 }
541 ]
Andrea Frittoli247058f2014-07-16 16:09:22 +0100542 rules = list()
543 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000544 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900545 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100546 rules.append(sg_rule)
547 return rules
548
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530549 def _create_security_group(self, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530550 """Create security group and add rules to security group"""
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530551 if not kwargs.get('name'):
552 kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
553 if not kwargs.get('description'):
554 kwargs['description'] = kwargs['name'] + " description"
John Warrenf2345512015-12-10 13:39:30 -0500555 secgroup = self.compute_security_groups_client.create_security_group(
Soniya Vyasa078ddd2020-12-04 18:34:44 +0530556 **kwargs)['security_group']
557 self.assertEqual(secgroup['name'], kwargs['name'])
558 self.assertEqual(secgroup['description'], kwargs['description'])
John Warrenf2345512015-12-10 13:39:30 -0500559 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100560 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500561 self.compute_security_groups_client.delete_security_group,
562 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100563
564 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300565 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100566 return secgroup
567
zhuflf52c7592017-05-25 13:55:24 +0800568 def get_remote_client(self, ip_address, username=None, private_key=None,
569 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100570 """Get a SSH client to a remote server
571
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600572 :param ip_address: the server floating or fixed IP address to use
573 for ssh validation
574 :param username: name of the Linux account on the remote server
575 :param private_key: the SSH private key to use
576 :param server: server dict, used for debugging purposes
577 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100578 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700579
Andrea Frittoli247058f2014-07-16 16:09:22 +0100580 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800581 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800582 # Set this with 'keypair' or others to log in with keypair or
583 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000584 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800585 password = None
586 if private_key is None:
587 private_key = self.keypair['private_key']
588 else:
lanoux283273b2015-12-04 03:01:54 -0800589 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800590 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800591 linux_client = remote_client.RemoteClient(
592 ip_address, username, pkey=private_key, password=password,
593 server=server, servers_client=self.servers_client)
594 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100595 return linux_client
596
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000597 def image_create(self, name='scenario-img', **kwargs):
Martin Kopec02af6a42020-03-03 12:39:12 +0000598 img_path = CONF.scenario.img_file
599 if not os.path.exists(img_path):
600 # TODO(kopecmartin): replace LOG.warning for rasing
601 # InvalidConfiguration exception after tempest 25.0.0 is
602 # released - there will be one release which accepts both
603 # behaviors in order to avoid many failures across CIs and etc.
604 LOG.warning(
605 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
606 'a full path for the image. CONF.scenario.img_dir was '
607 'deprecated and will be removed in the next release. Till '
608 'Tempest 25.0.0, old behavior is maintained and keep working '
609 'but starting Tempest 26.0.0, you need to specify the full '
610 'path in CONF.scenario.img_file config option.')
611 img_path = os.path.join(CONF.scenario.img_dir, img_path)
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300612 img_container_format = CONF.scenario.img_container_format
613 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000614 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400615 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Martin Kopec02af6a42020-03-03 12:39:12 +0000616 "properties: %s",
Jordan Pittier525ec712016-12-07 17:51:26 +0100617 img_path, img_container_format, img_disk_format,
Martin Kopec02af6a42020-03-03 12:39:12 +0000618 img_properties)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530619 if img_properties is None:
620 img_properties = {}
621 name = data_utils.rand_name('%s-' % name)
622 params = {
623 'name': name,
624 'container_format': img_container_format,
625 'disk_format': img_disk_format or img_container_format,
626 }
627 if CONF.image_feature_enabled.api_v1:
628 params['is_public'] = 'False'
629 if img_properties:
630 params['properties'] = img_properties
631 params = {'headers': common_image.image_meta_to_headers(**params)}
632 else:
633 params['visibility'] = 'private'
634 # Additional properties are flattened out in the v2 API.
635 if img_properties:
636 params.update(img_properties)
Lukas Piwowarskib50eabe2020-11-05 15:15:38 +0000637 params.update(kwargs)
Soniya Vyasbe8d5102020-08-17 17:23:30 +0530638 body = self.image_client.create_image(**params)
639 image = body['image'] if 'image' in body else body
640 self.addCleanup(self.image_client.delete_image, image['id'])
641 self.assertEqual("queued", image['status'])
642 with open(img_path, 'rb') as image_file:
643 if CONF.image_feature_enabled.api_v1:
644 self.image_client.update_image(image['id'], data=image_file)
645 else:
646 self.image_client.store_image_file(image['id'], image_file)
647 LOG.debug("image:%s", image['id'])
648 return image['id']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100649
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000650 def _log_console_output(self, servers=None, client=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530651 """Console log output"""
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400652 if not CONF.compute_feature_enabled.console_output:
653 LOG.debug('Console output not supported, cannot log')
654 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700655 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100656 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700657 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100658 servers = servers['servers']
659 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100660 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700661 console_output = client.get_console_output(
Lukas Piwowarski91ded042020-10-29 15:15:25 +0000662 server['id'], **kwargs)['output']
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100663 LOG.debug('Console output for %s\nbody=\n%s',
664 server['id'], console_output)
665 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100666 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100667 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100668
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000669 def _log_net_info(self, exc):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530670 """network debug is called as part of ssh init"""
Andrey Pavlov64723762015-04-29 06:24:58 +0300671 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000672 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000673
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000674 def create_server_snapshot(self, server, name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530675 """Creates server snapshot"""
nithya-ganesan882595e2014-07-29 18:51:07 +0000676 # Glance client
677 _image_client = self.image_client
678 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900679 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000680 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800681 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000682 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Lukas Piwowarski9ad9ca22020-10-29 15:36:30 +0000683 image = _images_client.create_image(server['id'], name=name, **kwargs)
David Kranza5299eb2015-01-15 17:24:05 -0500684 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300685 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200686
687 self.addCleanup(_image_client.wait_for_resource_deletion,
688 image_id)
689 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
690 _image_client.delete_image, image_id)
691
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400692 if CONF.image_feature_enabled.api_v1:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530693 # In glance v1 the additional properties are stored in the headers
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700694 resp = _image_client.check_image(image_id)
695 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400696 image_props = snapshot_image.get('properties', {})
697 else:
698 # In glance v2 the additional properties are flattened.
699 snapshot_image = _image_client.show_image(image_id)
700 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300701
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400702 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300703 if bdm:
704 bdm = json.loads(bdm)
705 if bdm and 'snapshot_id' in bdm[0]:
706 snapshot_id = bdm[0]['snapshot_id']
707 self.addCleanup(
708 self.snapshots_client.wait_for_resource_deletion,
709 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100710 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
711 self.snapshots_client.delete_snapshot,
712 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200713 waiters.wait_for_volume_resource_status(self.snapshots_client,
714 snapshot_id,
715 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000716 image_name = snapshot_image['name']
717 self.assertEqual(name, image_name)
718 LOG.debug("Created snapshot image %s for server %s",
719 image_name, server['name'])
720 return snapshot_image
721
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000722 def nova_volume_attach(self, server, volume_to_attach, **kwargs):
Soniya Vyasae631132020-08-28 13:37:12 +0530723 """Compute volume attach
724
725 This utility attaches volume from compute and waits for the
726 volume status to be 'in-use' state.
727 """
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000728 volume = self.servers_client.attach_volume(
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000729 server['id'], volumeId=volume_to_attach['id'],
730 **kwargs)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200731 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200732 waiters.wait_for_volume_resource_status(self.volumes_client,
733 volume['id'], 'in-use')
Lukas Piwowarski76819fa2020-10-29 13:46:07 +0000734 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
735 self.nova_volume_detach, server, volume)
Jordan Pittier7cf64762015-10-14 15:01:12 +0200736 # Return the updated volume after the attachment
737 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900738
Jordan Pittier7cf64762015-10-14 15:01:12 +0200739 def nova_volume_detach(self, server, volume):
Soniya Vyasae631132020-08-28 13:37:12 +0530740 """Compute volume detach
741
Lee Yarwood5423c532020-12-17 11:24:46 +0000742 This utility detaches the volume from the server and checks whether the
743 volume attachment has been removed from Nova.
Soniya Vyasae631132020-08-28 13:37:12 +0530744 """
Jordan Pittier7cf64762015-10-14 15:01:12 +0200745 self.servers_client.detach_volume(server['id'], volume['id'])
Lee Yarwood5423c532020-12-17 11:24:46 +0000746 waiters.wait_for_volume_attachment_remove_from_server(
747 self.servers_client, server['id'], volume['id'])
Jordan Pittier7cf64762015-10-14 15:01:12 +0200748
Steven Hardyda2a8352014-10-02 12:52:20 +0100749 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800750 ping_timeout=None, mtu=None, server=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530751 """ping ip address"""
lanoux5fc14522015-09-21 08:17:35 +0000752 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000753 cmd = ['ping', '-c1', '-w1']
754
755 if mtu:
756 cmd += [
757 # don't fragment
758 '-M', 'do',
759 # ping receives just the size of ICMP payload
760 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
761 ]
762 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700763
764 def ping():
765 proc = subprocess.Popen(cmd,
766 stdout=subprocess.PIPE,
767 stderr=subprocess.PIPE)
768 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000769
Aaron Rosena7df13b2014-09-23 09:45:45 -0700770 return (proc.returncode == 0) == should_succeed
771
Jordan Pittier9e227c52016-02-09 14:35:18 +0100772 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000773 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800774 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000775 'caller': caller, 'ip': ip_address, 'timeout': timeout,
776 'should_succeed':
777 'reachable' if should_succeed else 'unreachable'
778 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200779 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000780 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800781 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000782 'caller': caller, 'ip': ip_address, 'timeout': timeout,
783 'result': 'expected' if result else 'unexpected'
784 })
zhufl0ec74c42017-11-15 14:02:28 +0800785 if server:
786 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000787 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700788
Yair Friedae0e73d2014-11-24 11:56:26 +0200789 def check_vm_connectivity(self, ip_address,
790 username=None,
791 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000792 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800793 extra_msg="",
794 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000795 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000796 """Check server connectivity
797
Yair Friedae0e73d2014-11-24 11:56:26 +0200798 :param ip_address: server to test against
799 :param username: server's ssh username
800 :param private_key: server's ssh private key to be used
801 :param should_connect: True/False indicates positive/negative test
802 positive - attempt ping and ssh
803 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800804 :param extra_msg: Message to help with debugging if ``ping_ip_address``
805 fails
806 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000807 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200808
809 :raises: AssertError if the result of the connectivity check does
810 not match the value of the should_connect param
811 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530812
zhufl0ec74c42017-11-15 14:02:28 +0800813 LOG.debug('checking network connections to IP %s with user: %s',
814 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200815 if should_connect:
816 msg = "Timed out waiting for %s to become reachable" % ip_address
817 else:
818 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800819 if extra_msg:
820 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200821 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000822 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800823 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200824 msg=msg)
825 if should_connect:
826 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800827 try:
828 self.get_remote_client(ip_address, username, private_key,
829 server=server)
830 except Exception:
831 if not extra_msg:
832 extra_msg = 'Failed to ssh to %s' % ip_address
833 LOG.exception(extra_msg)
834 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200835
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000836 def create_floating_ip(self, server, pool_name=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000837 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200838
Marc Koderer3b57d802016-03-22 15:23:31 +0100839 if not pool_name:
840 pool_name = CONF.network.floating_network_name
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000841
John Warrene74890a2015-11-11 15:18:01 -0500842 floating_ip = (self.compute_floating_ips_client.
Lukas Piwowarskif759bc12020-11-05 10:51:29 +0000843 create_floating_ip(pool=pool_name,
844 **kwargs)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100845 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500846 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200847 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500848 self.compute_floating_ips_client.associate_floating_ip_to_server(
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530849 floating_ip['ip'], server['id'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200850 return floating_ip
851
Sean Dague20e98612016-01-06 14:33:28 -0500852 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000853 private_key=None, server=None, username=None,
854 fs='ext4'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530855 """Creates timestamp
856
857 This wrapper utility does ssh, creates timestamp and returns the
858 created timestamp.
859 """
Sean Dague20e98612016-01-06 14:33:28 -0500860 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200861 private_key=private_key,
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000862 server=server,
863 username=username)
864
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300865 if dev_name is not None:
Lukas Piwowarski25f7ba22020-10-29 14:01:34 +0000866 ssh_client.make_fs(dev_name, fs=fs)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800867 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
868 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300869 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
870 ssh_client.exec_command(cmd_timestamp)
871 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
872 % mount_path)
873 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800874 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300875 return timestamp
876
Sean Dague20e98612016-01-06 14:33:28 -0500877 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000878 private_key=None, server=None, username=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530879 """Returns timestamp
880
881 This wrapper utility does ssh and returns the timestamp.
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000882
883 :param ip_address: The floating IP or fixed IP of the remote server
884 :param dev_name: Name of the device that stores the timestamp
885 :param mount_path: Path which should be used as mount point for
886 dev_name
887 :param private_key: The SSH private key to use for authentication
888 :param server: Server dict, used for debugging purposes
889 :param username: Name of the Linux account on the remote server
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530890 """
891
Sean Dague20e98612016-01-06 14:33:28 -0500892 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200893 private_key=private_key,
Lukas Piwowarski2c230eb2020-10-30 10:09:18 +0000894 server=server,
895 username=username)
896
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300897 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700898 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300899 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
900 % mount_path)
901 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800902 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300903 return timestamp
904
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000905 def get_server_ip(self, server, **kwargs):
Sean Dague20e98612016-01-06 14:33:28 -0500906 """Get the server fixed or floating IP.
907
908 Based on the configuration we're in, return a correct ip
909 address for validating that a guest is up.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000910
911 If CONF.validation.connect_method is floating, then
912 a floating ip will be created passing kwargs as additional
913 argument.
Sean Dague20e98612016-01-06 14:33:28 -0500914 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530915
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200916 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500917 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800918 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500919 # method is creating the floating IP there.
Lukas Piwowarskib0642f92020-10-29 14:51:30 +0000920 return self.create_floating_ip(server, **kwargs)['ip']
Sean Dague20e98612016-01-06 14:33:28 -0500921 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400922 # Determine the network name to look for based on config or creds
923 # provider network resources.
924 if CONF.validation.network_for_ssh:
925 addresses = server['addresses'][
926 CONF.validation.network_for_ssh]
927 else:
zhufl7b4a7202017-09-28 10:29:27 +0800928 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400929 addresses = (server['addresses'][network['name']]
930 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500931 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200932 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
933 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500934 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800935 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200936 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400937 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200938
zhufl7bc916d2018-08-22 14:47:39 +0800939 @classmethod
940 def get_host_for_server(cls, server_id):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530941 """Gets host of server"""
942
zhufl7bc916d2018-08-22 14:47:39 +0800943 server_details = cls.os_admin.servers_client.show_server(server_id)
944 return server_details['server']['OS-EXT-SRV-ATTR:host']
945
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000946 def _get_bdm(self, source_id, source_type, delete_on_termination=False):
947 bd_map_v2 = [{
948 'uuid': source_id,
949 'source_type': source_type,
950 'destination_type': 'volume',
951 'boot_index': 0,
952 'delete_on_termination': delete_on_termination}]
953 return {'block_device_mapping_v2': bd_map_v2}
954
955 def boot_instance_from_resource(self, source_id,
956 source_type,
957 keypair=None,
958 security_group=None,
959 delete_on_termination=False,
Martin Kopecbee673e2020-11-04 09:40:52 +0000960 name=None, **kwargs):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +0530961 """Boot instance from resource
962
963 This wrapper utility boots instance from resource with block device
964 mapping with source info passed in arguments
965 """
966
Martin Kopecbee673e2020-11-04 09:40:52 +0000967 create_kwargs = dict({'image_id': ''})
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000968 if keypair:
969 create_kwargs['key_name'] = keypair['name']
970 if security_group:
971 create_kwargs['security_groups'] = [
972 {'name': security_group['name']}]
973 create_kwargs.update(self._get_bdm(
974 source_id,
975 source_type,
976 delete_on_termination=delete_on_termination))
977 if name:
978 create_kwargs['name'] = name
Martin Kopecbee673e2020-11-04 09:40:52 +0000979 create_kwargs.update(kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000980
Martin Kopecbee673e2020-11-04 09:40:52 +0000981 return self.create_server(**create_kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000982
Martin Kopec0216b372020-11-04 09:32:05 +0000983 def create_volume_from_image(self, **kwargs):
984 """Create volume from image.
985
986 :param image_id: ID of the image to create volume from,
987 CONF.compute.image_ref by default
988 :param name: name of the volume,
989 '$classname-volume-origin' by default
990 :param **kwargs: additional parameters
991 """
992 image_id = kwargs.pop('image_id', CONF.compute.image_ref)
993 name = kwargs.pop('name', None)
994 if not name:
995 namestart = self.__class__.__name__ + '-volume-origin'
996 name = data_utils.rand_name(namestart)
997 return self.create_volume(name=name, imageRef=image_id, **kwargs)
Rajat Dhasmanaa8bfa172020-01-14 17:34:44 +0000998
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100999
Andrea Frittoli4971fc82014-09-25 10:22:20 +01001000class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +03001001 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001002
Yair Fried1fc32a12014-08-04 09:11:30 +03001003 This class provide helpers for network scenario tests, using the neutron
1004 API. Helpers from ancestor which use the nova network API are overridden
1005 with the neutron API.
1006
1007 This Class also enforces using Neutron instead of novanetwork.
1008 Subclassed tests will be skipped if Neutron is not enabled
1009
1010 """
1011
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001012 credentials = ['primary', 'admin']
1013
Yair Fried1fc32a12014-08-04 09:11:30 +03001014 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001015 def skip_checks(cls):
1016 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001017 if not CONF.service_available.neutron:
1018 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +03001019
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001020 def _create_network(self, networks_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001021 project_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001022 namestart='network-smoke-',
Lajos Katonac87a06b2019-01-04 13:21:48 +01001023 port_security_enabled=True, **net_dict):
John Warren94d8faf2015-09-15 12:22:24 -04001024 if not networks_client:
1025 networks_client = self.networks_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001026 if not project_id:
1027 project_id = networks_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001028 name = data_utils.rand_name(namestart)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001029 network_kwargs = dict(name=name, project_id=project_id)
Lajos Katonac87a06b2019-01-04 13:21:48 +01001030 if net_dict:
1031 network_kwargs.update(net_dict)
Matt Riedemann039b2fe2016-09-15 16:12:24 -04001032 # Neutron disables port security by default so we have to check the
1033 # config before trying to create the network with port_security_enabled
1034 if CONF.network_feature_enabled.port_security:
1035 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +02001036 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001037 network = result['network']
1038
1039 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001040 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001041 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -05001042 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001043 return network
1044
zhufl5b0a52f2017-10-24 15:48:20 +08001045 def create_subnet(self, network, subnets_client=None,
1046 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001047 """Create a subnet for the given network
1048
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301049 This utility creates subnet for the given network
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001050 within the cidr block configured for tenant networks.
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301051
1052 :param **kwargs:
1053 See extra parameters below
1054
1055 :Keyword Arguments:
1056
1057 * *ip_version = ip version of the given network,
Yair Fried1fc32a12014-08-04 09:11:30 +03001058 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301059
John Warren3961acd2015-10-02 14:38:53 -04001060 if not subnets_client:
1061 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001062
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001063 def cidr_in_use(cidr, project_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001064 """Check cidr existence
1065
yangjianfeng4ad346e2020-11-22 06:49:19 +00001066 :returns: True if subnet with cidr already exist in tenant or
1067 external False else
Yair Fried1fc32a12014-08-04 09:11:30 +03001068 """
yangjianfeng4ad346e2020-11-22 06:49:19 +00001069 tenant_subnets = self.os_admin.subnets_client.list_subnets(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001070 project_id=project_id, cidr=cidr)['subnets']
yangjianfeng4ad346e2020-11-22 06:49:19 +00001071 external_nets = self.os_admin.networks_client.list_networks(
1072 **{"router:external": True})['networks']
1073 external_subnets = []
1074 for ext_net in external_nets:
1075 external_subnets.extend(
1076 self.os_admin.subnets_client.list_subnets(
1077 network_id=ext_net['id'], cidr=cidr)['subnets'])
1078 return len(tenant_subnets + external_subnets) != 0
Yair Fried1fc32a12014-08-04 09:11:30 +03001079
Kirill Shileev14113572014-11-21 16:58:02 +03001080 ip_version = kwargs.pop('ip_version', 4)
1081
1082 if ip_version == 6:
1083 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -04001084 CONF.network.project_network_v6_cidr)
1085 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001086 else:
Sean Dagueed6e5862016-04-04 10:49:13 -04001087 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
1088 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +03001089
Yair Fried1fc32a12014-08-04 09:11:30 +03001090 result = None
Kirill Shileev14113572014-11-21 16:58:02 +03001091 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +03001092 # Repeatedly attempt subnet creation with sequential cidr
1093 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +03001094 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +03001095 str_cidr = str(subnet_cidr)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001096 if cidr_in_use(str_cidr, project_id=network['project_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +03001097 continue
1098
1099 subnet = dict(
1100 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -05001101 network_id=network['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001102 project_id=network['project_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +03001103 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +03001104 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +03001105 **kwargs
1106 )
1107 try:
John Warren3961acd2015-10-02 14:38:53 -04001108 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +03001109 break
Masayuki Igawad9388762015-01-20 14:56:42 +09001110 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +03001111 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1112 if not is_overlapping_cidr:
1113 raise
1114 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -05001115
1116 subnet = result['subnet']
1117 self.assertEqual(subnet['cidr'], str_cidr)
1118
1119 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1120 subnets_client.delete_subnet, subnet['id'])
1121
Yair Fried1fc32a12014-08-04 09:11:30 +03001122 return subnet
1123
Lukas Piwowarski9523d512020-10-30 09:37:58 +00001124 def _get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
1125
1126 if ip_addr and not kwargs.get('fixed_ips'):
1127 kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
1128 ports = self.os_admin.ports_client.list_ports(
1129 device_id=server['id'], **kwargs)['ports']
1130
Kobi Samoray166500a2016-10-09 14:42:48 +03001131 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -05001132 # If the network is dual-stack (IPv4 + IPv6), this port is associated
1133 # with 2 subnets
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001134
1135 def _is_active(port):
1136 # NOTE(vsaienko) With Ironic, instances live on separate hardware
1137 # servers. Neutron does not bind ports for Ironic instances, as a
1138 # result the port remains in the DOWN state. This has been fixed
1139 # with the introduction of the networking-baremetal plugin but
1140 # it's not mandatory (and is not used on all stable branches).
1141 return (port['status'] == 'ACTIVE' or
1142 port.get('binding:vnic_type') == 'baremetal')
1143
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001144 port_map = [(p["id"], fxip["ip_address"])
1145 for p in ports
1146 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +02001147 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
Dmitry Tantsur5c191fa2020-04-14 12:13:09 +02001148 _is_active(p))]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -08001149 inactive = [p for p in ports if p['status'] != 'ACTIVE']
1150 if inactive:
1151 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001152
Masayuki Igawaf9009b42017-04-10 14:49:29 +09001153 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -08001154 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +02001155 self.assertEqual(len(port_map), 1,
1156 "Found multiple IPv4 addresses: %s. "
1157 "Unable to determine which port to target."
1158 % port_map)
1159 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001160
David Shrewsbury9bac3662014-08-07 15:07:01 -04001161 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +08001162 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +01001163 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +02001164 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001165 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -05001166 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -04001167
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301168 def create_floating_ip(self, server, external_network_id=None,
Martin Kopec5e761bf2020-11-03 16:11:08 +00001169 port_id=None, client=None, **kwargs):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +00001170 """Create a floating IP and associates to a resource/port on Neutron"""
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301171
Yair Friedae0e73d2014-11-24 11:56:26 +02001172 if not external_network_id:
1173 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +03001174 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -05001175 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001176 if not port_id:
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301177 port_id, ip4 = self._get_server_port_id_and_ip4(server)
Kirill Shileev14113572014-11-21 16:58:02 +03001178 else:
1179 ip4 = None
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001180
Martin Kopec5e761bf2020-11-03 16:11:08 +00001181 floatingip_kwargs = {
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001182 'floating_network_id': external_network_id,
1183 'port_id': port_id,
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301184 'tenant_id': server.get('project_id') or server['tenant_id'],
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001185 'fixed_ip_address': ip4,
1186 }
1187 if CONF.network.subnet_id:
Martin Kopec5e761bf2020-11-03 16:11:08 +00001188 floatingip_kwargs['subnet_id'] = CONF.network.subnet_id
1189
1190 floatingip_kwargs.update(kwargs)
1191 result = client.create_floatingip(**floatingip_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001192 floating_ip = result['floatingip']
Lukas Piwowarski2385e042020-01-31 12:28:20 +00001193
Jordan Pittier9e227c52016-02-09 14:35:18 +01001194 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +08001195 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -05001196 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001197 return floating_ip
1198
Soniya Vyased664472020-09-23 18:40:25 +05301199 def associate_floating_ip(self, floating_ip, server):
1200 """Associate floating ip
1201
1202 This wrapper utility attaches the floating_ip for
1203 the respective port_id of server
1204 """
1205 port_id, _ = self._get_server_port_id_and_ip4(server)
1206 kwargs = dict(port_id=port_id)
1207 floating_ip = self.floating_ips_client.update_floatingip(
1208 floating_ip['id'], **kwargs)['floatingip']
1209 self.assertEqual(port_id, floating_ip['port_id'])
1210 return floating_ip
1211
1212 def disassociate_floating_ip(self, floating_ip):
1213 """Disassociates floating ip
1214
1215 This wrapper utility disassociates given floating ip.
1216 :param floating_ip: a dict which is a return value of
1217 floating_ips_client.create_floatingip method
1218 """
1219 kwargs = dict(port_id=None)
1220 floating_ip = self.floating_ips_client.update_floatingip(
1221 floating_ip['id'], **kwargs)['floatingip']
1222 self.assertIsNone(floating_ip['port_id'])
1223 return floating_ip
1224
Yair Fried45f92952014-06-26 05:19:19 +03001225 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +00001226 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +03001227
Steve Heyman33735f22016-05-24 09:28:08 -05001228 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +03001229 :param status: target status
1230 :raises: AssertionError if status doesn't match
1231 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301232
Steve Heyman33735f22016-05-24 09:28:08 -05001233 floatingip_id = floating_ip['id']
1234
Carl Baldwina754e2d2014-10-23 22:47:41 +00001235 def refresh():
Martin Kopecf4b5df62020-01-27 09:44:29 +00001236 floating_ip = (self.floating_ips_client.
1237 show_floatingip(floatingip_id)['floatingip'])
1238 if status == floating_ip['status']:
1239 LOG.info("FloatingIP: {fp} is at status: {st}"
1240 .format(fp=floating_ip, st=status))
1241 return status == floating_ip['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +00001242
zhufl4dda94e2017-03-14 16:14:46 +08001243 if not test_utils.call_until_true(refresh,
1244 CONF.network.build_timeout,
1245 CONF.network.build_interval):
1246 floating_ip = self.floating_ips_client.show_floatingip(
1247 floatingip_id)['floatingip']
1248 self.assertEqual(status, floating_ip['status'],
1249 message="FloatingIP: {fp} is at status: {cst}. "
1250 "failed to reach status: {st}"
1251 .format(fp=floating_ip, cst=floating_ip['status'],
1252 st=status))
Yair Fried45f92952014-06-26 05:19:19 +03001253
zhufl420a0192017-09-28 11:04:50 +08001254 def check_tenant_network_connectivity(self, server,
1255 username,
1256 private_key,
1257 should_connect=True,
1258 servers_for_debug=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301259 """Checks tenant network connectivity"""
Sean Dagueed6e5862016-04-04 10:49:13 -04001260 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +03001261 msg = 'Tenant networks not configured to be reachable.'
1262 LOG.info(msg)
1263 return
1264 # The target login is assumed to have been configured for
1265 # key-based authentication by cloud-init.
1266 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +01001267 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +03001268 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +09001269 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +02001270 username,
1271 private_key,
1272 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +03001273 except Exception as e:
1274 LOG.exception('Tenant network connectivity check failed')
1275 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +00001276 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +03001277 raise
1278
zhufle9877c62017-10-13 09:38:19 +08001279 def check_remote_connectivity(self, source, dest, should_succeed=True,
Claudiu Belu33c3e602014-08-28 16:38:01 +03001280 nic=None, protocol='icmp'):
1281 """check server connectivity via source ssh connection
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001282
Claudiu Belu33c3e602014-08-28 16:38:01 +03001283 :param source: RemoteClient: an ssh connection from which to execute
1284 the check
1285 :param dest: an IP to check connectivity against
1286 :param should_succeed: boolean should connection succeed or not
1287 :param nic: specific network interface to test connectivity from
1288 :param protocol: the protocol used to test connectivity with.
1289 :returns: True, if the connection succeeded and it was expected to
1290 succeed. False otherwise.
Yair Fried1fc32a12014-08-04 09:11:30 +03001291 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301292
Claudiu Belu33c3e602014-08-28 16:38:01 +03001293 method_name = '%s_check' % protocol
1294 connectivity_checker = getattr(source, method_name)
1295
1296 def connect_remote():
Yair Fried1fc32a12014-08-04 09:11:30 +03001297 try:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001298 connectivity_checker(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +03001299 except lib_exc.SSHExecCommandFailed:
Claudiu Belu33c3e602014-08-28 16:38:01 +03001300 LOG.warning('Failed to check %(protocol)s connectivity for '
1301 'IP %(dest)s via a ssh connection from: %(src)s.',
1302 dict(protocol=protocol, dest=dest,
1303 src=source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +03001304 return not should_succeed
1305 return should_succeed
1306
Claudiu Belu33c3e602014-08-28 16:38:01 +03001307 result = test_utils.call_until_true(connect_remote,
zhufle9877c62017-10-13 09:38:19 +08001308 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001309 if result:
1310 return
1311
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001312 source_host = source.ssh_client.host
1313 if should_succeed:
1314 msg = "Timed out waiting for %s to become reachable from %s" \
1315 % (dest, source_host)
1316 else:
1317 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001318 self._log_console_output()
1319 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001320
John Warren456d9ae2016-01-12 15:36:33 -05001321 def _create_security_group(self, security_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001322 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001323 namestart='secgroup-smoke',
1324 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001325 if security_group_rules_client is None:
1326 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001327 if security_groups_client is None:
1328 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001329 if project_id is None:
1330 project_id = security_groups_client.project_id
John Warrenf9606e92015-12-10 12:12:42 -05001331 secgroup = self._create_empty_security_group(
1332 namestart=namestart, client=security_groups_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001333 project_id=project_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001334
1335 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -05001336 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001337 security_group_rules_client=security_group_rules_client,
1338 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001339 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001340 for rule in rules:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001341 self.assertEqual(project_id, rule['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001342 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001343 return secgroup
1344
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001345 def _create_empty_security_group(self, client=None, project_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001346 namestart='secgroup-smoke'):
1347 """Create a security group without rules.
1348
1349 Default rules will be created:
1350 - IPv4 egress to any
1351 - IPv6 egress to any
1352
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001353 :param project_id: secgroup will be created in this project
Steve Heyman33735f22016-05-24 09:28:08 -05001354 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001355 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301356
Yair Fried1fc32a12014-08-04 09:11:30 +03001357 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001358 client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001359 if not project_id:
1360 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001361 sg_name = data_utils.rand_name(namestart)
1362 sg_desc = sg_name + " description"
1363 sg_dict = dict(name=sg_name,
1364 description=sg_desc)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001365 sg_dict['project_id'] = project_id
David Kranz34e88122014-12-11 15:24:05 -05001366 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001367
1368 secgroup = result['security_group']
1369 self.assertEqual(secgroup['name'], sg_name)
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001370 self.assertEqual(project_id, secgroup['project_id'])
Steve Heyman33735f22016-05-24 09:28:08 -05001371 self.assertEqual(secgroup['description'], sg_desc)
1372
Jordan Pittier9e227c52016-02-09 14:35:18 +01001373 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001374 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001375 return secgroup
1376
John Warren456d9ae2016-01-12 15:36:33 -05001377 def _create_security_group_rule(self, secgroup=None,
1378 sec_group_rules_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001379 project_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001380 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001381 """Create a rule from a dictionary of rule parameters.
1382
1383 Create a rule in a secgroup. if secgroup not defined will search for
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001384 default secgroup in project_id.
Yair Fried1fc32a12014-08-04 09:11:30 +03001385
Steve Heyman33735f22016-05-24 09:28:08 -05001386 :param secgroup: the security group.
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001387 :param project_id: if secgroup not passed -- the tenant in which to
Yair Fried1fc32a12014-08-04 09:11:30 +03001388 search for default secgroup
1389 :param kwargs: a dictionary containing rule parameters:
1390 for example, to allow incoming ssh:
1391 rule = {
1392 direction: 'ingress'
1393 protocol:'tcp',
1394 port_range_min: 22,
1395 port_range_max: 22
1396 }
1397 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301398
John Warren456d9ae2016-01-12 15:36:33 -05001399 if sec_group_rules_client is None:
1400 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001401 if security_groups_client is None:
1402 security_groups_client = self.security_groups_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001403 if not project_id:
1404 project_id = security_groups_client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001405 if secgroup is None:
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001406 # Get default secgroup for project_id
zhuflb0b272e2017-09-22 16:01:46 +08001407 default_secgroups = security_groups_client.list_security_groups(
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001408 name='default', project_id=project_id)['security_groups']
1409 msg = "No default security group for project %s." % (project_id)
zhuflb0b272e2017-09-22 16:01:46 +08001410 self.assertNotEmpty(default_secgroups, msg)
1411 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001412
Steve Heyman33735f22016-05-24 09:28:08 -05001413 ruleset = dict(security_group_id=secgroup['id'],
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001414 project_id=secgroup['project_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001415 ruleset.update(kwargs)
1416
John Warren456d9ae2016-01-12 15:36:33 -05001417 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001418 sg_rule = sg_rule['security_group_rule']
1419
1420 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1421 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001422
1423 return sg_rule
1424
John Warren456d9ae2016-01-12 15:36:33 -05001425 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1426 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001427 security_groups_client=None):
Martin Kopece1d873a2020-11-02 12:19:54 +00001428 """Create loginable security group rule by neutron clients by default.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001429
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001430 This function will create:
1431 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1432 access for ipv4.
1433 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1434 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001435 """
1436
John Warren456d9ae2016-01-12 15:36:33 -05001437 if security_group_rules_client is None:
1438 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001439 if security_groups_client is None:
1440 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001441 rules = []
1442 rulesets = [
1443 dict(
1444 # ssh
1445 protocol='tcp',
1446 port_range_min=22,
1447 port_range_max=22,
1448 ),
1449 dict(
1450 # ping
1451 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001452 ),
1453 dict(
1454 # ipv6-icmp for ping6
1455 protocol='icmp',
1456 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001457 )
1458 ]
John Warren456d9ae2016-01-12 15:36:33 -05001459 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001460 for ruleset in rulesets:
1461 for r_direction in ['ingress', 'egress']:
1462 ruleset['direction'] = r_direction
1463 try:
1464 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001465 sec_group_rules_client=sec_group_rules_client,
1466 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001467 security_groups_client=security_groups_client,
1468 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001469 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001470 # if rule already exist - skip rule and continue
1471 msg = 'Security group rule already exists'
1472 if msg not in ex._error_string:
1473 raise ex
1474 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001475 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001476 rules.append(sg_rule)
1477
1478 return rules
1479
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001480 def _get_router(self, client=None, project_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001481 """Retrieve a router for the given tenant id.
1482
1483 If a public router has been configured, it will be returned.
1484
1485 If a public router has not been configured, but a public
1486 network has, a tenant router will be created and returned that
1487 routes traffic to the public network.
1488 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301489
Yair Frieddb6c9e92014-08-06 08:53:13 +03001490 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001491 client = self.routers_client
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001492 if not project_id:
1493 project_id = client.project_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001494 router_id = CONF.network.public_router_id
1495 network_id = CONF.network.public_network_id
1496 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001497 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001498 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001499 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001500 router = client.create_router(
1501 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1502 admin_state_up=True,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001503 project_id=project_id,
zhufl3484f992017-10-10 16:18:29 +08001504 external_gateway_info=dict(network_id=network_id))['router']
1505 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1506 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001507 return router
1508 else:
1509 raise Exception("Neither of 'public_router_id' or "
1510 "'public_network_id' has been defined.")
1511
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001512 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001513 routers_client=None, subnets_client=None,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001514 project_id=None, dns_nameservers=None,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001515 port_security_enabled=True, **net_dict):
Yair Fried1fc32a12014-08-04 09:11:30 +03001516 """Create a network with a subnet connected to a router.
1517
David Shrewsbury9bac3662014-08-07 15:07:01 -04001518 The baremetal driver is a special case since all nodes are
1519 on the same shared network.
1520
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001521 :param project_id: id of project to create resources in.
Yair Fried413bf2d2014-11-19 17:07:11 +02001522 :param dns_nameservers: list of dns servers to send to subnet.
Lajos Katonac87a06b2019-01-04 13:21:48 +01001523 :param port_security_enabled: whether or not port_security is enabled
elajkate453fc22019-06-13 15:03:43 +02001524 :param net_dict: a dict containing experimental network information in
Lajos Katonac87a06b2019-01-04 13:21:48 +01001525 a form like this: {'provider:network_type': 'vlan',
1526 'provider:physical_network': 'foo',
1527 'provider:segmentation_id': '42'}
Yair Fried1fc32a12014-08-04 09:11:30 +03001528 :returns: network, subnet, router
1529 """
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301530
Thiago Paiva66cded22016-08-15 14:55:58 -03001531 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001532 # NOTE(Shrews): This exception is for environments where tenant
1533 # credential isolation is available, but network separation is
1534 # not (the current baremetal case). Likely can be removed when
1535 # test account mgmt is reworked:
1536 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001537 if not CONF.compute.fixed_network_name:
1538 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001539 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001540 network = self._get_network_by_name(
1541 CONF.compute.fixed_network_name)
1542 router = None
1543 subnet = None
1544 else:
John Warren94d8faf2015-09-15 12:22:24 -04001545 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001546 networks_client=networks_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001547 project_id=project_id,
Lajos Katonac87a06b2019-01-04 13:21:48 +01001548 port_security_enabled=port_security_enabled,
1549 **net_dict)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001550 router = self._get_router(client=routers_client,
Rodolfo Alonso Hernandezc1449d42020-02-15 13:24:28 +00001551 project_id=project_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001552 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001553 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001554 # use explicit check because empty list is a valid option
1555 if dns_nameservers is not None:
1556 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001557 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001558 if not routers_client:
1559 routers_client = self.routers_client
1560 router_id = router['id']
1561 routers_client.add_router_interface(router_id,
1562 subnet_id=subnet['id'])
1563
1564 # save a cleanup job to remove this association between
1565 # router and subnet
1566 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1567 routers_client.remove_router_interface, router_id,
1568 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001569 return network, subnet, router
1570
1571
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001572class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001573 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001574
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001575 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001576
1577 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001578 def setup_clients(cls):
1579 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001580 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001581 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001582 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001583
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001584 def create_encryption_type(self, client=None, type_id=None, provider=None,
1585 key_size=None, cipher=None,
1586 control_location=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301587 """Creates an encryption type for volume"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001588 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001589 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001590 if not type_id:
1591 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001592 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001593 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001594 client.create_encryption_type(
1595 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001596 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001597
lkuchlan3023e752017-06-08 12:53:13 +03001598 def create_encrypted_volume(self, encryption_provider, volume_type,
1599 key_size=256, cipher='aes-xts-plain64',
1600 control_location='front-end'):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301601 """Creates an encrypted volume"""
lkuchlan3023e752017-06-08 12:53:13 +03001602 volume_type = self.create_volume_type(name=volume_type)
1603 self.create_encryption_type(type_id=volume_type['id'],
1604 provider=encryption_provider,
1605 key_size=key_size,
1606 cipher=cipher,
1607 control_location=control_location)
1608 return self.create_volume(volume_type=volume_type['name'])
1609
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001610
Masayuki Igawa0870db52015-09-18 21:08:36 +09001611class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001612 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001613
1614 Subclasses implement the tests that use the methods provided by this
1615 class.
1616 """
1617
1618 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001619 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001620 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001621 if not CONF.service_available.swift:
1622 skip_msg = ("%s skipped as swift is not available" %
1623 cls.__name__)
1624 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001625
1626 @classmethod
1627 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001628 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001629 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001630 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001631 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001632
1633 @classmethod
1634 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001635 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001636 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001637 cls.account_client = cls.os_operator.account_client
1638 cls.container_client = cls.os_operator.container_client
1639 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001640
Chris Dentde456a12014-09-10 12:41:15 +01001641 def get_swift_stat(self):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301642 """Get swift status for our user account."""
Chris Dent0d494112014-08-26 13:48:30 +01001643 self.account_client.list_account_containers()
1644 LOG.debug('Swift status information obtained successfully')
1645
Chris Dentde456a12014-09-10 12:41:15 +01001646 def create_container(self, container_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301647 """Creates container"""
Chris Dent0d494112014-08-26 13:48:30 +01001648 name = container_name or data_utils.rand_name(
1649 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001650 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001651 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001652 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001653 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001654 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001655 self.container_client.delete_container,
1656 name)
Chris Dent0d494112014-08-26 13:48:30 +01001657 return name
1658
Chris Dentde456a12014-09-10 12:41:15 +01001659 def delete_container(self, container_name):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301660 """Deletes container"""
Chris Dent0d494112014-08-26 13:48:30 +01001661 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001662 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001663
Chris Dentde456a12014-09-10 12:41:15 +01001664 def upload_object_to_container(self, container_name, obj_name=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301665 """Uploads object to container"""
Chris Dent0d494112014-08-26 13:48:30 +01001666 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001667 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001668 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001669 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001670 self.object_client.delete_object,
1671 container_name,
1672 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001673 return obj_name, obj_data
1674
Chris Dentde456a12014-09-10 12:41:15 +01001675 def delete_object(self, container_name, filename):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301676 """Deletes object"""
Chris Dent0d494112014-08-26 13:48:30 +01001677 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001678 self.list_and_check_container_objects(container_name,
1679 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001680
Chris Dentde456a12014-09-10 12:41:15 +01001681 def list_and_check_container_objects(self, container_name,
1682 present_obj=None,
1683 not_present_obj=None):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301684 """List and verify objects for a given container
1685
1686 This utility lists objects for a given container
1687 and asserts which are present and
1688 which are not
1689 """
1690
Ghanshyam2a180b82014-06-16 13:54:22 +09001691 if present_obj is None:
1692 present_obj = []
1693 if not_present_obj is None:
1694 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001695 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001696 container_name)
1697 if present_obj:
1698 for obj in present_obj:
1699 self.assertIn(obj, object_list)
1700 if not_present_obj:
1701 for obj in not_present_obj:
1702 self.assertNotIn(obj, object_list)
1703
Chris Dentde456a12014-09-10 12:41:15 +01001704 def download_and_verify(self, container_name, obj_name, expected_data):
Soniya Vyas0c84f3e2020-07-15 15:20:59 +05301705 """Asserts the object and expected data to verify if they are same"""
Chris Dent0d494112014-08-26 13:48:30 +01001706 _, obj = self.object_client.get_object(container_name, obj_name)
1707 self.assertEqual(obj, expected_data)