blob: d0c19737f1a0031863c892d92fcc9d7550a02dc2 [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
Masayuki Igawa77d3f0f2017-03-10 18:57:00 +0900211 def clear_resources(cls, resource_name, resources, resource_del_func):
212 LOG.debug('Clearing %s: %s', resource_name,
213 ','.join(map(str, resources)))
214 for res_id in resources:
Sean Dagued62bf1c2013-06-05 14:36:25 -0400215 try:
Jordan Pittier9e227c52016-02-09 14:35:18 +0100216 test_utils.call_and_ignore_notfound_exc(
Masayuki Igawa77d3f0f2017-03-10 18:57:00 +0900217 resource_del_func, res_id)
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800218 except Exception as exc:
Masayuki Igawa77d3f0f2017-03-10 18:57:00 +0900219 LOG.exception('Exception raised deleting %s: %s',
220 resource_name, res_id)
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800221 LOG.exception(exc)
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800222
223 @classmethod
Joe Gordon8843f0f2015-03-17 15:07:34 -0700224 def create_test_server(cls, validatable=False, volume_backed=False,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100225 validation_resources=None, **kwargs):
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000226 """Wrapper utility that returns a test server.
Rohit Karajgidc300b22012-05-04 08:11:00 -0700227
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000228 This wrapper utility calls the common create test server and
229 returns a test server. The purpose of this wrapper is to minimize
230 the impact on the code of the tests already using this
231 function.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700232
233 :param validatable: Whether the server will be pingable or sshable.
234 :param volume_backed: Whether the instance is volume backed or not.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100235 :param validation_resources: Dictionary of validation resources as
236 returned by `get_class_validation_resources`.
237 :param kwargs: Extra arguments are passed down to the
238 `compute.create_test_server` call.
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000239 """
zhufl7ae22682016-09-18 15:22:33 +0800240 if 'name' not in kwargs:
241 kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400242
243 request_version = api_version_request.APIVersionRequest(
244 cls.request_microversion)
245 v2_37_version = api_version_request.APIVersionRequest('2.37')
246
zhuflff9779c2018-01-04 14:41:40 +0800247 tenant_network = cls.get_tenant_network()
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400248 # NOTE(snikitin): since microversion v2.37 'networks' field is required
zhuflff9779c2018-01-04 14:41:40 +0800249 if (request_version >= v2_37_version and 'networks' not in kwargs and
250 not tenant_network):
Sergey Nikitin8654e5b2017-06-04 22:09:56 +0400251 kwargs['networks'] = 'none'
252
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000253 body, servers = compute.create_test_server(
zhufl04190882017-05-23 10:21:48 +0800254 cls.os_primary,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000255 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100256 validation_resources=validation_resources,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000257 tenant_network=tenant_network,
Joe Gordon8843f0f2015-03-17 15:07:34 -0700258 volume_backed=volume_backed,
Joseph Lanouxb3e1f872015-01-30 11:13:07 +0000259 **kwargs)
Ken'ichi Ohmichi51c8c262013-12-21 03:30:37 +0900260
Andrea Frittoli0d0a3f32017-08-29 18:21:37 +0100261 # For each server schedule wait and delete, so we first delete all
262 # and then wait for all
263 for server in servers:
264 cls.addClassResourceCleanup(waiters.wait_for_server_termination,
265 cls.servers_client, server['id'])
266 for server in servers:
267 cls.addClassResourceCleanup(
268 test_utils.call_and_ignore_notfound_exc,
269 cls.servers_client.delete_server, server['id'])
Sean Dague9b669e32012-12-13 18:40:08 -0500270
David Kranz0fb14292015-02-11 15:55:20 -0500271 return body
Sean Dague9b669e32012-12-13 18:40:08 -0500272
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800273 @classmethod
274 def create_security_group(cls, name=None, description=None):
275 if name is None:
276 name = data_utils.rand_name(cls.__name__ + "-securitygroup")
277 if description is None:
Ken'ichi Ohmichi4937f562015-03-23 00:15:01 +0000278 description = data_utils.rand_name('description')
ghanshyamb610b772015-08-24 17:29:38 +0900279 body = cls.security_groups_client.create_security_group(
280 name=name, description=description)['security_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100281 cls.addClassResourceCleanup(
282 test_utils.call_and_ignore_notfound_exc,
283 cls.security_groups_client.delete_security_group,
284 body['id'])
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800285
David Kranz9964b4e2015-02-06 15:45:29 -0500286 return body
Zhi Kun Liu02e7a7b2014-01-08 16:08:32 +0800287
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530288 @classmethod
Ghanshyam2a180b82014-06-16 13:54:22 +0900289 def create_test_server_group(cls, name="", policy=None):
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530290 if not name:
291 name = data_utils.rand_name(cls.__name__ + "-Server-Group")
Ghanshyam2a180b82014-06-16 13:54:22 +0900292 if policy is None:
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530293 policy = ['affinity']
Ken'ichi Ohmichi1f36daa2015-09-30 01:41:34 +0000294 body = cls.server_groups_client.create_server_group(
295 name=name, policies=policy)['server_group']
Andrea Frittoli238818c2017-08-29 18:28:11 +0100296 cls.addClassResourceCleanup(
297 test_utils.call_and_ignore_notfound_exc,
298 cls.server_groups_client.delete_server_group,
299 body['id'])
David Kranzae99b9a2015-02-16 13:37:01 -0500300 return body
Abhijeet.Jain87dd4452014-04-23 15:51:23 +0530301
David Kranzcf0040c2012-06-26 09:46:56 -0400302 def wait_for(self, condition):
Sean Daguef237ccb2013-01-04 15:19:14 -0500303 """Repeatedly calls condition() until a timeout."""
David Kranzcf0040c2012-06-26 09:46:56 -0400304 start_time = int(time.time())
305 while True:
306 try:
307 condition()
Matthew Treinish05d9fb92012-12-07 16:14:05 -0500308 except Exception:
David Kranzcf0040c2012-06-26 09:46:56 -0400309 pass
310 else:
311 return
312 if int(time.time()) - start_time >= self.build_timeout:
313 condition()
314 return
315 time.sleep(self.build_interval)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400316
Attila Fazekas423834d2014-03-14 17:33:13 +0100317 @classmethod
318 def prepare_instance_network(cls):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000319 if (CONF.validation.auth_method != 'disabled' and
320 CONF.validation.connect_method == 'floating'):
Attila Fazekas423834d2014-03-14 17:33:13 +0100321 cls.set_network_resources(network=True, subnet=True, router=True,
322 dhcp=True)
323
ivan-zhu8f992be2013-07-31 14:56:58 +0800324 @classmethod
325 def create_image_from_server(cls, server_id, **kwargs):
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500326 """Wrapper utility that returns an image created from the server.
327
328 If compute microversion >= 2.36, the returned image response will
329 be from the image service API rather than the compute image proxy API.
330 """
zhufl35a694b2017-02-14 17:10:53 +0800331 name = kwargs.pop('name',
332 data_utils.rand_name(cls.__name__ + "-image"))
333 wait_until = kwargs.pop('wait_until', None)
334 wait_for_server = kwargs.pop('wait_for_server', True)
ivan-zhu8f992be2013-07-31 14:56:58 +0800335
zhufl35a694b2017-02-14 17:10:53 +0800336 image = cls.compute_images_client.create_image(server_id, name=name,
337 **kwargs)
Felipe Monteiroe65ec452017-09-26 06:47:03 +0100338 if api_version_utils.compare_version_header_to_response(
339 "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
340 image_id = image['image_id']
341 else:
342 image_id = data_utils.parse_image_id(image.response['location'])
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500343
344 # The compute image proxy APIs were deprecated in 2.35 so
345 # use the images client directly if the API microversion being
346 # used is >=2.36.
347 if api_version_utils.compare_version_header_to_response(
348 "OpenStack-API-Version", "compute 2.36", image.response, "lt"):
349 client = cls.images_client
350 else:
351 client = cls.compute_images_client
Andrea Frittolib17f7a32017-08-29 17:45:58 +0100352 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500353 client.delete_image, image_id)
ivan-zhu8f992be2013-07-31 14:56:58 +0800354
zhufl35a694b2017-02-14 17:10:53 +0800355 if wait_until is not None:
Matt Riedemann13954352017-02-07 14:03:54 -0500356 try:
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500357 waiters.wait_for_image_status(client, image_id, wait_until)
Matt Riedemann13954352017-02-07 14:03:54 -0500358 except lib_exc.NotFound:
zhufl35a694b2017-02-14 17:10:53 +0800359 if wait_until.upper() == 'ACTIVE':
Matt Riedemann13954352017-02-07 14:03:54 -0500360 # If the image is not found after create_image returned
361 # that means the snapshot failed in nova-compute and nova
362 # deleted the image. There should be a compute fault
363 # recorded with the server in that case, so get the server
364 # and dump some details.
365 server = (
366 cls.servers_client.show_server(server_id)['server'])
367 if 'fault' in server:
368 raise exceptions.SnapshotNotFoundException(
369 server['fault'], image_id=image_id)
370 else:
371 raise exceptions.SnapshotNotFoundException(
372 image_id=image_id)
373 else:
374 raise
Matt Riedemannf110a4b2018-01-08 15:03:36 -0500375 image = client.show_image(image_id)
376 # Compute image client returns response wrapped in 'image' element
377 # which is not the case with Glance image client.
378 if 'image' in image:
379 image = image['image']
ivan-zhu8f992be2013-07-31 14:56:58 +0800380
zhufl35a694b2017-02-14 17:10:53 +0800381 if wait_until.upper() == 'ACTIVE':
382 if wait_for_server:
Bob Ball5fe62392017-02-20 09:51:00 +0000383 waiters.wait_for_server_status(cls.servers_client,
384 server_id, 'ACTIVE')
David Kranza5299eb2015-01-15 17:24:05 -0500385 return image
ivan-zhu8f992be2013-07-31 14:56:58 +0800386
387 @classmethod
zhuflba0e5532017-09-13 10:51:07 +0800388 def recreate_server(cls, server_id, validatable=False, **kwargs):
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100389 """Destroy an existing class level server and creates a new one
390
391 Some test classes use a test server that can be used by multiple
392 tests. This is done to optimise runtime and test load.
393 If something goes wrong with the test server, it can be rebuilt
394 using this helper.
395
396 This helper can also be used for the initial provisioning if no
397 server_id is specified.
398
399 :param server_id: UUID of the server to be rebuilt. If None is
400 specified, a new server is provisioned.
401 :param validatable: whether to the server needs to be
402 validatable. When True, validation resources are acquired via
403 the `get_class_validation_resources` helper.
404 :param kwargs: extra paramaters are passed through to the
405 `create_test_server` call.
406 :return: the UUID of the created server.
407 """
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000408 if server_id:
zhufl9b682902016-12-15 09:16:34 +0800409 cls.delete_server(server_id)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000410
Ghanshyam3390d9f2015-12-25 12:48:02 +0900411 cls.password = data_utils.rand_password()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000412 server = cls.create_test_server(
413 validatable,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100414 validation_resources=cls.get_class_validation_resources(
415 cls.os_primary),
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000416 wait_until='ACTIVE',
Ghanshyam3390d9f2015-12-25 12:48:02 +0900417 adminPass=cls.password,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000418 **kwargs)
Matthew Treinish2cd19a42013-12-02 21:54:42 +0000419 return server['id']
ivan-zhu8f992be2013-07-31 14:56:58 +0800420
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800421 @classmethod
Jesse Keating613b4982015-05-04 15:05:19 -0700422 def delete_server(cls, server_id):
423 """Deletes an existing server and waits for it to be gone."""
424 try:
425 cls.servers_client.delete_server(server_id)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000426 waiters.wait_for_server_termination(cls.servers_client,
427 server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700428 except Exception:
Jordan Pittier525ec712016-12-07 17:51:26 +0100429 LOG.exception('Failed to delete server %s', server_id)
Jesse Keating613b4982015-05-04 15:05:19 -0700430
431 @classmethod
zhufl3d018b02016-11-25 16:43:04 +0800432 def resize_server(cls, server_id, new_flavor_id, **kwargs):
433 """resize and confirm_resize an server, waits for it to be ACTIVE."""
434 cls.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
435 waiters.wait_for_server_status(cls.servers_client, server_id,
436 'VERIFY_RESIZE')
437 cls.servers_client.confirm_resize_server(server_id)
438 waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
lianghao476f84f2018-01-12 13:05:16 +0800439 server = cls.servers_client.show_server(server_id)['server']
Sean McGinnisb4fdd682018-01-19 16:33:38 -0600440 # Nova API > 2.46 no longer includes flavor.id
441 if server['flavor'].get('id'):
442 if new_flavor_id != server['flavor']['id']:
443 msg = ('Flavor id of %s is not equal to new_flavor_id.'
444 % server_id)
445 raise lib_exc.TempestException(msg)
zhufl3d018b02016-11-25 16:43:04 +0800446
447 @classmethod
Matt Riedemann5dc594c2014-01-27 11:40:28 -0800448 def delete_volume(cls, volume_id):
449 """Deletes the given volume and waits for it to be gone."""
zhufldecdcf62017-09-13 10:27:28 +0800450 try:
451 cls.volumes_client.delete_volume(volume_id)
452 # TODO(mriedem): We should move the wait_for_resource_deletion
453 # into the delete_volume method as a convenience to the caller.
454 cls.volumes_client.wait_for_resource_deletion(volume_id)
455 except lib_exc.NotFound:
456 LOG.warning("Unable to delete volume '%s' since it was not found. "
457 "Maybe it was already deleted?", volume_id)
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900458
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000459 @classmethod
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100460 def get_server_ip(cls, server, validation_resources=None):
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000461 """Get the server fixed or floating IP.
462
Sean Dague20e98612016-01-06 14:33:28 -0500463 Based on the configuration we're in, return a correct ip
464 address for validating that a guest is up.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100465
466 :param server: The server dict as returned by the API
467 :param validation_resources: The dict of validation resources
468 provisioned for the server.
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000469 """
470 if CONF.validation.connect_method == 'floating':
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100471 if validation_resources:
472 return validation_resources['floating_ip']['ip']
473 else:
474 msg = ('When validation.connect_method equals floating, '
475 'validation_resources cannot be None')
476 raise exceptions.InvalidParam(invalid_param=msg)
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000477 elif CONF.validation.connect_method == 'fixed':
Sean Dague20e98612016-01-06 14:33:28 -0500478 addresses = server['addresses'][CONF.validation.network_for_ssh]
479 for address in addresses:
480 if address['version'] == CONF.validation.ip_version_for_ssh:
481 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800482 raise exceptions.ServerUnreachable(server_id=server['id'])
Sean Dague20e98612016-01-06 14:33:28 -0500483 else:
guo yunxianffc4fc02016-11-15 09:56:08 +0800484 raise lib_exc.InvalidConfiguration()
Joseph Lanouxffe09dd2015-03-18 16:45:33 +0000485
Ghanshyam05049dd2016-02-12 17:44:48 +0900486 def setUp(self):
487 super(BaseV2ComputeTest, self).setUp()
488 self.useFixture(api_microversion_fixture.APIMicroversionFixture(
489 self.request_microversion))
490
Matt Riedemann342b37c2016-09-21 15:38:12 -0400491 @classmethod
zhufl8d23f922016-12-12 17:29:42 +0800492 def create_volume(cls, image_ref=None, **kwargs):
Matt Riedemann342b37c2016-09-21 15:38:12 -0400493 """Create a volume and wait for it to become 'available'.
494
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000495 :param image_ref: Specify an image id to create a bootable volume.
zhufl8d23f922016-12-12 17:29:42 +0800496 :**kwargs: other parameters to create volume.
Matt Riedemann342b37c2016-09-21 15:38:12 -0400497 :returns: The available volume.
498 """
zhufl8d23f922016-12-12 17:29:42 +0800499 if 'size' not in kwargs:
500 kwargs['size'] = CONF.volume.volume_size
501 if 'display_name' not in kwargs:
502 vol_name = data_utils.rand_name(cls.__name__ + '-volume')
503 kwargs['display_name'] = vol_name
Artom Lifshitzfc8f8e62016-04-13 11:08:32 +0000504 if image_ref is not None:
zhufl8d23f922016-12-12 17:29:42 +0800505 kwargs['imageRef'] = image_ref
506 volume = cls.volumes_client.create_volume(**kwargs)['volume']
Andrea Frittoli1fc499e2017-08-29 18:33:03 +0100507 cls.addClassResourceCleanup(
508 cls.volumes_client.wait_for_resource_deletion, volume['id'])
509 cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
510 cls.volumes_client.delete_volume,
511 volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200512 waiters.wait_for_volume_resource_status(cls.volumes_client,
513 volume['id'], 'available')
Matt Riedemann342b37c2016-09-21 15:38:12 -0400514 return volume
515
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400516 def _detach_volume(self, server, volume):
517 """Helper method to detach a volume.
518
519 Ignores 404 responses if the volume or server do not exist, or the
520 volume is already detached from the server.
521 """
522 try:
523 volume = self.volumes_client.show_volume(volume['id'])['volume']
524 # Check the status. You can only detach an in-use volume, otherwise
525 # the compute API will return a 400 response.
526 if volume['status'] == 'in-use':
527 self.servers_client.detach_volume(server['id'], volume['id'])
Eric Friedb3eab672017-11-21 12:42:18 -0600528 except lib_exc.NotFound:
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400529 # Ignore 404s on detach in case the server is deleted or the volume
530 # is already detached.
531 pass
532
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400533 def attach_volume(self, server, volume, device=None, tag=None):
Matt Riedemanncb16a662016-10-01 18:30:05 -0400534 """Attaches volume to server and waits for 'in-use' volume status.
535
536 The volume will be detached when the test tears down.
537
538 :param server: The server to which the volume will be attached.
539 :param volume: The volume to attach.
540 :param device: Optional mountpoint for the attached volume. Note that
541 this is not guaranteed for all hypervisors and is not recommended.
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400542 :param tag: Optional device role tag to apply to the volume.
Matt Riedemanncb16a662016-10-01 18:30:05 -0400543 """
544 attach_kwargs = dict(volumeId=volume['id'])
545 if device:
546 attach_kwargs['device'] = device
Artom Lifshitzb6b2bba2016-10-31 14:56:40 -0400547 if tag:
548 attach_kwargs['tag'] = tag
549
zhufl36f0a972017-02-28 15:43:33 +0800550 attachment = self.servers_client.attach_volume(
551 server['id'], **attach_kwargs)['volumeAttachment']
Matt Riedemann342b37c2016-09-21 15:38:12 -0400552 # On teardown detach the volume and wait for it to be available. This
553 # is so we don't error out when trying to delete the volume during
554 # teardown.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200555 self.addCleanup(waiters.wait_for_volume_resource_status,
Matt Riedemann342b37c2016-09-21 15:38:12 -0400556 self.volumes_client, volume['id'], 'available')
557 # Ignore 404s on detach in case the server is deleted or the volume
558 # is already detached.
Matt Riedemann0d4551b2017-10-10 13:00:48 -0400559 self.addCleanup(self._detach_volume, server, volume)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200560 waiters.wait_for_volume_resource_status(self.volumes_client,
Matt Riedemannb36186b2017-12-04 17:54:08 +0000561 volume['id'], 'in-use')
zhufl36f0a972017-02-28 15:43:33 +0800562 return attachment
Matt Riedemann342b37c2016-09-21 15:38:12 -0400563
Ken'ichi Ohmichi543a5d42014-05-02 08:44:15 +0900564
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000565class BaseV2ComputeAdminTest(BaseV2ComputeTest):
Ken'ichi Ohmichibcefa3d2014-05-09 08:14:05 +0900566 """Base test case class for Compute Admin API tests."""
ivan-zhuf2b00502013-10-18 10:06:52 +0800567
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000568 credentials = ['primary', 'admin']
Emily Hugenbruche7991d92014-12-12 16:53:36 +0000569
570 @classmethod
571 def setup_clients(cls):
Ken'ichi Ohmichi02a8ccd2015-11-05 06:05:29 +0000572 super(BaseV2ComputeAdminTest, cls).setup_clients()
Ken'ichi Ohmichi9f5adf82014-12-12 04:01:32 +0000573 cls.availability_zone_admin_client = (
Jordan Pittier8160d312017-04-18 11:52:23 +0200574 cls.os_admin.availability_zone_client)
575 cls.admin_flavors_client = cls.os_admin.flavors_client
576 cls.admin_servers_client = cls.os_admin.servers_client
zhufl36eeab02017-01-18 11:49:04 +0800577
578 def create_flavor(self, ram, vcpus, disk, name=None,
579 is_public='True', **kwargs):
580 if name is None:
581 name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
582 id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
583 client = self.admin_flavors_client
584 flavor = client.create_flavor(
585 ram=ram, vcpus=vcpus, disk=disk, name=name,
586 id=id, is_public=is_public, **kwargs)['flavor']
587 self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
588 self.addCleanup(client.delete_flavor, flavor['id'])
589 return flavor
Duc Truong09941202017-06-07 10:15:20 -0700590
591 def get_host_for_server(self, server_id):
592 server_details = self.admin_servers_client.show_server(server_id)
593 return server_details['server']['OS-EXT-SRV-ATTR:host']
594
595 def get_host_other_than(self, server_id):
596 source_host = self.get_host_for_server(server_id)
597
Radoslav Gerganov50325e22018-03-29 12:02:04 +0300598 svcs = self.os_admin.services_client.list_services(
599 binary='nova-compute')['services']
600 hosts = [svc['host'] for svc in svcs
601 if svc['state'] == 'up' and svc['status'] == 'enabled']
Duc Truong09941202017-06-07 10:15:20 -0700602
603 for target_host in hosts:
604 if source_host != target_host:
605 return target_host