blob: 85414141fa467622ab33dcc5328de1a432686722 [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
Sean Dague6dbc6da2013-05-08 17:49:46 -040017import subprocess
18
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import netaddr
Doug Hellmann583ce2c2015-03-11 14:55:46 +000020from oslo_log import log
Matthew Treinish96e9e882014-06-09 18:37:19 -040021import six
Matthew Treinish01472ff2015-02-20 17:26:52 -050022from tempest_lib.common.utils import data_utils
Matt Riedemann5f0ac522015-05-21 09:16:24 -070023from tempest_lib.common.utils import misc as misc_utils
Masayuki Igawad9388762015-01-20 14:56:42 +090024from tempest_lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
Rohan Kanade9ce97df2013-12-10 18:59:35 +053026from tempest.common import fixed_network
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090027from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000028from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020029from tempest import exceptions
Yair Fried1fc32a12014-08-04 09:11:30 +030030from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040031import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040032
Matthew Treinish6c072292014-01-29 19:15:52 +000033CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040034
Attila Fazekasfb7552a2013-08-27 13:02:26 +020035LOG = log.getLogger(__name__)
36
Sean Dague6dbc6da2013-05-08 17:49:46 -040037
Andrea Frittoli2e733b52014-07-16 14:12:11 +010038class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010039 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010040
Andrea Frittolib21de6c2015-02-06 20:12:38 +000041 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000042
43 @classmethod
44 def setup_clients(cls):
45 super(ScenarioTest, cls).setup_clients()
Andrea Frittoli247058f2014-07-16 16:09:22 +010046 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070047 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010048 cls.floating_ips_client = cls.manager.floating_ips_client
49 # Glance image client v1
50 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000051 # Compute image client
52 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010053 cls.keypairs_client = cls.manager.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010054 # Nova security groups client
55 cls.security_groups_client = cls.manager.security_groups_client
56 cls.servers_client = cls.manager.servers_client
57 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000058 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030059 cls.interface_client = cls.manager.interfaces_client
60 # Neutron network client
61 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090062 # Heat client
63 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010064
Andrea Frittoli247058f2014-07-16 16:09:22 +010065 # ## Methods to handle sync and async deletes
66
67 def setUp(self):
68 super(ScenarioTest, self).setUp()
69 self.cleanup_waits = []
70 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
71 # because scenario tests in the same test class should not share
72 # resources. If resources were shared between test cases then it
73 # should be a single scenario test instead of multiples.
74
75 # NOTE(yfried): this list is cleaned at the end of test_methods and
76 # not at the end of the class
77 self.addCleanup(self._wait_for_cleanups)
78
Yair Fried1fc32a12014-08-04 09:11:30 +030079 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +010080 """Ignores NotFound exceptions for delete operations.
81
Yair Fried1fc32a12014-08-04 09:11:30 +030082 @param delete_thing: delete method of a resource. method will be
83 executed as delete_thing(*args, **kwargs)
84
Andrea Frittoli247058f2014-07-16 16:09:22 +010085 """
86 try:
87 # Tempest clients return dicts, so there is no common delete
88 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +030089 delete_thing(*args, **kwargs)
Masayuki Igawabfa07602015-01-20 18:47:17 +090090 except lib_exc.NotFound:
Andrea Frittoli247058f2014-07-16 16:09:22 +010091 # If the resource is already missing, mission accomplished.
92 pass
93
94 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +090095 cleanup_callable, cleanup_args=None,
96 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -070097 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +010098
99 @param waiter_callable: callable to wait for the resource to delete
100 @param thing_id: the id of the resource to be cleaned-up
101 @param thing_id_param: the name of the id param in the waiter
102 @param cleanup_callable: method to load pass to self.addCleanup with
103 the following *cleanup_args, **cleanup_kwargs.
104 usually a delete method.
105 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900106 if cleanup_args is None:
107 cleanup_args = []
108 if cleanup_kwargs is None:
109 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100110 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
111 wait_dict = {
112 'waiter_callable': waiter_callable,
113 thing_id_param: thing_id
114 }
115 self.cleanup_waits.append(wait_dict)
116
117 def _wait_for_cleanups(self):
118 """To handle async delete actions, a list of waits is added
119 which will be iterated over as the last step of clearing the
120 cleanup queue. That way all the delete calls are made up front
121 and the tests won't succeed unless the deletes are eventually
122 successful. This is the same basic approach used in the api tests to
123 limit cleanup execution time except here it is multi-resource,
124 because of the nature of the scenario tests.
125 """
126 for wait in self.cleanup_waits:
127 waiter_callable = wait.pop('waiter_callable')
128 waiter_callable(**wait)
129
130 # ## Test functions library
131 #
132 # The create_[resource] functions only return body and discard the
133 # resp part which is not used in scenario tests
134
Yair Frieddb6c9e92014-08-06 08:53:13 +0300135 def create_keypair(self, client=None):
136 if not client:
137 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100138 name = data_utils.rand_name(self.__class__.__name__)
139 # We don't need to create a keypair by pubkey in scenario
David Kranz173f0e02015-02-06 13:47:57 -0500140 body = client.create_keypair(name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300141 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100142 return body
143
144 def create_server(self, name=None, image=None, flavor=None,
145 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900146 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100147 """Creates VM instance.
148
149 @param image: image from which to create the instance
150 @param wait_on_boot: wait for status ACTIVE before continue
151 @param wait_on_delete: force synchronous delete on cleanup
152 @param create_kwargs: additional details for instance creation
153 @return: server dict
154 """
155 if name is None:
156 name = data_utils.rand_name(self.__class__.__name__)
157 if image is None:
158 image = CONF.compute.image_ref
159 if flavor is None:
160 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900161 if create_kwargs is None:
162 create_kwargs = {}
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530163 network = self.get_tenant_network()
Matthew Treinish4bbc1992015-04-07 11:13:40 -0400164 create_kwargs = fixed_network.set_networks_kwarg(network,
165 create_kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100166
Andrea Frittoli247058f2014-07-16 16:09:22 +0100167 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
168 name, image, flavor)
David Kranz0fb14292015-02-11 15:55:20 -0500169 server = self.servers_client.create_server(name, image, flavor,
170 **create_kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100171 if wait_on_delete:
172 self.addCleanup(self.servers_client.wait_for_server_termination,
173 server['id'])
174 self.addCleanup_with_wait(
175 waiter_callable=self.servers_client.wait_for_server_termination,
176 thing_id=server['id'], thing_id_param='server_id',
177 cleanup_callable=self.delete_wrapper,
178 cleanup_args=[self.servers_client.delete_server, server['id']])
179 if wait_on_boot:
180 self.servers_client.wait_for_server_status(server_id=server['id'],
181 status='ACTIVE')
182 # The instance retrieved on creation is missing network
183 # details, necessitating retrieval after it becomes active to
184 # ensure correct details.
Ken'ichi Ohmichi76800242015-07-03 05:12:31 +0000185 server = self.servers_client.show_server(server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100186 self.assertEqual(server['name'], name)
187 return server
188
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100189 def create_volume(self, size=None, name=None, snapshot_id=None,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100190 imageRef=None, volume_type=None, wait_on_delete=True):
191 if name is None:
192 name = data_utils.rand_name(self.__class__.__name__)
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000193 volume = self.volumes_client.create_volume(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100194 size=size, display_name=name, snapshot_id=snapshot_id,
195 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700196
Andrea Frittoli247058f2014-07-16 16:09:22 +0100197 if wait_on_delete:
198 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
199 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700200 self.addCleanup(self.delete_wrapper,
201 self.volumes_client.delete_volume, volume['id'])
202 else:
203 self.addCleanup_with_wait(
204 waiter_callable=self.volumes_client.wait_for_resource_deletion,
205 thing_id=volume['id'], thing_id_param='id',
206 cleanup_callable=self.delete_wrapper,
207 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100208
209 self.assertEqual(name, volume['display_name'])
210 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
211 # The volume retrieved on creation has a non-up-to-date status.
212 # Retrieval after it becomes active ensures correct details.
Ken'ichi Ohmichi35798fb2015-04-06 01:22:41 +0000213 volume = self.volumes_client.show_volume(volume['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100214 return volume
215
Yair Fried1fc32a12014-08-04 09:11:30 +0300216 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100217 _client = self.security_groups_client
218 if secgroup_id is None:
David Kranz9964b4e2015-02-06 15:45:29 -0500219 sgs = _client.list_security_groups()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100220 for sg in sgs:
221 if sg['name'] == 'default':
222 secgroup_id = sg['id']
223
224 # These rules are intended to permit inbound ssh and icmp
225 # traffic from all sources, so no group_id is provided.
226 # Setting a group_id would only permit traffic from ports
227 # belonging to the same security group.
228 rulesets = [
229 {
230 # ssh
231 'ip_proto': 'tcp',
232 'from_port': 22,
233 'to_port': 22,
234 'cidr': '0.0.0.0/0',
235 },
236 {
237 # ping
238 'ip_proto': 'icmp',
239 'from_port': -1,
240 'to_port': -1,
241 'cidr': '0.0.0.0/0',
242 }
243 ]
244 rules = list()
245 for ruleset in rulesets:
David Kranz9964b4e2015-02-06 15:45:29 -0500246 sg_rule = _client.create_security_group_rule(secgroup_id,
247 **ruleset)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100248 self.addCleanup(self.delete_wrapper,
249 _client.delete_security_group_rule,
250 sg_rule['id'])
251 rules.append(sg_rule)
252 return rules
253
Yair Fried1fc32a12014-08-04 09:11:30 +0300254 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100255 # Create security group
256 sg_name = data_utils.rand_name(self.__class__.__name__)
257 sg_desc = sg_name + " description"
David Kranz9964b4e2015-02-06 15:45:29 -0500258 secgroup = self.security_groups_client.create_security_group(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100259 sg_name, sg_desc)
260 self.assertEqual(secgroup['name'], sg_name)
261 self.assertEqual(secgroup['description'], sg_desc)
262 self.addCleanup(self.delete_wrapper,
263 self.security_groups_client.delete_security_group,
264 secgroup['id'])
265
266 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300267 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100268
269 return secgroup
270
JordanP3fe2dc32014-11-17 13:06:01 +0100271 def get_remote_client(self, server_or_ip, username=None, private_key=None,
272 log_console_of_servers=None):
273 """Get a SSH client to a remote server
274
275 @param server_or_ip a server object as returned by Tempest compute
276 client or an IP address to connect to
277 @param username name of the Linux account on the remote server
278 @param private_key the SSH private key to use
279 @param log_console_of_servers a list of server objects. Each server
280 in the list will have its console printed in the logs in case the
281 SSH connection failed to be established
282 @return a RemoteClient object
283 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100284 if isinstance(server_or_ip, six.string_types):
285 ip = server_or_ip
286 else:
Andrew Boik4a3daf12015-03-27 01:59:31 -0400287 addrs = server_or_ip['addresses'][CONF.compute.network_for_ssh]
288 try:
289 ip = (addr['addr'] for addr in addrs if
290 netaddr.valid_ipv4(addr['addr'])).next()
291 except StopIteration:
292 raise lib_exc.NotFound("No IPv4 addresses to use for SSH to "
293 "remote server.")
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700294
Andrea Frittoli247058f2014-07-16 16:09:22 +0100295 if username is None:
296 username = CONF.scenario.ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800297 # Set this with 'keypair' or others to log in with keypair or
298 # username/password.
299 if CONF.compute.ssh_auth_method == 'keypair':
300 password = None
301 if private_key is None:
302 private_key = self.keypair['private_key']
303 else:
304 password = CONF.compute.image_ssh_password
305 private_key = None
Andrea Frittoli247058f2014-07-16 16:09:22 +0100306 linux_client = remote_client.RemoteClient(ip, username,
wantwatering896300c2015-03-27 15:17:42 +0800307 pkey=private_key,
308 password=password)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100309 try:
310 linux_client.validate_authentication()
Matt Riedemann5f0ac522015-05-21 09:16:24 -0700311 except Exception as e:
312 message = ('Initializing SSH connection to %(ip)s failed. '
313 'Error: %(error)s' % {'ip': ip, 'error': e})
314 caller = misc_utils.find_test_caller()
315 if caller:
316 message = '(%s) %s' % (caller, message)
317 LOG.exception(message)
Marc Kodererb06db502015-04-30 09:31:27 +0200318 # If we don't explicitly set for which servers we want to
JordanP3fe2dc32014-11-17 13:06:01 +0100319 # log the console output then all the servers will be logged.
320 # See the definition of _log_console_output()
321 self._log_console_output(log_console_of_servers)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100322 raise
323
324 return linux_client
325
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000326 def _image_create(self, name, fmt, path,
327 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900328 if properties is None:
329 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100330 name = data_utils.rand_name('%s-' % name)
331 image_file = open(path, 'rb')
332 self.addCleanup(image_file.close)
333 params = {
334 'name': name,
335 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000336 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100337 'is_public': 'False',
338 }
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000339 params['properties'] = properties
David Kranz34f18782015-01-06 13:43:55 -0500340 image = self.image_client.create_image(**params)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100341 self.addCleanup(self.image_client.delete_image, image['id'])
342 self.assertEqual("queued", image['status'])
343 self.image_client.update_image(image['id'], data=image_file)
344 return image['id']
345
346 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300347 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100348 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
349 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
350 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300351 img_container_format = CONF.scenario.img_container_format
352 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000353 img_properties = CONF.scenario.img_properties
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300354 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000355 "properties: %s, ami: %s, ari: %s, aki: %s" %
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300356 (img_path, img_container_format, img_disk_format,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000357 img_properties, ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100358 try:
359 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300360 img_container_format,
361 img_path,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000362 disk_format=img_disk_format,
363 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100364 except IOError:
365 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
366 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
367 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000368 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100369 self.image = self._image_create('scenario-ami', 'ami',
370 path=ami_img_path,
371 properties=properties)
372 LOG.debug("image:%s" % self.image)
373
374 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400375 if not CONF.compute_feature_enabled.console_output:
376 LOG.debug('Console output not supported, cannot log')
377 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100378 if not servers:
David Kranzae99b9a2015-02-16 13:37:01 -0500379 servers = self.servers_client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100380 servers = servers['servers']
381 for server in servers:
Brant Knudson566c5712014-09-24 20:04:50 -0500382 console_output = self.servers_client.get_console_output(
David Kranzae99b9a2015-02-16 13:37:01 -0500383 server['id'], length=None).data
384 LOG.debug('Console output for %s\nbody=\n%s',
385 server['id'], console_output)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100386
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000387 def _log_net_info(self, exc):
388 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300389 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000390 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000391
nithya-ganesan882595e2014-07-29 18:51:07 +0000392 def create_server_snapshot(self, server, name=None):
393 # Glance client
394 _image_client = self.image_client
395 # Compute client
396 _images_client = self.images_client
397 if name is None:
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +0000398 name = data_utils.rand_name('scenario-snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000399 LOG.debug("Creating a snapshot image for server: %s", server['name'])
David Kranza5299eb2015-01-15 17:24:05 -0500400 image = _images_client.create_image(server['id'], name)
401 image_id = image.response['location'].split('images/')[1]
nithya-ganesan882595e2014-07-29 18:51:07 +0000402 _image_client.wait_for_image_status(image_id, 'active')
403 self.addCleanup_with_wait(
404 waiter_callable=_image_client.wait_for_resource_deletion,
405 thing_id=image_id, thing_id_param='id',
406 cleanup_callable=self.delete_wrapper,
407 cleanup_args=[_image_client.delete_image, image_id])
David Kranz34f18782015-01-06 13:43:55 -0500408 snapshot_image = _image_client.get_image_meta(image_id)
nithya-ganesan882595e2014-07-29 18:51:07 +0000409 image_name = snapshot_image['name']
410 self.assertEqual(name, image_name)
411 LOG.debug("Created snapshot image %s for server %s",
412 image_name, server['name'])
413 return snapshot_image
414
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900415 def nova_volume_attach(self):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000416 volume = self.servers_client.attach_volume(
Alexander Gubanove0634ab2015-05-25 10:28:25 +0300417 self.server['id'], self.volume['id'], '/dev/%s'
418 % CONF.compute.volume_device_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900419 self.assertEqual(self.volume['id'], volume['id'])
420 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
421 # Refresh the volume after the attachment
Ken'ichi Ohmichi35798fb2015-04-06 01:22:41 +0000422 self.volume = self.volumes_client.show_volume(volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900423
424 def nova_volume_detach(self):
425 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
426 self.volumes_client.wait_for_volume_status(self.volume['id'],
427 'available')
428
Ken'ichi Ohmichi35798fb2015-04-06 01:22:41 +0000429 volume = self.volumes_client.show_volume(self.volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900430 self.assertEqual('available', volume['status'])
431
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700432 def rebuild_server(self, server_id, image=None,
433 preserve_ephemeral=False, wait=True,
434 rebuild_kwargs=None):
435 if image is None:
436 image = CONF.compute.image_ref
437
438 rebuild_kwargs = rebuild_kwargs or {}
439
440 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
441 server_id, image, preserve_ephemeral)
442 self.servers_client.rebuild(server_id=server_id, image_ref=image,
443 preserve_ephemeral=preserve_ephemeral,
444 **rebuild_kwargs)
445 if wait:
446 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
447
Steven Hardyda2a8352014-10-02 12:52:20 +0100448 def ping_ip_address(self, ip_address, should_succeed=True,
449 ping_timeout=None):
450 timeout = ping_timeout or CONF.compute.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700451 cmd = ['ping', '-c1', '-w1', ip_address]
452
453 def ping():
454 proc = subprocess.Popen(cmd,
455 stdout=subprocess.PIPE,
456 stderr=subprocess.PIPE)
457 proc.communicate()
458 return (proc.returncode == 0) == should_succeed
459
Steven Hardyda2a8352014-10-02 12:52:20 +0100460 return tempest.test.call_until_true(ping, timeout, 1)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700461
Yair Friedae0e73d2014-11-24 11:56:26 +0200462 def check_vm_connectivity(self, ip_address,
463 username=None,
464 private_key=None,
465 should_connect=True):
466 """
467 :param ip_address: server to test against
468 :param username: server's ssh username
469 :param private_key: server's ssh private key to be used
470 :param should_connect: True/False indicates positive/negative test
471 positive - attempt ping and ssh
472 negative - attempt ping and fail if succeed
473
474 :raises: AssertError if the result of the connectivity check does
475 not match the value of the should_connect param
476 """
477 if should_connect:
478 msg = "Timed out waiting for %s to become reachable" % ip_address
479 else:
480 msg = "ip address %s is reachable" % ip_address
481 self.assertTrue(self.ping_ip_address(ip_address,
482 should_succeed=should_connect),
483 msg=msg)
484 if should_connect:
485 # no need to check ssh for negative connectivity
486 self.get_remote_client(ip_address, username, private_key)
487
488 def check_public_network_connectivity(self, ip_address, username,
489 private_key, should_connect=True,
490 msg=None, servers=None):
491 # The target login is assumed to have been configured for
492 # key-based authentication by cloud-init.
493 LOG.debug('checking network connections to IP %s with user: %s' %
494 (ip_address, username))
495 try:
496 self.check_vm_connectivity(ip_address,
497 username,
498 private_key,
499 should_connect=should_connect)
Matthew Treinish53483132014-12-09 18:50:06 -0500500 except Exception:
Yair Friedae0e73d2014-11-24 11:56:26 +0200501 ex_msg = 'Public network connectivity check failed'
502 if msg:
503 ex_msg += ": " + msg
504 LOG.exception(ex_msg)
505 self._log_console_output(servers)
Yair Friedae0e73d2014-11-24 11:56:26 +0200506 raise
507
508 def create_floating_ip(self, thing, pool_name=None):
509 """Creates a floating IP and associates to a server using
510 Nova clients
511 """
512
David Kranze4e3b412015-02-10 10:50:42 -0500513 floating_ip = self.floating_ips_client.create_floating_ip(pool_name)
Yair Friedae0e73d2014-11-24 11:56:26 +0200514 self.addCleanup(self.delete_wrapper,
515 self.floating_ips_client.delete_floating_ip,
516 floating_ip['id'])
517 self.floating_ips_client.associate_floating_ip_to_server(
518 floating_ip['ip'], thing['id'])
519 return floating_ip
520
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100521
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100522class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300523 """Base class for network scenario tests.
524 This class provide helpers for network scenario tests, using the neutron
525 API. Helpers from ancestor which use the nova network API are overridden
526 with the neutron API.
527
528 This Class also enforces using Neutron instead of novanetwork.
529 Subclassed tests will be skipped if Neutron is not enabled
530
531 """
532
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000533 credentials = ['primary', 'admin']
534
Yair Fried1fc32a12014-08-04 09:11:30 +0300535 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000536 def skip_checks(cls):
537 super(NetworkScenarioTest, cls).skip_checks()
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):
543 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300544 cls.tenant_id = cls.manager.identity_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300545
Yair Frieddb6c9e92014-08-06 08:53:13 +0300546 def _create_network(self, client=None, tenant_id=None,
547 namestart='network-smoke-'):
548 if not client:
549 client = self.network_client
550 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000551 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300552 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500553 result = client.create_network(name=name, tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300554 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300555 **result['network'])
556 self.assertEqual(network.name, name)
557 self.addCleanup(self.delete_wrapper, network.delete)
558 return network
559
560 def _list_networks(self, *args, **kwargs):
561 """List networks using admin creds """
562 return self._admin_lister('networks')(*args, **kwargs)
563
564 def _list_subnets(self, *args, **kwargs):
565 """List subnets using admin creds """
566 return self._admin_lister('subnets')(*args, **kwargs)
567
568 def _list_routers(self, *args, **kwargs):
569 """List routers using admin creds """
570 return self._admin_lister('routers')(*args, **kwargs)
571
572 def _list_ports(self, *args, **kwargs):
573 """List ports using admin creds """
574 return self._admin_lister('ports')(*args, **kwargs)
575
576 def _admin_lister(self, resource_type):
577 def temp(*args, **kwargs):
578 temp_method = self.admin_manager.network_client.__getattr__(
579 'list_%s' % resource_type)
David Kranz34e88122014-12-11 15:24:05 -0500580 resource_list = temp_method(*args, **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300581 return resource_list[resource_type]
582 return temp
583
Yair Frieddb6c9e92014-08-06 08:53:13 +0300584 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
585 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300586 """
587 Create a subnet for the given network within the cidr block
588 configured for tenant networks.
589 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300590 if not client:
591 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300592
593 def cidr_in_use(cidr, tenant_id):
594 """
595 :return True if subnet with cidr already exist in tenant
596 False else
597 """
598 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
599 return len(cidr_in_use) != 0
600
Kirill Shileev14113572014-11-21 16:58:02 +0300601 ip_version = kwargs.pop('ip_version', 4)
602
603 if ip_version == 6:
604 tenant_cidr = netaddr.IPNetwork(
605 CONF.network.tenant_network_v6_cidr)
606 num_bits = CONF.network.tenant_network_v6_mask_bits
607 else:
608 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
609 num_bits = CONF.network.tenant_network_mask_bits
610
Yair Fried1fc32a12014-08-04 09:11:30 +0300611 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300612 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300613 # Repeatedly attempt subnet creation with sequential cidr
614 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300615 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300616 str_cidr = str(subnet_cidr)
617 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
618 continue
619
620 subnet = dict(
621 name=data_utils.rand_name(namestart),
Yair Fried1fc32a12014-08-04 09:11:30 +0300622 network_id=network.id,
623 tenant_id=network.tenant_id,
624 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300625 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300626 **kwargs
627 )
628 try:
David Kranz34e88122014-12-11 15:24:05 -0500629 result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300630 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900631 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300632 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
633 if not is_overlapping_cidr:
634 raise
635 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300636 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300637 **result['subnet'])
638 self.assertEqual(subnet.cidr, str_cidr)
639 self.addCleanup(self.delete_wrapper, subnet.delete)
640 return subnet
641
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200642 def _create_port(self, network_id, client=None, namestart='port-quotatest',
643 **kwargs):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300644 if not client:
645 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300646 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500647 result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300648 name=name,
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200649 network_id=network_id,
650 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300651 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300652 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300653 **result['port'])
654 self.addCleanup(self.delete_wrapper, port.delete)
655 return port
656
Kirill Shileev14113572014-11-21 16:58:02 +0300657 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300658 ports = self._list_ports(device_id=server['id'],
659 fixed_ip=ip_addr)
660 self.assertEqual(len(ports), 1,
661 "Unable to determine which port to target.")
Kirill Shileev14113572014-11-21 16:58:02 +0300662 # it might happen here that this port has more then one ip address
663 # as in case of dual stack- when this port is created on 2 subnets
664 for ip46 in ports[0]['fixed_ips']:
665 ip = ip46['ip_address']
666 if netaddr.valid_ipv4(ip):
667 return ports[0]['id'], ip
Yair Fried1fc32a12014-08-04 09:11:30 +0300668
David Shrewsbury9bac3662014-08-07 15:07:01 -0400669 def _get_network_by_name(self, network_name):
670 net = self._list_networks(name=network_name)
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700671 self.assertNotEqual(len(net), 0,
672 "Unable to get network by name: %s" % network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300673 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400674
Yair Friedae0e73d2014-11-24 11:56:26 +0200675 def create_floating_ip(self, thing, external_network_id=None,
676 port_id=None, client=None):
677 """Creates a floating IP and associates to a resource/port using
678 Neutron client
679 """
680 if not external_network_id:
681 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300682 if not client:
683 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300684 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300685 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
686 else:
687 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500688 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300689 floating_network_id=external_network_id,
690 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300691 tenant_id=thing['tenant_id'],
692 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300693 )
694 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300695 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300696 **result['floatingip'])
697 self.addCleanup(self.delete_wrapper, floating_ip.delete)
698 return floating_ip
699
700 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300701 port_id, _ = self._get_server_port_id_and_ip4(server)
Yair Fried1fc32a12014-08-04 09:11:30 +0300702 floating_ip.update(port_id=port_id)
703 self.assertEqual(port_id, floating_ip.port_id)
704 return floating_ip
705
706 def _disassociate_floating_ip(self, floating_ip):
707 """
708 :param floating_ip: type DeletableFloatingIp
709 """
710 floating_ip.update(port_id=None)
711 self.assertIsNone(floating_ip.port_id)
712 return floating_ip
713
Yair Fried45f92952014-06-26 05:19:19 +0300714 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000715 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300716
717 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
718 to check status
719 :param status: target status
720 :raises: AssertionError if status doesn't match
721 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000722 def refresh():
723 floating_ip.refresh()
724 return status == floating_ip.status
725
726 tempest.test.call_until_true(refresh,
727 CONF.network.build_timeout,
728 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300729 self.assertEqual(status, floating_ip.status,
730 message="FloatingIP: {fp} is at status: {cst}. "
731 "failed to reach status: {st}"
732 .format(fp=floating_ip, cst=floating_ip.status,
733 st=status))
734 LOG.info("FloatingIP: {fp} is at status: {st}"
735 .format(fp=floating_ip, st=status))
736
Yair Fried1fc32a12014-08-04 09:11:30 +0300737 def _check_tenant_network_connectivity(self, server,
738 username,
739 private_key,
740 should_connect=True,
741 servers_for_debug=None):
742 if not CONF.network.tenant_networks_reachable:
743 msg = 'Tenant networks not configured to be reachable.'
744 LOG.info(msg)
745 return
746 # The target login is assumed to have been configured for
747 # key-based authentication by cloud-init.
748 try:
Matthew Treinish71426682015-04-23 11:19:38 -0400749 for net_name, ip_addresses in six.iteritems(server['addresses']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300750 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900751 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200752 username,
753 private_key,
754 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300755 except Exception as e:
756 LOG.exception('Tenant network connectivity check failed')
757 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000758 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300759 raise
760
761 def _check_remote_connectivity(self, source, dest, should_succeed=True):
762 """
763 check ping server via source ssh connection
764
765 :param source: RemoteClient: an ssh connection from which to ping
766 :param dest: and IP to ping against
767 :param should_succeed: boolean should ping succeed or not
768 :returns: boolean -- should_succeed == ping
769 :returns: ping is false if ping failed
770 """
771 def ping_remote():
772 try:
773 source.ping_host(dest)
Andrey Pavlov64723762015-04-29 06:24:58 +0300774 except lib_exc.SSHExecCommandFailed:
Yair Fried1fc32a12014-08-04 09:11:30 +0300775 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
776 % (dest, source.ssh_client.host))
777 return not should_succeed
778 return should_succeed
779
780 return tempest.test.call_until_true(ping_remote,
781 CONF.compute.ping_timeout,
782 1)
783
Yair Frieddb6c9e92014-08-06 08:53:13 +0300784 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 namestart='secgroup-smoke'):
786 if client is None:
787 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300788 if tenant_id is None:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000789 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300790 secgroup = self._create_empty_security_group(namestart=namestart,
791 client=client,
792 tenant_id=tenant_id)
793
794 # Add rules to the security group
ghanshyam38890b52015-01-21 15:24:18 +0900795 rules = self._create_loginable_secgroup_rule(client=client,
796 secgroup=secgroup)
Yair Fried1fc32a12014-08-04 09:11:30 +0300797 for rule in rules:
798 self.assertEqual(tenant_id, rule.tenant_id)
799 self.assertEqual(secgroup.id, rule.security_group_id)
800 return secgroup
801
Yair Frieddb6c9e92014-08-06 08:53:13 +0300802 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300803 namestart='secgroup-smoke'):
804 """Create a security group without rules.
805
806 Default rules will be created:
807 - IPv4 egress to any
808 - IPv6 egress to any
809
810 :param tenant_id: secgroup will be created in this tenant
811 :returns: DeletableSecurityGroup -- containing the secgroup created
812 """
813 if client is None:
814 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300815 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000816 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300817 sg_name = data_utils.rand_name(namestart)
818 sg_desc = sg_name + " description"
819 sg_dict = dict(name=sg_name,
820 description=sg_desc)
821 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -0500822 result = client.create_security_group(**sg_dict)
Yair Fried1fc32a12014-08-04 09:11:30 +0300823 secgroup = net_resources.DeletableSecurityGroup(
824 client=client,
825 **result['security_group']
826 )
827 self.assertEqual(secgroup.name, sg_name)
828 self.assertEqual(tenant_id, secgroup.tenant_id)
829 self.assertEqual(secgroup.description, sg_desc)
830 self.addCleanup(self.delete_wrapper, secgroup.delete)
831 return secgroup
832
Yair Frieddb6c9e92014-08-06 08:53:13 +0300833 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300834 """Get default secgroup for given tenant_id.
835
836 :returns: DeletableSecurityGroup -- default secgroup for given tenant
837 """
838 if client is None:
839 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300840 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000841 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300842 sgs = [
843 sg for sg in client.list_security_groups().values()[0]
844 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
845 ]
846 msg = "No default security group for tenant %s." % (tenant_id)
847 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300848 return net_resources.DeletableSecurityGroup(client=client,
849 **sgs[0])
850
Yair Frieddb6c9e92014-08-06 08:53:13 +0300851 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300852 tenant_id=None, **kwargs):
853 """Create a rule from a dictionary of rule parameters.
854
855 Create a rule in a secgroup. if secgroup not defined will search for
856 default secgroup in tenant_id.
857
858 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300859 :param tenant_id: if secgroup not passed -- the tenant in which to
860 search for default secgroup
861 :param kwargs: a dictionary containing rule parameters:
862 for example, to allow incoming ssh:
863 rule = {
864 direction: 'ingress'
865 protocol:'tcp',
866 port_range_min: 22,
867 port_range_max: 22
868 }
869 """
870 if client is None:
871 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300872 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000873 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300874 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300875 secgroup = self._default_security_group(client=client,
876 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300877
878 ruleset = dict(security_group_id=secgroup.id,
879 tenant_id=secgroup.tenant_id)
880 ruleset.update(kwargs)
881
David Kranz34e88122014-12-11 15:24:05 -0500882 sg_rule = client.create_security_group_rule(**ruleset)
Yair Fried1fc32a12014-08-04 09:11:30 +0300883 sg_rule = net_resources.DeletableSecurityGroupRule(
884 client=client,
885 **sg_rule['security_group_rule']
886 )
887 self.addCleanup(self.delete_wrapper, sg_rule.delete)
888 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
889 self.assertEqual(secgroup.id, sg_rule.security_group_id)
890
891 return sg_rule
892
893 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
894 """These rules are intended to permit inbound ssh and icmp
895 traffic from all sources, so no group_id is provided.
896 Setting a group_id would only permit traffic from ports
897 belonging to the same security group.
898 """
899
900 if client is None:
901 client = self.network_client
902 rules = []
903 rulesets = [
904 dict(
905 # ssh
906 protocol='tcp',
907 port_range_min=22,
908 port_range_max=22,
909 ),
910 dict(
911 # ping
912 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +0100913 ),
914 dict(
915 # ipv6-icmp for ping6
916 protocol='icmp',
917 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +0300918 )
919 ]
920 for ruleset in rulesets:
921 for r_direction in ['ingress', 'egress']:
922 ruleset['direction'] = r_direction
923 try:
924 sg_rule = self._create_security_group_rule(
925 client=client, secgroup=secgroup, **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +0900926 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +0300927 # if rule already exist - skip rule and continue
928 msg = 'Security group rule already exists'
929 if msg not in ex._error_string:
930 raise ex
931 else:
932 self.assertEqual(r_direction, sg_rule.direction)
933 rules.append(sg_rule)
934
935 return rules
936
937 def _ssh_to_server(self, server, private_key):
938 ssh_login = CONF.compute.image_ssh_user
939 return self.get_remote_client(server,
940 username=ssh_login,
941 private_key=private_key)
942
Yair Frieddb6c9e92014-08-06 08:53:13 +0300943 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300944 """Retrieve a router for the given tenant id.
945
946 If a public router has been configured, it will be returned.
947
948 If a public router has not been configured, but a public
949 network has, a tenant router will be created and returned that
950 routes traffic to the public network.
951 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300952 if not client:
953 client = self.network_client
954 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000955 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300956 router_id = CONF.network.public_router_id
957 network_id = CONF.network.public_network_id
958 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -0400959 body = client.show_router(router_id)
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +0400960 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300961 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300962 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300963 router.set_gateway(network_id)
964 return router
965 else:
966 raise Exception("Neither of 'public_router_id' or "
967 "'public_network_id' has been defined.")
968
Yair Frieddb6c9e92014-08-06 08:53:13 +0300969 def _create_router(self, client=None, tenant_id=None,
970 namestart='router-smoke'):
971 if not client:
972 client = self.network_client
973 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000974 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300975 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500976 result = client.create_router(name=name,
977 admin_state_up=True,
978 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300979 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300980 **result['router'])
981 self.assertEqual(router.name, name)
982 self.addCleanup(self.delete_wrapper, router.delete)
983 return router
984
Alok Maurya6384bbb2014-07-13 06:44:29 -0700985 def _update_router_admin_state(self, router, admin_state_up):
986 router.update(admin_state_up=admin_state_up)
987 self.assertEqual(admin_state_up, router.admin_state_up)
988
Yair Fried413bf2d2014-11-19 17:07:11 +0200989 def create_networks(self, client=None, tenant_id=None,
990 dns_nameservers=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300991 """Create a network with a subnet connected to a router.
992
David Shrewsbury9bac3662014-08-07 15:07:01 -0400993 The baremetal driver is a special case since all nodes are
994 on the same shared network.
995
Yair Fried413bf2d2014-11-19 17:07:11 +0200996 :param client: network client to create resources with.
997 :param tenant_id: id of tenant to create resources in.
998 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +0300999 :returns: network, subnet, router
1000 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001001 if CONF.baremetal.driver_enabled:
1002 # NOTE(Shrews): This exception is for environments where tenant
1003 # credential isolation is available, but network separation is
1004 # not (the current baremetal case). Likely can be removed when
1005 # test account mgmt is reworked:
1006 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001007 if not CONF.compute.fixed_network_name:
1008 m = 'fixed_network_name must be specified in config'
1009 raise exceptions.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001010 network = self._get_network_by_name(
1011 CONF.compute.fixed_network_name)
1012 router = None
1013 subnet = None
1014 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001015 network = self._create_network(client=client, tenant_id=tenant_id)
1016 router = self._get_router(client=client, tenant_id=tenant_id)
Yair Fried413bf2d2014-11-19 17:07:11 +02001017
1018 subnet_kwargs = dict(network=network, client=client)
1019 # use explicit check because empty list is a valid option
1020 if dns_nameservers is not None:
1021 subnet_kwargs['dns_nameservers'] = dns_nameservers
1022 subnet = self._create_subnet(**subnet_kwargs)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001023 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001024 return network, subnet, router
1025
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001026 def create_server(self, name=None, image=None, flavor=None,
1027 wait_on_boot=True, wait_on_delete=True,
1028 create_kwargs=None):
1029 vnic_type = CONF.network.port_vnic_type
1030
1031 # If vnic_type is configured create port for
1032 # every network
1033 if vnic_type:
1034 ports = []
1035 networks = []
1036 create_port_body = {'binding:vnic_type': vnic_type,
1037 'namestart': 'port-smoke'}
1038 if create_kwargs:
1039 net_client = create_kwargs.get("network_client",
1040 self.network_client)
1041
1042 # Convert security group names to security group ids
1043 # to pass to create_port
1044 if create_kwargs.get('security_groups'):
1045 security_groups = net_client.list_security_groups().get(
1046 'security_groups')
1047 sec_dict = dict([(s['name'], s['id'])
1048 for s in security_groups])
1049
1050 sec_groups_names = [s['name'] for s in create_kwargs[
1051 'security_groups']]
1052 security_groups_ids = [sec_dict[s]
1053 for s in sec_groups_names]
1054
1055 if security_groups_ids:
1056 create_port_body[
1057 'security_groups'] = security_groups_ids
1058 networks = create_kwargs.get('networks')
1059 else:
1060 net_client = self.network_client
1061 # If there are no networks passed to us we look up
1062 # for the tenant's private networks and create a port
1063 # if there is only one private network. The same behaviour
1064 # as we would expect when passing the call to the clients
1065 # with no networks
1066 if not networks:
1067 networks = net_client.list_networks(filters={
1068 'router:external': False})
1069 self.assertEqual(1, len(networks),
1070 "There is more than one"
1071 " network for the tenant")
1072 for net in networks:
1073 net_id = net['uuid']
1074 port = self._create_port(network_id=net_id,
1075 client=net_client,
1076 **create_port_body)
1077 ports.append({'port': port.id})
1078 if ports:
1079 create_kwargs['networks'] = ports
1080
1081 return super(NetworkScenarioTest, self).create_server(
1082 name=name, image=image, flavor=flavor,
1083 wait_on_boot=wait_on_boot, wait_on_delete=wait_on_delete,
1084 create_kwargs=create_kwargs)
1085
Yair Fried1fc32a12014-08-04 09:11:30 +03001086
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001087# power/provision states as of icehouse
1088class BaremetalPowerStates(object):
1089 """Possible power states of an Ironic node."""
1090 POWER_ON = 'power on'
1091 POWER_OFF = 'power off'
1092 REBOOT = 'rebooting'
1093 SUSPEND = 'suspended'
1094
1095
1096class BaremetalProvisionStates(object):
1097 """Possible provision states of an Ironic node."""
1098 NOSTATE = None
1099 INIT = 'initializing'
1100 ACTIVE = 'active'
1101 BUILDING = 'building'
1102 DEPLOYWAIT = 'wait call-back'
1103 DEPLOYING = 'deploying'
1104 DEPLOYFAIL = 'deploy failed'
1105 DEPLOYDONE = 'deploy complete'
1106 DELETING = 'deleting'
1107 DELETED = 'deleted'
1108 ERROR = 'error'
1109
1110
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001111class BaremetalScenarioTest(ScenarioTest):
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001112
1113 credentials = ['primary', 'admin']
1114
Adam Gandelman4a48a602014-03-20 18:23:18 -07001115 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001116 def skip_checks(cls):
1117 super(BaremetalScenarioTest, cls).skip_checks()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001118 if (not CONF.service_available.ironic or
1119 not CONF.baremetal.driver_enabled):
1120 msg = 'Ironic not available or Ironic compute driver not enabled'
1121 raise cls.skipException(msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001122
1123 @classmethod
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001124 def setup_clients(cls):
1125 super(BaremetalScenarioTest, cls).setup_clients()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001126
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001127 cls.baremetal_client = cls.admin_manager.baremetal_client
Adam Gandelman4a48a602014-03-20 18:23:18 -07001128
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001129 @classmethod
1130 def resource_setup(cls):
1131 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001132 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001133 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001134
1135 def _node_state_timeout(self, node_id, state_attr,
1136 target_states, timeout=10, interval=1):
1137 if not isinstance(target_states, list):
1138 target_states = [target_states]
1139
1140 def check_state():
1141 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001142 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001143 return True
1144 return False
1145
1146 if not tempest.test.call_until_true(
1147 check_state, timeout, interval):
1148 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1149 (node_id, state_attr, target_states))
1150 raise exceptions.TimeoutException(msg)
1151
1152 def wait_provisioning_state(self, node_id, state, timeout):
1153 self._node_state_timeout(
1154 node_id=node_id, state_attr='provision_state',
1155 target_states=state, timeout=timeout)
1156
1157 def wait_power_state(self, node_id, state):
1158 self._node_state_timeout(
1159 node_id=node_id, state_attr='power_state',
1160 target_states=state, timeout=CONF.baremetal.power_timeout)
1161
1162 def wait_node(self, instance_id):
1163 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001164
Adam Gandelman4a48a602014-03-20 18:23:18 -07001165 def _get_node():
1166 node = None
1167 try:
1168 node = self.get_node(instance_id=instance_id)
Masayuki Igawabfa07602015-01-20 18:47:17 +09001169 except lib_exc.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001170 pass
1171 return node is not None
1172
1173 if not tempest.test.call_until_true(
1174 _get_node, CONF.baremetal.association_timeout, 1):
1175 msg = ('Timed out waiting to get Ironic node by instance id %s'
1176 % instance_id)
1177 raise exceptions.TimeoutException(msg)
1178
1179 def get_node(self, node_id=None, instance_id=None):
1180 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001181 _, body = self.baremetal_client.show_node(node_id)
1182 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001183 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001184 _, body = self.baremetal_client.show_node_by_instance_uuid(
1185 instance_id)
1186 if body['nodes']:
1187 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001188
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001189 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001190 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001191 _, body = self.baremetal_client.list_node_ports(node_uuid)
1192 for port in body['ports']:
1193 _, p = self.baremetal_client.show_port(port['uuid'])
1194 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001195 return ports
1196
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001197 def add_keypair(self):
1198 self.keypair = self.create_keypair()
1199
1200 def verify_connectivity(self, ip=None):
1201 if ip:
1202 dest = self.get_remote_client(ip)
1203 else:
1204 dest = self.get_remote_client(self.instance)
1205 dest.validate_authentication()
1206
1207 def boot_instance(self):
1208 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001209 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001210 }
1211 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001212 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001213
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001214 self.wait_node(self.instance['id'])
1215 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001216
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001217 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001218
1219 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001220 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001221 [BaremetalProvisionStates.DEPLOYWAIT,
1222 BaremetalProvisionStates.ACTIVE],
1223 timeout=15)
1224
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001225 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001226 BaremetalProvisionStates.ACTIVE,
1227 timeout=CONF.baremetal.active_timeout)
1228
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001229 self.servers_client.wait_for_server_status(self.instance['id'],
1230 'ACTIVE')
1231 self.node = self.get_node(instance_id=self.instance['id'])
Ken'ichi Ohmichi76800242015-07-03 05:12:31 +00001232 self.instance = self.servers_client.show_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001233
1234 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001235 self.servers_client.delete_server(self.instance['id'])
1236 self.wait_power_state(self.node['uuid'],
1237 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001238 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001239 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001240 BaremetalProvisionStates.NOSTATE,
1241 timeout=CONF.baremetal.unprovision_timeout)
1242
Adam Gandelman4a48a602014-03-20 18:23:18 -07001243
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001244class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001245 """
1246 Base class for encryption scenario tests
1247 """
1248
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001249 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001250
1251 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001252 def setup_clients(cls):
1253 super(EncryptionScenarioTest, cls).setup_clients()
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001254 cls.admin_volume_types_client = cls.os_adm.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001255
1256 def _wait_for_volume_status(self, status):
1257 self.status_timeout(
1258 self.volume_client.volumes, self.volume.id, status)
1259
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001260 def nova_boot(self):
1261 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001262 create_kwargs = {'key_name': self.keypair['name']}
1263 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001264 create_kwargs=create_kwargs)
1265
1266 def create_volume_type(self, client=None, name=None):
1267 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001268 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001269 if not name:
1270 name = 'generic'
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +00001271 randomized_name = data_utils.rand_name('scenario-type-' + name)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001272 LOG.debug("Creating a volume type: %s", randomized_name)
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001273 body = client.create_volume_type(
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001274 randomized_name)
1275 self.assertIn('id', body)
1276 self.addCleanup(client.delete_volume_type, body['id'])
1277 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001278
1279 def create_encryption_type(self, client=None, type_id=None, provider=None,
1280 key_size=None, cipher=None,
1281 control_location=None):
1282 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001283 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001284 if not type_id:
1285 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001286 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001287 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001288 client.create_encryption_type(
1289 type_id, provider=provider, key_size=key_size, cipher=cipher,
1290 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001291
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001292
Chris Dent0d494112014-08-26 13:48:30 +01001293class SwiftScenarioTest(ScenarioTest):
1294 """
1295 Provide harness to do Swift scenario tests.
1296
1297 Subclasses implement the tests that use the methods provided by this
1298 class.
1299 """
1300
1301 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001302 def skip_checks(cls):
1303 super(SwiftScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001304 if not CONF.service_available.swift:
1305 skip_msg = ("%s skipped as swift is not available" %
1306 cls.__name__)
1307 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001308
1309 @classmethod
1310 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001311 cls.set_network_resources()
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001312 super(SwiftScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001313 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001314 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001315
1316 @classmethod
1317 def setup_clients(cls):
1318 super(SwiftScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001319 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001320 cls.account_client = cls.os_operator.account_client
1321 cls.container_client = cls.os_operator.container_client
1322 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001323
Chris Dentde456a12014-09-10 12:41:15 +01001324 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001325 """get swift status for our user account."""
1326 self.account_client.list_account_containers()
1327 LOG.debug('Swift status information obtained successfully')
1328
Chris Dentde456a12014-09-10 12:41:15 +01001329 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001330 name = container_name or data_utils.rand_name(
1331 'swift-scenario-container')
1332 self.container_client.create_container(name)
1333 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001334 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001335 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001336 self.addCleanup(self.delete_wrapper,
1337 self.container_client.delete_container,
1338 name)
Chris Dent0d494112014-08-26 13:48:30 +01001339 return name
1340
Chris Dentde456a12014-09-10 12:41:15 +01001341 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001342 self.container_client.delete_container(container_name)
1343 LOG.debug('Container %s deleted' % (container_name))
1344
Chris Dentde456a12014-09-10 12:41:15 +01001345 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001346 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1347 obj_data = data_utils.arbitrary_string()
1348 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001349 self.addCleanup(self.delete_wrapper,
1350 self.object_client.delete_object,
1351 container_name,
1352 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001353 return obj_name, obj_data
1354
Chris Dentde456a12014-09-10 12:41:15 +01001355 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001356 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001357 self.list_and_check_container_objects(container_name,
1358 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001359
Chris Dentde456a12014-09-10 12:41:15 +01001360 def list_and_check_container_objects(self, container_name,
1361 present_obj=None,
1362 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001363 """
1364 List objects for a given container and assert which are present and
1365 which are not.
1366 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001367 if present_obj is None:
1368 present_obj = []
1369 if not_present_obj is None:
1370 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001371 _, object_list = self.container_client.list_container_contents(
1372 container_name)
1373 if present_obj:
1374 for obj in present_obj:
1375 self.assertIn(obj, object_list)
1376 if not_present_obj:
1377 for obj in not_present_obj:
1378 self.assertNotIn(obj, object_list)
1379
Chris Dentde456a12014-09-10 12:41:15 +01001380 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001381 metadata_param = {'metadata_prefix': 'x-container-',
1382 'metadata': {'read': acl}}
1383 self.container_client.update_container_metadata(container_name,
1384 **metadata_param)
1385 resp, _ = self.container_client.list_container_metadata(container_name)
1386 self.assertEqual(resp['x-container-read'], acl)
1387
Chris Dentde456a12014-09-10 12:41:15 +01001388 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001389 _, obj = self.object_client.get_object(container_name, obj_name)
1390 self.assertEqual(obj, expected_data)