blob: ff2f99c13c93a22c3f02aa1db9b23b85d2991bd6 [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.
334 if api_version_utils.compare_version_header_to_response(
335 "OpenStack-API-Version", "compute 2.36", image.response, "lt"):
336 client = cls.images_client
337 else:
338 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100339 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500340 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800341
zhufl35a694b2017-02-14 17:10:53 +0800342 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500343 try:
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500344 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500345 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800346 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500347 # If the image is not found after create_image returned
348 # that means the snapshot failed in nova-compute and nova
349 # deleted the image. There should be a compute fault
350 # recorded with the server in that case, so get the server
351 # and dump some details.
352 server = (
353 cls.servers_client.show_server(server_id)['server'])
354 if 'fault' in server:
355 raise exceptions.SnapshotNotFoundException(
356 server['fault'], image_id=image_id)
357 else:
358 raise exceptions.SnapshotNotFoundException(
359 image_id=image_id)
360 else:
361 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500362 image = client.show_image(image_id)
363 # Compute image client returns response wrapped in 'image' element
364 # which is not the case with Glance image client.
365 if 'image' in image:
366 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800367
zhufl35a694b2017-02-14 17:10:53 +0800368 if wait_until.upper() == 'ACTIVE':
369 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000370 waiters.wait_for_server_status(cls.servers_client,
371 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500372 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800373
374 @classmethod
zhuflba0e5532017-09-13 10:51:07 +0800375 def recreate_server(cls, server_id, validatable=False, **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100376 """Destroy an existing class level server and creates a new one
377
378 Some test classes use a test server that can be used by multiple
379 tests. This is done to optimise runtime and test load.
380 If something goes wrong with the test server, it can be rebuilt
381 using this helper.
382
383 This helper can also be used for the initial provisioning if no
384 server_id is specified.
385
386 :param server_id: UUID of the server to be rebuilt. If None is
387 specified, a new server is provisioned.
388 :param validatable: whether to the server needs to be
389 validatable. When True, validation resources are acquired via
390 the `get_class_validation_resources` helper.
391 :param kwargs: extra paramaters are passed through to the
392 `create_test_server` call.
393 :return: the UUID of the created server.
394 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000395 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800396 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000397
Ghanshyam3390d9f2015-12-25 12:48:02 +0900398 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000399 server = cls.create_test_server(
400 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100401 validation_resources=cls.get_class_validation_resources(
402 cls.os_primary),
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000403 wait_until='ACTIVE',
Ghanshyam3390d9f2015-12-25 12:48:02 +0900404 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000405 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000406 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800407
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800408 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700409 def delete_server(cls, server_id):
410 """Deletes an existing server and waits for it to be gone."""
411 try:
412 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000413 waiters.wait_for_server_termination(cls.servers_client,
414 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700415 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100416 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700417
zhuflbcb71172018-03-29 13:49:31 +0800418 def resize_server(self, server_id, new_flavor_id, **kwargs):
zhufl3d018b02016-11-25 16:43:04 +0800419 """resize and confirm_resize an server, waits for it to be ACTIVE."""
zhuflbcb71172018-03-29 13:49:31 +0800420 self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
421 waiters.wait_for_server_status(self.servers_client, server_id,
zhufl3d018b02016-11-25 16:43:04 +0800422 'VERIFY_RESIZE')
zhuflbcb71172018-03-29 13:49:31 +0800423 self.servers_client.confirm_resize_server(server_id)
424 waiters.wait_for_server_status(
425 self.servers_client, server_id, 'ACTIVE')
426 server = self.servers_client.show_server(server_id)['server']
427 self.assert_flavor_equal(new_flavor_id, server['flavor'])
zhufl3d018b02016-11-25 16:43:04 +0800428
429 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800430 def delete_volume(cls, volume_id):
431 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800432 try:
433 cls.volumes_client.delete_volume(volume_id)
434 # TODO(mriedem): We should move the wait_for_resource_deletion
435 # into the delete_volume method as a convenience to the caller.
436 cls.volumes_client.wait_for_resource_deletion(volume_id)
437 except lib_exc.NotFound:
438 LOG.warning("Unable to delete volume '%s' since it was not found. "
439 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900440
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000441 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100442 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000443 """Get the server fixed or floating IP.
444
Sean Dague20e98612016-01-06 14:33:28 -0500445 Based on the configuration we're in, return a correct ip
446 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100447
448 :param server: The server dict as returned by the API
449 :param validation_resources: The dict of validation resources
450 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000451 """
452 if CONF.validation.connect_method == 'floating':
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100453 if validation_resources:
454 return validation_resources['floating_ip']['ip']
455 else:
456 msg = ('When validation.connect_method equals floating, '
457 'validation_resources cannot be None')
458 raise exceptions.InvalidParam(invalid_param=msg)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000459 elif CONF.validation.connect_method == 'fixed':
Sean Dague20e98612016-01-06 14:33:28 -0500460 addresses = server['addresses'][CONF.validation.network_for_ssh]
461 for address in addresses:
462 if address['version'] == CONF.validation.ip_version_for_ssh:
463 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800464 raise exceptions.ServerUnreachable(server_id=server['id'])
Sean Dague20e98612016-01-06 14:33:28 -0500465 else:
guo yunxianffc4fc02016-11-15 09:56:08 +0800466 raise lib_exc.InvalidConfiguration()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000467
Ghanshyam05049dd2016-02-12 17:44:48 +0900468 def setUp(self):
469 super(BaseV2ComputeTest, self).setUp()
470 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
471 self.request_microversion))
472
Matt Riedemann342b37c2016-09-21 15:38:12 -0400473 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800474 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400475 """Create a volume and wait for it to become 'available'.
476
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000477 :param image_ref: Specify an image id to create a bootable volume.
zhufl8d23f922016-12-12 17:29:42 +0800478 :**kwargs: other parameters to create volume.
Matt Riedemann342b37c2016-09-21 15:38:12 -0400479 :returns: The available volume.
480 """
zhufl8d23f922016-12-12 17:29:42 +0800481 if 'size' not in kwargs:
482 kwargs['size'] = CONF.volume.volume_size
483 if 'display_name' not in kwargs:
484 vol_name = data_utils.rand_name(cls.__name__ + '-volume')
485 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000486 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800487 kwargs['imageRef'] = image_ref
488 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100489 cls.addClassResourceCleanup(
490 cls.volumes_client.wait_for_resource_deletion, volume['id'])
491 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
492 cls.volumes_client.delete_volume,
493 volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200494 waiters.wait_for_volume_resource_status(cls.volumes_client,
495 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400496 return volume
497
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400498 def _detach_volume(self, server, volume):
499 """Helper method to detach a volume.
500
501 Ignores 404 responses if the volume or server do not exist, or the
502 volume is already detached from the server.
503 """
504 try:
505 volume = self.volumes_client.show_volume(volume['id'])['volume']
506 # Check the status. You can only detach an in-use volume, otherwise
507 # the compute API will return a 400 response.
508 if volume['status'] == 'in-use':
509 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600510 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400511 # Ignore 404s on detach in case the server is deleted or the volume
512 # is already detached.
513 pass
514
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400515 def attach_volume(self, server, volume, device=None, tag=None):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400516 """Attaches volume to server and waits for 'in-use' volume status.
517
518 The volume will be detached when the test tears down.
519
520 :param server: The server to which the volume will be attached.
521 :param volume: The volume to attach.
522 :param device: Optional mountpoint for the attached volume. Note that
523 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400524 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400525 """
526 attach_kwargs = dict(volumeId=volume['id'])
527 if device:
528 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400529 if tag:
530 attach_kwargs['tag'] = tag
531
zhufl36f0a972017-02-28 15:43:33 +0800532 attachment = self.servers_client.attach_volume(
533 server['id'], **attach_kwargs)['volumeAttachment']
Matt Riedemann342b37c2016-09-21 15:38:12 -0400534 # On teardown detach the volume and wait for it to be available. This
535 # is so we don't error out when trying to delete the volume during
536 # teardown.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200537 self.addCleanup(waiters.wait_for_volume_resource_status,
Matt Riedemann342b37c2016-09-21 15:38:12 -0400538 self.volumes_client, volume['id'], 'available')
539 # Ignore 404s on detach in case the server is deleted or the volume
540 # is already detached.
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400541 self.addCleanup(self._detach_volume, server, volume)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200542 waiters.wait_for_volume_resource_status(self.volumes_client,
Matt Riedemannb36186b2017-12-04 17:54:08 +0000543 volume['id'], 'in-use')
zhufl36f0a972017-02-28 15:43:33 +0800544 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400545
zhuflbcb71172018-03-29 13:49:31 +0800546 def assert_flavor_equal(self, flavor_id, server_flavor):
547 """Check whether server_flavor equals to flavor.
548
549 :param flavor_id: flavor id
550 :param server_flavor: flavor info returned by show_server.
551 """
552 # Nova API > 2.46 no longer includes flavor.id, and schema check
553 # will cover whether 'id' should be in flavor
554 if server_flavor.get('id'):
555 msg = ('server flavor is not same as flavor!')
556 self.assertEqual(flavor_id, server_flavor['id'], msg)
557 else:
558 flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
559 self.assertEqual(flavor['name'], server_flavor['original_name'],
560 "original_name in server flavor is not same as "
561 "flavor name!")
562 for key in ['ram', 'vcpus', 'disk']:
563 msg = ('attribute %s in server flavor is not same as '
564 'flavor!' % key)
565 self.assertEqual(flavor[key], server_flavor[key], msg)
566
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900567
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000568class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900569 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800570
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000571 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000572
573 @classmethod
574 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000575 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000576 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200577 cls.os_admin.availability_zone_client)
578 cls.admin_flavors_client = cls.os_admin.flavors_client
579 cls.admin_servers_client = cls.os_admin.servers_client
zhufl36eeab02017-01-18 11:49:04 +0800580
581 def create_flavor(self, ram, vcpus, disk, name=None,
582 is_public='True', **kwargs):
583 if name is None:
584 name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
585 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
586 client = self.admin_flavors_client
587 flavor = client.create_flavor(
588 ram=ram, vcpus=vcpus, disk=disk, name=name,
589 id=id, is_public=is_public, **kwargs)['flavor']
590 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
591 self.addCleanup(client.delete_flavor, flavor['id'])
592 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700593
zhufl7bc916d2018-08-22 14:47:39 +0800594 @classmethod
595 def get_host_for_server(cls, server_id):
596 server_details = cls.admin_servers_client.show_server(server_id)
Duc Truong09941202017-06-07 10:15:20 -0700597 return server_details['server']['OS-EXT-SRV-ATTR:host']
598
599 def get_host_other_than(self, server_id):
600 source_host = self.get_host_for_server(server_id)
601
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300602 svcs = self.os_admin.services_client.list_services(
603 binary='nova-compute')['services']
604 hosts = [svc['host'] for svc in svcs
605 if svc['state'] == 'up' and svc['status'] == 'enabled']
Duc Truong09941202017-06-07 10:15:20 -0700606
607 for target_host in hosts:
608 if source_host != target_host:
609 return target_host