blob: 4e2b59bdf6b5799dfb88f2950f2c32205a9674f9 [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
Ghanshyam05049dd2016-02-12 17:44:48 +090020from tempest.api.compute import api_microversion_fixture
Joseph Lanouxb3e1f872015-01-30 11:13:07 +000021from tempest.common import compute
Ken'ichi Ohmichi8b9c7802015-07-08 05:57:37 +000022from tempest.common import waiters
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000023from tempest import config
Sean Dague20e98612016-01-06 14:33:28 -050024from tempest import exceptions
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
Chris Yeoh8a79b9d2013-01-18 19:32:47 +103042
Andrea Frittolib21de6c2015-02-06 20:12:38 +000043 # TODO(andreaf) We should care also for the alt_manager here
44 # but only once client lazy load in the manager is done
45 credentials = ['primary']
46
Jay Pipesf38eaac2012-06-21 13:37:35 -040047 @classmethod
Emily Hugenbruche7991d92014-12-12 16:53:36 +000048 def skip_checks(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000049 super(BaseV2ComputeTest, cls).skip_checks()
Matthew Treinishf8ff3582015-08-25 12:41:56 -040050 if not CONF.service_available.nova:
51 raise cls.skipException("Nova is not available")
ghanshyam29591532016-03-11 17:12:43 +090052 cfg_min_version = CONF.compute.min_microversion
53 cfg_max_version = CONF.compute.max_microversion
Ken'ichi Ohmichi4d237e72015-11-05 06:32:33 +000054 api_version_utils.check_skip_with_microversion(cls.min_microversion,
55 cls.max_microversion,
56 cfg_min_version,
57 cfg_max_version)
Jay Pipesf38eaac2012-06-21 13:37:35 -040058
Emily Hugenbruche7991d92014-12-12 16:53:36 +000059 @classmethod
60 def setup_credentials(cls):
61 cls.set_network_resources()
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000062 super(BaseV2ComputeTest, cls).setup_credentials()
Daryl Walleckc7251962012-03-12 17:26:54 -050063
Emily Hugenbruche7991d92014-12-12 16:53:36 +000064 @classmethod
65 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +000066 super(BaseV2ComputeTest, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +020067 cls.servers_client = cls.os_primary.servers_client
68 cls.server_groups_client = cls.os_primary.server_groups_client
69 cls.flavors_client = cls.os_primary.flavors_client
70 cls.compute_images_client = cls.os_primary.compute_images_client
71 cls.extensions_client = cls.os_primary.extensions_client
72 cls.floating_ip_pools_client = cls.os_primary.floating_ip_pools_client
73 cls.floating_ips_client = cls.os_primary.compute_floating_ips_client
74 cls.keypairs_client = cls.os_primary.keypairs_client
John Warren5cdbf422016-01-05 12:42:43 -050075 cls.security_group_rules_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +020076 cls.os_primary.compute_security_group_rules_client)
77 cls.security_groups_client =\
78 cls.os_primary.compute_security_groups_client
79 cls.quotas_client = cls.os_primary.quotas_client
80 cls.compute_networks_client = cls.os_primary.compute_networks_client
81 cls.limits_client = cls.os_primary.limits_client
82 cls.volumes_extensions_client =\
83 cls.os_primary.volumes_extensions_client
84 cls.snapshots_extensions_client =\
85 cls.os_primary.snapshots_extensions_client
86 cls.interfaces_client = cls.os_primary.interfaces_client
87 cls.fixed_ips_client = cls.os_primary.fixed_ips_client
88 cls.availability_zone_client = cls.os_primary.availability_zone_client
89 cls.agents_client = cls.os_primary.agents_client
90 cls.aggregates_client = cls.os_primary.aggregates_client
91 cls.services_client = cls.os_primary.services_client
Emily Hugenbruche7991d92014-12-12 16:53:36 +000092 cls.instance_usages_audit_log_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +020093 cls.os_primary.instance_usages_audit_log_client)
94 cls.hypervisor_client = cls.os_primary.hypervisor_client
95 cls.certificates_client = cls.os_primary.certificates_client
96 cls.migrations_client = cls.os_primary.migrations_client
Emily Hugenbruche7991d92014-12-12 16:53:36 +000097 cls.security_group_default_rules_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +020098 cls.os_primary.security_group_default_rules_client)
99 cls.versions_client = cls.os_primary.compute_versions_client
Andrea Frittolia6b30152017-08-04 10:46:10 +0100100 if CONF.service_available.cinder:
101 cls.volumes_client = cls.os_primary.volumes_client_latest
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500102 if CONF.service_available.glance:
103 if CONF.image_feature_enabled.api_v1:
104 cls.images_client = cls.os_primary.image_client
105 elif CONF.image_feature_enabled.api_v2:
106 cls.images_client = cls.os_primary.image_client_v2
107 else:
108 raise lib_exc.InvalidConfiguration(
109 'Either api_v1 or api_v2 must be True in '
110 '[image-feature-enabled].')
Matt Riedemann14e5e482018-05-31 15:13:18 -0400111 cls._check_depends_on_nova_network()
112
113 @classmethod
114 def _check_depends_on_nova_network(cls):
115 # Since nova-network APIs were removed from Nova in the Rocky release,
116 # determine, based on the max version from the version document, if
117 # the compute API is >Queens and if so, skip tests that rely on
118 # nova-network.
119 if not getattr(cls, 'depends_on_nova_network', False):
120 return
121 versions = cls.versions_client.list_versions()['versions']
122 # Find the v2.1 version which will tell us our max version for the
123 # compute API we're testing against.
124 for version in versions:
125 if version['id'] == 'v2.1':
126 max_version = api_version_request.APIVersionRequest(
127 version['version'])
128 break
129 else:
130 LOG.warning(
131 'Unable to determine max v2.1 compute API version: %s',
132 versions)
133 return
134
135 # The max compute API version in Queens is 2.60 so we cap
136 # at that version.
137 queens = api_version_request.APIVersionRequest('2.60')
138 if max_version > queens:
139 raise cls.skipException('nova-network is gone')
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300140
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000141 @classmethod
142 def resource_setup(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000143 super(BaseV2ComputeTest, cls).resource_setup()
Ghanshyam05049dd2016-02-12 17:44:48 +0900144 cls.request_microversion = (
145 api_version_utils.select_request_microversion(
146 cls.min_microversion,
ghanshyam29591532016-03-11 17:12:43 +0900147 CONF.compute.min_microversion))
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000148 cls.build_interval = CONF.compute.build_interval
149 cls.build_timeout = CONF.compute.build_timeout
Matthew Treinishb0a78fc2014-01-29 16:49:12 +0000150 cls.image_ref = CONF.compute.image_ref
151 cls.image_ref_alt = CONF.compute.image_ref_alt
152 cls.flavor_ref = CONF.compute.flavor_ref
153 cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
lanoux283273b2015-12-04 03:01:54 -0800154 cls.ssh_user = CONF.validation.image_ssh_user
155 cls.image_ssh_user = CONF.validation.image_ssh_user
156 cls.image_ssh_password = CONF.validation.image_ssh_password
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900157
Matthew Treinishf7fca6a2013-12-09 16:27:23 +0000158 @classmethod
ghanshyam66b9aed2018-03-30 08:11:10 +0000159 def is_requested_microversion_compatible(cls, max_version):
160 """Check the compatibility of selected request microversion
161
162 This method will check if selected request microversion
163 (cls.request_microversion) for test is compatible with respect
164 to 'max_version'. Compatible means if selected request microversion
165 is in the range(<=) of 'max_version'.
166
167 :param max_version: maximum microversion to compare for compatibility.
168 Example: '2.30'
169 :returns: True if selected request microversion is compatible with
170 'max_version'. False in other case.
171 """
172 try:
173 req_version_obj = api_version_request.APIVersionRequest(
174 cls.request_microversion)
175 # NOTE(gmann): This is case where this method is used before calling
176 # resource_setup(), where cls.request_microversion is set. There may
177 # not be any such case but still we can handle this case.
178 except AttributeError:
179 request_microversion = (
180 api_version_utils.select_request_microversion(
181 cls.min_microversion,
182 CONF.compute.min_microversion))
183 req_version_obj = api_version_request.APIVersionRequest(
184 request_microversion)
185 max_version_obj = api_version_request.APIVersionRequest(max_version)
186 return req_version_obj <= max_version_obj
187
188 @classmethod
Attila Fazekas305e65b2013-10-29 13:23:07 +0100189 def server_check_teardown(cls):
190 """Checks is the shared server clean enough for subsequent test.
Ken'ichi Ohmichi88363cb2015-11-19 08:00:54 +0000191
Attila Fazekas305e65b2013-10-29 13:23:07 +0100192 Method will delete the server when it's dirty.
193 The setUp method is responsible for creating a new server.
194 Exceptions raised in tearDown class are fails the test case,
Marian Horban6afb0232015-11-10 22:47:12 -0500195 This method supposed to use only by tearDown methods, when
Attila Fazekas305e65b2013-10-29 13:23:07 +0100196 the shared server_id is stored in the server_id of the class.
197 """
198 if getattr(cls, 'server_id', None) is not None:
199 try:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000200 waiters.wait_for_server_status(cls.servers_client,
201 cls.server_id, 'ACTIVE')
Attila Fazekas305e65b2013-10-29 13:23:07 +0100202 except Exception as exc:
203 LOG.exception(exc)
204 cls.servers_client.delete_server(cls.server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000205 waiters.wait_for_server_termination(cls.servers_client,
206 cls.server_id)
Attila Fazekas305e65b2013-10-29 13:23:07 +0100207 cls.server_id = None
208 raise
209
210 @classmethod
Joe Gordon8843f0f2015-03-17 15:07:34 -0700211 def create_test_server(cls, validatable=False, volume_backed=False,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100212 validation_resources=None, **kwargs):
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000213 """Wrapper utility that returns a test server.
Rohit Karajgidc300b22012-05-04 08:11:00 -0700214
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000215 This wrapper utility calls the common create test server and
216 returns a test server. The purpose of this wrapper is to minimize
217 the impact on the code of the tests already using this
218 function.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700219
220 :param validatable: Whether the server will be pingable or sshable.
221 :param volume_backed: Whether the instance is volume backed or not.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100222 :param validation_resources: Dictionary of validation resources as
223 returned by `get_class_validation_resources`.
224 :param kwargs: Extra arguments are passed down to the
225 `compute.create_test_server` call.
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000226 """
zhufl7ae22682016-09-18 15:22:33 +0800227 if 'name' not in kwargs:
228 kwargs['name'] = data_utils.rand_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
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000240 body, servers = compute.create_test_server(
zhufl04190882017-05-23 10:21:48 +0800241 cls.os_primary,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000242 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100243 validation_resources=validation_resources,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000244 tenant_network=tenant_network,
Joe Gordon8843f0f2015-03-17 15:07:34 -0700245 volume_backed=volume_backed,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000246 **kwargs)
Ken'ichi Ohmichi51c8c262013-12-21 03:30:37 +0900247
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100248 # For each server schedule wait and delete, so we first delete all
249 # and then wait for all
250 for server in servers:
251 cls.addClassResourceCleanup(waiters.wait_for_server_termination,
252 cls.servers_client, server['id'])
253 for server in servers:
254 cls.addClassResourceCleanup(
255 test_utils.call_and_ignore_notfound_exc,
256 cls.servers_client.delete_server, server['id'])
Sean Dague9b669e32012-12-13 18:40:08 -0500257
David Kranz0fb14292015-02-11 15:55:20 -0500258 return body
Sean Dague9b669e32012-12-13 18:40:08 -0500259
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800260 @classmethod
261 def create_security_group(cls, name=None, description=None):
262 if name is None:
263 name = data_utils.rand_name(cls.__name__ + "-securitygroup")
264 if description is None:
Ken'ichi Ohmichi4937f562015-03-23 00:15:01 +0000265 description = data_utils.rand_name('description')
ghanshyamb610b772015-08-24 17:29:38 +0900266 body = cls.security_groups_client.create_security_group(
267 name=name, description=description)['security_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100268 cls.addClassResourceCleanup(
269 test_utils.call_and_ignore_notfound_exc,
270 cls.security_groups_client.delete_security_group,
271 body['id'])
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800272
David Kranz9964b4e2015-02-06 15:45:29 -0500273 return body
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800274
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530275 @classmethod
Ghanshyam2a180b82014-06-16 13:54:22 +0900276 def create_test_server_group(cls, name="", policy=None):
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530277 if not name:
278 name = data_utils.rand_name(cls.__name__ + "-Server-Group")
Ghanshyam2a180b82014-06-16 13:54:22 +0900279 if policy is None:
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530280 policy = ['affinity']
Ken'ichi Ohmichi1f36daa2015-09-30 01:41:34 +0000281 body = cls.server_groups_client.create_server_group(
282 name=name, policies=policy)['server_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100283 cls.addClassResourceCleanup(
284 test_utils.call_and_ignore_notfound_exc,
285 cls.server_groups_client.delete_server_group,
286 body['id'])
David Kranzae99b9a2015-02-16 13:37:01 -0500287 return body
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530288
David Kranzcf0040c2012-06-26 09:46:56 -0400289 def wait_for(self, condition):
Sean Daguef237ccb2013-01-04 15:19:14 -0500290 """Repeatedly calls condition() until a timeout."""
David Kranzcf0040c2012-06-26 09:46:56 -0400291 start_time = int(time.time())
292 while True:
293 try:
294 condition()
Matthew Treinish05d9fb92012-12-07 16:14:05 -0500295 except Exception:
David Kranzcf0040c2012-06-26 09:46:56 -0400296 pass
297 else:
298 return
299 if int(time.time()) - start_time >= self.build_timeout:
300 condition()
301 return
302 time.sleep(self.build_interval)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400303
Attila Fazekas423834d2014-03-14 17:33:13 +0100304 @classmethod
305 def prepare_instance_network(cls):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000306 if (CONF.validation.auth_method != 'disabled' and
307 CONF.validation.connect_method == 'floating'):
Attila Fazekas423834d2014-03-14 17:33:13 +0100308 cls.set_network_resources(network=True, subnet=True, router=True,
309 dhcp=True)
310
ivan-zhu8f992be2013-07-31 14:56:58 +0800311 @classmethod
312 def create_image_from_server(cls, server_id, **kwargs):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500313 """Wrapper utility that returns an image created from the server.
314
315 If compute microversion >= 2.36, the returned image response will
316 be from the image service API rather than the compute image proxy API.
317 """
zhufl35a694b2017-02-14 17:10:53 +0800318 name = kwargs.pop('name',
319 data_utils.rand_name(cls.__name__ + "-image"))
320 wait_until = kwargs.pop('wait_until', None)
321 wait_for_server = kwargs.pop('wait_for_server', True)
ivan-zhu8f992be2013-07-31 14:56:58 +0800322
zhufl35a694b2017-02-14 17:10:53 +0800323 image = cls.compute_images_client.create_image(server_id, name=name,
324 **kwargs)
Felipe Monteiroe65ec452017-09-26 06:47:03 +0100325 if api_version_utils.compare_version_header_to_response(
326 "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
327 image_id = image['image_id']
328 else:
329 image_id = data_utils.parse_image_id(image.response['location'])
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500330
331 # The compute image proxy APIs were deprecated in 2.35 so
332 # use the images client directly if the API microversion being
333 # used is >=2.36.
zhufl66275c22018-03-28 15:32:14 +0800334 if not cls.is_requested_microversion_compatible('2.35'):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500335 client = cls.images_client
336 else:
337 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100338 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500339 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800340
zhufl35a694b2017-02-14 17:10:53 +0800341 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500342 try:
zhufl66275c22018-03-28 15:32:14 +0800343 wait_until = wait_until.upper()
344 if not cls.is_requested_microversion_compatible('2.35'):
345 wait_until = wait_until.lower()
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500346 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500347 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800348 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500349 # If the image is not found after create_image returned
350 # that means the snapshot failed in nova-compute and nova
351 # deleted the image. There should be a compute fault
352 # recorded with the server in that case, so get the server
353 # and dump some details.
354 server = (
355 cls.servers_client.show_server(server_id)['server'])
356 if 'fault' in server:
357 raise exceptions.SnapshotNotFoundException(
358 server['fault'], image_id=image_id)
359 else:
360 raise exceptions.SnapshotNotFoundException(
361 image_id=image_id)
362 else:
363 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500364 image = client.show_image(image_id)
365 # Compute image client returns response wrapped in 'image' element
366 # which is not the case with Glance image client.
367 if 'image' in image:
368 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800369
zhufl35a694b2017-02-14 17:10:53 +0800370 if wait_until.upper() == 'ACTIVE':
371 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000372 waiters.wait_for_server_status(cls.servers_client,
373 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500374 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800375
376 @classmethod
zhuflba0e5532017-09-13 10:51:07 +0800377 def recreate_server(cls, server_id, validatable=False, **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100378 """Destroy an existing class level server and creates a new one
379
380 Some test classes use a test server that can be used by multiple
381 tests. This is done to optimise runtime and test load.
382 If something goes wrong with the test server, it can be rebuilt
383 using this helper.
384
385 This helper can also be used for the initial provisioning if no
386 server_id is specified.
387
388 :param server_id: UUID of the server to be rebuilt. If None is
389 specified, a new server is provisioned.
390 :param validatable: whether to the server needs to be
391 validatable. When True, validation resources are acquired via
392 the `get_class_validation_resources` helper.
393 :param kwargs: extra paramaters are passed through to the
394 `create_test_server` call.
395 :return: the UUID of the created server.
396 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000397 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800398 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000399
Ghanshyam3390d9f2015-12-25 12:48:02 +0900400 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000401 server = cls.create_test_server(
402 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100403 validation_resources=cls.get_class_validation_resources(
404 cls.os_primary),
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000405 wait_until='ACTIVE',
Ghanshyam3390d9f2015-12-25 12:48:02 +0900406 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000407 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000408 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800409
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800410 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700411 def delete_server(cls, server_id):
412 """Deletes an existing server and waits for it to be gone."""
413 try:
414 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000415 waiters.wait_for_server_termination(cls.servers_client,
416 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700417 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100418 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700419
zhuflbcb71172018-03-29 13:49:31 +0800420 def resize_server(self, server_id, new_flavor_id, **kwargs):
zhufl3d018b02016-11-25 16:43:04 +0800421 """resize and confirm_resize an server, waits for it to be ACTIVE."""
zhuflbcb71172018-03-29 13:49:31 +0800422 self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
423 waiters.wait_for_server_status(self.servers_client, server_id,
zhufl3d018b02016-11-25 16:43:04 +0800424 'VERIFY_RESIZE')
zhuflbcb71172018-03-29 13:49:31 +0800425 self.servers_client.confirm_resize_server(server_id)
426 waiters.wait_for_server_status(
427 self.servers_client, server_id, 'ACTIVE')
428 server = self.servers_client.show_server(server_id)['server']
429 self.assert_flavor_equal(new_flavor_id, server['flavor'])
zhufl3d018b02016-11-25 16:43:04 +0800430
431 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800432 def delete_volume(cls, volume_id):
433 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800434 try:
435 cls.volumes_client.delete_volume(volume_id)
436 # TODO(mriedem): We should move the wait_for_resource_deletion
437 # into the delete_volume method as a convenience to the caller.
438 cls.volumes_client.wait_for_resource_deletion(volume_id)
439 except lib_exc.NotFound:
440 LOG.warning("Unable to delete volume '%s' since it was not found. "
441 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900442
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000443 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100444 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000445 """Get the server fixed or floating IP.
446
Sean Dague20e98612016-01-06 14:33:28 -0500447 Based on the configuration we're in, return a correct ip
448 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100449
450 :param server: The server dict as returned by the API
451 :param validation_resources: The dict of validation resources
452 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000453 """
454 if CONF.validation.connect_method == 'floating':
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100455 if validation_resources:
456 return validation_resources['floating_ip']['ip']
457 else:
458 msg = ('When validation.connect_method equals floating, '
459 'validation_resources cannot be None')
460 raise exceptions.InvalidParam(invalid_param=msg)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000461 elif CONF.validation.connect_method == 'fixed':
Sean Dague20e98612016-01-06 14:33:28 -0500462 addresses = server['addresses'][CONF.validation.network_for_ssh]
463 for address in addresses:
464 if address['version'] == CONF.validation.ip_version_for_ssh:
465 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800466 raise exceptions.ServerUnreachable(server_id=server['id'])
Sean Dague20e98612016-01-06 14:33:28 -0500467 else:
guo yunxianffc4fc02016-11-15 09:56:08 +0800468 raise lib_exc.InvalidConfiguration()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000469
Ghanshyam05049dd2016-02-12 17:44:48 +0900470 def setUp(self):
471 super(BaseV2ComputeTest, self).setUp()
472 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
473 self.request_microversion))
474
Matt Riedemann342b37c2016-09-21 15:38:12 -0400475 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800476 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400477 """Create a volume and wait for it to become 'available'.
478
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000479 :param image_ref: Specify an image id to create a bootable volume.
zhufl8d23f922016-12-12 17:29:42 +0800480 :**kwargs: other parameters to create volume.
Matt Riedemann342b37c2016-09-21 15:38:12 -0400481 :returns: The available volume.
482 """
zhufl8d23f922016-12-12 17:29:42 +0800483 if 'size' not in kwargs:
484 kwargs['size'] = CONF.volume.volume_size
485 if 'display_name' not in kwargs:
486 vol_name = data_utils.rand_name(cls.__name__ + '-volume')
487 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000488 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800489 kwargs['imageRef'] = image_ref
490 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100491 cls.addClassResourceCleanup(
492 cls.volumes_client.wait_for_resource_deletion, volume['id'])
493 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
494 cls.volumes_client.delete_volume,
495 volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200496 waiters.wait_for_volume_resource_status(cls.volumes_client,
497 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400498 return volume
499
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400500 def _detach_volume(self, server, volume):
501 """Helper method to detach a volume.
502
503 Ignores 404 responses if the volume or server do not exist, or the
504 volume is already detached from the server.
505 """
506 try:
507 volume = self.volumes_client.show_volume(volume['id'])['volume']
508 # Check the status. You can only detach an in-use volume, otherwise
509 # the compute API will return a 400 response.
510 if volume['status'] == 'in-use':
511 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600512 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400513 # Ignore 404s on detach in case the server is deleted or the volume
514 # is already detached.
515 pass
516
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400517 def attach_volume(self, server, volume, device=None, tag=None):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400518 """Attaches volume to server and waits for 'in-use' volume status.
519
520 The volume will be detached when the test tears down.
521
522 :param server: The server to which the volume will be attached.
523 :param volume: The volume to attach.
524 :param device: Optional mountpoint for the attached volume. Note that
525 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400526 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400527 """
528 attach_kwargs = dict(volumeId=volume['id'])
529 if device:
530 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400531 if tag:
532 attach_kwargs['tag'] = tag
533
zhufl36f0a972017-02-28 15:43:33 +0800534 attachment = self.servers_client.attach_volume(
535 server['id'], **attach_kwargs)['volumeAttachment']
Matt Riedemann342b37c2016-09-21 15:38:12 -0400536 # On teardown detach the volume and wait for it to be available. This
537 # is so we don't error out when trying to delete the volume during
538 # teardown.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200539 self.addCleanup(waiters.wait_for_volume_resource_status,
Matt Riedemann342b37c2016-09-21 15:38:12 -0400540 self.volumes_client, volume['id'], 'available')
541 # Ignore 404s on detach in case the server is deleted or the volume
542 # is already detached.
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400543 self.addCleanup(self._detach_volume, server, volume)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200544 waiters.wait_for_volume_resource_status(self.volumes_client,
Matt Riedemannb36186b2017-12-04 17:54:08 +0000545 volume['id'], 'in-use')
zhufl36f0a972017-02-28 15:43:33 +0800546 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400547
zhuflbcb71172018-03-29 13:49:31 +0800548 def assert_flavor_equal(self, flavor_id, server_flavor):
549 """Check whether server_flavor equals to flavor.
550
551 :param flavor_id: flavor id
552 :param server_flavor: flavor info returned by show_server.
553 """
554 # Nova API > 2.46 no longer includes flavor.id, and schema check
555 # will cover whether 'id' should be in flavor
556 if server_flavor.get('id'):
557 msg = ('server flavor is not same as flavor!')
558 self.assertEqual(flavor_id, server_flavor['id'], msg)
559 else:
560 flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
561 self.assertEqual(flavor['name'], server_flavor['original_name'],
562 "original_name in server flavor is not same as "
563 "flavor name!")
564 for key in ['ram', 'vcpus', 'disk']:
565 msg = ('attribute %s in server flavor is not same as '
566 'flavor!' % key)
567 self.assertEqual(flavor[key], server_flavor[key], msg)
568
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900569
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000570class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900571 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800572
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000573 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000574
575 @classmethod
576 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000577 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000578 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200579 cls.os_admin.availability_zone_client)
580 cls.admin_flavors_client = cls.os_admin.flavors_client
581 cls.admin_servers_client = cls.os_admin.servers_client
zhufl36eeab02017-01-18 11:49:04 +0800582
583 def create_flavor(self, ram, vcpus, disk, name=None,
584 is_public='True', **kwargs):
585 if name is None:
586 name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
587 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
588 client = self.admin_flavors_client
589 flavor = client.create_flavor(
590 ram=ram, vcpus=vcpus, disk=disk, name=name,
591 id=id, is_public=is_public, **kwargs)['flavor']
592 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
593 self.addCleanup(client.delete_flavor, flavor['id'])
594 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700595
zhufl7bc916d2018-08-22 14:47:39 +0800596 @classmethod
597 def get_host_for_server(cls, server_id):
598 server_details = cls.admin_servers_client.show_server(server_id)
Duc Truong09941202017-06-07 10:15:20 -0700599 return server_details['server']['OS-EXT-SRV-ATTR:host']
600
601 def get_host_other_than(self, server_id):
602 source_host = self.get_host_for_server(server_id)
603
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300604 svcs = self.os_admin.services_client.list_services(
605 binary='nova-compute')['services']
606 hosts = [svc['host'] for svc in svcs
607 if svc['state'] == 'up' and svc['status'] == 'enabled']
Duc Truong09941202017-06-07 10:15:20 -0700608
609 for target_host in hosts:
610 if source_host != target_host:
611 return target_host