blob: 61fb088fb0550ce6cffd59790ab308efc05b7e56 [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
Matthew Treinish96e9e882014-06-09 18:37:19 -040021import six
Matthew Treinish01472ff2015-02-20 17:26:52 -050022from tempest_lib.common.utils import data_utils
Masayuki Igawad9388762015-01-20 14:56:42 +090023from tempest_lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040024
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000025from tempest import clients
Andrea Frittoli9efbe952015-01-29 12:43:09 +000026from tempest.common import cred_provider
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010027from tempest.common import credentials
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090028from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000029from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020030from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020031from tempest.openstack.common import log
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
Andrea Frittoliac20b5e2014-09-15 13:31:14 +010044 def resource_setup(cls):
45 super(ScenarioTest, cls).resource_setup()
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())
54 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070055 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010056 cls.floating_ips_client = cls.manager.floating_ips_client
57 # Glance image client v1
58 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000059 # Compute image client
60 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010061 cls.keypairs_client = cls.manager.keypairs_client
62 cls.networks_client = cls.admin_manager.networks_client
63 # Nova security groups client
64 cls.security_groups_client = cls.manager.security_groups_client
65 cls.servers_client = cls.manager.servers_client
66 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000067 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030068 cls.interface_client = cls.manager.interfaces_client
69 # Neutron network client
70 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090071 # Heat client
72 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010073
74 @classmethod
Andrea Frittoli2e733b52014-07-16 14:12:11 +010075 def credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010076 return cls.isolated_creds.get_primary_creds()
Andrea Frittoli2e733b52014-07-16 14:12:11 +010077
Masayuki Igawaccd66592014-07-17 00:42:42 +090078 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +030079 def alt_credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010080 return cls.isolated_creds.get_alt_creds()
Yair Frieddb6c9e92014-08-06 08:53:13 +030081
82 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +090083 def admin_credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010084 try:
85 return cls.isolated_creds.get_admin_creds()
86 except NotImplementedError:
87 raise cls.skipException('Admin Credentials are not available')
Masayuki Igawaccd66592014-07-17 00:42:42 +090088
Andrea Frittoli247058f2014-07-16 16:09:22 +010089 # ## Methods to handle sync and async deletes
90
91 def setUp(self):
92 super(ScenarioTest, self).setUp()
93 self.cleanup_waits = []
94 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
95 # because scenario tests in the same test class should not share
96 # resources. If resources were shared between test cases then it
97 # should be a single scenario test instead of multiples.
98
99 # NOTE(yfried): this list is cleaned at the end of test_methods and
100 # not at the end of the class
101 self.addCleanup(self._wait_for_cleanups)
102
Yair Fried1fc32a12014-08-04 09:11:30 +0300103 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100104 """Ignores NotFound exceptions for delete operations.
105
Yair Fried1fc32a12014-08-04 09:11:30 +0300106 @param delete_thing: delete method of a resource. method will be
107 executed as delete_thing(*args, **kwargs)
108
Andrea Frittoli247058f2014-07-16 16:09:22 +0100109 """
110 try:
111 # Tempest clients return dicts, so there is no common delete
112 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300113 delete_thing(*args, **kwargs)
Masayuki Igawabfa07602015-01-20 18:47:17 +0900114 except lib_exc.NotFound:
Andrea Frittoli247058f2014-07-16 16:09:22 +0100115 # If the resource is already missing, mission accomplished.
116 pass
117
118 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900119 cleanup_callable, cleanup_args=None,
120 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700121 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100122
123 @param waiter_callable: callable to wait for the resource to delete
124 @param thing_id: the id of the resource to be cleaned-up
125 @param thing_id_param: the name of the id param in the waiter
126 @param cleanup_callable: method to load pass to self.addCleanup with
127 the following *cleanup_args, **cleanup_kwargs.
128 usually a delete method.
129 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900130 if cleanup_args is None:
131 cleanup_args = []
132 if cleanup_kwargs is None:
133 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100134 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
135 wait_dict = {
136 'waiter_callable': waiter_callable,
137 thing_id_param: thing_id
138 }
139 self.cleanup_waits.append(wait_dict)
140
141 def _wait_for_cleanups(self):
142 """To handle async delete actions, a list of waits is added
143 which will be iterated over as the last step of clearing the
144 cleanup queue. That way all the delete calls are made up front
145 and the tests won't succeed unless the deletes are eventually
146 successful. This is the same basic approach used in the api tests to
147 limit cleanup execution time except here it is multi-resource,
148 because of the nature of the scenario tests.
149 """
150 for wait in self.cleanup_waits:
151 waiter_callable = wait.pop('waiter_callable')
152 waiter_callable(**wait)
153
154 # ## Test functions library
155 #
156 # The create_[resource] functions only return body and discard the
157 # resp part which is not used in scenario tests
158
Yair Frieddb6c9e92014-08-06 08:53:13 +0300159 def create_keypair(self, client=None):
160 if not client:
161 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100162 name = data_utils.rand_name(self.__class__.__name__)
163 # We don't need to create a keypair by pubkey in scenario
David Kranz173f0e02015-02-06 13:47:57 -0500164 body = client.create_keypair(name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300165 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100166 return body
167
168 def create_server(self, name=None, image=None, flavor=None,
169 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900170 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100171 """Creates VM instance.
172
173 @param image: image from which to create the instance
174 @param wait_on_boot: wait for status ACTIVE before continue
175 @param wait_on_delete: force synchronous delete on cleanup
176 @param create_kwargs: additional details for instance creation
177 @return: server dict
178 """
179 if name is None:
180 name = data_utils.rand_name(self.__class__.__name__)
181 if image is None:
182 image = CONF.compute.image_ref
183 if flavor is None:
184 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900185 if create_kwargs is None:
186 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100187
Andrea Frittoli247058f2014-07-16 16:09:22 +0100188 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
189 name, image, flavor)
David Kranz0fb14292015-02-11 15:55:20 -0500190 server = self.servers_client.create_server(name, image, flavor,
191 **create_kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100192 if wait_on_delete:
193 self.addCleanup(self.servers_client.wait_for_server_termination,
194 server['id'])
195 self.addCleanup_with_wait(
196 waiter_callable=self.servers_client.wait_for_server_termination,
197 thing_id=server['id'], thing_id_param='server_id',
198 cleanup_callable=self.delete_wrapper,
199 cleanup_args=[self.servers_client.delete_server, server['id']])
200 if wait_on_boot:
201 self.servers_client.wait_for_server_status(server_id=server['id'],
202 status='ACTIVE')
203 # The instance retrieved on creation is missing network
204 # details, necessitating retrieval after it becomes active to
205 # ensure correct details.
David Kranz0fb14292015-02-11 15:55:20 -0500206 server = self.servers_client.get_server(server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100207 self.assertEqual(server['name'], name)
208 return server
209
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100210 def create_volume(self, size=None, name=None, snapshot_id=None,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100211 imageRef=None, volume_type=None, wait_on_delete=True):
212 if name is None:
213 name = data_utils.rand_name(self.__class__.__name__)
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000214 volume = self.volumes_client.create_volume(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100215 size=size, display_name=name, snapshot_id=snapshot_id,
216 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700217
Andrea Frittoli247058f2014-07-16 16:09:22 +0100218 if wait_on_delete:
219 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
220 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700221 self.addCleanup(self.delete_wrapper,
222 self.volumes_client.delete_volume, volume['id'])
223 else:
224 self.addCleanup_with_wait(
225 waiter_callable=self.volumes_client.wait_for_resource_deletion,
226 thing_id=volume['id'], thing_id_param='id',
227 cleanup_callable=self.delete_wrapper,
228 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100229
230 self.assertEqual(name, volume['display_name'])
231 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
232 # The volume retrieved on creation has a non-up-to-date status.
233 # Retrieval after it becomes active ensures correct details.
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000234 volume = self.volumes_client.get_volume(volume['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100235 return volume
236
Yair Fried1fc32a12014-08-04 09:11:30 +0300237 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100238 _client = self.security_groups_client
239 if secgroup_id is None:
David Kranz9964b4e2015-02-06 15:45:29 -0500240 sgs = _client.list_security_groups()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100241 for sg in sgs:
242 if sg['name'] == 'default':
243 secgroup_id = sg['id']
244
245 # These rules are intended to permit inbound ssh and icmp
246 # traffic from all sources, so no group_id is provided.
247 # Setting a group_id would only permit traffic from ports
248 # belonging to the same security group.
249 rulesets = [
250 {
251 # ssh
252 'ip_proto': 'tcp',
253 'from_port': 22,
254 'to_port': 22,
255 'cidr': '0.0.0.0/0',
256 },
257 {
258 # ping
259 'ip_proto': 'icmp',
260 'from_port': -1,
261 'to_port': -1,
262 'cidr': '0.0.0.0/0',
263 }
264 ]
265 rules = list()
266 for ruleset in rulesets:
David Kranz9964b4e2015-02-06 15:45:29 -0500267 sg_rule = _client.create_security_group_rule(secgroup_id,
268 **ruleset)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100269 self.addCleanup(self.delete_wrapper,
270 _client.delete_security_group_rule,
271 sg_rule['id'])
272 rules.append(sg_rule)
273 return rules
274
Yair Fried1fc32a12014-08-04 09:11:30 +0300275 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100276 # Create security group
277 sg_name = data_utils.rand_name(self.__class__.__name__)
278 sg_desc = sg_name + " description"
David Kranz9964b4e2015-02-06 15:45:29 -0500279 secgroup = self.security_groups_client.create_security_group(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100280 sg_name, sg_desc)
281 self.assertEqual(secgroup['name'], sg_name)
282 self.assertEqual(secgroup['description'], sg_desc)
283 self.addCleanup(self.delete_wrapper,
284 self.security_groups_client.delete_security_group,
285 secgroup['id'])
286
287 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300288 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100289
290 return secgroup
291
JordanP3fe2dc32014-11-17 13:06:01 +0100292 def get_remote_client(self, server_or_ip, username=None, private_key=None,
293 log_console_of_servers=None):
294 """Get a SSH client to a remote server
295
296 @param server_or_ip a server object as returned by Tempest compute
297 client or an IP address to connect to
298 @param username name of the Linux account on the remote server
299 @param private_key the SSH private key to use
300 @param log_console_of_servers a list of server objects. Each server
301 in the list will have its console printed in the logs in case the
302 SSH connection failed to be established
303 @return a RemoteClient object
304 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100305 if isinstance(server_or_ip, six.string_types):
306 ip = server_or_ip
307 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700308 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
309 ip = addr['addr']
310
Andrea Frittoli247058f2014-07-16 16:09:22 +0100311 if username is None:
312 username = CONF.scenario.ssh_user
313 if private_key is None:
314 private_key = self.keypair['private_key']
315 linux_client = remote_client.RemoteClient(ip, username,
316 pkey=private_key)
317 try:
318 linux_client.validate_authentication()
JordanP3fe2dc32014-11-17 13:06:01 +0100319 except Exception:
320 LOG.exception('Initializing SSH connection to %s failed' % ip)
JordanP3fe2dc32014-11-17 13:06:01 +0100321 # If we don't explicitely set for which servers we want to
322 # log the console output then all the servers will be logged.
323 # See the definition of _log_console_output()
324 self._log_console_output(log_console_of_servers)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100325 raise
326
327 return linux_client
328
Ghanshyam2a180b82014-06-16 13:54:22 +0900329 def _image_create(self, name, fmt, path, properties=None):
330 if properties is None:
331 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100332 name = data_utils.rand_name('%s-' % name)
333 image_file = open(path, 'rb')
334 self.addCleanup(image_file.close)
335 params = {
336 'name': name,
337 'container_format': fmt,
338 'disk_format': fmt,
339 'is_public': 'False',
340 }
341 params.update(properties)
David Kranz34f18782015-01-06 13:43:55 -0500342 image = self.image_client.create_image(**params)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100343 self.addCleanup(self.image_client.delete_image, image['id'])
344 self.assertEqual("queued", image['status'])
345 self.image_client.update_image(image['id'], data=image_file)
346 return image['id']
347
348 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300349 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100350 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
351 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
352 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300353 img_container_format = CONF.scenario.img_container_format
354 img_disk_format = CONF.scenario.img_disk_format
355 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
356 "ami: %s, ari: %s, aki: %s" %
357 (img_path, img_container_format, img_disk_format,
358 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100359 try:
360 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300361 img_container_format,
362 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100363 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300364 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100365 except IOError:
366 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
367 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
368 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
369 properties = {
370 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
371 }
372 self.image = self._image_create('scenario-ami', 'ami',
373 path=ami_img_path,
374 properties=properties)
375 LOG.debug("image:%s" % self.image)
376
377 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400378 if not CONF.compute_feature_enabled.console_output:
379 LOG.debug('Console output not supported, cannot log')
380 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100381 if not servers:
David Kranzae99b9a2015-02-16 13:37:01 -0500382 servers = self.servers_client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100383 servers = servers['servers']
384 for server in servers:
Brant Knudson566c5712014-09-24 20:04:50 -0500385 console_output = self.servers_client.get_console_output(
David Kranzae99b9a2015-02-16 13:37:01 -0500386 server['id'], length=None).data
387 LOG.debug('Console output for %s\nbody=\n%s',
388 server['id'], console_output)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100389
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000390 def _log_net_info(self, exc):
391 # network debug is called as part of ssh init
392 if not isinstance(exc, exceptions.SSHTimeout):
393 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000394
nithya-ganesan882595e2014-07-29 18:51:07 +0000395 def create_server_snapshot(self, server, name=None):
396 # Glance client
397 _image_client = self.image_client
398 # Compute client
399 _images_client = self.images_client
400 if name is None:
401 name = data_utils.rand_name('scenario-snapshot-')
402 LOG.debug("Creating a snapshot image for server: %s", server['name'])
David Kranza5299eb2015-01-15 17:24:05 -0500403 image = _images_client.create_image(server['id'], name)
404 image_id = image.response['location'].split('images/')[1]
nithya-ganesan882595e2014-07-29 18:51:07 +0000405 _image_client.wait_for_image_status(image_id, 'active')
406 self.addCleanup_with_wait(
407 waiter_callable=_image_client.wait_for_resource_deletion,
408 thing_id=image_id, thing_id_param='id',
409 cleanup_callable=self.delete_wrapper,
410 cleanup_args=[_image_client.delete_image, image_id])
David Kranz34f18782015-01-06 13:43:55 -0500411 snapshot_image = _image_client.get_image_meta(image_id)
nithya-ganesan882595e2014-07-29 18:51:07 +0000412 image_name = snapshot_image['name']
413 self.assertEqual(name, image_name)
414 LOG.debug("Created snapshot image %s for server %s",
415 image_name, server['name'])
416 return snapshot_image
417
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900418 def nova_volume_attach(self):
419 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000420 volume = self.servers_client.attach_volume(
David Kranz3ebc7212015-02-10 12:19:19 -0500421 self.server['id'], self.volume['id'], '/dev/vdb')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900422 self.assertEqual(self.volume['id'], volume['id'])
423 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
424 # Refresh the volume after the attachment
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000425 self.volume = self.volumes_client.get_volume(volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900426
427 def nova_volume_detach(self):
428 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
429 self.volumes_client.wait_for_volume_status(self.volume['id'],
430 'available')
431
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000432 volume = self.volumes_client.get_volume(self.volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900433 self.assertEqual('available', volume['status'])
434
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700435 def rebuild_server(self, server_id, image=None,
436 preserve_ephemeral=False, wait=True,
437 rebuild_kwargs=None):
438 if image is None:
439 image = CONF.compute.image_ref
440
441 rebuild_kwargs = rebuild_kwargs or {}
442
443 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
444 server_id, image, preserve_ephemeral)
445 self.servers_client.rebuild(server_id=server_id, image_ref=image,
446 preserve_ephemeral=preserve_ephemeral,
447 **rebuild_kwargs)
448 if wait:
449 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
450
Steven Hardyda2a8352014-10-02 12:52:20 +0100451 def ping_ip_address(self, ip_address, should_succeed=True,
452 ping_timeout=None):
453 timeout = ping_timeout or CONF.compute.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700454 cmd = ['ping', '-c1', '-w1', ip_address]
455
456 def ping():
457 proc = subprocess.Popen(cmd,
458 stdout=subprocess.PIPE,
459 stderr=subprocess.PIPE)
460 proc.communicate()
461 return (proc.returncode == 0) == should_succeed
462
Steven Hardyda2a8352014-10-02 12:52:20 +0100463 return tempest.test.call_until_true(ping, timeout, 1)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700464
Yair Friedae0e73d2014-11-24 11:56:26 +0200465 def check_vm_connectivity(self, ip_address,
466 username=None,
467 private_key=None,
468 should_connect=True):
469 """
470 :param ip_address: server to test against
471 :param username: server's ssh username
472 :param private_key: server's ssh private key to be used
473 :param should_connect: True/False indicates positive/negative test
474 positive - attempt ping and ssh
475 negative - attempt ping and fail if succeed
476
477 :raises: AssertError if the result of the connectivity check does
478 not match the value of the should_connect param
479 """
480 if should_connect:
481 msg = "Timed out waiting for %s to become reachable" % ip_address
482 else:
483 msg = "ip address %s is reachable" % ip_address
484 self.assertTrue(self.ping_ip_address(ip_address,
485 should_succeed=should_connect),
486 msg=msg)
487 if should_connect:
488 # no need to check ssh for negative connectivity
489 self.get_remote_client(ip_address, username, private_key)
490
491 def check_public_network_connectivity(self, ip_address, username,
492 private_key, should_connect=True,
493 msg=None, servers=None):
494 # The target login is assumed to have been configured for
495 # key-based authentication by cloud-init.
496 LOG.debug('checking network connections to IP %s with user: %s' %
497 (ip_address, username))
498 try:
499 self.check_vm_connectivity(ip_address,
500 username,
501 private_key,
502 should_connect=should_connect)
Matthew Treinish53483132014-12-09 18:50:06 -0500503 except Exception:
Yair Friedae0e73d2014-11-24 11:56:26 +0200504 ex_msg = 'Public network connectivity check failed'
505 if msg:
506 ex_msg += ": " + msg
507 LOG.exception(ex_msg)
508 self._log_console_output(servers)
Yair Friedae0e73d2014-11-24 11:56:26 +0200509 raise
510
511 def create_floating_ip(self, thing, pool_name=None):
512 """Creates a floating IP and associates to a server using
513 Nova clients
514 """
515
David Kranze4e3b412015-02-10 10:50:42 -0500516 floating_ip = self.floating_ips_client.create_floating_ip(pool_name)
Yair Friedae0e73d2014-11-24 11:56:26 +0200517 self.addCleanup(self.delete_wrapper,
518 self.floating_ips_client.delete_floating_ip,
519 floating_ip['id'])
520 self.floating_ips_client.associate_floating_ip_to_server(
521 floating_ip['ip'], thing['id'])
522 return floating_ip
523
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100524
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100525class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300526 """Base class for network scenario tests.
527 This class provide helpers for network scenario tests, using the neutron
528 API. Helpers from ancestor which use the nova network API are overridden
529 with the neutron API.
530
531 This Class also enforces using Neutron instead of novanetwork.
532 Subclassed tests will be skipped if Neutron is not enabled
533
534 """
535
536 @classmethod
537 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100538 if not CONF.service_available.neutron:
539 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300540
541 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100542 def resource_setup(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +0900543 cls.check_preconditions()
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100544 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300545 cls.tenant_id = cls.manager.identity_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300546
Yair Frieddb6c9e92014-08-06 08:53:13 +0300547 def _create_network(self, client=None, tenant_id=None,
548 namestart='network-smoke-'):
549 if not client:
550 client = self.network_client
551 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000552 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300553 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500554 result = client.create_network(name=name, tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300555 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300556 **result['network'])
557 self.assertEqual(network.name, name)
558 self.addCleanup(self.delete_wrapper, network.delete)
559 return network
560
561 def _list_networks(self, *args, **kwargs):
562 """List networks using admin creds """
563 return self._admin_lister('networks')(*args, **kwargs)
564
565 def _list_subnets(self, *args, **kwargs):
566 """List subnets using admin creds """
567 return self._admin_lister('subnets')(*args, **kwargs)
568
569 def _list_routers(self, *args, **kwargs):
570 """List routers using admin creds """
571 return self._admin_lister('routers')(*args, **kwargs)
572
573 def _list_ports(self, *args, **kwargs):
574 """List ports using admin creds """
575 return self._admin_lister('ports')(*args, **kwargs)
576
577 def _admin_lister(self, resource_type):
578 def temp(*args, **kwargs):
579 temp_method = self.admin_manager.network_client.__getattr__(
580 'list_%s' % resource_type)
David Kranz34e88122014-12-11 15:24:05 -0500581 resource_list = temp_method(*args, **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300582 return resource_list[resource_type]
583 return temp
584
Yair Frieddb6c9e92014-08-06 08:53:13 +0300585 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
586 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300587 """
588 Create a subnet for the given network within the cidr block
589 configured for tenant networks.
590 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300591 if not client:
592 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300593
594 def cidr_in_use(cidr, tenant_id):
595 """
596 :return True if subnet with cidr already exist in tenant
597 False else
598 """
599 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
600 return len(cidr_in_use) != 0
601
Kirill Shileev14113572014-11-21 16:58:02 +0300602 ip_version = kwargs.pop('ip_version', 4)
603
604 if ip_version == 6:
605 tenant_cidr = netaddr.IPNetwork(
606 CONF.network.tenant_network_v6_cidr)
607 num_bits = CONF.network.tenant_network_v6_mask_bits
608 else:
609 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
610 num_bits = CONF.network.tenant_network_mask_bits
611
Yair Fried1fc32a12014-08-04 09:11:30 +0300612 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300613 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300614 # Repeatedly attempt subnet creation with sequential cidr
615 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300616 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300617 str_cidr = str(subnet_cidr)
618 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
619 continue
620
621 subnet = dict(
622 name=data_utils.rand_name(namestart),
Yair Fried1fc32a12014-08-04 09:11:30 +0300623 network_id=network.id,
624 tenant_id=network.tenant_id,
625 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300626 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300627 **kwargs
628 )
629 try:
David Kranz34e88122014-12-11 15:24:05 -0500630 result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300631 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900632 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300633 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
634 if not is_overlapping_cidr:
635 raise
636 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300637 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300638 **result['subnet'])
639 self.assertEqual(subnet.cidr, str_cidr)
640 self.addCleanup(self.delete_wrapper, subnet.delete)
641 return subnet
642
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200643 def _create_port(self, network_id, client=None, namestart='port-quotatest',
644 **kwargs):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300645 if not client:
646 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300647 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500648 result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300649 name=name,
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200650 network_id=network_id,
651 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300652 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300653 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300654 **result['port'])
655 self.addCleanup(self.delete_wrapper, port.delete)
656 return port
657
Kirill Shileev14113572014-11-21 16:58:02 +0300658 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300659 ports = self._list_ports(device_id=server['id'],
660 fixed_ip=ip_addr)
661 self.assertEqual(len(ports), 1,
662 "Unable to determine which port to target.")
Kirill Shileev14113572014-11-21 16:58:02 +0300663 # it might happen here that this port has more then one ip address
664 # as in case of dual stack- when this port is created on 2 subnets
665 for ip46 in ports[0]['fixed_ips']:
666 ip = ip46['ip_address']
667 if netaddr.valid_ipv4(ip):
668 return ports[0]['id'], ip
Yair Fried1fc32a12014-08-04 09:11:30 +0300669
David Shrewsbury9bac3662014-08-07 15:07:01 -0400670 def _get_network_by_name(self, network_name):
671 net = self._list_networks(name=network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300672 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400673
Yair Friedae0e73d2014-11-24 11:56:26 +0200674 def create_floating_ip(self, thing, external_network_id=None,
675 port_id=None, client=None):
676 """Creates a floating IP and associates to a resource/port using
677 Neutron client
678 """
679 if not external_network_id:
680 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300681 if not client:
682 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300683 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300684 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
685 else:
686 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500687 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300688 floating_network_id=external_network_id,
689 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300690 tenant_id=thing['tenant_id'],
691 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300692 )
693 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300694 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300695 **result['floatingip'])
696 self.addCleanup(self.delete_wrapper, floating_ip.delete)
697 return floating_ip
698
699 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300700 port_id, _ = self._get_server_port_id_and_ip4(server)
Yair Fried1fc32a12014-08-04 09:11:30 +0300701 floating_ip.update(port_id=port_id)
702 self.assertEqual(port_id, floating_ip.port_id)
703 return floating_ip
704
705 def _disassociate_floating_ip(self, floating_ip):
706 """
707 :param floating_ip: type DeletableFloatingIp
708 """
709 floating_ip.update(port_id=None)
710 self.assertIsNone(floating_ip.port_id)
711 return floating_ip
712
Yair Fried45f92952014-06-26 05:19:19 +0300713 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000714 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300715
716 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
717 to check status
718 :param status: target status
719 :raises: AssertionError if status doesn't match
720 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000721 def refresh():
722 floating_ip.refresh()
723 return status == floating_ip.status
724
725 tempest.test.call_until_true(refresh,
726 CONF.network.build_timeout,
727 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300728 self.assertEqual(status, floating_ip.status,
729 message="FloatingIP: {fp} is at status: {cst}. "
730 "failed to reach status: {st}"
731 .format(fp=floating_ip, cst=floating_ip.status,
732 st=status))
733 LOG.info("FloatingIP: {fp} is at status: {st}"
734 .format(fp=floating_ip, st=status))
735
Yair Fried1fc32a12014-08-04 09:11:30 +0300736 def _check_tenant_network_connectivity(self, server,
737 username,
738 private_key,
739 should_connect=True,
740 servers_for_debug=None):
741 if not CONF.network.tenant_networks_reachable:
742 msg = 'Tenant networks not configured to be reachable.'
743 LOG.info(msg)
744 return
745 # The target login is assumed to have been configured for
746 # key-based authentication by cloud-init.
747 try:
ghanshyam807211c2014-12-18 13:21:22 +0900748 for net_name, ip_addresses in server['addresses'].iteritems():
Yair Fried1fc32a12014-08-04 09:11:30 +0300749 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900750 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200751 username,
752 private_key,
753 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300754 except Exception as e:
755 LOG.exception('Tenant network connectivity check failed')
756 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000757 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300758 raise
759
760 def _check_remote_connectivity(self, source, dest, should_succeed=True):
761 """
762 check ping server via source ssh connection
763
764 :param source: RemoteClient: an ssh connection from which to ping
765 :param dest: and IP to ping against
766 :param should_succeed: boolean should ping succeed or not
767 :returns: boolean -- should_succeed == ping
768 :returns: ping is false if ping failed
769 """
770 def ping_remote():
771 try:
772 source.ping_host(dest)
773 except exceptions.SSHExecCommandFailed:
774 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
775 % (dest, source.ssh_client.host))
776 return not should_succeed
777 return should_succeed
778
779 return tempest.test.call_until_true(ping_remote,
780 CONF.compute.ping_timeout,
781 1)
782
Yair Frieddb6c9e92014-08-06 08:53:13 +0300783 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300784 namestart='secgroup-smoke'):
785 if client is None:
786 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300787 if tenant_id is None:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000788 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300789 secgroup = self._create_empty_security_group(namestart=namestart,
790 client=client,
791 tenant_id=tenant_id)
792
793 # Add rules to the security group
ghanshyam38890b52015-01-21 15:24:18 +0900794 rules = self._create_loginable_secgroup_rule(client=client,
795 secgroup=secgroup)
Yair Fried1fc32a12014-08-04 09:11:30 +0300796 for rule in rules:
797 self.assertEqual(tenant_id, rule.tenant_id)
798 self.assertEqual(secgroup.id, rule.security_group_id)
799 return secgroup
800
Yair Frieddb6c9e92014-08-06 08:53:13 +0300801 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300802 namestart='secgroup-smoke'):
803 """Create a security group without rules.
804
805 Default rules will be created:
806 - IPv4 egress to any
807 - IPv6 egress to any
808
809 :param tenant_id: secgroup will be created in this tenant
810 :returns: DeletableSecurityGroup -- containing the secgroup created
811 """
812 if client is None:
813 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300814 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000815 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300816 sg_name = data_utils.rand_name(namestart)
817 sg_desc = sg_name + " description"
818 sg_dict = dict(name=sg_name,
819 description=sg_desc)
820 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -0500821 result = client.create_security_group(**sg_dict)
Yair Fried1fc32a12014-08-04 09:11:30 +0300822 secgroup = net_resources.DeletableSecurityGroup(
823 client=client,
824 **result['security_group']
825 )
826 self.assertEqual(secgroup.name, sg_name)
827 self.assertEqual(tenant_id, secgroup.tenant_id)
828 self.assertEqual(secgroup.description, sg_desc)
829 self.addCleanup(self.delete_wrapper, secgroup.delete)
830 return secgroup
831
Yair Frieddb6c9e92014-08-06 08:53:13 +0300832 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300833 """Get default secgroup for given tenant_id.
834
835 :returns: DeletableSecurityGroup -- default secgroup for given tenant
836 """
837 if client is None:
838 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300839 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000840 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300841 sgs = [
842 sg for sg in client.list_security_groups().values()[0]
843 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
844 ]
845 msg = "No default security group for tenant %s." % (tenant_id)
846 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300847 return net_resources.DeletableSecurityGroup(client=client,
848 **sgs[0])
849
Yair Frieddb6c9e92014-08-06 08:53:13 +0300850 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300851 tenant_id=None, **kwargs):
852 """Create a rule from a dictionary of rule parameters.
853
854 Create a rule in a secgroup. if secgroup not defined will search for
855 default secgroup in tenant_id.
856
857 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300858 :param tenant_id: if secgroup not passed -- the tenant in which to
859 search for default secgroup
860 :param kwargs: a dictionary containing rule parameters:
861 for example, to allow incoming ssh:
862 rule = {
863 direction: 'ingress'
864 protocol:'tcp',
865 port_range_min: 22,
866 port_range_max: 22
867 }
868 """
869 if client is None:
870 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300871 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000872 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300873 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300874 secgroup = self._default_security_group(client=client,
875 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300876
877 ruleset = dict(security_group_id=secgroup.id,
878 tenant_id=secgroup.tenant_id)
879 ruleset.update(kwargs)
880
David Kranz34e88122014-12-11 15:24:05 -0500881 sg_rule = client.create_security_group_rule(**ruleset)
Yair Fried1fc32a12014-08-04 09:11:30 +0300882 sg_rule = net_resources.DeletableSecurityGroupRule(
883 client=client,
884 **sg_rule['security_group_rule']
885 )
886 self.addCleanup(self.delete_wrapper, sg_rule.delete)
887 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
888 self.assertEqual(secgroup.id, sg_rule.security_group_id)
889
890 return sg_rule
891
892 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
893 """These rules are intended to permit inbound ssh and icmp
894 traffic from all sources, so no group_id is provided.
895 Setting a group_id would only permit traffic from ports
896 belonging to the same security group.
897 """
898
899 if client is None:
900 client = self.network_client
901 rules = []
902 rulesets = [
903 dict(
904 # ssh
905 protocol='tcp',
906 port_range_min=22,
907 port_range_max=22,
908 ),
909 dict(
910 # ping
911 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +0100912 ),
913 dict(
914 # ipv6-icmp for ping6
915 protocol='icmp',
916 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +0300917 )
918 ]
919 for ruleset in rulesets:
920 for r_direction in ['ingress', 'egress']:
921 ruleset['direction'] = r_direction
922 try:
923 sg_rule = self._create_security_group_rule(
924 client=client, secgroup=secgroup, **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +0900925 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 # if rule already exist - skip rule and continue
927 msg = 'Security group rule already exists'
928 if msg not in ex._error_string:
929 raise ex
930 else:
931 self.assertEqual(r_direction, sg_rule.direction)
932 rules.append(sg_rule)
933
934 return rules
935
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500936 def _create_pool(self, lb_method, protocol, subnet_id):
937 """Wrapper utility that returns a test pool."""
938 client = self.network_client
939 name = data_utils.rand_name('pool')
David Kranz34e88122014-12-11 15:24:05 -0500940 resp_pool = client.create_pool(protocol=protocol, name=name,
941 subnet_id=subnet_id,
942 lb_method=lb_method)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500943 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
944 self.assertEqual(pool['name'], name)
945 self.addCleanup(self.delete_wrapper, pool.delete)
946 return pool
947
948 def _create_member(self, address, protocol_port, pool_id):
949 """Wrapper utility that returns a test member."""
950 client = self.network_client
David Kranz34e88122014-12-11 15:24:05 -0500951 resp_member = client.create_member(protocol_port=protocol_port,
952 pool_id=pool_id,
953 address=address)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500954 member = net_resources.DeletableMember(client=client,
955 **resp_member['member'])
956 self.addCleanup(self.delete_wrapper, member.delete)
957 return member
958
959 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
960 """Wrapper utility that returns a test vip."""
961 client = self.network_client
962 name = data_utils.rand_name('vip')
David Kranz34e88122014-12-11 15:24:05 -0500963 resp_vip = client.create_vip(protocol=protocol, name=name,
964 subnet_id=subnet_id, pool_id=pool_id,
965 protocol_port=protocol_port)
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500966 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
967 self.assertEqual(vip['name'], name)
968 self.addCleanup(self.delete_wrapper, vip.delete)
969 return vip
970
Yair Fried1fc32a12014-08-04 09:11:30 +0300971 def _ssh_to_server(self, server, private_key):
972 ssh_login = CONF.compute.image_ssh_user
973 return self.get_remote_client(server,
974 username=ssh_login,
975 private_key=private_key)
976
Yair Frieddb6c9e92014-08-06 08:53:13 +0300977 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300978 """Retrieve a router for the given tenant id.
979
980 If a public router has been configured, it will be returned.
981
982 If a public router has not been configured, but a public
983 network has, a tenant router will be created and returned that
984 routes traffic to the public network.
985 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300986 if not client:
987 client = self.network_client
988 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000989 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300990 router_id = CONF.network.public_router_id
991 network_id = CONF.network.public_network_id
992 if router_id:
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +0400993 resp, body = client.show_router(router_id)
994 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300995 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300996 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300997 router.set_gateway(network_id)
998 return router
999 else:
1000 raise Exception("Neither of 'public_router_id' or "
1001 "'public_network_id' has been defined.")
1002
Yair Frieddb6c9e92014-08-06 08:53:13 +03001003 def _create_router(self, client=None, tenant_id=None,
1004 namestart='router-smoke'):
1005 if not client:
1006 client = self.network_client
1007 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001008 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001009 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -05001010 result = client.create_router(name=name,
1011 admin_state_up=True,
1012 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001013 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +03001014 **result['router'])
1015 self.assertEqual(router.name, name)
1016 self.addCleanup(self.delete_wrapper, router.delete)
1017 return router
1018
Alok Maurya6384bbb2014-07-13 06:44:29 -07001019 def _update_router_admin_state(self, router, admin_state_up):
1020 router.update(admin_state_up=admin_state_up)
1021 self.assertEqual(admin_state_up, router.admin_state_up)
1022
Yair Fried413bf2d2014-11-19 17:07:11 +02001023 def create_networks(self, client=None, tenant_id=None,
1024 dns_nameservers=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001025 """Create a network with a subnet connected to a router.
1026
David Shrewsbury9bac3662014-08-07 15:07:01 -04001027 The baremetal driver is a special case since all nodes are
1028 on the same shared network.
1029
Yair Fried413bf2d2014-11-19 17:07:11 +02001030 :param client: network client to create resources with.
1031 :param tenant_id: id of tenant to create resources in.
1032 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001033 :returns: network, subnet, router
1034 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001035 if CONF.baremetal.driver_enabled:
1036 # NOTE(Shrews): This exception is for environments where tenant
1037 # credential isolation is available, but network separation is
1038 # not (the current baremetal case). Likely can be removed when
1039 # test account mgmt is reworked:
1040 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
1041 network = self._get_network_by_name(
1042 CONF.compute.fixed_network_name)
1043 router = None
1044 subnet = None
1045 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001046 network = self._create_network(client=client, tenant_id=tenant_id)
1047 router = self._get_router(client=client, tenant_id=tenant_id)
Yair Fried413bf2d2014-11-19 17:07:11 +02001048
1049 subnet_kwargs = dict(network=network, client=client)
1050 # use explicit check because empty list is a valid option
1051 if dns_nameservers is not None:
1052 subnet_kwargs['dns_nameservers'] = dns_nameservers
1053 subnet = self._create_subnet(**subnet_kwargs)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001054 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001055 return network, subnet, router
1056
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001057 def create_server(self, name=None, image=None, flavor=None,
1058 wait_on_boot=True, wait_on_delete=True,
1059 create_kwargs=None):
1060 vnic_type = CONF.network.port_vnic_type
1061
1062 # If vnic_type is configured create port for
1063 # every network
1064 if vnic_type:
1065 ports = []
1066 networks = []
1067 create_port_body = {'binding:vnic_type': vnic_type,
1068 'namestart': 'port-smoke'}
1069 if create_kwargs:
1070 net_client = create_kwargs.get("network_client",
1071 self.network_client)
1072
1073 # Convert security group names to security group ids
1074 # to pass to create_port
1075 if create_kwargs.get('security_groups'):
1076 security_groups = net_client.list_security_groups().get(
1077 'security_groups')
1078 sec_dict = dict([(s['name'], s['id'])
1079 for s in security_groups])
1080
1081 sec_groups_names = [s['name'] for s in create_kwargs[
1082 'security_groups']]
1083 security_groups_ids = [sec_dict[s]
1084 for s in sec_groups_names]
1085
1086 if security_groups_ids:
1087 create_port_body[
1088 'security_groups'] = security_groups_ids
1089 networks = create_kwargs.get('networks')
1090 else:
1091 net_client = self.network_client
1092 # If there are no networks passed to us we look up
1093 # for the tenant's private networks and create a port
1094 # if there is only one private network. The same behaviour
1095 # as we would expect when passing the call to the clients
1096 # with no networks
1097 if not networks:
1098 networks = net_client.list_networks(filters={
1099 'router:external': False})
1100 self.assertEqual(1, len(networks),
1101 "There is more than one"
1102 " network for the tenant")
1103 for net in networks:
1104 net_id = net['uuid']
1105 port = self._create_port(network_id=net_id,
1106 client=net_client,
1107 **create_port_body)
1108 ports.append({'port': port.id})
1109 if ports:
1110 create_kwargs['networks'] = ports
1111
1112 return super(NetworkScenarioTest, self).create_server(
1113 name=name, image=image, flavor=flavor,
1114 wait_on_boot=wait_on_boot, wait_on_delete=wait_on_delete,
1115 create_kwargs=create_kwargs)
1116
Yair Fried1fc32a12014-08-04 09:11:30 +03001117
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001118# power/provision states as of icehouse
1119class BaremetalPowerStates(object):
1120 """Possible power states of an Ironic node."""
1121 POWER_ON = 'power on'
1122 POWER_OFF = 'power off'
1123 REBOOT = 'rebooting'
1124 SUSPEND = 'suspended'
1125
1126
1127class BaremetalProvisionStates(object):
1128 """Possible provision states of an Ironic node."""
1129 NOSTATE = None
1130 INIT = 'initializing'
1131 ACTIVE = 'active'
1132 BUILDING = 'building'
1133 DEPLOYWAIT = 'wait call-back'
1134 DEPLOYING = 'deploying'
1135 DEPLOYFAIL = 'deploy failed'
1136 DEPLOYDONE = 'deploy complete'
1137 DELETING = 'deleting'
1138 DELETED = 'deleted'
1139 ERROR = 'error'
1140
1141
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001142class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001143 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001144 def resource_setup(cls):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001145 if (not CONF.service_available.ironic or
1146 not CONF.baremetal.driver_enabled):
1147 msg = 'Ironic not available or Ironic compute driver not enabled'
1148 raise cls.skipException(msg)
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001149 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001150
1151 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001152 manager = clients.Manager(
1153 credentials=cls.admin_credentials()
1154 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001155 cls.baremetal_client = manager.baremetal_client
1156
1157 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001158 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001159
1160 def _node_state_timeout(self, node_id, state_attr,
1161 target_states, timeout=10, interval=1):
1162 if not isinstance(target_states, list):
1163 target_states = [target_states]
1164
1165 def check_state():
1166 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001167 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001168 return True
1169 return False
1170
1171 if not tempest.test.call_until_true(
1172 check_state, timeout, interval):
1173 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1174 (node_id, state_attr, target_states))
1175 raise exceptions.TimeoutException(msg)
1176
1177 def wait_provisioning_state(self, node_id, state, timeout):
1178 self._node_state_timeout(
1179 node_id=node_id, state_attr='provision_state',
1180 target_states=state, timeout=timeout)
1181
1182 def wait_power_state(self, node_id, state):
1183 self._node_state_timeout(
1184 node_id=node_id, state_attr='power_state',
1185 target_states=state, timeout=CONF.baremetal.power_timeout)
1186
1187 def wait_node(self, instance_id):
1188 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001189
Adam Gandelman4a48a602014-03-20 18:23:18 -07001190 def _get_node():
1191 node = None
1192 try:
1193 node = self.get_node(instance_id=instance_id)
Masayuki Igawabfa07602015-01-20 18:47:17 +09001194 except lib_exc.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001195 pass
1196 return node is not None
1197
1198 if not tempest.test.call_until_true(
1199 _get_node, CONF.baremetal.association_timeout, 1):
1200 msg = ('Timed out waiting to get Ironic node by instance id %s'
1201 % instance_id)
1202 raise exceptions.TimeoutException(msg)
1203
1204 def get_node(self, node_id=None, instance_id=None):
1205 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001206 _, body = self.baremetal_client.show_node(node_id)
1207 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001208 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001209 _, body = self.baremetal_client.show_node_by_instance_uuid(
1210 instance_id)
1211 if body['nodes']:
1212 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001213
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001214 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001215 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001216 _, body = self.baremetal_client.list_node_ports(node_uuid)
1217 for port in body['ports']:
1218 _, p = self.baremetal_client.show_port(port['uuid'])
1219 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001220 return ports
1221
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001222 def add_keypair(self):
1223 self.keypair = self.create_keypair()
1224
1225 def verify_connectivity(self, ip=None):
1226 if ip:
1227 dest = self.get_remote_client(ip)
1228 else:
1229 dest = self.get_remote_client(self.instance)
1230 dest.validate_authentication()
1231
1232 def boot_instance(self):
1233 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001234 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001235 }
1236 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001237 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001238
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001239 self.wait_node(self.instance['id'])
1240 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001241
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001242 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001243
1244 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001245 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001246 [BaremetalProvisionStates.DEPLOYWAIT,
1247 BaremetalProvisionStates.ACTIVE],
1248 timeout=15)
1249
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001250 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001251 BaremetalProvisionStates.ACTIVE,
1252 timeout=CONF.baremetal.active_timeout)
1253
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001254 self.servers_client.wait_for_server_status(self.instance['id'],
1255 'ACTIVE')
1256 self.node = self.get_node(instance_id=self.instance['id'])
David Kranz0fb14292015-02-11 15:55:20 -05001257 self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001258
1259 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001260 self.servers_client.delete_server(self.instance['id'])
1261 self.wait_power_state(self.node['uuid'],
1262 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001263 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001264 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001265 BaremetalProvisionStates.NOSTATE,
1266 timeout=CONF.baremetal.unprovision_timeout)
1267
Adam Gandelman4a48a602014-03-20 18:23:18 -07001268
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001269class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001270 """
1271 Base class for encryption scenario tests
1272 """
1273
1274 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001275 def resource_setup(cls):
1276 super(EncryptionScenarioTest, cls).resource_setup()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001277 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001278
1279 def _wait_for_volume_status(self, status):
1280 self.status_timeout(
1281 self.volume_client.volumes, self.volume.id, status)
1282
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001283 def nova_boot(self):
1284 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001285 create_kwargs = {'key_name': self.keypair['name']}
1286 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001287 create_kwargs=create_kwargs)
1288
1289 def create_volume_type(self, client=None, name=None):
1290 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001291 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001292 if not name:
1293 name = 'generic'
1294 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1295 LOG.debug("Creating a volume type: %s", randomized_name)
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001296 body = client.create_volume_type(
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001297 randomized_name)
1298 self.assertIn('id', body)
1299 self.addCleanup(client.delete_volume_type, body['id'])
1300 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001301
1302 def create_encryption_type(self, client=None, type_id=None, provider=None,
1303 key_size=None, cipher=None,
1304 control_location=None):
1305 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001306 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001307 if not type_id:
1308 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001309 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001310 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001311 client.create_encryption_type(
1312 type_id, provider=provider, key_size=key_size, cipher=cipher,
1313 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001314
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001315
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001316class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001317 """
1318 Base class for orchestration scenario tests
1319 """
1320
1321 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001322 def resource_setup(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001323 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001324 raise cls.skipException("Heat support is required")
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001325 super(OrchestrationScenarioTest, cls).resource_setup()
Matt Riedemann11c5b642013-08-24 08:45:38 -07001326
1327 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001328 def credentials(cls):
Andrea Frittoli9efbe952015-01-29 12:43:09 +00001329 admin_creds = cred_provider.get_configured_credentials(
1330 'identity_admin')
1331 creds = cred_provider.get_configured_credentials('user')
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001332 admin_creds.tenant_name = creds.tenant_name
1333 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001334
1335 def _load_template(self, base_file, file_name):
1336 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1337 file_name)
1338 with open(filepath) as f:
1339 return f.read()
1340
1341 @classmethod
1342 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001343 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001344
1345 @classmethod
1346 def _get_default_network(cls):
David Kranz34e88122014-12-11 15:24:05 -05001347 networks = cls.networks_client.list_networks()
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001348 for net in networks:
1349 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001350 return net
Steve Baker22c16602014-05-05 13:34:19 +12001351
1352 @staticmethod
1353 def _stack_output(stack, output_key):
1354 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001355 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001356 if o['output_key'] == output_key), None)
1357
Chris Dent0d494112014-08-26 13:48:30 +01001358
1359class SwiftScenarioTest(ScenarioTest):
1360 """
1361 Provide harness to do Swift scenario tests.
1362
1363 Subclasses implement the tests that use the methods provided by this
1364 class.
1365 """
1366
1367 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001368 def resource_setup(cls):
Chris Dent0d494112014-08-26 13:48:30 +01001369 if not CONF.service_available.swift:
1370 skip_msg = ("%s skipped as swift is not available" %
1371 cls.__name__)
1372 raise cls.skipException(skip_msg)
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001373 cls.set_network_resources()
1374 super(SwiftScenarioTest, cls).resource_setup()
Matthew Treinish8f268292015-02-24 20:01:36 -05001375 cls.os_operator = clients.Manager(
1376 cls.isolated_creds.get_creds_by_roles(
1377 [CONF.object_storage.operator_role]))
Chris Dent0d494112014-08-26 13:48:30 +01001378 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001379 cls.account_client = cls.os_operator.account_client
1380 cls.container_client = cls.os_operator.container_client
1381 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001382
Chris Dentde456a12014-09-10 12:41:15 +01001383 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001384 """get swift status for our user account."""
1385 self.account_client.list_account_containers()
1386 LOG.debug('Swift status information obtained successfully')
1387
Chris Dentde456a12014-09-10 12:41:15 +01001388 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001389 name = container_name or data_utils.rand_name(
1390 'swift-scenario-container')
1391 self.container_client.create_container(name)
1392 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001393 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001394 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001395 self.addCleanup(self.delete_wrapper,
1396 self.container_client.delete_container,
1397 name)
Chris Dent0d494112014-08-26 13:48:30 +01001398 return name
1399
Chris Dentde456a12014-09-10 12:41:15 +01001400 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001401 self.container_client.delete_container(container_name)
1402 LOG.debug('Container %s deleted' % (container_name))
1403
Chris Dentde456a12014-09-10 12:41:15 +01001404 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001405 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1406 obj_data = data_utils.arbitrary_string()
1407 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001408 self.addCleanup(self.delete_wrapper,
1409 self.object_client.delete_object,
1410 container_name,
1411 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001412 return obj_name, obj_data
1413
Chris Dentde456a12014-09-10 12:41:15 +01001414 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001415 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001416 self.list_and_check_container_objects(container_name,
1417 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001418
Chris Dentde456a12014-09-10 12:41:15 +01001419 def list_and_check_container_objects(self, container_name,
1420 present_obj=None,
1421 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001422 """
1423 List objects for a given container and assert which are present and
1424 which are not.
1425 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001426 if present_obj is None:
1427 present_obj = []
1428 if not_present_obj is None:
1429 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001430 _, object_list = self.container_client.list_container_contents(
1431 container_name)
1432 if present_obj:
1433 for obj in present_obj:
1434 self.assertIn(obj, object_list)
1435 if not_present_obj:
1436 for obj in not_present_obj:
1437 self.assertNotIn(obj, object_list)
1438
Chris Dentde456a12014-09-10 12:41:15 +01001439 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001440 metadata_param = {'metadata_prefix': 'x-container-',
1441 'metadata': {'read': acl}}
1442 self.container_client.update_container_metadata(container_name,
1443 **metadata_param)
1444 resp, _ = self.container_client.list_container_metadata(container_name)
1445 self.assertEqual(resp['x-container-read'], acl)
1446
Chris Dentde456a12014-09-10 12:41:15 +01001447 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001448 _, obj = self.object_client.get_object(container_name, obj_name)
1449 self.assertEqual(obj, expected_data)