blob: d19b4cdd6aa73ecf7068f4a1919458744e053a64 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Jay Pipes13b479b2012-06-11 14:52:27 -04002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
David Kranzcf0040c2012-06-26 09:46:56 -040016import time
Jay Pipesf38eaac2012-06-21 13:37:35 -040017
Doug Hellmann583ce2c2015-03-11 14:55:46 +000018from oslo_log import log as logging
Matthew Treinish01472ff2015-02-20 17:26:52 -050019
Joseph Lanouxb3e1f872015-01-30 11:13:07 +000020from tempest.common import compute
Ken'ichi Ohmichi8b9c7802015-07-08 05:57:37 +000021from tempest.common import waiters
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000022from tempest import config
Sean Dague20e98612016-01-06 14:33:28 -050023from tempest import exceptions
Ghanshyam Mannad8737c2019-04-13 01:12:58 +000024from tempest.lib.common import api_microversion_fixture
Sergey Nikitin8654e5b2017-06-04 22:09:56 +040025from tempest.lib.common import api_version_request
Ghanshyam1f47cf92016-02-25 04:57:18 +090026from tempest.lib.common import api_version_utils
Ken'ichi Ohmichi757833a2017-03-10 10:30:30 -080027from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010028from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050029from tempest.lib import exceptions as lib_exc
Attila Fazekasdc216422013-01-29 15:12:14 +010030import tempest.test
Jay Pipesf38eaac2012-06-21 13:37:35 -040031
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000032CONF = config.CONF
Tiago Melloeda03b52012-08-22 23:47:29 -030033
Jay Pipesf38eaac2012-06-21 13:37:35 -040034LOG = logging.getLogger(__name__)
Daryl Walleckc7251962012-03-12 17:26:54 -050035
36
Ken'ichi Ohmichi4d237e72015-11-05 06:32:33 +000037class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
38 tempest.test.BaseTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -050039 """Base test case class for all Compute API tests."""
Daryl Walleckc7251962012-03-12 17:26:54 -050040
Attila Fazekas430dae32013-10-17 15:19:32 +020041 force_tenant_isolation = False
Eric Friedbfaa50f2020-01-09 12:04:54 -060042 # Set this to True in subclasses to create a default network. See
43 # https://bugs.launchpad.net/tempest/+bug/1844568
44 create_default_network = False
Chris Yeoh8a79b9d2013-01-18 19:32:47 +103045
Andrea Frittolib21de6c2015-02-06 20:12:38 +000046 # TODO(andreaf) We should care also for the alt_manager here
47 # but only once client lazy load in the manager is done
48 credentials = ['primary']
49
Jay Pipesf38eaac2012-06-21 13:37:35 -040050 @classmethod
Emily Hugenbruche7991d92014-12-12 16:53:36 +000051 def skip_checks(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000052 super(BaseV2ComputeTest, cls).skip_checks()
Matthew Treinishf8ff3582015-08-25 12:41:56 -040053 if not CONF.service_available.nova:
54 raise cls.skipException("Nova is not available")
Lee Yarwood4b95d4b2020-01-15 10:49:54 +000055 api_version_utils.check_skip_with_microversion(
56 cls.min_microversion, cls.max_microversion,
57 CONF.compute.min_microversion, CONF.compute.max_microversion)
58 api_version_utils.check_skip_with_microversion(
59 cls.volume_min_microversion, cls.volume_max_microversion,
60 CONF.volume.min_microversion, CONF.volume.max_microversion)
61 api_version_utils.check_skip_with_microversion(
62 cls.placement_min_microversion, cls.placement_max_microversion,
63 CONF.placement.min_microversion, CONF.placement.max_microversion)
Jay Pipesf38eaac2012-06-21 13:37:35 -040064
Emily Hugenbruche7991d92014-12-12 16:53:36 +000065 @classmethod
66 def setup_credentials(cls):
Eric Friedbfaa50f2020-01-09 12:04:54 -060067 # Setting network=True, subnet=True creates a default network
68 cls.set_network_resources(
69 network=cls.create_default_network,
70 subnet=cls.create_default_network)
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000071 super(BaseV2ComputeTest, cls).setup_credentials()
Daryl Walleckc7251962012-03-12 17:26:54 -050072
Emily Hugenbruche7991d92014-12-12 16:53:36 +000073 @classmethod
74 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000075 super(BaseV2ComputeTest, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +020076 cls.servers_client = cls.os_primary.servers_client
77 cls.server_groups_client = cls.os_primary.server_groups_client
78 cls.flavors_client = cls.os_primary.flavors_client
79 cls.compute_images_client = cls.os_primary.compute_images_client
80 cls.extensions_client = cls.os_primary.extensions_client
81 cls.floating_ip_pools_client = cls.os_primary.floating_ip_pools_client
82 cls.floating_ips_client = cls.os_primary.compute_floating_ips_client
83 cls.keypairs_client = cls.os_primary.keypairs_client
John Warren5cdbf422016-01-05 12:42:43 -050084 cls.security_group_rules_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +020085 cls.os_primary.compute_security_group_rules_client)
86 cls.security_groups_client =\
87 cls.os_primary.compute_security_groups_client
88 cls.quotas_client = cls.os_primary.quotas_client
89 cls.compute_networks_client = cls.os_primary.compute_networks_client
90 cls.limits_client = cls.os_primary.limits_client
91 cls.volumes_extensions_client =\
92 cls.os_primary.volumes_extensions_client
93 cls.snapshots_extensions_client =\
94 cls.os_primary.snapshots_extensions_client
95 cls.interfaces_client = cls.os_primary.interfaces_client
96 cls.fixed_ips_client = cls.os_primary.fixed_ips_client
97 cls.availability_zone_client = cls.os_primary.availability_zone_client
98 cls.agents_client = cls.os_primary.agents_client
99 cls.aggregates_client = cls.os_primary.aggregates_client
100 cls.services_client = cls.os_primary.services_client
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000101 cls.instance_usages_audit_log_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200102 cls.os_primary.instance_usages_audit_log_client)
103 cls.hypervisor_client = cls.os_primary.hypervisor_client
104 cls.certificates_client = cls.os_primary.certificates_client
105 cls.migrations_client = cls.os_primary.migrations_client
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000106 cls.security_group_default_rules_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200107 cls.os_primary.security_group_default_rules_client)
108 cls.versions_client = cls.os_primary.compute_versions_client
Andrea Frittolia6b30152017-08-04 10:46:10 +0100109 if CONF.service_available.cinder:
110 cls.volumes_client = cls.os_primary.volumes_client_latest
Lee Yarwood4b108522020-01-15 10:50:24 +0000111 cls.attachments_client = cls.os_primary.attachments_client_latest
Lee Yarwood2ad7ca42020-01-07 13:06:25 +0000112 cls.snapshots_client = cls.os_primary.snapshots_client_latest
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500113 if CONF.service_available.glance:
114 if CONF.image_feature_enabled.api_v1:
115 cls.images_client = cls.os_primary.image_client
116 elif CONF.image_feature_enabled.api_v2:
117 cls.images_client = cls.os_primary.image_client_v2
118 else:
119 raise lib_exc.InvalidConfiguration(
120 'Either api_v1 or api_v2 must be True in '
121 '[image-feature-enabled].')
Matt Riedemann14e5e482018-05-31 15:13:18 -0400122 cls._check_depends_on_nova_network()
123
124 @classmethod
125 def _check_depends_on_nova_network(cls):
126 # Since nova-network APIs were removed from Nova in the Rocky release,
127 # determine, based on the max version from the version document, if
128 # the compute API is >Queens and if so, skip tests that rely on
129 # nova-network.
130 if not getattr(cls, 'depends_on_nova_network', False):
131 return
132 versions = cls.versions_client.list_versions()['versions']
133 # Find the v2.1 version which will tell us our max version for the
134 # compute API we're testing against.
135 for version in versions:
136 if version['id'] == 'v2.1':
137 max_version = api_version_request.APIVersionRequest(
138 version['version'])
139 break
140 else:
141 LOG.warning(
142 'Unable to determine max v2.1 compute API version: %s',
143 versions)
144 return
145
146 # The max compute API version in Queens is 2.60 so we cap
147 # at that version.
148 queens = api_version_request.APIVersionRequest('2.60')
149 if max_version > queens:
150 raise cls.skipException('nova-network is gone')
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300151
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000152 @classmethod
153 def resource_setup(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000154 super(BaseV2ComputeTest, cls).resource_setup()
Ghanshyam05049dd2016-02-12 17:44:48 +0900155 cls.request_microversion = (
156 api_version_utils.select_request_microversion(
157 cls.min_microversion,
ghanshyam29591532016-03-11 17:12:43 +0900158 CONF.compute.min_microversion))
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000159 cls.volume_request_microversion = (
160 api_version_utils.select_request_microversion(
161 cls.volume_min_microversion,
162 CONF.volume.min_microversion))
163 cls.placement_request_microversion = (
164 api_version_utils.select_request_microversion(
165 cls.placement_min_microversion,
166 CONF.placement.min_microversion))
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000167 cls.build_interval = CONF.compute.build_interval
168 cls.build_timeout = CONF.compute.build_timeout
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000169 cls.image_ref = CONF.compute.image_ref
170 cls.image_ref_alt = CONF.compute.image_ref_alt
171 cls.flavor_ref = CONF.compute.flavor_ref
172 cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
lanoux283273b2015-12-04 03:01:54 -0800173 cls.ssh_user = CONF.validation.image_ssh_user
Weronika Sikorac54a9112019-09-18 13:55:07 +0000174 cls.ssh_alt_user = CONF.validation.image_alt_ssh_user
lanoux283273b2015-12-04 03:01:54 -0800175 cls.image_ssh_user = CONF.validation.image_ssh_user
Weronika Sikorac54a9112019-09-18 13:55:07 +0000176 cls.image_alt_ssh_user = CONF.validation.image_alt_ssh_user
lanoux283273b2015-12-04 03:01:54 -0800177 cls.image_ssh_password = CONF.validation.image_ssh_password
Weronika Sikorac54a9112019-09-18 13:55:07 +0000178 cls.image_alt_ssh_password = CONF.validation.image_alt_ssh_password
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900179
Matthew Treinishf7fca6a2013-12-09 16:27:23 +0000180 @classmethod
ghanshyam66b9aed2018-03-30 08:11:10 +0000181 def is_requested_microversion_compatible(cls, max_version):
182 """Check the compatibility of selected request microversion
183
184 This method will check if selected request microversion
185 (cls.request_microversion) for test is compatible with respect
186 to 'max_version'. Compatible means if selected request microversion
187 is in the range(<=) of 'max_version'.
188
189 :param max_version: maximum microversion to compare for compatibility.
190 Example: '2.30'
191 :returns: True if selected request microversion is compatible with
192 'max_version'. False in other case.
193 """
194 try:
195 req_version_obj = api_version_request.APIVersionRequest(
196 cls.request_microversion)
197 # NOTE(gmann): This is case where this method is used before calling
198 # resource_setup(), where cls.request_microversion is set. There may
199 # not be any such case but still we can handle this case.
200 except AttributeError:
201 request_microversion = (
202 api_version_utils.select_request_microversion(
203 cls.min_microversion,
204 CONF.compute.min_microversion))
205 req_version_obj = api_version_request.APIVersionRequest(
206 request_microversion)
207 max_version_obj = api_version_request.APIVersionRequest(max_version)
208 return req_version_obj <= max_version_obj
209
210 @classmethod
Attila Fazekas305e65b2013-10-29 13:23:07 +0100211 def server_check_teardown(cls):
212 """Checks is the shared server clean enough for subsequent test.
Ken'ichi Ohmichi88363cb2015-11-19 08:00:54 +0000213
Attila Fazekas305e65b2013-10-29 13:23:07 +0100214 Method will delete the server when it's dirty.
215 The setUp method is responsible for creating a new server.
216 Exceptions raised in tearDown class are fails the test case,
Marian Horban6afb0232015-11-10 22:47:12 -0500217 This method supposed to use only by tearDown methods, when
Attila Fazekas305e65b2013-10-29 13:23:07 +0100218 the shared server_id is stored in the server_id of the class.
219 """
220 if getattr(cls, 'server_id', None) is not None:
221 try:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000222 waiters.wait_for_server_status(cls.servers_client,
223 cls.server_id, 'ACTIVE')
Attila Fazekas305e65b2013-10-29 13:23:07 +0100224 except Exception as exc:
225 LOG.exception(exc)
226 cls.servers_client.delete_server(cls.server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000227 waiters.wait_for_server_termination(cls.servers_client,
228 cls.server_id)
Attila Fazekas305e65b2013-10-29 13:23:07 +0100229 cls.server_id = None
230 raise
231
232 @classmethod
Joe Gordon8843f0f2015-03-17 15:07:34 -0700233 def create_test_server(cls, validatable=False, volume_backed=False,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400234 validation_resources=None, clients=None, **kwargs):
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000235 """Wrapper utility that returns a test server.
Rohit Karajgidc300b22012-05-04 08:11:00 -0700236
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000237 This wrapper utility calls the common create test server and
238 returns a test server. The purpose of this wrapper is to minimize
239 the impact on the code of the tests already using this
240 function.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700241
242 :param validatable: Whether the server will be pingable or sshable.
243 :param volume_backed: Whether the instance is volume backed or not.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100244 :param validation_resources: Dictionary of validation resources as
245 returned by `get_class_validation_resources`.
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400246 :param clients: Client manager, defaults to os_primary.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100247 :param kwargs: Extra arguments are passed down to the
248 `compute.create_test_server` call.
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000249 """
zhufl7ae22682016-09-18 15:22:33 +0800250 if 'name' not in kwargs:
251 kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400252
253 request_version = api_version_request.APIVersionRequest(
254 cls.request_microversion)
255 v2_37_version = api_version_request.APIVersionRequest('2.37')
256
zhuflff9779c2018-01-04 14:41:40 +0800257 tenant_network = cls.get_tenant_network()
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400258 # NOTE(snikitin): since microversion v2.37 'networks' field is required
zhuflff9779c2018-01-04 14:41:40 +0800259 if (request_version >= v2_37_version and 'networks' not in kwargs and
260 not tenant_network):
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400261 kwargs['networks'] = 'none'
262
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400263 if clients is None:
264 clients = cls.os_primary
265
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000266 body, servers = compute.create_test_server(
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400267 clients,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000268 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100269 validation_resources=validation_resources,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000270 tenant_network=tenant_network,
Joe Gordon8843f0f2015-03-17 15:07:34 -0700271 volume_backed=volume_backed,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000272 **kwargs)
Ken'ichi Ohmichi51c8c262013-12-21 03:30:37 +0900273
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100274 # For each server schedule wait and delete, so we first delete all
275 # and then wait for all
276 for server in servers:
277 cls.addClassResourceCleanup(waiters.wait_for_server_termination,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400278 clients.servers_client, server['id'])
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100279 for server in servers:
280 cls.addClassResourceCleanup(
281 test_utils.call_and_ignore_notfound_exc,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400282 clients.servers_client.delete_server, server['id'])
Sean Dague9b669e32012-12-13 18:40:08 -0500283
David Kranz0fb14292015-02-11 15:55:20 -0500284 return body
Sean Dague9b669e32012-12-13 18:40:08 -0500285
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800286 @classmethod
287 def create_security_group(cls, name=None, description=None):
288 if name is None:
289 name = data_utils.rand_name(cls.__name__ + "-securitygroup")
290 if description is None:
Ken'ichi Ohmichi4937f562015-03-23 00:15:01 +0000291 description = data_utils.rand_name('description')
ghanshyamb610b772015-08-24 17:29:38 +0900292 body = cls.security_groups_client.create_security_group(
293 name=name, description=description)['security_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100294 cls.addClassResourceCleanup(
295 test_utils.call_and_ignore_notfound_exc,
296 cls.security_groups_client.delete_security_group,
297 body['id'])
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800298
David Kranz9964b4e2015-02-06 15:45:29 -0500299 return body
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800300
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530301 @classmethod
Ghanshyam2a180b82014-06-16 13:54:22 +0900302 def create_test_server_group(cls, name="", policy=None):
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530303 if not name:
304 name = data_utils.rand_name(cls.__name__ + "-Server-Group")
Ghanshyam2a180b82014-06-16 13:54:22 +0900305 if policy is None:
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530306 policy = ['affinity']
Ken'ichi Ohmichi1f36daa2015-09-30 01:41:34 +0000307 body = cls.server_groups_client.create_server_group(
308 name=name, policies=policy)['server_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100309 cls.addClassResourceCleanup(
310 test_utils.call_and_ignore_notfound_exc,
311 cls.server_groups_client.delete_server_group,
312 body['id'])
David Kranzae99b9a2015-02-16 13:37:01 -0500313 return body
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530314
David Kranzcf0040c2012-06-26 09:46:56 -0400315 def wait_for(self, condition):
Sean Daguef237ccb2013-01-04 15:19:14 -0500316 """Repeatedly calls condition() until a timeout."""
David Kranzcf0040c2012-06-26 09:46:56 -0400317 start_time = int(time.time())
318 while True:
319 try:
320 condition()
Matthew Treinish05d9fb92012-12-07 16:14:05 -0500321 except Exception:
David Kranzcf0040c2012-06-26 09:46:56 -0400322 pass
323 else:
324 return
325 if int(time.time()) - start_time >= self.build_timeout:
326 condition()
327 return
328 time.sleep(self.build_interval)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400329
Attila Fazekas423834d2014-03-14 17:33:13 +0100330 @classmethod
331 def prepare_instance_network(cls):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000332 if (CONF.validation.auth_method != 'disabled' and
333 CONF.validation.connect_method == 'floating'):
Attila Fazekas423834d2014-03-14 17:33:13 +0100334 cls.set_network_resources(network=True, subnet=True, router=True,
335 dhcp=True)
336
ivan-zhu8f992be2013-07-31 14:56:58 +0800337 @classmethod
338 def create_image_from_server(cls, server_id, **kwargs):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500339 """Wrapper utility that returns an image created from the server.
340
341 If compute microversion >= 2.36, the returned image response will
342 be from the image service API rather than the compute image proxy API.
343 """
zhufl35a694b2017-02-14 17:10:53 +0800344 name = kwargs.pop('name',
345 data_utils.rand_name(cls.__name__ + "-image"))
346 wait_until = kwargs.pop('wait_until', None)
347 wait_for_server = kwargs.pop('wait_for_server', True)
ivan-zhu8f992be2013-07-31 14:56:58 +0800348
zhufl35a694b2017-02-14 17:10:53 +0800349 image = cls.compute_images_client.create_image(server_id, name=name,
350 **kwargs)
Felipe Monteiroe65ec452017-09-26 06:47:03 +0100351 if api_version_utils.compare_version_header_to_response(
352 "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
353 image_id = image['image_id']
354 else:
355 image_id = data_utils.parse_image_id(image.response['location'])
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500356
357 # The compute image proxy APIs were deprecated in 2.35 so
358 # use the images client directly if the API microversion being
359 # used is >=2.36.
zhufl66275c22018-03-28 15:32:14 +0800360 if not cls.is_requested_microversion_compatible('2.35'):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500361 client = cls.images_client
362 else:
363 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100364 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500365 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800366
zhufl35a694b2017-02-14 17:10:53 +0800367 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500368 try:
zhufl66275c22018-03-28 15:32:14 +0800369 wait_until = wait_until.upper()
370 if not cls.is_requested_microversion_compatible('2.35'):
371 wait_until = wait_until.lower()
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500372 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500373 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800374 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500375 # If the image is not found after create_image returned
376 # that means the snapshot failed in nova-compute and nova
377 # deleted the image. There should be a compute fault
378 # recorded with the server in that case, so get the server
379 # and dump some details.
380 server = (
381 cls.servers_client.show_server(server_id)['server'])
382 if 'fault' in server:
383 raise exceptions.SnapshotNotFoundException(
384 server['fault'], image_id=image_id)
385 else:
386 raise exceptions.SnapshotNotFoundException(
387 image_id=image_id)
388 else:
389 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500390 image = client.show_image(image_id)
391 # Compute image client returns response wrapped in 'image' element
392 # which is not the case with Glance image client.
393 if 'image' in image:
394 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800395
zhufl35a694b2017-02-14 17:10:53 +0800396 if wait_until.upper() == 'ACTIVE':
397 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000398 waiters.wait_for_server_status(cls.servers_client,
399 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500400 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800401
402 @classmethod
zhuflba0e5532017-09-13 10:51:07 +0800403 def recreate_server(cls, server_id, validatable=False, **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100404 """Destroy an existing class level server and creates a new one
405
406 Some test classes use a test server that can be used by multiple
407 tests. This is done to optimise runtime and test load.
408 If something goes wrong with the test server, it can be rebuilt
409 using this helper.
410
411 This helper can also be used for the initial provisioning if no
412 server_id is specified.
413
414 :param server_id: UUID of the server to be rebuilt. If None is
415 specified, a new server is provisioned.
416 :param validatable: whether to the server needs to be
417 validatable. When True, validation resources are acquired via
418 the `get_class_validation_resources` helper.
419 :param kwargs: extra paramaters are passed through to the
420 `create_test_server` call.
421 :return: the UUID of the created server.
422 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000423 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800424 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000425
Ghanshyam3390d9f2015-12-25 12:48:02 +0900426 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000427 server = cls.create_test_server(
428 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100429 validation_resources=cls.get_class_validation_resources(
430 cls.os_primary),
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000431 wait_until='ACTIVE',
Ghanshyam3390d9f2015-12-25 12:48:02 +0900432 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000433 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000434 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800435
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800436 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700437 def delete_server(cls, server_id):
438 """Deletes an existing server and waits for it to be gone."""
439 try:
440 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000441 waiters.wait_for_server_termination(cls.servers_client,
442 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700443 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100444 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700445
zhuflbcb71172018-03-29 13:49:31 +0800446 def resize_server(self, server_id, new_flavor_id, **kwargs):
zhufl3d018b02016-11-25 16:43:04 +0800447 """resize and confirm_resize an server, waits for it to be ACTIVE."""
zhuflbcb71172018-03-29 13:49:31 +0800448 self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
449 waiters.wait_for_server_status(self.servers_client, server_id,
zhufl3d018b02016-11-25 16:43:04 +0800450 'VERIFY_RESIZE')
zhuflbcb71172018-03-29 13:49:31 +0800451 self.servers_client.confirm_resize_server(server_id)
452 waiters.wait_for_server_status(
453 self.servers_client, server_id, 'ACTIVE')
454 server = self.servers_client.show_server(server_id)['server']
455 self.assert_flavor_equal(new_flavor_id, server['flavor'])
zhufl3d018b02016-11-25 16:43:04 +0800456
457 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800458 def delete_volume(cls, volume_id):
459 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800460 try:
461 cls.volumes_client.delete_volume(volume_id)
462 # TODO(mriedem): We should move the wait_for_resource_deletion
463 # into the delete_volume method as a convenience to the caller.
464 cls.volumes_client.wait_for_resource_deletion(volume_id)
465 except lib_exc.NotFound:
466 LOG.warning("Unable to delete volume '%s' since it was not found. "
467 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900468
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000469 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100470 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000471 """Get the server fixed or floating IP.
472
Sean Dague20e98612016-01-06 14:33:28 -0500473 Based on the configuration we're in, return a correct ip
474 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100475
476 :param server: The server dict as returned by the API
477 :param validation_resources: The dict of validation resources
478 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000479 """
480 if CONF.validation.connect_method == 'floating':
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100481 if validation_resources:
482 return validation_resources['floating_ip']['ip']
483 else:
484 msg = ('When validation.connect_method equals floating, '
485 'validation_resources cannot be None')
Masayuki Igawa2efbea12019-01-29 18:46:38 +0900486 raise lib_exc.InvalidParam(invalid_param=msg)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000487 elif CONF.validation.connect_method == 'fixed':
Sean Dague20e98612016-01-06 14:33:28 -0500488 addresses = server['addresses'][CONF.validation.network_for_ssh]
489 for address in addresses:
490 if address['version'] == CONF.validation.ip_version_for_ssh:
491 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800492 raise exceptions.ServerUnreachable(server_id=server['id'])
Sean Dague20e98612016-01-06 14:33:28 -0500493 else:
guo yunxianffc4fc02016-11-15 09:56:08 +0800494 raise lib_exc.InvalidConfiguration()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000495
Ghanshyam05049dd2016-02-12 17:44:48 +0900496 def setUp(self):
497 super(BaseV2ComputeTest, self).setUp()
498 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000499 compute_microversion=self.request_microversion,
500 volume_microversion=self.volume_request_microversion,
501 placement_microversion=self.placement_request_microversion))
Ghanshyam05049dd2016-02-12 17:44:48 +0900502
Matt Riedemann342b37c2016-09-21 15:38:12 -0400503 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800504 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400505 """Create a volume and wait for it to become 'available'.
506
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000507 :param image_ref: Specify an image id to create a bootable volume.
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600508 :param kwargs: other parameters to create volume.
Matt Riedemann342b37c2016-09-21 15:38:12 -0400509 :returns: The available volume.
510 """
zhufl8d23f922016-12-12 17:29:42 +0800511 if 'size' not in kwargs:
512 kwargs['size'] = CONF.volume.volume_size
513 if 'display_name' not in kwargs:
514 vol_name = data_utils.rand_name(cls.__name__ + '-volume')
515 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000516 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800517 kwargs['imageRef'] = image_ref
Martin Kopec00e6d6c2019-06-05 14:30:06 +0000518 if CONF.compute.compute_volume_common_az:
519 kwargs.setdefault('availability_zone',
520 CONF.compute.compute_volume_common_az)
zhufl8d23f922016-12-12 17:29:42 +0800521 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100522 cls.addClassResourceCleanup(
523 cls.volumes_client.wait_for_resource_deletion, volume['id'])
524 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
525 cls.volumes_client.delete_volume,
526 volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200527 waiters.wait_for_volume_resource_status(cls.volumes_client,
528 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400529 return volume
530
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400531 def _detach_volume(self, server, volume):
532 """Helper method to detach a volume.
533
534 Ignores 404 responses if the volume or server do not exist, or the
535 volume is already detached from the server.
536 """
537 try:
538 volume = self.volumes_client.show_volume(volume['id'])['volume']
539 # Check the status. You can only detach an in-use volume, otherwise
540 # the compute API will return a 400 response.
541 if volume['status'] == 'in-use':
542 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600543 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400544 # Ignore 404s on detach in case the server is deleted or the volume
545 # is already detached.
546 pass
547
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400548 def attach_volume(self, server, volume, device=None, tag=None):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400549 """Attaches volume to server and waits for 'in-use' volume status.
550
551 The volume will be detached when the test tears down.
552
553 :param server: The server to which the volume will be attached.
554 :param volume: The volume to attach.
555 :param device: Optional mountpoint for the attached volume. Note that
556 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400557 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400558 """
559 attach_kwargs = dict(volumeId=volume['id'])
560 if device:
561 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400562 if tag:
563 attach_kwargs['tag'] = tag
564
zhufl36f0a972017-02-28 15:43:33 +0800565 attachment = self.servers_client.attach_volume(
566 server['id'], **attach_kwargs)['volumeAttachment']
Lee Yarwoodf644baa2020-01-08 19:03:05 +0000567 # On teardown detach the volume and for multiattach volumes wait for
568 # the attachment to be removed. For non-multiattach volumes wait for
569 # the state of the volume to change to available. This is so we don't
570 # error out when trying to delete the volume during teardown.
571 if volume['multiattach']:
Peter Penchev5c243a92020-09-06 02:26:03 +0300572 att = waiters.wait_for_volume_attachment_create(
573 self.volumes_client, volume['id'], server['id'])
Lee Yarwoodf644baa2020-01-08 19:03:05 +0000574 self.addCleanup(waiters.wait_for_volume_attachment_remove,
575 self.volumes_client, volume['id'],
Peter Penchev5c243a92020-09-06 02:26:03 +0300576 att['attachment_id'])
Lee Yarwoodf644baa2020-01-08 19:03:05 +0000577 else:
578 self.addCleanup(waiters.wait_for_volume_resource_status,
579 self.volumes_client, volume['id'], 'available')
Peter Penchev5c243a92020-09-06 02:26:03 +0300580 waiters.wait_for_volume_resource_status(self.volumes_client,
581 volume['id'], 'in-use')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400582 # Ignore 404s on detach in case the server is deleted or the volume
583 # is already detached.
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400584 self.addCleanup(self._detach_volume, server, volume)
zhufl36f0a972017-02-28 15:43:33 +0800585 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400586
Lee Yarwood2ad7ca42020-01-07 13:06:25 +0000587 def create_volume_snapshot(self, volume_id, name=None, description=None,
588 metadata=None, force=False):
589 name = name or data_utils.rand_name(
590 self.__class__.__name__ + '-snapshot')
591 snapshot = self.snapshots_client.create_snapshot(
592 volume_id=volume_id,
593 force=force,
594 display_name=name,
595 description=description,
596 metadata=metadata)['snapshot']
597 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
598 snapshot['id'])
599 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
600 waiters.wait_for_volume_resource_status(self.snapshots_client,
601 snapshot['id'], 'available')
602 snapshot = self.snapshots_client.show_snapshot(
603 snapshot['id'])['snapshot']
604 return snapshot
605
zhuflbcb71172018-03-29 13:49:31 +0800606 def assert_flavor_equal(self, flavor_id, server_flavor):
607 """Check whether server_flavor equals to flavor.
608
609 :param flavor_id: flavor id
610 :param server_flavor: flavor info returned by show_server.
611 """
612 # Nova API > 2.46 no longer includes flavor.id, and schema check
613 # will cover whether 'id' should be in flavor
614 if server_flavor.get('id'):
615 msg = ('server flavor is not same as flavor!')
616 self.assertEqual(flavor_id, server_flavor['id'], msg)
617 else:
618 flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
619 self.assertEqual(flavor['name'], server_flavor['original_name'],
620 "original_name in server flavor is not same as "
621 "flavor name!")
622 for key in ['ram', 'vcpus', 'disk']:
623 msg = ('attribute %s in server flavor is not same as '
624 'flavor!' % key)
625 self.assertEqual(flavor[key], server_flavor[key], msg)
626
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900627
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000628class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900629 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800630
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000631 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000632
633 @classmethod
634 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000635 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000636 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200637 cls.os_admin.availability_zone_client)
638 cls.admin_flavors_client = cls.os_admin.flavors_client
639 cls.admin_servers_client = cls.os_admin.servers_client
zhufl36eeab02017-01-18 11:49:04 +0800640
641 def create_flavor(self, ram, vcpus, disk, name=None,
642 is_public='True', **kwargs):
643 if name is None:
644 name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
645 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
646 client = self.admin_flavors_client
647 flavor = client.create_flavor(
648 ram=ram, vcpus=vcpus, disk=disk, name=name,
649 id=id, is_public=is_public, **kwargs)['flavor']
650 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
651 self.addCleanup(client.delete_flavor, flavor['id'])
652 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700653
zhufl7bc916d2018-08-22 14:47:39 +0800654 @classmethod
655 def get_host_for_server(cls, server_id):
656 server_details = cls.admin_servers_client.show_server(server_id)
Duc Truong09941202017-06-07 10:15:20 -0700657 return server_details['server']['OS-EXT-SRV-ATTR:host']
658
659 def get_host_other_than(self, server_id):
660 source_host = self.get_host_for_server(server_id)
661
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300662 svcs = self.os_admin.services_client.list_services(
663 binary='nova-compute')['services']
Marian Krcmarik6e3f99e2020-02-26 23:27:51 +0100664 hosts = []
665 for svc in svcs:
666 if svc['state'] == 'up' and svc['status'] == 'enabled':
667 if CONF.compute.compute_volume_common_az:
668 if svc['zone'] == CONF.compute.compute_volume_common_az:
669 hosts.append(svc['host'])
670 else:
671 hosts.append(svc['host'])
Duc Truong09941202017-06-07 10:15:20 -0700672
673 for target_host in hosts:
674 if source_host != target_host:
675 return target_host