blob: eab2a8db3254df1f510bd4e482f6dc4a615f3e9c [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
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500112 if CONF.service_available.glance:
113 if CONF.image_feature_enabled.api_v1:
114 cls.images_client = cls.os_primary.image_client
115 elif CONF.image_feature_enabled.api_v2:
116 cls.images_client = cls.os_primary.image_client_v2
117 else:
118 raise lib_exc.InvalidConfiguration(
119 'Either api_v1 or api_v2 must be True in '
120 '[image-feature-enabled].')
Matt Riedemann14e5e482018-05-31 15:13:18 -0400121 cls._check_depends_on_nova_network()
122
123 @classmethod
124 def _check_depends_on_nova_network(cls):
125 # Since nova-network APIs were removed from Nova in the Rocky release,
126 # determine, based on the max version from the version document, if
127 # the compute API is >Queens and if so, skip tests that rely on
128 # nova-network.
129 if not getattr(cls, 'depends_on_nova_network', False):
130 return
131 versions = cls.versions_client.list_versions()['versions']
132 # Find the v2.1 version which will tell us our max version for the
133 # compute API we're testing against.
134 for version in versions:
135 if version['id'] == 'v2.1':
136 max_version = api_version_request.APIVersionRequest(
137 version['version'])
138 break
139 else:
140 LOG.warning(
141 'Unable to determine max v2.1 compute API version: %s',
142 versions)
143 return
144
145 # The max compute API version in Queens is 2.60 so we cap
146 # at that version.
147 queens = api_version_request.APIVersionRequest('2.60')
148 if max_version > queens:
149 raise cls.skipException('nova-network is gone')
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300150
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000151 @classmethod
152 def resource_setup(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000153 super(BaseV2ComputeTest, cls).resource_setup()
Ghanshyam05049dd2016-02-12 17:44:48 +0900154 cls.request_microversion = (
155 api_version_utils.select_request_microversion(
156 cls.min_microversion,
ghanshyam29591532016-03-11 17:12:43 +0900157 CONF.compute.min_microversion))
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000158 cls.volume_request_microversion = (
159 api_version_utils.select_request_microversion(
160 cls.volume_min_microversion,
161 CONF.volume.min_microversion))
162 cls.placement_request_microversion = (
163 api_version_utils.select_request_microversion(
164 cls.placement_min_microversion,
165 CONF.placement.min_microversion))
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000166 cls.build_interval = CONF.compute.build_interval
167 cls.build_timeout = CONF.compute.build_timeout
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000168 cls.image_ref = CONF.compute.image_ref
169 cls.image_ref_alt = CONF.compute.image_ref_alt
170 cls.flavor_ref = CONF.compute.flavor_ref
171 cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
lanoux283273b2015-12-04 03:01:54 -0800172 cls.ssh_user = CONF.validation.image_ssh_user
173 cls.image_ssh_user = CONF.validation.image_ssh_user
174 cls.image_ssh_password = CONF.validation.image_ssh_password
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900175
Matthew Treinishf7fca6a2013-12-09 16:27:23 +0000176 @classmethod
ghanshyam66b9aed2018-03-30 08:11:10 +0000177 def is_requested_microversion_compatible(cls, max_version):
178 """Check the compatibility of selected request microversion
179
180 This method will check if selected request microversion
181 (cls.request_microversion) for test is compatible with respect
182 to 'max_version'. Compatible means if selected request microversion
183 is in the range(<=) of 'max_version'.
184
185 :param max_version: maximum microversion to compare for compatibility.
186 Example: '2.30'
187 :returns: True if selected request microversion is compatible with
188 'max_version'. False in other case.
189 """
190 try:
191 req_version_obj = api_version_request.APIVersionRequest(
192 cls.request_microversion)
193 # NOTE(gmann): This is case where this method is used before calling
194 # resource_setup(), where cls.request_microversion is set. There may
195 # not be any such case but still we can handle this case.
196 except AttributeError:
197 request_microversion = (
198 api_version_utils.select_request_microversion(
199 cls.min_microversion,
200 CONF.compute.min_microversion))
201 req_version_obj = api_version_request.APIVersionRequest(
202 request_microversion)
203 max_version_obj = api_version_request.APIVersionRequest(max_version)
204 return req_version_obj <= max_version_obj
205
206 @classmethod
Attila Fazekas305e65b2013-10-29 13:23:07 +0100207 def server_check_teardown(cls):
208 """Checks is the shared server clean enough for subsequent test.
Ken'ichi Ohmichi88363cb2015-11-19 08:00:54 +0000209
Attila Fazekas305e65b2013-10-29 13:23:07 +0100210 Method will delete the server when it's dirty.
211 The setUp method is responsible for creating a new server.
212 Exceptions raised in tearDown class are fails the test case,
Marian Horban6afb0232015-11-10 22:47:12 -0500213 This method supposed to use only by tearDown methods, when
Attila Fazekas305e65b2013-10-29 13:23:07 +0100214 the shared server_id is stored in the server_id of the class.
215 """
216 if getattr(cls, 'server_id', None) is not None:
217 try:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000218 waiters.wait_for_server_status(cls.servers_client,
219 cls.server_id, 'ACTIVE')
Attila Fazekas305e65b2013-10-29 13:23:07 +0100220 except Exception as exc:
221 LOG.exception(exc)
222 cls.servers_client.delete_server(cls.server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000223 waiters.wait_for_server_termination(cls.servers_client,
224 cls.server_id)
Attila Fazekas305e65b2013-10-29 13:23:07 +0100225 cls.server_id = None
226 raise
227
228 @classmethod
Joe Gordon8843f0f2015-03-17 15:07:34 -0700229 def create_test_server(cls, validatable=False, volume_backed=False,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400230 validation_resources=None, clients=None, **kwargs):
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000231 """Wrapper utility that returns a test server.
Rohit Karajgidc300b22012-05-04 08:11:00 -0700232
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000233 This wrapper utility calls the common create test server and
234 returns a test server. The purpose of this wrapper is to minimize
235 the impact on the code of the tests already using this
236 function.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700237
238 :param validatable: Whether the server will be pingable or sshable.
239 :param volume_backed: Whether the instance is volume backed or not.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100240 :param validation_resources: Dictionary of validation resources as
241 returned by `get_class_validation_resources`.
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400242 :param clients: Client manager, defaults to os_primary.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100243 :param kwargs: Extra arguments are passed down to the
244 `compute.create_test_server` call.
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000245 """
zhufl7ae22682016-09-18 15:22:33 +0800246 if 'name' not in kwargs:
247 kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400248
249 request_version = api_version_request.APIVersionRequest(
250 cls.request_microversion)
251 v2_37_version = api_version_request.APIVersionRequest('2.37')
252
zhuflff9779c2018-01-04 14:41:40 +0800253 tenant_network = cls.get_tenant_network()
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400254 # NOTE(snikitin): since microversion v2.37 'networks' field is required
zhuflff9779c2018-01-04 14:41:40 +0800255 if (request_version >= v2_37_version and 'networks' not in kwargs and
256 not tenant_network):
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400257 kwargs['networks'] = 'none'
258
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400259 if clients is None:
260 clients = cls.os_primary
261
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000262 body, servers = compute.create_test_server(
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400263 clients,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000264 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100265 validation_resources=validation_resources,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000266 tenant_network=tenant_network,
Joe Gordon8843f0f2015-03-17 15:07:34 -0700267 volume_backed=volume_backed,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000268 **kwargs)
Ken'ichi Ohmichi51c8c262013-12-21 03:30:37 +0900269
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100270 # For each server schedule wait and delete, so we first delete all
271 # and then wait for all
272 for server in servers:
273 cls.addClassResourceCleanup(waiters.wait_for_server_termination,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400274 clients.servers_client, server['id'])
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100275 for server in servers:
276 cls.addClassResourceCleanup(
277 test_utils.call_and_ignore_notfound_exc,
Artom Lifshitz68f1e5e2020-04-03 13:12:36 -0400278 clients.servers_client.delete_server, server['id'])
Sean Dague9b669e32012-12-13 18:40:08 -0500279
David Kranz0fb14292015-02-11 15:55:20 -0500280 return body
Sean Dague9b669e32012-12-13 18:40:08 -0500281
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800282 @classmethod
283 def create_security_group(cls, name=None, description=None):
284 if name is None:
285 name = data_utils.rand_name(cls.__name__ + "-securitygroup")
286 if description is None:
Ken'ichi Ohmichi4937f562015-03-23 00:15:01 +0000287 description = data_utils.rand_name('description')
ghanshyamb610b772015-08-24 17:29:38 +0900288 body = cls.security_groups_client.create_security_group(
289 name=name, description=description)['security_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100290 cls.addClassResourceCleanup(
291 test_utils.call_and_ignore_notfound_exc,
292 cls.security_groups_client.delete_security_group,
293 body['id'])
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800294
David Kranz9964b4e2015-02-06 15:45:29 -0500295 return body
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800296
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530297 @classmethod
Ghanshyam2a180b82014-06-16 13:54:22 +0900298 def create_test_server_group(cls, name="", policy=None):
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530299 if not name:
300 name = data_utils.rand_name(cls.__name__ + "-Server-Group")
Ghanshyam2a180b82014-06-16 13:54:22 +0900301 if policy is None:
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530302 policy = ['affinity']
Ken'ichi Ohmichi1f36daa2015-09-30 01:41:34 +0000303 body = cls.server_groups_client.create_server_group(
304 name=name, policies=policy)['server_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100305 cls.addClassResourceCleanup(
306 test_utils.call_and_ignore_notfound_exc,
307 cls.server_groups_client.delete_server_group,
308 body['id'])
David Kranzae99b9a2015-02-16 13:37:01 -0500309 return body
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530310
David Kranzcf0040c2012-06-26 09:46:56 -0400311 def wait_for(self, condition):
Sean Daguef237ccb2013-01-04 15:19:14 -0500312 """Repeatedly calls condition() until a timeout."""
David Kranzcf0040c2012-06-26 09:46:56 -0400313 start_time = int(time.time())
314 while True:
315 try:
316 condition()
Matthew Treinish05d9fb92012-12-07 16:14:05 -0500317 except Exception:
David Kranzcf0040c2012-06-26 09:46:56 -0400318 pass
319 else:
320 return
321 if int(time.time()) - start_time >= self.build_timeout:
322 condition()
323 return
324 time.sleep(self.build_interval)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400325
Attila Fazekas423834d2014-03-14 17:33:13 +0100326 @classmethod
327 def prepare_instance_network(cls):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000328 if (CONF.validation.auth_method != 'disabled' and
329 CONF.validation.connect_method == 'floating'):
Attila Fazekas423834d2014-03-14 17:33:13 +0100330 cls.set_network_resources(network=True, subnet=True, router=True,
331 dhcp=True)
332
ivan-zhu8f992be2013-07-31 14:56:58 +0800333 @classmethod
334 def create_image_from_server(cls, server_id, **kwargs):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500335 """Wrapper utility that returns an image created from the server.
336
337 If compute microversion >= 2.36, the returned image response will
338 be from the image service API rather than the compute image proxy API.
339 """
zhufl35a694b2017-02-14 17:10:53 +0800340 name = kwargs.pop('name',
341 data_utils.rand_name(cls.__name__ + "-image"))
342 wait_until = kwargs.pop('wait_until', None)
343 wait_for_server = kwargs.pop('wait_for_server', True)
ivan-zhu8f992be2013-07-31 14:56:58 +0800344
zhufl35a694b2017-02-14 17:10:53 +0800345 image = cls.compute_images_client.create_image(server_id, name=name,
346 **kwargs)
Felipe Monteiroe65ec452017-09-26 06:47:03 +0100347 if api_version_utils.compare_version_header_to_response(
348 "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
349 image_id = image['image_id']
350 else:
351 image_id = data_utils.parse_image_id(image.response['location'])
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500352
353 # The compute image proxy APIs were deprecated in 2.35 so
354 # use the images client directly if the API microversion being
355 # used is >=2.36.
zhufl66275c22018-03-28 15:32:14 +0800356 if not cls.is_requested_microversion_compatible('2.35'):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500357 client = cls.images_client
358 else:
359 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100360 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500361 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800362
zhufl35a694b2017-02-14 17:10:53 +0800363 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500364 try:
zhufl66275c22018-03-28 15:32:14 +0800365 wait_until = wait_until.upper()
366 if not cls.is_requested_microversion_compatible('2.35'):
367 wait_until = wait_until.lower()
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500368 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500369 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800370 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500371 # If the image is not found after create_image returned
372 # that means the snapshot failed in nova-compute and nova
373 # deleted the image. There should be a compute fault
374 # recorded with the server in that case, so get the server
375 # and dump some details.
376 server = (
377 cls.servers_client.show_server(server_id)['server'])
378 if 'fault' in server:
379 raise exceptions.SnapshotNotFoundException(
380 server['fault'], image_id=image_id)
381 else:
382 raise exceptions.SnapshotNotFoundException(
383 image_id=image_id)
384 else:
385 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500386 image = client.show_image(image_id)
387 # Compute image client returns response wrapped in 'image' element
388 # which is not the case with Glance image client.
389 if 'image' in image:
390 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800391
zhufl35a694b2017-02-14 17:10:53 +0800392 if wait_until.upper() == 'ACTIVE':
393 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000394 waiters.wait_for_server_status(cls.servers_client,
395 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500396 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800397
398 @classmethod
zhuflba0e5532017-09-13 10:51:07 +0800399 def recreate_server(cls, server_id, validatable=False, **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100400 """Destroy an existing class level server and creates a new one
401
402 Some test classes use a test server that can be used by multiple
403 tests. This is done to optimise runtime and test load.
404 If something goes wrong with the test server, it can be rebuilt
405 using this helper.
406
407 This helper can also be used for the initial provisioning if no
408 server_id is specified.
409
410 :param server_id: UUID of the server to be rebuilt. If None is
411 specified, a new server is provisioned.
412 :param validatable: whether to the server needs to be
413 validatable. When True, validation resources are acquired via
414 the `get_class_validation_resources` helper.
415 :param kwargs: extra paramaters are passed through to the
416 `create_test_server` call.
417 :return: the UUID of the created server.
418 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000419 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800420 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000421
Ghanshyam3390d9f2015-12-25 12:48:02 +0900422 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000423 server = cls.create_test_server(
424 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100425 validation_resources=cls.get_class_validation_resources(
426 cls.os_primary),
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000427 wait_until='ACTIVE',
Ghanshyam3390d9f2015-12-25 12:48:02 +0900428 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000429 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000430 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800431
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800432 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700433 def delete_server(cls, server_id):
434 """Deletes an existing server and waits for it to be gone."""
435 try:
436 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000437 waiters.wait_for_server_termination(cls.servers_client,
438 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700439 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100440 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700441
zhuflbcb71172018-03-29 13:49:31 +0800442 def resize_server(self, server_id, new_flavor_id, **kwargs):
zhufl3d018b02016-11-25 16:43:04 +0800443 """resize and confirm_resize an server, waits for it to be ACTIVE."""
zhuflbcb71172018-03-29 13:49:31 +0800444 self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
445 waiters.wait_for_server_status(self.servers_client, server_id,
zhufl3d018b02016-11-25 16:43:04 +0800446 'VERIFY_RESIZE')
zhuflbcb71172018-03-29 13:49:31 +0800447 self.servers_client.confirm_resize_server(server_id)
448 waiters.wait_for_server_status(
449 self.servers_client, server_id, 'ACTIVE')
450 server = self.servers_client.show_server(server_id)['server']
451 self.assert_flavor_equal(new_flavor_id, server['flavor'])
zhufl3d018b02016-11-25 16:43:04 +0800452
453 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800454 def delete_volume(cls, volume_id):
455 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800456 try:
457 cls.volumes_client.delete_volume(volume_id)
458 # TODO(mriedem): We should move the wait_for_resource_deletion
459 # into the delete_volume method as a convenience to the caller.
460 cls.volumes_client.wait_for_resource_deletion(volume_id)
461 except lib_exc.NotFound:
462 LOG.warning("Unable to delete volume '%s' since it was not found. "
463 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900464
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000465 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100466 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000467 """Get the server fixed or floating IP.
468
Sean Dague20e98612016-01-06 14:33:28 -0500469 Based on the configuration we're in, return a correct ip
470 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100471
472 :param server: The server dict as returned by the API
473 :param validation_resources: The dict of validation resources
474 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000475 """
476 if CONF.validation.connect_method == 'floating':
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100477 if validation_resources:
478 return validation_resources['floating_ip']['ip']
479 else:
480 msg = ('When validation.connect_method equals floating, '
481 'validation_resources cannot be None')
Masayuki Igawa2efbea12019-01-29 18:46:38 +0900482 raise lib_exc.InvalidParam(invalid_param=msg)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000483 elif CONF.validation.connect_method == 'fixed':
Sean Dague20e98612016-01-06 14:33:28 -0500484 addresses = server['addresses'][CONF.validation.network_for_ssh]
485 for address in addresses:
486 if address['version'] == CONF.validation.ip_version_for_ssh:
487 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800488 raise exceptions.ServerUnreachable(server_id=server['id'])
Sean Dague20e98612016-01-06 14:33:28 -0500489 else:
guo yunxianffc4fc02016-11-15 09:56:08 +0800490 raise lib_exc.InvalidConfiguration()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000491
Ghanshyam05049dd2016-02-12 17:44:48 +0900492 def setUp(self):
493 super(BaseV2ComputeTest, self).setUp()
494 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000495 compute_microversion=self.request_microversion,
496 volume_microversion=self.volume_request_microversion,
497 placement_microversion=self.placement_request_microversion))
Ghanshyam05049dd2016-02-12 17:44:48 +0900498
Matt Riedemann342b37c2016-09-21 15:38:12 -0400499 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800500 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400501 """Create a volume and wait for it to become 'available'.
502
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000503 :param image_ref: Specify an image id to create a bootable volume.
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600504 :param kwargs: other parameters to create volume.
Matt Riedemann342b37c2016-09-21 15:38:12 -0400505 :returns: The available volume.
506 """
zhufl8d23f922016-12-12 17:29:42 +0800507 if 'size' not in kwargs:
508 kwargs['size'] = CONF.volume.volume_size
509 if 'display_name' not in kwargs:
510 vol_name = data_utils.rand_name(cls.__name__ + '-volume')
511 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000512 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800513 kwargs['imageRef'] = image_ref
Martin Kopec00e6d6c2019-06-05 14:30:06 +0000514 if CONF.compute.compute_volume_common_az:
515 kwargs.setdefault('availability_zone',
516 CONF.compute.compute_volume_common_az)
zhufl8d23f922016-12-12 17:29:42 +0800517 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100518 cls.addClassResourceCleanup(
519 cls.volumes_client.wait_for_resource_deletion, volume['id'])
520 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
521 cls.volumes_client.delete_volume,
522 volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200523 waiters.wait_for_volume_resource_status(cls.volumes_client,
524 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400525 return volume
526
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400527 def _detach_volume(self, server, volume):
528 """Helper method to detach a volume.
529
530 Ignores 404 responses if the volume or server do not exist, or the
531 volume is already detached from the server.
532 """
533 try:
534 volume = self.volumes_client.show_volume(volume['id'])['volume']
535 # Check the status. You can only detach an in-use volume, otherwise
536 # the compute API will return a 400 response.
537 if volume['status'] == 'in-use':
538 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600539 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400540 # Ignore 404s on detach in case the server is deleted or the volume
541 # is already detached.
542 pass
543
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400544 def attach_volume(self, server, volume, device=None, tag=None):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400545 """Attaches volume to server and waits for 'in-use' volume status.
546
547 The volume will be detached when the test tears down.
548
549 :param server: The server to which the volume will be attached.
550 :param volume: The volume to attach.
551 :param device: Optional mountpoint for the attached volume. Note that
552 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400553 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400554 """
555 attach_kwargs = dict(volumeId=volume['id'])
556 if device:
557 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400558 if tag:
559 attach_kwargs['tag'] = tag
560
zhufl36f0a972017-02-28 15:43:33 +0800561 attachment = self.servers_client.attach_volume(
562 server['id'], **attach_kwargs)['volumeAttachment']
Lee Yarwoodf644baa2020-01-08 19:03:05 +0000563 # On teardown detach the volume and for multiattach volumes wait for
564 # the attachment to be removed. For non-multiattach volumes wait for
565 # the state of the volume to change to available. This is so we don't
566 # error out when trying to delete the volume during teardown.
567 if volume['multiattach']:
568 self.addCleanup(waiters.wait_for_volume_attachment_remove,
569 self.volumes_client, volume['id'],
570 attachment['id'])
571 else:
572 self.addCleanup(waiters.wait_for_volume_resource_status,
573 self.volumes_client, volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400574 # Ignore 404s on detach in case the server is deleted or the volume
575 # is already detached.
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400576 self.addCleanup(self._detach_volume, server, volume)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200577 waiters.wait_for_volume_resource_status(self.volumes_client,
Matt Riedemannb36186b2017-12-04 17:54:08 +0000578 volume['id'], 'in-use')
zhufl36f0a972017-02-28 15:43:33 +0800579 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400580
zhuflbcb71172018-03-29 13:49:31 +0800581 def assert_flavor_equal(self, flavor_id, server_flavor):
582 """Check whether server_flavor equals to flavor.
583
584 :param flavor_id: flavor id
585 :param server_flavor: flavor info returned by show_server.
586 """
587 # Nova API > 2.46 no longer includes flavor.id, and schema check
588 # will cover whether 'id' should be in flavor
589 if server_flavor.get('id'):
590 msg = ('server flavor is not same as flavor!')
591 self.assertEqual(flavor_id, server_flavor['id'], msg)
592 else:
593 flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
594 self.assertEqual(flavor['name'], server_flavor['original_name'],
595 "original_name in server flavor is not same as "
596 "flavor name!")
597 for key in ['ram', 'vcpus', 'disk']:
598 msg = ('attribute %s in server flavor is not same as '
599 'flavor!' % key)
600 self.assertEqual(flavor[key], server_flavor[key], msg)
601
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900602
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000603class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900604 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800605
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000606 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000607
608 @classmethod
609 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000610 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000611 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200612 cls.os_admin.availability_zone_client)
613 cls.admin_flavors_client = cls.os_admin.flavors_client
614 cls.admin_servers_client = cls.os_admin.servers_client
zhufl36eeab02017-01-18 11:49:04 +0800615
616 def create_flavor(self, ram, vcpus, disk, name=None,
617 is_public='True', **kwargs):
618 if name is None:
619 name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
620 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
621 client = self.admin_flavors_client
622 flavor = client.create_flavor(
623 ram=ram, vcpus=vcpus, disk=disk, name=name,
624 id=id, is_public=is_public, **kwargs)['flavor']
625 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
626 self.addCleanup(client.delete_flavor, flavor['id'])
627 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700628
zhufl7bc916d2018-08-22 14:47:39 +0800629 @classmethod
630 def get_host_for_server(cls, server_id):
631 server_details = cls.admin_servers_client.show_server(server_id)
Duc Truong09941202017-06-07 10:15:20 -0700632 return server_details['server']['OS-EXT-SRV-ATTR:host']
633
634 def get_host_other_than(self, server_id):
635 source_host = self.get_host_for_server(server_id)
636
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300637 svcs = self.os_admin.services_client.list_services(
638 binary='nova-compute')['services']
Marian Krcmarik6e3f99e2020-02-26 23:27:51 +0100639 hosts = []
640 for svc in svcs:
641 if svc['state'] == 'up' and svc['status'] == 'enabled':
642 if CONF.compute.compute_volume_common_az:
643 if svc['zone'] == CONF.compute.compute_volume_common_az:
644 hosts.append(svc['host'])
645 else:
646 hosts.append(svc['host'])
Duc Truong09941202017-06-07 10:15:20 -0700647
648 for target_host in hosts:
649 if source_host != target_host:
650 return target_host