blob: 81e771cbea5accd70a2d005fee84727ee4bdbff9 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120017import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040018import subprocess
19
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import netaddr
Doug Hellmann583ce2c2015-03-11 14:55:46 +000021from oslo_log import log
Matthew Treinish96e9e882014-06-09 18:37:19 -040022import six
Matthew Treinish01472ff2015-02-20 17:26:52 -050023from tempest_lib.common.utils import data_utils
Masayuki Igawad9388762015-01-20 14:56:42 +090024from tempest_lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000026from tempest import clients
Andrea Frittoli9efbe952015-01-29 12:43:09 +000027from tempest.common import cred_provider
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010028from tempest.common import credentials
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090029from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000030from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020031from tempest import exceptions
Yair Fried1fc32a12014-08-04 09:11:30 +030032from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040033import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040034
Matthew Treinish6c072292014-01-29 19:15:52 +000035CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040036
Attila Fazekasfb7552a2013-08-27 13:02:26 +020037LOG = log.getLogger(__name__)
38
Sean Dague6dbc6da2013-05-08 17:49:46 -040039
Andrea Frittoli2e733b52014-07-16 14:12:11 +010040class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010041 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010042
43 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000044 def setup_credentials(cls):
45 super(ScenarioTest, cls).setup_credentials()
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010046 # TODO(andreaf) Some of the code from this resource_setup could be
47 # moved into `BaseTestCase`
48 cls.isolated_creds = credentials.get_isolated_credentials(
Andrea Frittoliae9aca02014-09-25 11:43:11 +010049 cls.__name__, network_resources=cls.network_resources)
Andrea Frittoli2e733b52014-07-16 14:12:11 +010050 cls.manager = clients.Manager(
51 credentials=cls.credentials()
52 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010053 cls.admin_manager = clients.Manager(cls.admin_credentials())
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000054
55 @classmethod
56 def setup_clients(cls):
57 super(ScenarioTest, cls).setup_clients()
Andrea Frittoli247058f2014-07-16 16:09:22 +010058 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070059 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010060 cls.floating_ips_client = cls.manager.floating_ips_client
61 # Glance image client v1
62 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000063 # Compute image client
64 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010065 cls.keypairs_client = cls.manager.keypairs_client
66 cls.networks_client = cls.admin_manager.networks_client
67 # Nova security groups client
68 cls.security_groups_client = cls.manager.security_groups_client
69 cls.servers_client = cls.manager.servers_client
70 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000071 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030072 cls.interface_client = cls.manager.interfaces_client
73 # Neutron network client
74 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090075 # Heat client
76 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010077
78 @classmethod
Andrea Frittoli2e733b52014-07-16 14:12:11 +010079 def credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010080 return cls.isolated_creds.get_primary_creds()
Andrea Frittoli2e733b52014-07-16 14:12:11 +010081
Masayuki Igawaccd66592014-07-17 00:42:42 +090082 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +030083 def alt_credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010084 return cls.isolated_creds.get_alt_creds()
Yair Frieddb6c9e92014-08-06 08:53:13 +030085
86 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +090087 def admin_credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010088 try:
89 return cls.isolated_creds.get_admin_creds()
90 except NotImplementedError:
91 raise cls.skipException('Admin Credentials are not available')
Masayuki Igawaccd66592014-07-17 00:42:42 +090092
Andrea Frittoli247058f2014-07-16 16:09:22 +010093 # ## Methods to handle sync and async deletes
94
95 def setUp(self):
96 super(ScenarioTest, self).setUp()
97 self.cleanup_waits = []
98 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
99 # because scenario tests in the same test class should not share
100 # resources. If resources were shared between test cases then it
101 # should be a single scenario test instead of multiples.
102
103 # NOTE(yfried): this list is cleaned at the end of test_methods and
104 # not at the end of the class
105 self.addCleanup(self._wait_for_cleanups)
106
Yair Fried1fc32a12014-08-04 09:11:30 +0300107 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100108 """Ignores NotFound exceptions for delete operations.
109
Yair Fried1fc32a12014-08-04 09:11:30 +0300110 @param delete_thing: delete method of a resource. method will be
111 executed as delete_thing(*args, **kwargs)
112
Andrea Frittoli247058f2014-07-16 16:09:22 +0100113 """
114 try:
115 # Tempest clients return dicts, so there is no common delete
116 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300117 delete_thing(*args, **kwargs)
Masayuki Igawabfa07602015-01-20 18:47:17 +0900118 except lib_exc.NotFound:
Andrea Frittoli247058f2014-07-16 16:09:22 +0100119 # If the resource is already missing, mission accomplished.
120 pass
121
122 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900123 cleanup_callable, cleanup_args=None,
124 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700125 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100126
127 @param waiter_callable: callable to wait for the resource to delete
128 @param thing_id: the id of the resource to be cleaned-up
129 @param thing_id_param: the name of the id param in the waiter
130 @param cleanup_callable: method to load pass to self.addCleanup with
131 the following *cleanup_args, **cleanup_kwargs.
132 usually a delete method.
133 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900134 if cleanup_args is None:
135 cleanup_args = []
136 if cleanup_kwargs is None:
137 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100138 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
139 wait_dict = {
140 'waiter_callable': waiter_callable,
141 thing_id_param: thing_id
142 }
143 self.cleanup_waits.append(wait_dict)
144
145 def _wait_for_cleanups(self):
146 """To handle async delete actions, a list of waits is added
147 which will be iterated over as the last step of clearing the
148 cleanup queue. That way all the delete calls are made up front
149 and the tests won't succeed unless the deletes are eventually
150 successful. This is the same basic approach used in the api tests to
151 limit cleanup execution time except here it is multi-resource,
152 because of the nature of the scenario tests.
153 """
154 for wait in self.cleanup_waits:
155 waiter_callable = wait.pop('waiter_callable')
156 waiter_callable(**wait)
157
158 # ## Test functions library
159 #
160 # The create_[resource] functions only return body and discard the
161 # resp part which is not used in scenario tests
162
Yair Frieddb6c9e92014-08-06 08:53:13 +0300163 def create_keypair(self, client=None):
164 if not client:
165 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100166 name = data_utils.rand_name(self.__class__.__name__)
167 # We don't need to create a keypair by pubkey in scenario
David Kranz173f0e02015-02-06 13:47:57 -0500168 body = client.create_keypair(name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300169 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100170 return body
171
172 def create_server(self, name=None, image=None, flavor=None,
173 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900174 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100175 """Creates VM instance.
176
177 @param image: image from which to create the instance
178 @param wait_on_boot: wait for status ACTIVE before continue
179 @param wait_on_delete: force synchronous delete on cleanup
180 @param create_kwargs: additional details for instance creation
181 @return: server dict
182 """
183 if name is None:
184 name = data_utils.rand_name(self.__class__.__name__)
185 if image is None:
186 image = CONF.compute.image_ref
187 if flavor is None:
188 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900189 if create_kwargs is None:
190 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100191
Andrea Frittoli247058f2014-07-16 16:09:22 +0100192 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
193 name, image, flavor)
David Kranz0fb14292015-02-11 15:55:20 -0500194 server = self.servers_client.create_server(name, image, flavor,
195 **create_kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100196 if wait_on_delete:
197 self.addCleanup(self.servers_client.wait_for_server_termination,
198 server['id'])
199 self.addCleanup_with_wait(
200 waiter_callable=self.servers_client.wait_for_server_termination,
201 thing_id=server['id'], thing_id_param='server_id',
202 cleanup_callable=self.delete_wrapper,
203 cleanup_args=[self.servers_client.delete_server, server['id']])
204 if wait_on_boot:
205 self.servers_client.wait_for_server_status(server_id=server['id'],
206 status='ACTIVE')
207 # The instance retrieved on creation is missing network
208 # details, necessitating retrieval after it becomes active to
209 # ensure correct details.
David Kranz0fb14292015-02-11 15:55:20 -0500210 server = self.servers_client.get_server(server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100211 self.assertEqual(server['name'], name)
212 return server
213
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100214 def create_volume(self, size=None, name=None, snapshot_id=None,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100215 imageRef=None, volume_type=None, wait_on_delete=True):
216 if name is None:
217 name = data_utils.rand_name(self.__class__.__name__)
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000218 volume = self.volumes_client.create_volume(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100219 size=size, display_name=name, snapshot_id=snapshot_id,
220 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700221
Andrea Frittoli247058f2014-07-16 16:09:22 +0100222 if wait_on_delete:
223 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
224 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700225 self.addCleanup(self.delete_wrapper,
226 self.volumes_client.delete_volume, volume['id'])
227 else:
228 self.addCleanup_with_wait(
229 waiter_callable=self.volumes_client.wait_for_resource_deletion,
230 thing_id=volume['id'], thing_id_param='id',
231 cleanup_callable=self.delete_wrapper,
232 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100233
234 self.assertEqual(name, volume['display_name'])
235 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
236 # The volume retrieved on creation has a non-up-to-date status.
237 # Retrieval after it becomes active ensures correct details.
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000238 volume = self.volumes_client.get_volume(volume['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100239 return volume
240
Yair Fried1fc32a12014-08-04 09:11:30 +0300241 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100242 _client = self.security_groups_client
243 if secgroup_id is None:
David Kranz9964b4e2015-02-06 15:45:29 -0500244 sgs = _client.list_security_groups()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100245 for sg in sgs:
246 if sg['name'] == 'default':
247 secgroup_id = sg['id']
248
249 # These rules are intended to permit inbound ssh and icmp
250 # traffic from all sources, so no group_id is provided.
251 # Setting a group_id would only permit traffic from ports
252 # belonging to the same security group.
253 rulesets = [
254 {
255 # ssh
256 'ip_proto': 'tcp',
257 'from_port': 22,
258 'to_port': 22,
259 'cidr': '0.0.0.0/0',
260 },
261 {
262 # ping
263 'ip_proto': 'icmp',
264 'from_port': -1,
265 'to_port': -1,
266 'cidr': '0.0.0.0/0',
267 }
268 ]
269 rules = list()
270 for ruleset in rulesets:
David Kranz9964b4e2015-02-06 15:45:29 -0500271 sg_rule = _client.create_security_group_rule(secgroup_id,
272 **ruleset)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100273 self.addCleanup(self.delete_wrapper,
274 _client.delete_security_group_rule,
275 sg_rule['id'])
276 rules.append(sg_rule)
277 return rules
278
Yair Fried1fc32a12014-08-04 09:11:30 +0300279 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100280 # Create security group
281 sg_name = data_utils.rand_name(self.__class__.__name__)
282 sg_desc = sg_name + " description"
David Kranz9964b4e2015-02-06 15:45:29 -0500283 secgroup = self.security_groups_client.create_security_group(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100284 sg_name, sg_desc)
285 self.assertEqual(secgroup['name'], sg_name)
286 self.assertEqual(secgroup['description'], sg_desc)
287 self.addCleanup(self.delete_wrapper,
288 self.security_groups_client.delete_security_group,
289 secgroup['id'])
290
291 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300292 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100293
294 return secgroup
295
JordanP3fe2dc32014-11-17 13:06:01 +0100296 def get_remote_client(self, server_or_ip, username=None, private_key=None,
297 log_console_of_servers=None):
298 """Get a SSH client to a remote server
299
300 @param server_or_ip a server object as returned by Tempest compute
301 client or an IP address to connect to
302 @param username name of the Linux account on the remote server
303 @param private_key the SSH private key to use
304 @param log_console_of_servers a list of server objects. Each server
305 in the list will have its console printed in the logs in case the
306 SSH connection failed to be established
307 @return a RemoteClient object
308 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100309 if isinstance(server_or_ip, six.string_types):
310 ip = server_or_ip
311 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700312 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
313 ip = addr['addr']
314
Andrea Frittoli247058f2014-07-16 16:09:22 +0100315 if username is None:
316 username = CONF.scenario.ssh_user
317 if private_key is None:
318 private_key = self.keypair['private_key']
319 linux_client = remote_client.RemoteClient(ip, username,
320 pkey=private_key)
321 try:
322 linux_client.validate_authentication()
JordanP3fe2dc32014-11-17 13:06:01 +0100323 except Exception:
324 LOG.exception('Initializing SSH connection to %s failed' % ip)
JordanP3fe2dc32014-11-17 13:06:01 +0100325 # If we don't explicitely set for which servers we want to
326 # log the console output then all the servers will be logged.
327 # See the definition of _log_console_output()
328 self._log_console_output(log_console_of_servers)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100329 raise
330
331 return linux_client
332
Ghanshyam2a180b82014-06-16 13:54:22 +0900333 def _image_create(self, name, fmt, path, properties=None):
334 if properties is None:
335 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100336 name = data_utils.rand_name('%s-' % name)
337 image_file = open(path, 'rb')
338 self.addCleanup(image_file.close)
339 params = {
340 'name': name,
341 'container_format': fmt,
342 'disk_format': fmt,
343 'is_public': 'False',
344 }
345 params.update(properties)
David Kranz34f18782015-01-06 13:43:55 -0500346 image = self.image_client.create_image(**params)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100347 self.addCleanup(self.image_client.delete_image, image['id'])
348 self.assertEqual("queued", image['status'])
349 self.image_client.update_image(image['id'], data=image_file)
350 return image['id']
351
352 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300353 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100354 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
355 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
356 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300357 img_container_format = CONF.scenario.img_container_format
358 img_disk_format = CONF.scenario.img_disk_format
359 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
360 "ami: %s, ari: %s, aki: %s" %
361 (img_path, img_container_format, img_disk_format,
362 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100363 try:
364 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300365 img_container_format,
366 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100367 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300368 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100369 except IOError:
370 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
371 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
372 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
373 properties = {
374 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
375 }
376 self.image = self._image_create('scenario-ami', 'ami',
377 path=ami_img_path,
378 properties=properties)
379 LOG.debug("image:%s" % self.image)
380
381 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400382 if not CONF.compute_feature_enabled.console_output:
383 LOG.debug('Console output not supported, cannot log')
384 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100385 if not servers:
David Kranzae99b9a2015-02-16 13:37:01 -0500386 servers = self.servers_client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100387 servers = servers['servers']
388 for server in servers:
Brant Knudson566c5712014-09-24 20:04:50 -0500389 console_output = self.servers_client.get_console_output(
David Kranzae99b9a2015-02-16 13:37:01 -0500390 server['id'], length=None).data
391 LOG.debug('Console output for %s\nbody=\n%s',
392 server['id'], console_output)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100393
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000394 def _log_net_info(self, exc):
395 # network debug is called as part of ssh init
396 if not isinstance(exc, exceptions.SSHTimeout):
397 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000398
nithya-ganesan882595e2014-07-29 18:51:07 +0000399 def create_server_snapshot(self, server, name=None):
400 # Glance client
401 _image_client = self.image_client
402 # Compute client
403 _images_client = self.images_client
404 if name is None:
405 name = data_utils.rand_name('scenario-snapshot-')
406 LOG.debug("Creating a snapshot image for server: %s", server['name'])
David Kranza5299eb2015-01-15 17:24:05 -0500407 image = _images_client.create_image(server['id'], name)
408 image_id = image.response['location'].split('images/')[1]
nithya-ganesan882595e2014-07-29 18:51:07 +0000409 _image_client.wait_for_image_status(image_id, 'active')
410 self.addCleanup_with_wait(
411 waiter_callable=_image_client.wait_for_resource_deletion,
412 thing_id=image_id, thing_id_param='id',
413 cleanup_callable=self.delete_wrapper,
414 cleanup_args=[_image_client.delete_image, image_id])
David Kranz34f18782015-01-06 13:43:55 -0500415 snapshot_image = _image_client.get_image_meta(image_id)
nithya-ganesan882595e2014-07-29 18:51:07 +0000416 image_name = snapshot_image['name']
417 self.assertEqual(name, image_name)
418 LOG.debug("Created snapshot image %s for server %s",
419 image_name, server['name'])
420 return snapshot_image
421
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900422 def nova_volume_attach(self):
423 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000424 volume = self.servers_client.attach_volume(
David Kranz3ebc7212015-02-10 12:19:19 -0500425 self.server['id'], self.volume['id'], '/dev/vdb')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900426 self.assertEqual(self.volume['id'], volume['id'])
427 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
428 # Refresh the volume after the attachment
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000429 self.volume = self.volumes_client.get_volume(volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900430
431 def nova_volume_detach(self):
432 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
433 self.volumes_client.wait_for_volume_status(self.volume['id'],
434 'available')
435
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000436 volume = self.volumes_client.get_volume(self.volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900437 self.assertEqual('available', volume['status'])
438
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700439 def rebuild_server(self, server_id, image=None,
440 preserve_ephemeral=False, wait=True,
441 rebuild_kwargs=None):
442 if image is None:
443 image = CONF.compute.image_ref
444
445 rebuild_kwargs = rebuild_kwargs or {}
446
447 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
448 server_id, image, preserve_ephemeral)
449 self.servers_client.rebuild(server_id=server_id, image_ref=image,
450 preserve_ephemeral=preserve_ephemeral,
451 **rebuild_kwargs)
452 if wait:
453 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
454
Steven Hardyda2a8352014-10-02 12:52:20 +0100455 def ping_ip_address(self, ip_address, should_succeed=True,
456 ping_timeout=None):
457 timeout = ping_timeout or CONF.compute.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700458 cmd = ['ping', '-c1', '-w1', ip_address]
459
460 def ping():
461 proc = subprocess.Popen(cmd,
462 stdout=subprocess.PIPE,
463 stderr=subprocess.PIPE)
464 proc.communicate()
465 return (proc.returncode == 0) == should_succeed
466
Steven Hardyda2a8352014-10-02 12:52:20 +0100467 return tempest.test.call_until_true(ping, timeout, 1)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700468
Yair Friedae0e73d2014-11-24 11:56:26 +0200469 def check_vm_connectivity(self, ip_address,
470 username=None,
471 private_key=None,
472 should_connect=True):
473 """
474 :param ip_address: server to test against
475 :param username: server's ssh username
476 :param private_key: server's ssh private key to be used
477 :param should_connect: True/False indicates positive/negative test
478 positive - attempt ping and ssh
479 negative - attempt ping and fail if succeed
480
481 :raises: AssertError if the result of the connectivity check does
482 not match the value of the should_connect param
483 """
484 if should_connect:
485 msg = "Timed out waiting for %s to become reachable" % ip_address
486 else:
487 msg = "ip address %s is reachable" % ip_address
488 self.assertTrue(self.ping_ip_address(ip_address,
489 should_succeed=should_connect),
490 msg=msg)
491 if should_connect:
492 # no need to check ssh for negative connectivity
493 self.get_remote_client(ip_address, username, private_key)
494
495 def check_public_network_connectivity(self, ip_address, username,
496 private_key, should_connect=True,
497 msg=None, servers=None):
498 # The target login is assumed to have been configured for
499 # key-based authentication by cloud-init.
500 LOG.debug('checking network connections to IP %s with user: %s' %
501 (ip_address, username))
502 try:
503 self.check_vm_connectivity(ip_address,
504 username,
505 private_key,
506 should_connect=should_connect)
Matthew Treinish53483132014-12-09 18:50:06 -0500507 except Exception:
Yair Friedae0e73d2014-11-24 11:56:26 +0200508 ex_msg = 'Public network connectivity check failed'
509 if msg:
510 ex_msg += ": " + msg
511 LOG.exception(ex_msg)
512 self._log_console_output(servers)
Yair Friedae0e73d2014-11-24 11:56:26 +0200513 raise
514
515 def create_floating_ip(self, thing, pool_name=None):
516 """Creates a floating IP and associates to a server using
517 Nova clients
518 """
519
David Kranze4e3b412015-02-10 10:50:42 -0500520 floating_ip = self.floating_ips_client.create_floating_ip(pool_name)
Yair Friedae0e73d2014-11-24 11:56:26 +0200521 self.addCleanup(self.delete_wrapper,
522 self.floating_ips_client.delete_floating_ip,
523 floating_ip['id'])
524 self.floating_ips_client.associate_floating_ip_to_server(
525 floating_ip['ip'], thing['id'])
526 return floating_ip
527
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100528
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100529class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300530 """Base class for network scenario tests.
531 This class provide helpers for network scenario tests, using the neutron
532 API. Helpers from ancestor which use the nova network API are overridden
533 with the neutron API.
534
535 This Class also enforces using Neutron instead of novanetwork.
536 Subclassed tests will be skipped if Neutron is not enabled
537
538 """
539
540 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000541 def skip_checks(cls):
542 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100543 if not CONF.service_available.neutron:
544 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300545
546 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100547 def resource_setup(cls):
548 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300549 cls.tenant_id = cls.manager.identity_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300550
Yair Frieddb6c9e92014-08-06 08:53:13 +0300551 def _create_network(self, client=None, tenant_id=None,
552 namestart='network-smoke-'):
553 if not client:
554 client = self.network_client
555 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000556 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300557 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500558 result = client.create_network(name=name, tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300559 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300560 **result['network'])
561 self.assertEqual(network.name, name)
562 self.addCleanup(self.delete_wrapper, network.delete)
563 return network
564
565 def _list_networks(self, *args, **kwargs):
566 """List networks using admin creds """
567 return self._admin_lister('networks')(*args, **kwargs)
568
569 def _list_subnets(self, *args, **kwargs):
570 """List subnets using admin creds """
571 return self._admin_lister('subnets')(*args, **kwargs)
572
573 def _list_routers(self, *args, **kwargs):
574 """List routers using admin creds """
575 return self._admin_lister('routers')(*args, **kwargs)
576
577 def _list_ports(self, *args, **kwargs):
578 """List ports using admin creds """
579 return self._admin_lister('ports')(*args, **kwargs)
580
581 def _admin_lister(self, resource_type):
582 def temp(*args, **kwargs):
583 temp_method = self.admin_manager.network_client.__getattr__(
584 'list_%s' % resource_type)
David Kranz34e88122014-12-11 15:24:05 -0500585 resource_list = temp_method(*args, **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300586 return resource_list[resource_type]
587 return temp
588
Yair Frieddb6c9e92014-08-06 08:53:13 +0300589 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
590 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300591 """
592 Create a subnet for the given network within the cidr block
593 configured for tenant networks.
594 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300595 if not client:
596 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300597
598 def cidr_in_use(cidr, tenant_id):
599 """
600 :return True if subnet with cidr already exist in tenant
601 False else
602 """
603 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
604 return len(cidr_in_use) != 0
605
Kirill Shileev14113572014-11-21 16:58:02 +0300606 ip_version = kwargs.pop('ip_version', 4)
607
608 if ip_version == 6:
609 tenant_cidr = netaddr.IPNetwork(
610 CONF.network.tenant_network_v6_cidr)
611 num_bits = CONF.network.tenant_network_v6_mask_bits
612 else:
613 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
614 num_bits = CONF.network.tenant_network_mask_bits
615
Yair Fried1fc32a12014-08-04 09:11:30 +0300616 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300617 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300618 # Repeatedly attempt subnet creation with sequential cidr
619 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300620 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300621 str_cidr = str(subnet_cidr)
622 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
623 continue
624
625 subnet = dict(
626 name=data_utils.rand_name(namestart),
Yair Fried1fc32a12014-08-04 09:11:30 +0300627 network_id=network.id,
628 tenant_id=network.tenant_id,
629 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300630 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300631 **kwargs
632 )
633 try:
David Kranz34e88122014-12-11 15:24:05 -0500634 result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300635 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900636 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300637 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
638 if not is_overlapping_cidr:
639 raise
640 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300641 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300642 **result['subnet'])
643 self.assertEqual(subnet.cidr, str_cidr)
644 self.addCleanup(self.delete_wrapper, subnet.delete)
645 return subnet
646
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200647 def _create_port(self, network_id, client=None, namestart='port-quotatest',
648 **kwargs):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300649 if not client:
650 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300651 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500652 result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300653 name=name,
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200654 network_id=network_id,
655 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300656 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300657 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300658 **result['port'])
659 self.addCleanup(self.delete_wrapper, port.delete)
660 return port
661
Kirill Shileev14113572014-11-21 16:58:02 +0300662 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300663 ports = self._list_ports(device_id=server['id'],
664 fixed_ip=ip_addr)
665 self.assertEqual(len(ports), 1,
666 "Unable to determine which port to target.")
Kirill Shileev14113572014-11-21 16:58:02 +0300667 # it might happen here that this port has more then one ip address
668 # as in case of dual stack- when this port is created on 2 subnets
669 for ip46 in ports[0]['fixed_ips']:
670 ip = ip46['ip_address']
671 if netaddr.valid_ipv4(ip):
672 return ports[0]['id'], ip
Yair Fried1fc32a12014-08-04 09:11:30 +0300673
David Shrewsbury9bac3662014-08-07 15:07:01 -0400674 def _get_network_by_name(self, network_name):
675 net = self._list_networks(name=network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300676 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400677
Yair Friedae0e73d2014-11-24 11:56:26 +0200678 def create_floating_ip(self, thing, external_network_id=None,
679 port_id=None, client=None):
680 """Creates a floating IP and associates to a resource/port using
681 Neutron client
682 """
683 if not external_network_id:
684 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300685 if not client:
686 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300687 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300688 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
689 else:
690 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500691 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300692 floating_network_id=external_network_id,
693 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300694 tenant_id=thing['tenant_id'],
695 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300696 )
697 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300698 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300699 **result['floatingip'])
700 self.addCleanup(self.delete_wrapper, floating_ip.delete)
701 return floating_ip
702
703 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300704 port_id, _ = self._get_server_port_id_and_ip4(server)
Yair Fried1fc32a12014-08-04 09:11:30 +0300705 floating_ip.update(port_id=port_id)
706 self.assertEqual(port_id, floating_ip.port_id)
707 return floating_ip
708
709 def _disassociate_floating_ip(self, floating_ip):
710 """
711 :param floating_ip: type DeletableFloatingIp
712 """
713 floating_ip.update(port_id=None)
714 self.assertIsNone(floating_ip.port_id)
715 return floating_ip
716
Yair Fried45f92952014-06-26 05:19:19 +0300717 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000718 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300719
720 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
721 to check status
722 :param status: target status
723 :raises: AssertionError if status doesn't match
724 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000725 def refresh():
726 floating_ip.refresh()
727 return status == floating_ip.status
728
729 tempest.test.call_until_true(refresh,
730 CONF.network.build_timeout,
731 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300732 self.assertEqual(status, floating_ip.status,
733 message="FloatingIP: {fp} is at status: {cst}. "
734 "failed to reach status: {st}"
735 .format(fp=floating_ip, cst=floating_ip.status,
736 st=status))
737 LOG.info("FloatingIP: {fp} is at status: {st}"
738 .format(fp=floating_ip, st=status))
739
Yair Fried1fc32a12014-08-04 09:11:30 +0300740 def _check_tenant_network_connectivity(self, server,
741 username,
742 private_key,
743 should_connect=True,
744 servers_for_debug=None):
745 if not CONF.network.tenant_networks_reachable:
746 msg = 'Tenant networks not configured to be reachable.'
747 LOG.info(msg)
748 return
749 # The target login is assumed to have been configured for
750 # key-based authentication by cloud-init.
751 try:
ghanshyam807211c2014-12-18 13:21:22 +0900752 for net_name, ip_addresses in server['addresses'].iteritems():
Yair Fried1fc32a12014-08-04 09:11:30 +0300753 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900754 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200755 username,
756 private_key,
757 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300758 except Exception as e:
759 LOG.exception('Tenant network connectivity check failed')
760 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000761 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300762 raise
763
764 def _check_remote_connectivity(self, source, dest, should_succeed=True):
765 """
766 check ping server via source ssh connection
767
768 :param source: RemoteClient: an ssh connection from which to ping
769 :param dest: and IP to ping against
770 :param should_succeed: boolean should ping succeed or not
771 :returns: boolean -- should_succeed == ping
772 :returns: ping is false if ping failed
773 """
774 def ping_remote():
775 try:
776 source.ping_host(dest)
777 except exceptions.SSHExecCommandFailed:
778 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
779 % (dest, source.ssh_client.host))
780 return not should_succeed
781 return should_succeed
782
783 return tempest.test.call_until_true(ping_remote,
784 CONF.compute.ping_timeout,
785 1)
786
Yair Frieddb6c9e92014-08-06 08:53:13 +0300787 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300788 namestart='secgroup-smoke'):
789 if client is None:
790 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300791 if tenant_id is None:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000792 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300793 secgroup = self._create_empty_security_group(namestart=namestart,
794 client=client,
795 tenant_id=tenant_id)
796
797 # Add rules to the security group
ghanshyam38890b52015-01-21 15:24:18 +0900798 rules = self._create_loginable_secgroup_rule(client=client,
799 secgroup=secgroup)
Yair Fried1fc32a12014-08-04 09:11:30 +0300800 for rule in rules:
801 self.assertEqual(tenant_id, rule.tenant_id)
802 self.assertEqual(secgroup.id, rule.security_group_id)
803 return secgroup
804
Yair Frieddb6c9e92014-08-06 08:53:13 +0300805 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300806 namestart='secgroup-smoke'):
807 """Create a security group without rules.
808
809 Default rules will be created:
810 - IPv4 egress to any
811 - IPv6 egress to any
812
813 :param tenant_id: secgroup will be created in this tenant
814 :returns: DeletableSecurityGroup -- containing the secgroup created
815 """
816 if client is None:
817 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300818 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000819 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300820 sg_name = data_utils.rand_name(namestart)
821 sg_desc = sg_name + " description"
822 sg_dict = dict(name=sg_name,
823 description=sg_desc)
824 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -0500825 result = client.create_security_group(**sg_dict)
Yair Fried1fc32a12014-08-04 09:11:30 +0300826 secgroup = net_resources.DeletableSecurityGroup(
827 client=client,
828 **result['security_group']
829 )
830 self.assertEqual(secgroup.name, sg_name)
831 self.assertEqual(tenant_id, secgroup.tenant_id)
832 self.assertEqual(secgroup.description, sg_desc)
833 self.addCleanup(self.delete_wrapper, secgroup.delete)
834 return secgroup
835
Yair Frieddb6c9e92014-08-06 08:53:13 +0300836 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300837 """Get default secgroup for given tenant_id.
838
839 :returns: DeletableSecurityGroup -- default secgroup for given tenant
840 """
841 if client is None:
842 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300843 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000844 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300845 sgs = [
846 sg for sg in client.list_security_groups().values()[0]
847 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
848 ]
849 msg = "No default security group for tenant %s." % (tenant_id)
850 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300851 return net_resources.DeletableSecurityGroup(client=client,
852 **sgs[0])
853
Yair Frieddb6c9e92014-08-06 08:53:13 +0300854 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300855 tenant_id=None, **kwargs):
856 """Create a rule from a dictionary of rule parameters.
857
858 Create a rule in a secgroup. if secgroup not defined will search for
859 default secgroup in tenant_id.
860
861 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300862 :param tenant_id: if secgroup not passed -- the tenant in which to
863 search for default secgroup
864 :param kwargs: a dictionary containing rule parameters:
865 for example, to allow incoming ssh:
866 rule = {
867 direction: 'ingress'
868 protocol:'tcp',
869 port_range_min: 22,
870 port_range_max: 22
871 }
872 """
873 if client is None:
874 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300875 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000876 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300877 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300878 secgroup = self._default_security_group(client=client,
879 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300880
881 ruleset = dict(security_group_id=secgroup.id,
882 tenant_id=secgroup.tenant_id)
883 ruleset.update(kwargs)
884
David Kranz34e88122014-12-11 15:24:05 -0500885 sg_rule = client.create_security_group_rule(**ruleset)
Yair Fried1fc32a12014-08-04 09:11:30 +0300886 sg_rule = net_resources.DeletableSecurityGroupRule(
887 client=client,
888 **sg_rule['security_group_rule']
889 )
890 self.addCleanup(self.delete_wrapper, sg_rule.delete)
891 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
892 self.assertEqual(secgroup.id, sg_rule.security_group_id)
893
894 return sg_rule
895
896 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
897 """These rules are intended to permit inbound ssh and icmp
898 traffic from all sources, so no group_id is provided.
899 Setting a group_id would only permit traffic from ports
900 belonging to the same security group.
901 """
902
903 if client is None:
904 client = self.network_client
905 rules = []
906 rulesets = [
907 dict(
908 # ssh
909 protocol='tcp',
910 port_range_min=22,
911 port_range_max=22,
912 ),
913 dict(
914 # ping
915 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +0100916 ),
917 dict(
918 # ipv6-icmp for ping6
919 protocol='icmp',
920 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +0300921 )
922 ]
923 for ruleset in rulesets:
924 for r_direction in ['ingress', 'egress']:
925 ruleset['direction'] = r_direction
926 try:
927 sg_rule = self._create_security_group_rule(
928 client=client, secgroup=secgroup, **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +0900929 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +0300930 # if rule already exist - skip rule and continue
931 msg = 'Security group rule already exists'
932 if msg not in ex._error_string:
933 raise ex
934 else:
935 self.assertEqual(r_direction, sg_rule.direction)
936 rules.append(sg_rule)
937
938 return rules
939
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500940 def _create_pool(self, lb_method, protocol, subnet_id):
941 """Wrapper utility that returns a test pool."""
942 client = self.network_client
943 name = data_utils.rand_name('pool')
David Kranz34e88122014-12-11 15:24:05 -0500944 resp_pool = client.create_pool(protocol=protocol, name=name,
945 subnet_id=subnet_id,
946 lb_method=lb_method)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500947 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
948 self.assertEqual(pool['name'], name)
949 self.addCleanup(self.delete_wrapper, pool.delete)
950 return pool
951
952 def _create_member(self, address, protocol_port, pool_id):
953 """Wrapper utility that returns a test member."""
954 client = self.network_client
David Kranz34e88122014-12-11 15:24:05 -0500955 resp_member = client.create_member(protocol_port=protocol_port,
956 pool_id=pool_id,
957 address=address)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500958 member = net_resources.DeletableMember(client=client,
959 **resp_member['member'])
960 self.addCleanup(self.delete_wrapper, member.delete)
961 return member
962
963 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
964 """Wrapper utility that returns a test vip."""
965 client = self.network_client
966 name = data_utils.rand_name('vip')
David Kranz34e88122014-12-11 15:24:05 -0500967 resp_vip = client.create_vip(protocol=protocol, name=name,
968 subnet_id=subnet_id, pool_id=pool_id,
969 protocol_port=protocol_port)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500970 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
971 self.assertEqual(vip['name'], name)
972 self.addCleanup(self.delete_wrapper, vip.delete)
973 return vip
974
Yair Fried1fc32a12014-08-04 09:11:30 +0300975 def _ssh_to_server(self, server, private_key):
976 ssh_login = CONF.compute.image_ssh_user
977 return self.get_remote_client(server,
978 username=ssh_login,
979 private_key=private_key)
980
Yair Frieddb6c9e92014-08-06 08:53:13 +0300981 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300982 """Retrieve a router for the given tenant id.
983
984 If a public router has been configured, it will be returned.
985
986 If a public router has not been configured, but a public
987 network has, a tenant router will be created and returned that
988 routes traffic to the public network.
989 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300990 if not client:
991 client = self.network_client
992 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000993 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300994 router_id = CONF.network.public_router_id
995 network_id = CONF.network.public_network_id
996 if router_id:
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +0400997 resp, body = client.show_router(router_id)
998 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300999 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001000 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001001 router.set_gateway(network_id)
1002 return router
1003 else:
1004 raise Exception("Neither of 'public_router_id' or "
1005 "'public_network_id' has been defined.")
1006
Yair Frieddb6c9e92014-08-06 08:53:13 +03001007 def _create_router(self, client=None, tenant_id=None,
1008 namestart='router-smoke'):
1009 if not client:
1010 client = self.network_client
1011 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001012 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001013 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -05001014 result = client.create_router(name=name,
1015 admin_state_up=True,
1016 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001017 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +03001018 **result['router'])
1019 self.assertEqual(router.name, name)
1020 self.addCleanup(self.delete_wrapper, router.delete)
1021 return router
1022
Alok Maurya6384bbb2014-07-13 06:44:29 -07001023 def _update_router_admin_state(self, router, admin_state_up):
1024 router.update(admin_state_up=admin_state_up)
1025 self.assertEqual(admin_state_up, router.admin_state_up)
1026
Yair Fried413bf2d2014-11-19 17:07:11 +02001027 def create_networks(self, client=None, tenant_id=None,
1028 dns_nameservers=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001029 """Create a network with a subnet connected to a router.
1030
David Shrewsbury9bac3662014-08-07 15:07:01 -04001031 The baremetal driver is a special case since all nodes are
1032 on the same shared network.
1033
Yair Fried413bf2d2014-11-19 17:07:11 +02001034 :param client: network client to create resources with.
1035 :param tenant_id: id of tenant to create resources in.
1036 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001037 :returns: network, subnet, router
1038 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001039 if CONF.baremetal.driver_enabled:
1040 # NOTE(Shrews): This exception is for environments where tenant
1041 # credential isolation is available, but network separation is
1042 # not (the current baremetal case). Likely can be removed when
1043 # test account mgmt is reworked:
1044 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
1045 network = self._get_network_by_name(
1046 CONF.compute.fixed_network_name)
1047 router = None
1048 subnet = None
1049 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001050 network = self._create_network(client=client, tenant_id=tenant_id)
1051 router = self._get_router(client=client, tenant_id=tenant_id)
Yair Fried413bf2d2014-11-19 17:07:11 +02001052
1053 subnet_kwargs = dict(network=network, client=client)
1054 # use explicit check because empty list is a valid option
1055 if dns_nameservers is not None:
1056 subnet_kwargs['dns_nameservers'] = dns_nameservers
1057 subnet = self._create_subnet(**subnet_kwargs)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001058 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001059 return network, subnet, router
1060
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001061 def create_server(self, name=None, image=None, flavor=None,
1062 wait_on_boot=True, wait_on_delete=True,
1063 create_kwargs=None):
1064 vnic_type = CONF.network.port_vnic_type
1065
1066 # If vnic_type is configured create port for
1067 # every network
1068 if vnic_type:
1069 ports = []
1070 networks = []
1071 create_port_body = {'binding:vnic_type': vnic_type,
1072 'namestart': 'port-smoke'}
1073 if create_kwargs:
1074 net_client = create_kwargs.get("network_client",
1075 self.network_client)
1076
1077 # Convert security group names to security group ids
1078 # to pass to create_port
1079 if create_kwargs.get('security_groups'):
1080 security_groups = net_client.list_security_groups().get(
1081 'security_groups')
1082 sec_dict = dict([(s['name'], s['id'])
1083 for s in security_groups])
1084
1085 sec_groups_names = [s['name'] for s in create_kwargs[
1086 'security_groups']]
1087 security_groups_ids = [sec_dict[s]
1088 for s in sec_groups_names]
1089
1090 if security_groups_ids:
1091 create_port_body[
1092 'security_groups'] = security_groups_ids
1093 networks = create_kwargs.get('networks')
1094 else:
1095 net_client = self.network_client
1096 # If there are no networks passed to us we look up
1097 # for the tenant's private networks and create a port
1098 # if there is only one private network. The same behaviour
1099 # as we would expect when passing the call to the clients
1100 # with no networks
1101 if not networks:
1102 networks = net_client.list_networks(filters={
1103 'router:external': False})
1104 self.assertEqual(1, len(networks),
1105 "There is more than one"
1106 " network for the tenant")
1107 for net in networks:
1108 net_id = net['uuid']
1109 port = self._create_port(network_id=net_id,
1110 client=net_client,
1111 **create_port_body)
1112 ports.append({'port': port.id})
1113 if ports:
1114 create_kwargs['networks'] = ports
1115
1116 return super(NetworkScenarioTest, self).create_server(
1117 name=name, image=image, flavor=flavor,
1118 wait_on_boot=wait_on_boot, wait_on_delete=wait_on_delete,
1119 create_kwargs=create_kwargs)
1120
Yair Fried1fc32a12014-08-04 09:11:30 +03001121
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001122# power/provision states as of icehouse
1123class BaremetalPowerStates(object):
1124 """Possible power states of an Ironic node."""
1125 POWER_ON = 'power on'
1126 POWER_OFF = 'power off'
1127 REBOOT = 'rebooting'
1128 SUSPEND = 'suspended'
1129
1130
1131class BaremetalProvisionStates(object):
1132 """Possible provision states of an Ironic node."""
1133 NOSTATE = None
1134 INIT = 'initializing'
1135 ACTIVE = 'active'
1136 BUILDING = 'building'
1137 DEPLOYWAIT = 'wait call-back'
1138 DEPLOYING = 'deploying'
1139 DEPLOYFAIL = 'deploy failed'
1140 DEPLOYDONE = 'deploy complete'
1141 DELETING = 'deleting'
1142 DELETED = 'deleted'
1143 ERROR = 'error'
1144
1145
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001146class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001147 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001148 def skip_checks(cls):
1149 super(BaremetalScenarioTest, cls).skip_checks()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001150 if (not CONF.service_available.ironic or
1151 not CONF.baremetal.driver_enabled):
1152 msg = 'Ironic not available or Ironic compute driver not enabled'
1153 raise cls.skipException(msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001154
1155 @classmethod
1156 def setup_credentials(cls):
1157 super(BaremetalScenarioTest, cls).setup_credentials()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001158
1159 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001160 manager = clients.Manager(
1161 credentials=cls.admin_credentials()
1162 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001163 cls.baremetal_client = manager.baremetal_client
1164
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001165 @classmethod
1166 def resource_setup(cls):
1167 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001168 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001169 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001170
1171 def _node_state_timeout(self, node_id, state_attr,
1172 target_states, timeout=10, interval=1):
1173 if not isinstance(target_states, list):
1174 target_states = [target_states]
1175
1176 def check_state():
1177 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001178 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001179 return True
1180 return False
1181
1182 if not tempest.test.call_until_true(
1183 check_state, timeout, interval):
1184 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1185 (node_id, state_attr, target_states))
1186 raise exceptions.TimeoutException(msg)
1187
1188 def wait_provisioning_state(self, node_id, state, timeout):
1189 self._node_state_timeout(
1190 node_id=node_id, state_attr='provision_state',
1191 target_states=state, timeout=timeout)
1192
1193 def wait_power_state(self, node_id, state):
1194 self._node_state_timeout(
1195 node_id=node_id, state_attr='power_state',
1196 target_states=state, timeout=CONF.baremetal.power_timeout)
1197
1198 def wait_node(self, instance_id):
1199 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001200
Adam Gandelman4a48a602014-03-20 18:23:18 -07001201 def _get_node():
1202 node = None
1203 try:
1204 node = self.get_node(instance_id=instance_id)
Masayuki Igawabfa07602015-01-20 18:47:17 +09001205 except lib_exc.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001206 pass
1207 return node is not None
1208
1209 if not tempest.test.call_until_true(
1210 _get_node, CONF.baremetal.association_timeout, 1):
1211 msg = ('Timed out waiting to get Ironic node by instance id %s'
1212 % instance_id)
1213 raise exceptions.TimeoutException(msg)
1214
1215 def get_node(self, node_id=None, instance_id=None):
1216 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001217 _, body = self.baremetal_client.show_node(node_id)
1218 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001219 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001220 _, body = self.baremetal_client.show_node_by_instance_uuid(
1221 instance_id)
1222 if body['nodes']:
1223 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001224
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001225 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001226 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001227 _, body = self.baremetal_client.list_node_ports(node_uuid)
1228 for port in body['ports']:
1229 _, p = self.baremetal_client.show_port(port['uuid'])
1230 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001231 return ports
1232
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001233 def add_keypair(self):
1234 self.keypair = self.create_keypair()
1235
1236 def verify_connectivity(self, ip=None):
1237 if ip:
1238 dest = self.get_remote_client(ip)
1239 else:
1240 dest = self.get_remote_client(self.instance)
1241 dest.validate_authentication()
1242
1243 def boot_instance(self):
1244 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001245 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001246 }
1247 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001248 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001249
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001250 self.wait_node(self.instance['id'])
1251 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001252
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001253 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001254
1255 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001256 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001257 [BaremetalProvisionStates.DEPLOYWAIT,
1258 BaremetalProvisionStates.ACTIVE],
1259 timeout=15)
1260
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001261 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001262 BaremetalProvisionStates.ACTIVE,
1263 timeout=CONF.baremetal.active_timeout)
1264
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001265 self.servers_client.wait_for_server_status(self.instance['id'],
1266 'ACTIVE')
1267 self.node = self.get_node(instance_id=self.instance['id'])
David Kranz0fb14292015-02-11 15:55:20 -05001268 self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001269
1270 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001271 self.servers_client.delete_server(self.instance['id'])
1272 self.wait_power_state(self.node['uuid'],
1273 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001274 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001275 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001276 BaremetalProvisionStates.NOSTATE,
1277 timeout=CONF.baremetal.unprovision_timeout)
1278
Adam Gandelman4a48a602014-03-20 18:23:18 -07001279
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001280class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001281 """
1282 Base class for encryption scenario tests
1283 """
1284
1285 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001286 def setup_clients(cls):
1287 super(EncryptionScenarioTest, cls).setup_clients()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001288 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001289
1290 def _wait_for_volume_status(self, status):
1291 self.status_timeout(
1292 self.volume_client.volumes, self.volume.id, status)
1293
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001294 def nova_boot(self):
1295 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001296 create_kwargs = {'key_name': self.keypair['name']}
1297 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001298 create_kwargs=create_kwargs)
1299
1300 def create_volume_type(self, client=None, name=None):
1301 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001302 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001303 if not name:
1304 name = 'generic'
1305 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1306 LOG.debug("Creating a volume type: %s", randomized_name)
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001307 body = client.create_volume_type(
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001308 randomized_name)
1309 self.assertIn('id', body)
1310 self.addCleanup(client.delete_volume_type, body['id'])
1311 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001312
1313 def create_encryption_type(self, client=None, type_id=None, provider=None,
1314 key_size=None, cipher=None,
1315 control_location=None):
1316 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001317 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001318 if not type_id:
1319 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001320 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001321 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001322 client.create_encryption_type(
1323 type_id, provider=provider, key_size=key_size, cipher=cipher,
1324 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001325
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001326
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001327class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001328 """
1329 Base class for orchestration scenario tests
1330 """
1331
1332 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001333 def skip_checks(cls):
1334 super(OrchestrationScenarioTest, cls).skip_checks()
Matthew Treinish6c072292014-01-29 19:15:52 +00001335 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001336 raise cls.skipException("Heat support is required")
1337
1338 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001339 def credentials(cls):
Andrea Frittoli9efbe952015-01-29 12:43:09 +00001340 admin_creds = cred_provider.get_configured_credentials(
1341 'identity_admin')
1342 creds = cred_provider.get_configured_credentials('user')
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001343 admin_creds.tenant_name = creds.tenant_name
1344 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001345
1346 def _load_template(self, base_file, file_name):
1347 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1348 file_name)
1349 with open(filepath) as f:
1350 return f.read()
1351
1352 @classmethod
1353 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001354 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001355
1356 @classmethod
1357 def _get_default_network(cls):
David Kranz34e88122014-12-11 15:24:05 -05001358 networks = cls.networks_client.list_networks()
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001359 for net in networks:
1360 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001361 return net
Steve Baker22c16602014-05-05 13:34:19 +12001362
1363 @staticmethod
1364 def _stack_output(stack, output_key):
1365 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001366 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001367 if o['output_key'] == output_key), None)
1368
Chris Dent0d494112014-08-26 13:48:30 +01001369
1370class SwiftScenarioTest(ScenarioTest):
1371 """
1372 Provide harness to do Swift scenario tests.
1373
1374 Subclasses implement the tests that use the methods provided by this
1375 class.
1376 """
1377
1378 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001379 def skip_checks(cls):
1380 super(SwiftScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001381 if not CONF.service_available.swift:
1382 skip_msg = ("%s skipped as swift is not available" %
1383 cls.__name__)
1384 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001385
1386 @classmethod
1387 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001388 cls.set_network_resources()
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001389 super(SwiftScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001390 operator_role = CONF.object_storage.operator_role
1391 if not cls.isolated_creds.is_role_available(operator_role):
1392 skip_msg = ("%s skipped because the configured credential provider"
1393 " is not able to provide credentials with the %s role "
1394 "assigned." % (cls.__name__, operator_role))
1395 raise cls.skipException(skip_msg)
1396 else:
1397 cls.os_operator = clients.Manager(
1398 cls.isolated_creds.get_creds_by_roles(
1399 [operator_role]))
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001400
1401 @classmethod
1402 def setup_clients(cls):
1403 super(SwiftScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001404 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001405 cls.account_client = cls.os_operator.account_client
1406 cls.container_client = cls.os_operator.container_client
1407 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001408
Chris Dentde456a12014-09-10 12:41:15 +01001409 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001410 """get swift status for our user account."""
1411 self.account_client.list_account_containers()
1412 LOG.debug('Swift status information obtained successfully')
1413
Chris Dentde456a12014-09-10 12:41:15 +01001414 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001415 name = container_name or data_utils.rand_name(
1416 'swift-scenario-container')
1417 self.container_client.create_container(name)
1418 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001419 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001420 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001421 self.addCleanup(self.delete_wrapper,
1422 self.container_client.delete_container,
1423 name)
Chris Dent0d494112014-08-26 13:48:30 +01001424 return name
1425
Chris Dentde456a12014-09-10 12:41:15 +01001426 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001427 self.container_client.delete_container(container_name)
1428 LOG.debug('Container %s deleted' % (container_name))
1429
Chris Dentde456a12014-09-10 12:41:15 +01001430 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001431 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1432 obj_data = data_utils.arbitrary_string()
1433 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001434 self.addCleanup(self.delete_wrapper,
1435 self.object_client.delete_object,
1436 container_name,
1437 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001438 return obj_name, obj_data
1439
Chris Dentde456a12014-09-10 12:41:15 +01001440 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001441 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001442 self.list_and_check_container_objects(container_name,
1443 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001444
Chris Dentde456a12014-09-10 12:41:15 +01001445 def list_and_check_container_objects(self, container_name,
1446 present_obj=None,
1447 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001448 """
1449 List objects for a given container and assert which are present and
1450 which are not.
1451 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001452 if present_obj is None:
1453 present_obj = []
1454 if not_present_obj is None:
1455 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001456 _, object_list = self.container_client.list_container_contents(
1457 container_name)
1458 if present_obj:
1459 for obj in present_obj:
1460 self.assertIn(obj, object_list)
1461 if not_present_obj:
1462 for obj in not_present_obj:
1463 self.assertNotIn(obj, object_list)
1464
Chris Dentde456a12014-09-10 12:41:15 +01001465 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001466 metadata_param = {'metadata_prefix': 'x-container-',
1467 'metadata': {'read': acl}}
1468 self.container_client.update_container_metadata(container_name,
1469 **metadata_param)
1470 resp, _ = self.container_client.list_container_metadata(container_name)
1471 self.assertEqual(resp['x-container-read'], acl)
1472
Chris Dentde456a12014-09-10 12:41:15 +01001473 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001474 _, obj = self.object_client.get_object(container_name, obj_name)
1475 self.assertEqual(obj, expected_data)