blob: 2557e478472f405d56b7e81914e0e645d23a75b7 [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
Sergey Nikitin8654e5b2017-06-04 22:09:56 +040024from tempest.lib.common import api_version_request
Ghanshyam1f47cf92016-02-25 04:57:18 +090025from tempest.lib.common import api_version_utils
Ken'ichi Ohmichi757833a2017-03-10 10:30:30 -080026from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010027from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050028from tempest.lib import exceptions as lib_exc
Attila Fazekasdc216422013-01-29 15:12:14 +010029import tempest.test
Jay Pipesf38eaac2012-06-21 13:37:35 -040030
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000031CONF = config.CONF
Tiago Melloeda03b52012-08-22 23:47:29 -030032
Jay Pipesf38eaac2012-06-21 13:37:35 -040033LOG = logging.getLogger(__name__)
Daryl Walleckc7251962012-03-12 17:26:54 -050034
35
Ken'ichi Ohmichi4d237e72015-11-05 06:32:33 +000036class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
37 tempest.test.BaseTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -050038 """Base test case class for all Compute API tests."""
Daryl Walleckc7251962012-03-12 17:26:54 -050039
Attila Fazekas430dae32013-10-17 15:19:32 +020040 force_tenant_isolation = False
Eric Friedbfaa50f2020-01-09 12:04:54 -060041 # Set this to True in subclasses to create a default network. See
42 # https://bugs.launchpad.net/tempest/+bug/1844568
43 create_default_network = False
Chris Yeoh8a79b9d2013-01-18 19:32:47 +103044
Andrea Frittolib21de6c2015-02-06 20:12:38 +000045 # TODO(andreaf) We should care also for the alt_manager here
46 # but only once client lazy load in the manager is done
47 credentials = ['primary']
48
Jay Pipesf38eaac2012-06-21 13:37:35 -040049 @classmethod
Emily Hugenbruche7991d92014-12-12 16:53:36 +000050 def skip_checks(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000051 super(BaseV2ComputeTest, cls).skip_checks()
Matthew Treinishf8ff3582015-08-25 12:41:56 -040052 if not CONF.service_available.nova:
53 raise cls.skipException("Nova is not available")
Ghanshyam Mannfd90dac2023-08-03 16:26:44 -070054 if cls.create_default_network and not CONF.service_available.neutron:
55 raise cls.skipException("Neutron is not available")
56
Lee Yarwood4b95d4b2020-01-15 10:49:54 +000057 api_version_utils.check_skip_with_microversion(
58 cls.min_microversion, cls.max_microversion,
59 CONF.compute.min_microversion, CONF.compute.max_microversion)
60 api_version_utils.check_skip_with_microversion(
61 cls.volume_min_microversion, cls.volume_max_microversion,
62 CONF.volume.min_microversion, CONF.volume.max_microversion)
63 api_version_utils.check_skip_with_microversion(
64 cls.placement_min_microversion, cls.placement_max_microversion,
65 CONF.placement.min_microversion, CONF.placement.max_microversion)
Jay Pipesf38eaac2012-06-21 13:37:35 -040066
Emily Hugenbruche7991d92014-12-12 16:53:36 +000067 @classmethod
68 def setup_credentials(cls):
Eric Friedbfaa50f2020-01-09 12:04:54 -060069 # Setting network=True, subnet=True creates a default network
70 cls.set_network_resources(
71 network=cls.create_default_network,
Lee Yarwooddb2f5612021-11-12 13:03:57 +000072 subnet=cls.create_default_network,
73 router=cls.create_default_network,
74 dhcp=cls.create_default_network)
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000075 super(BaseV2ComputeTest, cls).setup_credentials()
Daryl Walleckc7251962012-03-12 17:26:54 -050076
Emily Hugenbruche7991d92014-12-12 16:53:36 +000077 @classmethod
78 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000079 super(BaseV2ComputeTest, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +020080 cls.servers_client = cls.os_primary.servers_client
81 cls.server_groups_client = cls.os_primary.server_groups_client
82 cls.flavors_client = cls.os_primary.flavors_client
83 cls.compute_images_client = cls.os_primary.compute_images_client
84 cls.extensions_client = cls.os_primary.extensions_client
Jordan Pittier8160d312017-04-18 11:52:23 +020085 cls.floating_ips_client = cls.os_primary.compute_floating_ips_client
86 cls.keypairs_client = cls.os_primary.keypairs_client
John Warren5cdbf422016-01-05 12:42:43 -050087 cls.security_group_rules_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +020088 cls.os_primary.compute_security_group_rules_client)
89 cls.security_groups_client =\
90 cls.os_primary.compute_security_groups_client
91 cls.quotas_client = cls.os_primary.quotas_client
92 cls.compute_networks_client = cls.os_primary.compute_networks_client
93 cls.limits_client = cls.os_primary.limits_client
94 cls.volumes_extensions_client =\
95 cls.os_primary.volumes_extensions_client
96 cls.snapshots_extensions_client =\
97 cls.os_primary.snapshots_extensions_client
98 cls.interfaces_client = cls.os_primary.interfaces_client
Jordan Pittier8160d312017-04-18 11:52:23 +020099 cls.availability_zone_client = cls.os_primary.availability_zone_client
100 cls.agents_client = cls.os_primary.agents_client
101 cls.aggregates_client = cls.os_primary.aggregates_client
102 cls.services_client = cls.os_primary.services_client
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000103 cls.instance_usages_audit_log_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200104 cls.os_primary.instance_usages_audit_log_client)
105 cls.hypervisor_client = cls.os_primary.hypervisor_client
106 cls.certificates_client = cls.os_primary.certificates_client
107 cls.migrations_client = cls.os_primary.migrations_client
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000108 cls.security_group_default_rules_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200109 cls.os_primary.security_group_default_rules_client)
110 cls.versions_client = cls.os_primary.compute_versions_client
Andrea Frittolia6b30152017-08-04 10:46:10 +0100111 if CONF.service_available.cinder:
112 cls.volumes_client = cls.os_primary.volumes_client_latest
Lee Yarwood4b108522020-01-15 10:50:24 +0000113 cls.attachments_client = cls.os_primary.attachments_client_latest
Lee Yarwood2ad7ca42020-01-07 13:06:25 +0000114 cls.snapshots_client = cls.os_primary.snapshots_client_latest
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500115 if CONF.service_available.glance:
Ghanshyam Mann3562cd02023-08-05 17:22:03 -0700116 if CONF.image_feature_enabled.api_v2:
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500117 cls.images_client = cls.os_primary.image_client_v2
118 else:
119 raise lib_exc.InvalidConfiguration(
Ghanshyam Mann3562cd02023-08-05 17:22:03 -0700120 'api_v2 must be True in [image-feature-enabled].')
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300121
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000122 @classmethod
123 def resource_setup(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000124 super(BaseV2ComputeTest, cls).resource_setup()
Ghanshyam05049dd2016-02-12 17:44:48 +0900125 cls.request_microversion = (
126 api_version_utils.select_request_microversion(
127 cls.min_microversion,
ghanshyam29591532016-03-11 17:12:43 +0900128 CONF.compute.min_microversion))
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000129 cls.volume_request_microversion = (
130 api_version_utils.select_request_microversion(
131 cls.volume_min_microversion,
132 CONF.volume.min_microversion))
133 cls.placement_request_microversion = (
134 api_version_utils.select_request_microversion(
135 cls.placement_min_microversion,
136 CONF.placement.min_microversion))
Ghanshyam Mann18b45d72021-12-07 12:37:29 -0600137 cls.setup_api_microversion_fixture(
138 compute_microversion=cls.request_microversion,
139 volume_microversion=cls.volume_request_microversion,
140 placement_microversion=cls.placement_request_microversion)
141
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000142 cls.build_interval = CONF.compute.build_interval
143 cls.build_timeout = CONF.compute.build_timeout
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000144 cls.image_ref = CONF.compute.image_ref
145 cls.image_ref_alt = CONF.compute.image_ref_alt
146 cls.flavor_ref = CONF.compute.flavor_ref
147 cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
lanoux283273b2015-12-04 03:01:54 -0800148 cls.ssh_user = CONF.validation.image_ssh_user
Weronika Sikorac54a9112019-09-18 13:55:07 +0000149 cls.ssh_alt_user = CONF.validation.image_alt_ssh_user
lanoux283273b2015-12-04 03:01:54 -0800150 cls.image_ssh_user = CONF.validation.image_ssh_user
Weronika Sikorac54a9112019-09-18 13:55:07 +0000151 cls.image_alt_ssh_user = CONF.validation.image_alt_ssh_user
lanoux283273b2015-12-04 03:01:54 -0800152 cls.image_ssh_password = CONF.validation.image_ssh_password
Weronika Sikorac54a9112019-09-18 13:55:07 +0000153 cls.image_alt_ssh_password = CONF.validation.image_alt_ssh_password
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900154
Matthew Treinishf7fca6a2013-12-09 16:27:23 +0000155 @classmethod
ghanshyam66b9aed2018-03-30 08:11:10 +0000156 def is_requested_microversion_compatible(cls, max_version):
157 """Check the compatibility of selected request microversion
158
159 This method will check if selected request microversion
160 (cls.request_microversion) for test is compatible with respect
161 to 'max_version'. Compatible means if selected request microversion
162 is in the range(<=) of 'max_version'.
163
164 :param max_version: maximum microversion to compare for compatibility.
165 Example: '2.30'
166 :returns: True if selected request microversion is compatible with
167 'max_version'. False in other case.
168 """
169 try:
170 req_version_obj = api_version_request.APIVersionRequest(
171 cls.request_microversion)
172 # NOTE(gmann): This is case where this method is used before calling
173 # resource_setup(), where cls.request_microversion is set. There may
174 # not be any such case but still we can handle this case.
175 except AttributeError:
176 request_microversion = (
177 api_version_utils.select_request_microversion(
178 cls.min_microversion,
179 CONF.compute.min_microversion))
180 req_version_obj = api_version_request.APIVersionRequest(
181 request_microversion)
182 max_version_obj = api_version_request.APIVersionRequest(max_version)
183 return req_version_obj <= max_version_obj
184
185 @classmethod
Attila Fazekas305e65b2013-10-29 13:23:07 +0100186 def server_check_teardown(cls):
187 """Checks is the shared server clean enough for subsequent test.
Ken'ichi Ohmichi88363cb2015-11-19 08:00:54 +0000188
Attila Fazekas305e65b2013-10-29 13:23:07 +0100189 Method will delete the server when it's dirty.
190 The setUp method is responsible for creating a new server.
191 Exceptions raised in tearDown class are fails the test case,
Marian Horban6afb0232015-11-10 22:47:12 -0500192 This method supposed to use only by tearDown methods, when
Attila Fazekas305e65b2013-10-29 13:23:07 +0100193 the shared server_id is stored in the server_id of the class.
194 """
195 if getattr(cls, 'server_id', None) is not None:
196 try:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000197 waiters.wait_for_server_status(cls.servers_client,
198 cls.server_id, 'ACTIVE')
Attila Fazekas305e65b2013-10-29 13:23:07 +0100199 except Exception as exc:
200 LOG.exception(exc)
201 cls.servers_client.delete_server(cls.server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000202 waiters.wait_for_server_termination(cls.servers_client,
203 cls.server_id)
Attila Fazekas305e65b2013-10-29 13:23:07 +0100204 cls.server_id = None
205 raise
206
207 @classmethod
Joe Gordon8843f0f2015-03-17 15:07:34 -0700208 def create_test_server(cls, validatable=False, volume_backed=False,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400209 validation_resources=None, clients=None, **kwargs):
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000210 """Wrapper utility that returns a test server.
Rohit Karajgidc300b22012-05-04 08:11:00 -0700211
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000212 This wrapper utility calls the common create test server and
213 returns a test server. The purpose of this wrapper is to minimize
214 the impact on the code of the tests already using this
215 function.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700216
217 :param validatable: Whether the server will be pingable or sshable.
218 :param volume_backed: Whether the instance is volume backed or not.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100219 :param validation_resources: Dictionary of validation resources as
220 returned by `get_class_validation_resources`.
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400221 :param clients: Client manager, defaults to os_primary.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100222 :param kwargs: Extra arguments are passed down to the
223 `compute.create_test_server` call.
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000224 """
zhufl7ae22682016-09-18 15:22:33 +0800225 if 'name' not in kwargs:
Martin Kopec213d0a42023-11-30 10:28:14 +0100226 kwargs['name'] = data_utils.rand_name(
227 prefix=CONF.resource_name_prefix,
228 name=cls.__name__ + "-server")
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400229
230 request_version = api_version_request.APIVersionRequest(
231 cls.request_microversion)
232 v2_37_version = api_version_request.APIVersionRequest('2.37')
233
zhuflff9779c2018-01-04 14:41:40 +0800234 tenant_network = cls.get_tenant_network()
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400235 # NOTE(snikitin): since microversion v2.37 'networks' field is required
zhuflff9779c2018-01-04 14:41:40 +0800236 if (request_version >= v2_37_version and 'networks' not in kwargs and
237 not tenant_network):
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400238 kwargs['networks'] = 'none'
239
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400240 if clients is None:
241 clients = cls.os_primary
242
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000243 body, servers = compute.create_test_server(
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400244 clients,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000245 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100246 validation_resources=validation_resources,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000247 tenant_network=tenant_network,
Joe Gordon8843f0f2015-03-17 15:07:34 -0700248 volume_backed=volume_backed,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000249 **kwargs)
Ken'ichi Ohmichi51c8c262013-12-21 03:30:37 +0900250
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100251 # For each server schedule wait and delete, so we first delete all
252 # and then wait for all
253 for server in servers:
254 cls.addClassResourceCleanup(waiters.wait_for_server_termination,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400255 clients.servers_client, server['id'])
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100256 for server in servers:
257 cls.addClassResourceCleanup(
258 test_utils.call_and_ignore_notfound_exc,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400259 clients.servers_client.delete_server, server['id'])
Sean Dague9b669e32012-12-13 18:40:08 -0500260
David Kranz0fb14292015-02-11 15:55:20 -0500261 return body
Sean Dague9b669e32012-12-13 18:40:08 -0500262
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800263 @classmethod
264 def create_security_group(cls, name=None, description=None):
Martin Kopec213d0a42023-11-30 10:28:14 +0100265 prefix = CONF.resource_name_prefix
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800266 if name is None:
Martin Kopec213d0a42023-11-30 10:28:14 +0100267 name = data_utils.rand_name(
268 prefix=prefix, name=cls.__name__ + "-securitygroup")
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800269 if description is None:
Martin Kopec213d0a42023-11-30 10:28:14 +0100270 description = data_utils.rand_name(
271 prefix=prefix, name='description')
ghanshyamb610b772015-08-24 17:29:38 +0900272 body = cls.security_groups_client.create_security_group(
273 name=name, description=description)['security_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100274 cls.addClassResourceCleanup(
275 test_utils.call_and_ignore_notfound_exc,
276 cls.security_groups_client.delete_security_group,
277 body['id'])
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800278
David Kranz9964b4e2015-02-06 15:45:29 -0500279 return body
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800280
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530281 @classmethod
Ghanshyam2a180b82014-06-16 13:54:22 +0900282 def create_test_server_group(cls, name="", policy=None):
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530283 if not name:
Martin Kopec213d0a42023-11-30 10:28:14 +0100284 name = data_utils.rand_name(
285 prefix=CONF.resource_name_prefix,
286 name=cls.__name__ + "-Server-Group")
zhufled6d1022020-06-05 16:04:49 +0800287 if cls.is_requested_microversion_compatible('2.63'):
288 policy = policy or ['affinity']
289 if not isinstance(policy, list):
290 policy = [policy]
291 kwargs = {'policies': policy}
292 else:
293 policy = policy or 'affinity'
294 if isinstance(policy, list):
295 policy = policy[0]
296 kwargs = {'policy': policy}
Ken'ichi Ohmichi1f36daa2015-09-30 01:41:34 +0000297 body = cls.server_groups_client.create_server_group(
zhufled6d1022020-06-05 16:04:49 +0800298 name=name, **kwargs)['server_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100299 cls.addClassResourceCleanup(
300 test_utils.call_and_ignore_notfound_exc,
301 cls.server_groups_client.delete_server_group,
302 body['id'])
David Kranzae99b9a2015-02-16 13:37:01 -0500303 return body
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530304
Ghanshyam Mann0208fc02023-07-27 11:57:31 -0700305 def wait_for(self, condition, *args):
Sean Daguef237ccb2013-01-04 15:19:14 -0500306 """Repeatedly calls condition() until a timeout."""
David Kranzcf0040c2012-06-26 09:46:56 -0400307 start_time = int(time.time())
308 while True:
309 try:
Ghanshyam Mann0208fc02023-07-27 11:57:31 -0700310 condition(*args)
Matthew Treinish05d9fb92012-12-07 16:14:05 -0500311 except Exception:
David Kranzcf0040c2012-06-26 09:46:56 -0400312 pass
313 else:
314 return
315 if int(time.time()) - start_time >= self.build_timeout:
Ghanshyam Mann0208fc02023-07-27 11:57:31 -0700316 condition(*args)
David Kranzcf0040c2012-06-26 09:46:56 -0400317 return
318 time.sleep(self.build_interval)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400319
Attila Fazekas423834d2014-03-14 17:33:13 +0100320 @classmethod
321 def prepare_instance_network(cls):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000322 if (CONF.validation.auth_method != 'disabled' and
323 CONF.validation.connect_method == 'floating'):
Attila Fazekas423834d2014-03-14 17:33:13 +0100324 cls.set_network_resources(network=True, subnet=True, router=True,
325 dhcp=True)
326
ivan-zhu8f992be2013-07-31 14:56:58 +0800327 @classmethod
328 def create_image_from_server(cls, server_id, **kwargs):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500329 """Wrapper utility that returns an image created from the server.
330
331 If compute microversion >= 2.36, the returned image response will
332 be from the image service API rather than the compute image proxy API.
333 """
Martin Kopec213d0a42023-11-30 10:28:14 +0100334 name = kwargs.pop(
335 'name',
336 data_utils.rand_name(
337 prefix=CONF.resource_name_prefix,
338 name=cls.__name__ + "-image"))
zhufl35a694b2017-02-14 17:10:53 +0800339 wait_until = kwargs.pop('wait_until', None)
340 wait_for_server = kwargs.pop('wait_for_server', True)
ivan-zhu8f992be2013-07-31 14:56:58 +0800341
zhufl35a694b2017-02-14 17:10:53 +0800342 image = cls.compute_images_client.create_image(server_id, name=name,
343 **kwargs)
Felipe Monteiroe65ec452017-09-26 06:47:03 +0100344 if api_version_utils.compare_version_header_to_response(
345 "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
346 image_id = image['image_id']
347 else:
348 image_id = data_utils.parse_image_id(image.response['location'])
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500349
350 # The compute image proxy APIs were deprecated in 2.35 so
351 # use the images client directly if the API microversion being
352 # used is >=2.36.
zhufl66275c22018-03-28 15:32:14 +0800353 if not cls.is_requested_microversion_compatible('2.35'):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500354 client = cls.images_client
355 else:
356 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100357 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500358 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800359
zhufl35a694b2017-02-14 17:10:53 +0800360 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500361 try:
zhufl66275c22018-03-28 15:32:14 +0800362 wait_until = wait_until.upper()
363 if not cls.is_requested_microversion_compatible('2.35'):
364 wait_until = wait_until.lower()
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500365 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500366 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800367 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500368 # If the image is not found after create_image returned
369 # that means the snapshot failed in nova-compute and nova
370 # deleted the image. There should be a compute fault
371 # recorded with the server in that case, so get the server
372 # and dump some details.
373 server = (
374 cls.servers_client.show_server(server_id)['server'])
375 if 'fault' in server:
376 raise exceptions.SnapshotNotFoundException(
377 server['fault'], image_id=image_id)
378 else:
379 raise exceptions.SnapshotNotFoundException(
380 image_id=image_id)
381 else:
382 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500383 image = client.show_image(image_id)
384 # Compute image client returns response wrapped in 'image' element
385 # which is not the case with Glance image client.
386 if 'image' in image:
387 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800388
zhufl35a694b2017-02-14 17:10:53 +0800389 if wait_until.upper() == 'ACTIVE':
390 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000391 waiters.wait_for_server_status(cls.servers_client,
392 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500393 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800394
395 @classmethod
Artom Lifshitz4fc47f62022-05-05 14:17:27 -0400396 def recreate_server(cls, server_id, validatable=False, wait_until='ACTIVE',
397 **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100398 """Destroy an existing class level server and creates a new one
399
400 Some test classes use a test server that can be used by multiple
401 tests. This is done to optimise runtime and test load.
402 If something goes wrong with the test server, it can be rebuilt
403 using this helper.
404
405 This helper can also be used for the initial provisioning if no
406 server_id is specified.
407
408 :param server_id: UUID of the server to be rebuilt. If None is
409 specified, a new server is provisioned.
410 :param validatable: whether to the server needs to be
411 validatable. When True, validation resources are acquired via
412 the `get_class_validation_resources` helper.
413 :param kwargs: extra paramaters are passed through to the
414 `create_test_server` call.
415 :return: the UUID of the created server.
416 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000417 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800418 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000419
Ghanshyam3390d9f2015-12-25 12:48:02 +0900420 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000421 server = cls.create_test_server(
422 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100423 validation_resources=cls.get_class_validation_resources(
424 cls.os_primary),
Artom Lifshitz4fc47f62022-05-05 14:17:27 -0400425 wait_until=wait_until,
Ghanshyam3390d9f2015-12-25 12:48:02 +0900426 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000427 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000428 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800429
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800430 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700431 def delete_server(cls, server_id):
432 """Deletes an existing server and waits for it to be gone."""
433 try:
434 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000435 waiters.wait_for_server_termination(cls.servers_client,
436 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700437 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100438 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700439
Balazs Gibizerd8bbaba2022-05-17 17:15:40 +0200440 def resize_server(
441 self, server_id, new_flavor_id, wait_until='ACTIVE', **kwargs
442 ):
zhufl3d018b02016-11-25 16:43:04 +0800443 """resize and confirm_resize an server, waits for it to be ACTIVE."""
Jorge San Emeterio572ac542023-03-09 10:38:56 +0000444 body = self.servers_client.resize_server(
445 server_id, new_flavor_id, **kwargs)
446 waiters.wait_for_server_status(
447 self.servers_client, server_id, 'VERIFY_RESIZE',
448 request_id=body.response['x-openstack-request-id'])
zhuflbcb71172018-03-29 13:49:31 +0800449 self.servers_client.confirm_resize_server(server_id)
Balazs Gibizerd8bbaba2022-05-17 17:15:40 +0200450
zhuflbcb71172018-03-29 13:49:31 +0800451 waiters.wait_for_server_status(
452 self.servers_client, server_id, 'ACTIVE')
453 server = self.servers_client.show_server(server_id)['server']
Balazs Gibizerd8bbaba2022-05-17 17:15:40 +0200454
455 validation_resources = self.get_class_validation_resources(
456 self.os_primary)
457 if (
458 validation_resources and
459 wait_until in ("SSHABLE", "PINGABLE") and
460 CONF.validation.run_validation
461 ):
462 tenant_network = self.get_tenant_network()
463 compute.wait_for_ssh_or_ping(
464 server, self.os_primary, tenant_network,
465 True, validation_resources, wait_until, True)
466
zhuflbcb71172018-03-29 13:49:31 +0800467 self.assert_flavor_equal(new_flavor_id, server['flavor'])
zhufl3d018b02016-11-25 16:43:04 +0800468
Artom Lifshitzea2b59c2021-08-19 14:34:00 -0400469 def reboot_server(self, server_id, type):
470 """Reboot a server and wait for it to be ACTIVE."""
471 self.servers_client.reboot_server(server_id, type=type)
472 waiters.wait_for_server_status(
473 self.servers_client, server_id, 'ACTIVE')
474
zhufl3d018b02016-11-25 16:43:04 +0800475 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800476 def delete_volume(cls, volume_id):
477 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800478 try:
479 cls.volumes_client.delete_volume(volume_id)
480 # TODO(mriedem): We should move the wait_for_resource_deletion
481 # into the delete_volume method as a convenience to the caller.
482 cls.volumes_client.wait_for_resource_deletion(volume_id)
483 except lib_exc.NotFound:
484 LOG.warning("Unable to delete volume '%s' since it was not found. "
485 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900486
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000487 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100488 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000489 """Get the server fixed or floating IP.
490
Sean Dague20e98612016-01-06 14:33:28 -0500491 Based on the configuration we're in, return a correct ip
492 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100493
494 :param server: The server dict as returned by the API
495 :param validation_resources: The dict of validation resources
496 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000497 """
Lee Yarwood20556df2021-11-12 09:38:18 +0000498 return compute.get_server_ip(
499 server, validation_resources=validation_resources)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000500
Matt Riedemann342b37c2016-09-21 15:38:12 -0400501 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800502 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400503 """Create a volume and wait for it to become 'available'.
504
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000505 :param image_ref: Specify an image id to create a bootable volume.
Dan Smith5e2019f2023-07-21 10:38:59 -0700506 :param wait_for_available: Wait until the volume becomes available
507 before returning
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:
Martin Kopec213d0a42023-11-30 10:28:14 +0100514 vol_name = data_utils.rand_name(
515 prefix=CONF.resource_name_prefix,
516 name=cls.__name__ + '-volume')
zhufl8d23f922016-12-12 17:29:42 +0800517 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000518 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800519 kwargs['imageRef'] = image_ref
Dan Smith5e2019f2023-07-21 10:38:59 -0700520 wait = kwargs.pop('wait_for_available', True)
Sophie Huangdba4c9d2021-06-11 16:26:32 +0000521 if CONF.volume.volume_type and 'volume_type' not in kwargs:
522 # If volume_type is not provided in config then no need to
523 # add a volume type and
524 # if volume_type has already been added by child class then
525 # no need to override.
526 kwargs['volume_type'] = CONF.volume.volume_type
Martin Kopec00e6d6c2019-06-05 14:30:06 +0000527 if CONF.compute.compute_volume_common_az:
528 kwargs.setdefault('availability_zone',
529 CONF.compute.compute_volume_common_az)
zhufl8d23f922016-12-12 17:29:42 +0800530 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100531 cls.addClassResourceCleanup(
532 cls.volumes_client.wait_for_resource_deletion, volume['id'])
533 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
534 cls.volumes_client.delete_volume,
535 volume['id'])
Dan Smith5e2019f2023-07-21 10:38:59 -0700536 if wait:
537 waiters.wait_for_volume_resource_status(cls.volumes_client,
538 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400539 return volume
540
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400541 def _detach_volume(self, server, volume):
542 """Helper method to detach a volume.
543
544 Ignores 404 responses if the volume or server do not exist, or the
545 volume is already detached from the server.
546 """
547 try:
548 volume = self.volumes_client.show_volume(volume['id'])['volume']
549 # Check the status. You can only detach an in-use volume, otherwise
550 # the compute API will return a 400 response.
551 if volume['status'] == 'in-use':
552 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600553 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400554 # Ignore 404s on detach in case the server is deleted or the volume
555 # is already detached.
556 pass
557
Dan Smithd3155552023-02-27 06:48:38 -0800558 def attach_volume(self, server, volume, device=None, tag=None,
559 wait_for_detach=True):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400560 """Attaches volume to server and waits for 'in-use' volume status.
561
562 The volume will be detached when the test tears down.
563
564 :param server: The server to which the volume will be attached.
565 :param volume: The volume to attach.
566 :param device: Optional mountpoint for the attached volume. Note that
567 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400568 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400569 """
570 attach_kwargs = dict(volumeId=volume['id'])
571 if device:
572 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400573 if tag:
574 attach_kwargs['tag'] = tag
575
zhufl36f0a972017-02-28 15:43:33 +0800576 attachment = self.servers_client.attach_volume(
577 server['id'], **attach_kwargs)['volumeAttachment']
Lee Yarwood1bd60592021-06-04 10:18:35 +0100578
579 # NOTE(lyarwood): During attach we initially wait for the volume
580 # attachment and then check the volume state.
581 waiters.wait_for_volume_attachment_create(
582 self.volumes_client, volume['id'], server['id'])
583 # TODO(lyarwood): Remove the following volume status checks and move to
584 # attachment status checks across all volumes now with the 3.27
585 # microversion somehow.
586 if not volume['multiattach']:
587 waiters.wait_for_volume_resource_status(
588 self.volumes_client, volume['id'], 'in-use')
589
590 # NOTE(lyarwood): On teardown (LIFO) initially wait for the volume
591 # attachment in Nova to be removed. While this technically happens last
592 # we want this to be the first waiter as if it fails we can then dump
593 # the contents of the console log. The final check of the volume state
594 # should be a no-op by this point and is just added for completeness
595 # when detaching non-multiattach volumes.
Dan Smithd3155552023-02-27 06:48:38 -0800596 if not volume['multiattach'] and wait_for_detach:
Lee Yarwood1bd60592021-06-04 10:18:35 +0100597 self.addCleanup(
598 waiters.wait_for_volume_resource_status, self.volumes_client,
599 volume['id'], 'available')
600 self.addCleanup(
601 waiters.wait_for_volume_attachment_remove_from_server,
602 self.servers_client, server['id'], volume['id'])
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400603 self.addCleanup(self._detach_volume, server, volume)
Lee Yarwood1bd60592021-06-04 10:18:35 +0100604
zhufl36f0a972017-02-28 15:43:33 +0800605 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400606
Lee Yarwood2ad7ca42020-01-07 13:06:25 +0000607 def create_volume_snapshot(self, volume_id, name=None, description=None,
608 metadata=None, force=False):
609 name = name or data_utils.rand_name(
Martin Kopec213d0a42023-11-30 10:28:14 +0100610 prefix=CONF.resource_name_prefix,
611 name=self.__class__.__name__ + '-snapshot')
Lee Yarwood2ad7ca42020-01-07 13:06:25 +0000612 snapshot = self.snapshots_client.create_snapshot(
613 volume_id=volume_id,
614 force=force,
615 display_name=name,
616 description=description,
617 metadata=metadata)['snapshot']
618 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
619 snapshot['id'])
620 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
621 waiters.wait_for_volume_resource_status(self.snapshots_client,
622 snapshot['id'], 'available')
623 snapshot = self.snapshots_client.show_snapshot(
624 snapshot['id'])['snapshot']
625 return snapshot
626
zhuflbcb71172018-03-29 13:49:31 +0800627 def assert_flavor_equal(self, flavor_id, server_flavor):
628 """Check whether server_flavor equals to flavor.
629
630 :param flavor_id: flavor id
631 :param server_flavor: flavor info returned by show_server.
632 """
633 # Nova API > 2.46 no longer includes flavor.id, and schema check
634 # will cover whether 'id' should be in flavor
635 if server_flavor.get('id'):
636 msg = ('server flavor is not same as flavor!')
637 self.assertEqual(flavor_id, server_flavor['id'], msg)
638 else:
639 flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
640 self.assertEqual(flavor['name'], server_flavor['original_name'],
641 "original_name in server flavor is not same as "
642 "flavor name!")
643 for key in ['ram', 'vcpus', 'disk']:
644 msg = ('attribute %s in server flavor is not same as '
645 'flavor!' % key)
646 self.assertEqual(flavor[key], server_flavor[key], msg)
647
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900648
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000649class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900650 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800651
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000652 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000653
654 @classmethod
655 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000656 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000657 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200658 cls.os_admin.availability_zone_client)
659 cls.admin_flavors_client = cls.os_admin.flavors_client
660 cls.admin_servers_client = cls.os_admin.servers_client
Artom Lifshitzb0ee03e52021-12-01 14:04:15 -0500661 cls.admin_image_client = cls.os_admin.image_client_v2
Rao Adnan Khanac1aaf62017-04-27 08:01:18 -0500662 cls.admin_assisted_volume_snapshots_client = \
663 cls.os_admin.assisted_volume_snapshots_client
zhufl36eeab02017-01-18 11:49:04 +0800664
665 def create_flavor(self, ram, vcpus, disk, name=None,
666 is_public='True', **kwargs):
667 if name is None:
Martin Kopec213d0a42023-11-30 10:28:14 +0100668 name = data_utils.rand_name(
669 prefix=CONF.resource_name_prefix,
670 name=self.__class__.__name__ + "-flavor")
zhufl36eeab02017-01-18 11:49:04 +0800671 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
672 client = self.admin_flavors_client
673 flavor = client.create_flavor(
674 ram=ram, vcpus=vcpus, disk=disk, name=name,
675 id=id, is_public=is_public, **kwargs)['flavor']
676 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
677 self.addCleanup(client.delete_flavor, flavor['id'])
678 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700679
zhufl7bc916d2018-08-22 14:47:39 +0800680 @classmethod
681 def get_host_for_server(cls, server_id):
682 server_details = cls.admin_servers_client.show_server(server_id)
Duc Truong09941202017-06-07 10:15:20 -0700683 return server_details['server']['OS-EXT-SRV-ATTR:host']
684
685 def get_host_other_than(self, server_id):
686 source_host = self.get_host_for_server(server_id)
687
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300688 svcs = self.os_admin.services_client.list_services(
689 binary='nova-compute')['services']
Marian Krcmarik6e3f99e2020-02-26 23:27:51 +0100690 hosts = []
691 for svc in svcs:
Maksim Malchukf4970a32022-11-16 23:29:33 +0300692 if svc['host'].endswith('-ironic'):
693 continue
Marian Krcmarik6e3f99e2020-02-26 23:27:51 +0100694 if svc['state'] == 'up' and svc['status'] == 'enabled':
695 if CONF.compute.compute_volume_common_az:
696 if svc['zone'] == CONF.compute.compute_volume_common_az:
697 hosts.append(svc['host'])
698 else:
699 hosts.append(svc['host'])
Duc Truong09941202017-06-07 10:15:20 -0700700
701 for target_host in hosts:
702 if source_host != target_host:
703 return target_host