blob: d7ee39c2c8bc6a3ce9bcf6972ff3b1da711c1f42 [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
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500111 if CONF.service_available.glance:
112 if CONF.image_feature_enabled.api_v1:
113 cls.images_client = cls.os_primary.image_client
114 elif CONF.image_feature_enabled.api_v2:
115 cls.images_client = cls.os_primary.image_client_v2
116 else:
117 raise lib_exc.InvalidConfiguration(
118 'Either api_v1 or api_v2 must be True in '
119 '[image-feature-enabled].')
Matt Riedemann14e5e482018-05-31 15:13:18 -0400120 cls._check_depends_on_nova_network()
121
122 @classmethod
123 def _check_depends_on_nova_network(cls):
124 # Since nova-network APIs were removed from Nova in the Rocky release,
125 # determine, based on the max version from the version document, if
126 # the compute API is >Queens and if so, skip tests that rely on
127 # nova-network.
128 if not getattr(cls, 'depends_on_nova_network', False):
129 return
130 versions = cls.versions_client.list_versions()['versions']
131 # Find the v2.1 version which will tell us our max version for the
132 # compute API we're testing against.
133 for version in versions:
134 if version['id'] == 'v2.1':
135 max_version = api_version_request.APIVersionRequest(
136 version['version'])
137 break
138 else:
139 LOG.warning(
140 'Unable to determine max v2.1 compute API version: %s',
141 versions)
142 return
143
144 # The max compute API version in Queens is 2.60 so we cap
145 # at that version.
146 queens = api_version_request.APIVersionRequest('2.60')
147 if max_version > queens:
148 raise cls.skipException('nova-network is gone')
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300149
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000150 @classmethod
151 def resource_setup(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000152 super(BaseV2ComputeTest, cls).resource_setup()
Ghanshyam05049dd2016-02-12 17:44:48 +0900153 cls.request_microversion = (
154 api_version_utils.select_request_microversion(
155 cls.min_microversion,
ghanshyam29591532016-03-11 17:12:43 +0900156 CONF.compute.min_microversion))
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000157 cls.volume_request_microversion = (
158 api_version_utils.select_request_microversion(
159 cls.volume_min_microversion,
160 CONF.volume.min_microversion))
161 cls.placement_request_microversion = (
162 api_version_utils.select_request_microversion(
163 cls.placement_min_microversion,
164 CONF.placement.min_microversion))
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000165 cls.build_interval = CONF.compute.build_interval
166 cls.build_timeout = CONF.compute.build_timeout
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000167 cls.image_ref = CONF.compute.image_ref
168 cls.image_ref_alt = CONF.compute.image_ref_alt
169 cls.flavor_ref = CONF.compute.flavor_ref
170 cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
lanoux283273b2015-12-04 03:01:54 -0800171 cls.ssh_user = CONF.validation.image_ssh_user
172 cls.image_ssh_user = CONF.validation.image_ssh_user
173 cls.image_ssh_password = CONF.validation.image_ssh_password
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900174
Matthew Treinishf7fca6a2013-12-09 16:27:23 +0000175 @classmethod
ghanshyam66b9aed2018-03-30 08:11:10 +0000176 def is_requested_microversion_compatible(cls, max_version):
177 """Check the compatibility of selected request microversion
178
179 This method will check if selected request microversion
180 (cls.request_microversion) for test is compatible with respect
181 to 'max_version'. Compatible means if selected request microversion
182 is in the range(<=) of 'max_version'.
183
184 :param max_version: maximum microversion to compare for compatibility.
185 Example: '2.30'
186 :returns: True if selected request microversion is compatible with
187 'max_version'. False in other case.
188 """
189 try:
190 req_version_obj = api_version_request.APIVersionRequest(
191 cls.request_microversion)
192 # NOTE(gmann): This is case where this method is used before calling
193 # resource_setup(), where cls.request_microversion is set. There may
194 # not be any such case but still we can handle this case.
195 except AttributeError:
196 request_microversion = (
197 api_version_utils.select_request_microversion(
198 cls.min_microversion,
199 CONF.compute.min_microversion))
200 req_version_obj = api_version_request.APIVersionRequest(
201 request_microversion)
202 max_version_obj = api_version_request.APIVersionRequest(max_version)
203 return req_version_obj <= max_version_obj
204
205 @classmethod
Attila Fazekas305e65b2013-10-29 13:23:07 +0100206 def server_check_teardown(cls):
207 """Checks is the shared server clean enough for subsequent test.
Ken'ichi Ohmichi88363cb2015-11-19 08:00:54 +0000208
Attila Fazekas305e65b2013-10-29 13:23:07 +0100209 Method will delete the server when it's dirty.
210 The setUp method is responsible for creating a new server.
211 Exceptions raised in tearDown class are fails the test case,
Marian Horban6afb0232015-11-10 22:47:12 -0500212 This method supposed to use only by tearDown methods, when
Attila Fazekas305e65b2013-10-29 13:23:07 +0100213 the shared server_id is stored in the server_id of the class.
214 """
215 if getattr(cls, 'server_id', None) is not None:
216 try:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000217 waiters.wait_for_server_status(cls.servers_client,
218 cls.server_id, 'ACTIVE')
Attila Fazekas305e65b2013-10-29 13:23:07 +0100219 except Exception as exc:
220 LOG.exception(exc)
221 cls.servers_client.delete_server(cls.server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000222 waiters.wait_for_server_termination(cls.servers_client,
223 cls.server_id)
Attila Fazekas305e65b2013-10-29 13:23:07 +0100224 cls.server_id = None
225 raise
226
227 @classmethod
Joe Gordon8843f0f2015-03-17 15:07:34 -0700228 def create_test_server(cls, validatable=False, volume_backed=False,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100229 validation_resources=None, **kwargs):
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000230 """Wrapper utility that returns a test server.
Rohit Karajgidc300b22012-05-04 08:11:00 -0700231
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000232 This wrapper utility calls the common create test server and
233 returns a test server. The purpose of this wrapper is to minimize
234 the impact on the code of the tests already using this
235 function.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700236
237 :param validatable: Whether the server will be pingable or sshable.
238 :param volume_backed: Whether the instance is volume backed or not.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100239 :param validation_resources: Dictionary of validation resources as
240 returned by `get_class_validation_resources`.
241 :param kwargs: Extra arguments are passed down to the
242 `compute.create_test_server` call.
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000243 """
zhufl7ae22682016-09-18 15:22:33 +0800244 if 'name' not in kwargs:
245 kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400246
247 request_version = api_version_request.APIVersionRequest(
248 cls.request_microversion)
249 v2_37_version = api_version_request.APIVersionRequest('2.37')
250
zhuflff9779c2018-01-04 14:41:40 +0800251 tenant_network = cls.get_tenant_network()
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400252 # NOTE(snikitin): since microversion v2.37 'networks' field is required
zhuflff9779c2018-01-04 14:41:40 +0800253 if (request_version >= v2_37_version and 'networks' not in kwargs and
254 not tenant_network):
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400255 kwargs['networks'] = 'none'
256
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000257 body, servers = compute.create_test_server(
zhufl04190882017-05-23 10:21:48 +0800258 cls.os_primary,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000259 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100260 validation_resources=validation_resources,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000261 tenant_network=tenant_network,
Joe Gordon8843f0f2015-03-17 15:07:34 -0700262 volume_backed=volume_backed,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000263 **kwargs)
Ken'ichi Ohmichi51c8c262013-12-21 03:30:37 +0900264
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100265 # For each server schedule wait and delete, so we first delete all
266 # and then wait for all
267 for server in servers:
268 cls.addClassResourceCleanup(waiters.wait_for_server_termination,
269 cls.servers_client, server['id'])
270 for server in servers:
271 cls.addClassResourceCleanup(
272 test_utils.call_and_ignore_notfound_exc,
273 cls.servers_client.delete_server, server['id'])
Sean Dague9b669e32012-12-13 18:40:08 -0500274
David Kranz0fb14292015-02-11 15:55:20 -0500275 return body
Sean Dague9b669e32012-12-13 18:40:08 -0500276
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800277 @classmethod
278 def create_security_group(cls, name=None, description=None):
279 if name is None:
280 name = data_utils.rand_name(cls.__name__ + "-securitygroup")
281 if description is None:
Ken'ichi Ohmichi4937f562015-03-23 00:15:01 +0000282 description = data_utils.rand_name('description')
ghanshyamb610b772015-08-24 17:29:38 +0900283 body = cls.security_groups_client.create_security_group(
284 name=name, description=description)['security_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100285 cls.addClassResourceCleanup(
286 test_utils.call_and_ignore_notfound_exc,
287 cls.security_groups_client.delete_security_group,
288 body['id'])
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800289
David Kranz9964b4e2015-02-06 15:45:29 -0500290 return body
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800291
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530292 @classmethod
Ghanshyam2a180b82014-06-16 13:54:22 +0900293 def create_test_server_group(cls, name="", policy=None):
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530294 if not name:
295 name = data_utils.rand_name(cls.__name__ + "-Server-Group")
Ghanshyam2a180b82014-06-16 13:54:22 +0900296 if policy is None:
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530297 policy = ['affinity']
Ken'ichi Ohmichi1f36daa2015-09-30 01:41:34 +0000298 body = cls.server_groups_client.create_server_group(
299 name=name, policies=policy)['server_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100300 cls.addClassResourceCleanup(
301 test_utils.call_and_ignore_notfound_exc,
302 cls.server_groups_client.delete_server_group,
303 body['id'])
David Kranzae99b9a2015-02-16 13:37:01 -0500304 return body
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530305
David Kranzcf0040c2012-06-26 09:46:56 -0400306 def wait_for(self, condition):
Sean Daguef237ccb2013-01-04 15:19:14 -0500307 """Repeatedly calls condition() until a timeout."""
David Kranzcf0040c2012-06-26 09:46:56 -0400308 start_time = int(time.time())
309 while True:
310 try:
311 condition()
Matthew Treinish05d9fb92012-12-07 16:14:05 -0500312 except Exception:
David Kranzcf0040c2012-06-26 09:46:56 -0400313 pass
314 else:
315 return
316 if int(time.time()) - start_time >= self.build_timeout:
317 condition()
318 return
319 time.sleep(self.build_interval)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400320
Attila Fazekas423834d2014-03-14 17:33:13 +0100321 @classmethod
322 def prepare_instance_network(cls):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000323 if (CONF.validation.auth_method != 'disabled' and
324 CONF.validation.connect_method == 'floating'):
Attila Fazekas423834d2014-03-14 17:33:13 +0100325 cls.set_network_resources(network=True, subnet=True, router=True,
326 dhcp=True)
327
ivan-zhu8f992be2013-07-31 14:56:58 +0800328 @classmethod
329 def create_image_from_server(cls, server_id, **kwargs):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500330 """Wrapper utility that returns an image created from the server.
331
332 If compute microversion >= 2.36, the returned image response will
333 be from the image service API rather than the compute image proxy API.
334 """
zhufl35a694b2017-02-14 17:10:53 +0800335 name = kwargs.pop('name',
336 data_utils.rand_name(cls.__name__ + "-image"))
337 wait_until = kwargs.pop('wait_until', None)
338 wait_for_server = kwargs.pop('wait_for_server', True)
ivan-zhu8f992be2013-07-31 14:56:58 +0800339
zhufl35a694b2017-02-14 17:10:53 +0800340 image = cls.compute_images_client.create_image(server_id, name=name,
341 **kwargs)
Felipe Monteiroe65ec452017-09-26 06:47:03 +0100342 if api_version_utils.compare_version_header_to_response(
343 "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
344 image_id = image['image_id']
345 else:
346 image_id = data_utils.parse_image_id(image.response['location'])
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500347
348 # The compute image proxy APIs were deprecated in 2.35 so
349 # use the images client directly if the API microversion being
350 # used is >=2.36.
zhufl66275c22018-03-28 15:32:14 +0800351 if not cls.is_requested_microversion_compatible('2.35'):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500352 client = cls.images_client
353 else:
354 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100355 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500356 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800357
zhufl35a694b2017-02-14 17:10:53 +0800358 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500359 try:
zhufl66275c22018-03-28 15:32:14 +0800360 wait_until = wait_until.upper()
361 if not cls.is_requested_microversion_compatible('2.35'):
362 wait_until = wait_until.lower()
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500363 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500364 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800365 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500366 # If the image is not found after create_image returned
367 # that means the snapshot failed in nova-compute and nova
368 # deleted the image. There should be a compute fault
369 # recorded with the server in that case, so get the server
370 # and dump some details.
371 server = (
372 cls.servers_client.show_server(server_id)['server'])
373 if 'fault' in server:
374 raise exceptions.SnapshotNotFoundException(
375 server['fault'], image_id=image_id)
376 else:
377 raise exceptions.SnapshotNotFoundException(
378 image_id=image_id)
379 else:
380 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500381 image = client.show_image(image_id)
382 # Compute image client returns response wrapped in 'image' element
383 # which is not the case with Glance image client.
384 if 'image' in image:
385 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800386
zhufl35a694b2017-02-14 17:10:53 +0800387 if wait_until.upper() == 'ACTIVE':
388 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000389 waiters.wait_for_server_status(cls.servers_client,
390 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500391 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800392
393 @classmethod
zhuflba0e5532017-09-13 10:51:07 +0800394 def recreate_server(cls, server_id, validatable=False, **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100395 """Destroy an existing class level server and creates a new one
396
397 Some test classes use a test server that can be used by multiple
398 tests. This is done to optimise runtime and test load.
399 If something goes wrong with the test server, it can be rebuilt
400 using this helper.
401
402 This helper can also be used for the initial provisioning if no
403 server_id is specified.
404
405 :param server_id: UUID of the server to be rebuilt. If None is
406 specified, a new server is provisioned.
407 :param validatable: whether to the server needs to be
408 validatable. When True, validation resources are acquired via
409 the `get_class_validation_resources` helper.
410 :param kwargs: extra paramaters are passed through to the
411 `create_test_server` call.
412 :return: the UUID of the created server.
413 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000414 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800415 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000416
Ghanshyam3390d9f2015-12-25 12:48:02 +0900417 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000418 server = cls.create_test_server(
419 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100420 validation_resources=cls.get_class_validation_resources(
421 cls.os_primary),
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000422 wait_until='ACTIVE',
Ghanshyam3390d9f2015-12-25 12:48:02 +0900423 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000424 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000425 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800426
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800427 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700428 def delete_server(cls, server_id):
429 """Deletes an existing server and waits for it to be gone."""
430 try:
431 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000432 waiters.wait_for_server_termination(cls.servers_client,
433 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700434 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100435 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700436
zhuflbcb71172018-03-29 13:49:31 +0800437 def resize_server(self, server_id, new_flavor_id, **kwargs):
zhufl3d018b02016-11-25 16:43:04 +0800438 """resize and confirm_resize an server, waits for it to be ACTIVE."""
zhuflbcb71172018-03-29 13:49:31 +0800439 self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
440 waiters.wait_for_server_status(self.servers_client, server_id,
zhufl3d018b02016-11-25 16:43:04 +0800441 'VERIFY_RESIZE')
zhuflbcb71172018-03-29 13:49:31 +0800442 self.servers_client.confirm_resize_server(server_id)
443 waiters.wait_for_server_status(
444 self.servers_client, server_id, 'ACTIVE')
445 server = self.servers_client.show_server(server_id)['server']
446 self.assert_flavor_equal(new_flavor_id, server['flavor'])
zhufl3d018b02016-11-25 16:43:04 +0800447
448 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800449 def delete_volume(cls, volume_id):
450 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800451 try:
452 cls.volumes_client.delete_volume(volume_id)
453 # TODO(mriedem): We should move the wait_for_resource_deletion
454 # into the delete_volume method as a convenience to the caller.
455 cls.volumes_client.wait_for_resource_deletion(volume_id)
456 except lib_exc.NotFound:
457 LOG.warning("Unable to delete volume '%s' since it was not found. "
458 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900459
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000460 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100461 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000462 """Get the server fixed or floating IP.
463
Sean Dague20e98612016-01-06 14:33:28 -0500464 Based on the configuration we're in, return a correct ip
465 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100466
467 :param server: The server dict as returned by the API
468 :param validation_resources: The dict of validation resources
469 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000470 """
471 if CONF.validation.connect_method == 'floating':
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100472 if validation_resources:
473 return validation_resources['floating_ip']['ip']
474 else:
475 msg = ('When validation.connect_method equals floating, '
476 'validation_resources cannot be None')
Masayuki Igawa2efbea12019-01-29 18:46:38 +0900477 raise lib_exc.InvalidParam(invalid_param=msg)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000478 elif CONF.validation.connect_method == 'fixed':
Sean Dague20e98612016-01-06 14:33:28 -0500479 addresses = server['addresses'][CONF.validation.network_for_ssh]
480 for address in addresses:
481 if address['version'] == CONF.validation.ip_version_for_ssh:
482 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800483 raise exceptions.ServerUnreachable(server_id=server['id'])
Sean Dague20e98612016-01-06 14:33:28 -0500484 else:
guo yunxianffc4fc02016-11-15 09:56:08 +0800485 raise lib_exc.InvalidConfiguration()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000486
Ghanshyam05049dd2016-02-12 17:44:48 +0900487 def setUp(self):
488 super(BaseV2ComputeTest, self).setUp()
489 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
Lee Yarwood4b95d4b2020-01-15 10:49:54 +0000490 compute_microversion=self.request_microversion,
491 volume_microversion=self.volume_request_microversion,
492 placement_microversion=self.placement_request_microversion))
Ghanshyam05049dd2016-02-12 17:44:48 +0900493
Matt Riedemann342b37c2016-09-21 15:38:12 -0400494 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800495 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400496 """Create a volume and wait for it to become 'available'.
497
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000498 :param image_ref: Specify an image id to create a bootable volume.
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600499 :param kwargs: other parameters to create volume.
Matt Riedemann342b37c2016-09-21 15:38:12 -0400500 :returns: The available volume.
501 """
zhufl8d23f922016-12-12 17:29:42 +0800502 if 'size' not in kwargs:
503 kwargs['size'] = CONF.volume.volume_size
504 if 'display_name' not in kwargs:
505 vol_name = data_utils.rand_name(cls.__name__ + '-volume')
506 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000507 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800508 kwargs['imageRef'] = image_ref
Martin Kopec00e6d6c2019-06-05 14:30:06 +0000509 if CONF.compute.compute_volume_common_az:
510 kwargs.setdefault('availability_zone',
511 CONF.compute.compute_volume_common_az)
zhufl8d23f922016-12-12 17:29:42 +0800512 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100513 cls.addClassResourceCleanup(
514 cls.volumes_client.wait_for_resource_deletion, volume['id'])
515 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
516 cls.volumes_client.delete_volume,
517 volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200518 waiters.wait_for_volume_resource_status(cls.volumes_client,
519 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400520 return volume
521
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400522 def _detach_volume(self, server, volume):
523 """Helper method to detach a volume.
524
525 Ignores 404 responses if the volume or server do not exist, or the
526 volume is already detached from the server.
527 """
528 try:
529 volume = self.volumes_client.show_volume(volume['id'])['volume']
530 # Check the status. You can only detach an in-use volume, otherwise
531 # the compute API will return a 400 response.
532 if volume['status'] == 'in-use':
533 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600534 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400535 # Ignore 404s on detach in case the server is deleted or the volume
536 # is already detached.
537 pass
538
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400539 def attach_volume(self, server, volume, device=None, tag=None):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400540 """Attaches volume to server and waits for 'in-use' volume status.
541
542 The volume will be detached when the test tears down.
543
544 :param server: The server to which the volume will be attached.
545 :param volume: The volume to attach.
546 :param device: Optional mountpoint for the attached volume. Note that
547 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400548 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400549 """
550 attach_kwargs = dict(volumeId=volume['id'])
551 if device:
552 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400553 if tag:
554 attach_kwargs['tag'] = tag
555
zhufl36f0a972017-02-28 15:43:33 +0800556 attachment = self.servers_client.attach_volume(
557 server['id'], **attach_kwargs)['volumeAttachment']
Lee Yarwoodf644baa2020-01-08 19:03:05 +0000558 # On teardown detach the volume and for multiattach volumes wait for
559 # the attachment to be removed. For non-multiattach volumes wait for
560 # the state of the volume to change to available. This is so we don't
561 # error out when trying to delete the volume during teardown.
562 if volume['multiattach']:
563 self.addCleanup(waiters.wait_for_volume_attachment_remove,
564 self.volumes_client, volume['id'],
565 attachment['id'])
566 else:
567 self.addCleanup(waiters.wait_for_volume_resource_status,
568 self.volumes_client, volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400569 # Ignore 404s on detach in case the server is deleted or the volume
570 # is already detached.
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400571 self.addCleanup(self._detach_volume, server, volume)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200572 waiters.wait_for_volume_resource_status(self.volumes_client,
Matt Riedemannb36186b2017-12-04 17:54:08 +0000573 volume['id'], 'in-use')
zhufl36f0a972017-02-28 15:43:33 +0800574 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400575
zhuflbcb71172018-03-29 13:49:31 +0800576 def assert_flavor_equal(self, flavor_id, server_flavor):
577 """Check whether server_flavor equals to flavor.
578
579 :param flavor_id: flavor id
580 :param server_flavor: flavor info returned by show_server.
581 """
582 # Nova API > 2.46 no longer includes flavor.id, and schema check
583 # will cover whether 'id' should be in flavor
584 if server_flavor.get('id'):
585 msg = ('server flavor is not same as flavor!')
586 self.assertEqual(flavor_id, server_flavor['id'], msg)
587 else:
588 flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
589 self.assertEqual(flavor['name'], server_flavor['original_name'],
590 "original_name in server flavor is not same as "
591 "flavor name!")
592 for key in ['ram', 'vcpus', 'disk']:
593 msg = ('attribute %s in server flavor is not same as '
594 'flavor!' % key)
595 self.assertEqual(flavor[key], server_flavor[key], msg)
596
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900597
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000598class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900599 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800600
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000601 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000602
603 @classmethod
604 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000605 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000606 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200607 cls.os_admin.availability_zone_client)
608 cls.admin_flavors_client = cls.os_admin.flavors_client
609 cls.admin_servers_client = cls.os_admin.servers_client
zhufl36eeab02017-01-18 11:49:04 +0800610
611 def create_flavor(self, ram, vcpus, disk, name=None,
612 is_public='True', **kwargs):
613 if name is None:
614 name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
615 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
616 client = self.admin_flavors_client
617 flavor = client.create_flavor(
618 ram=ram, vcpus=vcpus, disk=disk, name=name,
619 id=id, is_public=is_public, **kwargs)['flavor']
620 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
621 self.addCleanup(client.delete_flavor, flavor['id'])
622 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700623
zhufl7bc916d2018-08-22 14:47:39 +0800624 @classmethod
625 def get_host_for_server(cls, server_id):
626 server_details = cls.admin_servers_client.show_server(server_id)
Duc Truong09941202017-06-07 10:15:20 -0700627 return server_details['server']['OS-EXT-SRV-ATTR:host']
628
629 def get_host_other_than(self, server_id):
630 source_host = self.get_host_for_server(server_id)
631
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300632 svcs = self.os_admin.services_client.list_services(
633 binary='nova-compute')['services']
634 hosts = [svc['host'] for svc in svcs
635 if svc['state'] == 'up' and svc['status'] == 'enabled']
Duc Truong09941202017-06-07 10:15:20 -0700636
637 for target_host in hosts:
638 if source_host != target_host:
639 return target_host