blob: 8911ff0ee98e58990401586666aa90566cb05acd [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
Attila Fazekasfb7552a2013-08-27 13:02:26 +020017import logging
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120018import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import subprocess
20
Sean Dague6dbc6da2013-05-08 17:49:46 -040021import netaddr
Matthew Treinish96e9e882014-06-09 18:37:19 -040022import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040023
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000024from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000025from tempest import clients
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010026from tempest.common import credentials
Matt Riedemann343305f2014-05-27 09:55:03 -070027from tempest.common import debug
Masayuki Igawa259c1132013-10-31 17:48:44 +090028from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090029from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000030from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020031from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020032from tempest.openstack.common import log
Yair Fried1fc32a12014-08-04 09:11:30 +030033from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040034import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040035
Matthew Treinish6c072292014-01-29 19:15:52 +000036CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040037
Attila Fazekasfb7552a2013-08-27 13:02:26 +020038LOG = log.getLogger(__name__)
39
40# NOTE(afazekas): Workaround for the stdout logging
41LOG_nova_client = logging.getLogger('novaclient.client')
42LOG_nova_client.addHandler(log.NullHandler())
43
44LOG_cinder_client = logging.getLogger('cinderclient.client')
45LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
47
Andrea Frittoli2e733b52014-07-16 14:12:11 +010048class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010049 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010050
51 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +010052 def resource_setup(cls):
53 super(ScenarioTest, cls).resource_setup()
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010054 # TODO(andreaf) Some of the code from this resource_setup could be
55 # moved into `BaseTestCase`
56 cls.isolated_creds = credentials.get_isolated_credentials(
Andrea Frittoliae9aca02014-09-25 11:43:11 +010057 cls.__name__, network_resources=cls.network_resources)
Andrea Frittoli2e733b52014-07-16 14:12:11 +010058 cls.manager = clients.Manager(
59 credentials=cls.credentials()
60 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010061 cls.admin_manager = clients.Manager(cls.admin_credentials())
62 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070063 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010064 cls.floating_ips_client = cls.manager.floating_ips_client
65 # Glance image client v1
66 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000067 # Compute image client
68 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010069 cls.keypairs_client = cls.manager.keypairs_client
70 cls.networks_client = cls.admin_manager.networks_client
71 # Nova security groups client
72 cls.security_groups_client = cls.manager.security_groups_client
73 cls.servers_client = cls.manager.servers_client
74 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000075 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030076 cls.interface_client = cls.manager.interfaces_client
77 # Neutron network client
78 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090079 # Heat client
80 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010081
82 @classmethod
Andrea Frittoli2e733b52014-07-16 14:12:11 +010083 def credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010084 return cls.isolated_creds.get_primary_creds()
Andrea Frittoli2e733b52014-07-16 14:12:11 +010085
Masayuki Igawaccd66592014-07-17 00:42:42 +090086 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +030087 def alt_credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010088 return cls.isolated_creds.get_alt_creds()
Yair Frieddb6c9e92014-08-06 08:53:13 +030089
90 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +090091 def admin_credentials(cls):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010092 try:
93 return cls.isolated_creds.get_admin_creds()
94 except NotImplementedError:
95 raise cls.skipException('Admin Credentials are not available')
Masayuki Igawaccd66592014-07-17 00:42:42 +090096
Andrea Frittoli247058f2014-07-16 16:09:22 +010097 # ## Methods to handle sync and async deletes
98
99 def setUp(self):
100 super(ScenarioTest, self).setUp()
101 self.cleanup_waits = []
102 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
103 # because scenario tests in the same test class should not share
104 # resources. If resources were shared between test cases then it
105 # should be a single scenario test instead of multiples.
106
107 # NOTE(yfried): this list is cleaned at the end of test_methods and
108 # not at the end of the class
109 self.addCleanup(self._wait_for_cleanups)
110
Yair Fried1fc32a12014-08-04 09:11:30 +0300111 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100112 """Ignores NotFound exceptions for delete operations.
113
Yair Fried1fc32a12014-08-04 09:11:30 +0300114 @param delete_thing: delete method of a resource. method will be
115 executed as delete_thing(*args, **kwargs)
116
Andrea Frittoli247058f2014-07-16 16:09:22 +0100117 """
118 try:
119 # Tempest clients return dicts, so there is no common delete
120 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300121 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100122 except exceptions.NotFound:
123 # If the resource is already missing, mission accomplished.
124 pass
125
126 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900127 cleanup_callable, cleanup_args=None,
128 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700129 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100130
131 @param waiter_callable: callable to wait for the resource to delete
132 @param thing_id: the id of the resource to be cleaned-up
133 @param thing_id_param: the name of the id param in the waiter
134 @param cleanup_callable: method to load pass to self.addCleanup with
135 the following *cleanup_args, **cleanup_kwargs.
136 usually a delete method.
137 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900138 if cleanup_args is None:
139 cleanup_args = []
140 if cleanup_kwargs is None:
141 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100142 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
143 wait_dict = {
144 'waiter_callable': waiter_callable,
145 thing_id_param: thing_id
146 }
147 self.cleanup_waits.append(wait_dict)
148
149 def _wait_for_cleanups(self):
150 """To handle async delete actions, a list of waits is added
151 which will be iterated over as the last step of clearing the
152 cleanup queue. That way all the delete calls are made up front
153 and the tests won't succeed unless the deletes are eventually
154 successful. This is the same basic approach used in the api tests to
155 limit cleanup execution time except here it is multi-resource,
156 because of the nature of the scenario tests.
157 """
158 for wait in self.cleanup_waits:
159 waiter_callable = wait.pop('waiter_callable')
160 waiter_callable(**wait)
161
162 # ## Test functions library
163 #
164 # The create_[resource] functions only return body and discard the
165 # resp part which is not used in scenario tests
166
Yair Frieddb6c9e92014-08-06 08:53:13 +0300167 def create_keypair(self, client=None):
168 if not client:
169 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100170 name = data_utils.rand_name(self.__class__.__name__)
171 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300172 resp, body = client.create_keypair(name)
173 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100174 return body
175
176 def create_server(self, name=None, image=None, flavor=None,
177 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900178 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100179 """Creates VM instance.
180
181 @param image: image from which to create the instance
182 @param wait_on_boot: wait for status ACTIVE before continue
183 @param wait_on_delete: force synchronous delete on cleanup
184 @param create_kwargs: additional details for instance creation
185 @return: server dict
186 """
187 if name is None:
188 name = data_utils.rand_name(self.__class__.__name__)
189 if image is None:
190 image = CONF.compute.image_ref
191 if flavor is None:
192 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900193 if create_kwargs is None:
194 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100195
Andrea Frittoli247058f2014-07-16 16:09:22 +0100196 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
197 name, image, flavor)
198 _, server = self.servers_client.create_server(name, image, flavor,
199 **create_kwargs)
200 if wait_on_delete:
201 self.addCleanup(self.servers_client.wait_for_server_termination,
202 server['id'])
203 self.addCleanup_with_wait(
204 waiter_callable=self.servers_client.wait_for_server_termination,
205 thing_id=server['id'], thing_id_param='server_id',
206 cleanup_callable=self.delete_wrapper,
207 cleanup_args=[self.servers_client.delete_server, server['id']])
208 if wait_on_boot:
209 self.servers_client.wait_for_server_status(server_id=server['id'],
210 status='ACTIVE')
211 # The instance retrieved on creation is missing network
212 # details, necessitating retrieval after it becomes active to
213 # ensure correct details.
214 _, server = self.servers_client.get_server(server['id'])
215 self.assertEqual(server['name'], name)
216 return server
217
218 def create_volume(self, size=1, name=None, snapshot_id=None,
219 imageRef=None, volume_type=None, wait_on_delete=True):
220 if name is None:
221 name = data_utils.rand_name(self.__class__.__name__)
222 _, volume = self.volumes_client.create_volume(
223 size=size, display_name=name, snapshot_id=snapshot_id,
224 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700225
Andrea Frittoli247058f2014-07-16 16:09:22 +0100226 if wait_on_delete:
227 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
228 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700229 self.addCleanup(self.delete_wrapper,
230 self.volumes_client.delete_volume, volume['id'])
231 else:
232 self.addCleanup_with_wait(
233 waiter_callable=self.volumes_client.wait_for_resource_deletion,
234 thing_id=volume['id'], thing_id_param='id',
235 cleanup_callable=self.delete_wrapper,
236 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100237
238 self.assertEqual(name, volume['display_name'])
239 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
240 # The volume retrieved on creation has a non-up-to-date status.
241 # Retrieval after it becomes active ensures correct details.
242 _, volume = self.volumes_client.get_volume(volume['id'])
243 return volume
244
Yair Fried1fc32a12014-08-04 09:11:30 +0300245 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100246 _client = self.security_groups_client
247 if secgroup_id is None:
248 _, sgs = _client.list_security_groups()
249 for sg in sgs:
250 if sg['name'] == 'default':
251 secgroup_id = sg['id']
252
253 # These rules are intended to permit inbound ssh and icmp
254 # traffic from all sources, so no group_id is provided.
255 # Setting a group_id would only permit traffic from ports
256 # belonging to the same security group.
257 rulesets = [
258 {
259 # ssh
260 'ip_proto': 'tcp',
261 'from_port': 22,
262 'to_port': 22,
263 'cidr': '0.0.0.0/0',
264 },
265 {
266 # ping
267 'ip_proto': 'icmp',
268 'from_port': -1,
269 'to_port': -1,
270 'cidr': '0.0.0.0/0',
271 }
272 ]
273 rules = list()
274 for ruleset in rulesets:
275 _, sg_rule = _client.create_security_group_rule(secgroup_id,
276 **ruleset)
277 self.addCleanup(self.delete_wrapper,
278 _client.delete_security_group_rule,
279 sg_rule['id'])
280 rules.append(sg_rule)
281 return rules
282
Yair Fried1fc32a12014-08-04 09:11:30 +0300283 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100284 # Create security group
285 sg_name = data_utils.rand_name(self.__class__.__name__)
286 sg_desc = sg_name + " description"
287 _, secgroup = self.security_groups_client.create_security_group(
288 sg_name, sg_desc)
289 self.assertEqual(secgroup['name'], sg_name)
290 self.assertEqual(secgroup['description'], sg_desc)
291 self.addCleanup(self.delete_wrapper,
292 self.security_groups_client.delete_security_group,
293 secgroup['id'])
294
295 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300296 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100297
298 return secgroup
299
300 def get_remote_client(self, server_or_ip, username=None, private_key=None):
301 if isinstance(server_or_ip, six.string_types):
302 ip = server_or_ip
303 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700304 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
305 ip = addr['addr']
306
Andrea Frittoli247058f2014-07-16 16:09:22 +0100307 if username is None:
308 username = CONF.scenario.ssh_user
309 if private_key is None:
310 private_key = self.keypair['private_key']
311 linux_client = remote_client.RemoteClient(ip, username,
312 pkey=private_key)
313 try:
314 linux_client.validate_authentication()
315 except exceptions.SSHTimeout:
316 LOG.exception('ssh connection to %s failed' % ip)
317 debug.log_net_debug()
318 raise
319
320 return linux_client
321
Ghanshyam2a180b82014-06-16 13:54:22 +0900322 def _image_create(self, name, fmt, path, properties=None):
323 if properties is None:
324 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100325 name = data_utils.rand_name('%s-' % name)
326 image_file = open(path, 'rb')
327 self.addCleanup(image_file.close)
328 params = {
329 'name': name,
330 'container_format': fmt,
331 'disk_format': fmt,
332 'is_public': 'False',
333 }
334 params.update(properties)
335 _, image = self.image_client.create_image(**params)
336 self.addCleanup(self.image_client.delete_image, image['id'])
337 self.assertEqual("queued", image['status'])
338 self.image_client.update_image(image['id'], data=image_file)
339 return image['id']
340
341 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300342 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100343 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
344 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
345 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300346 img_container_format = CONF.scenario.img_container_format
347 img_disk_format = CONF.scenario.img_disk_format
348 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
349 "ami: %s, ari: %s, aki: %s" %
350 (img_path, img_container_format, img_disk_format,
351 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100352 try:
353 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300354 img_container_format,
355 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100356 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300357 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100358 except IOError:
359 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
360 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
361 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
362 properties = {
363 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
364 }
365 self.image = self._image_create('scenario-ami', 'ami',
366 path=ami_img_path,
367 properties=properties)
368 LOG.debug("image:%s" % self.image)
369
370 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400371 if not CONF.compute_feature_enabled.console_output:
372 LOG.debug('Console output not supported, cannot log')
373 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100374 if not servers:
375 _, servers = self.servers_client.list_servers()
376 servers = servers['servers']
377 for server in servers:
Brant Knudson566c5712014-09-24 20:04:50 -0500378 console_output = self.servers_client.get_console_output(
379 server['id'], length=None)
380 LOG.debug('Console output for %s\nhead=%s\nbody=\n%s',
381 server['id'], console_output[0], console_output[1])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100382
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000383 def _log_net_info(self, exc):
384 # network debug is called as part of ssh init
385 if not isinstance(exc, exceptions.SSHTimeout):
386 LOG.debug('Network information on a devstack host')
387 debug.log_net_debug()
388
nithya-ganesan882595e2014-07-29 18:51:07 +0000389 def create_server_snapshot(self, server, name=None):
390 # Glance client
391 _image_client = self.image_client
392 # Compute client
393 _images_client = self.images_client
394 if name is None:
395 name = data_utils.rand_name('scenario-snapshot-')
396 LOG.debug("Creating a snapshot image for server: %s", server['name'])
397 resp, image = _images_client.create_image(server['id'], name)
398 image_id = resp['location'].split('images/')[1]
399 _image_client.wait_for_image_status(image_id, 'active')
400 self.addCleanup_with_wait(
401 waiter_callable=_image_client.wait_for_resource_deletion,
402 thing_id=image_id, thing_id_param='id',
403 cleanup_callable=self.delete_wrapper,
404 cleanup_args=[_image_client.delete_image, image_id])
405 _, snapshot_image = _image_client.get_image_meta(image_id)
406 image_name = snapshot_image['name']
407 self.assertEqual(name, image_name)
408 LOG.debug("Created snapshot image %s for server %s",
409 image_name, server['name'])
410 return snapshot_image
411
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900412 def nova_volume_attach(self):
413 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
Ghanshyam5c2a5582014-04-14 17:16:57 +0900414 _, volume = self.servers_client.attach_volume(
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900415 self.server['id'], self.volume['id'], '/dev/vdb')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900416 self.assertEqual(self.volume['id'], volume['id'])
417 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
418 # Refresh the volume after the attachment
419 _, self.volume = self.volumes_client.get_volume(volume['id'])
420
421 def nova_volume_detach(self):
422 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
423 self.volumes_client.wait_for_volume_status(self.volume['id'],
424 'available')
425
426 _, volume = self.volumes_client.get_volume(self.volume['id'])
427 self.assertEqual('available', volume['status'])
428
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700429 def rebuild_server(self, server_id, image=None,
430 preserve_ephemeral=False, wait=True,
431 rebuild_kwargs=None):
432 if image is None:
433 image = CONF.compute.image_ref
434
435 rebuild_kwargs = rebuild_kwargs or {}
436
437 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
438 server_id, image, preserve_ephemeral)
439 self.servers_client.rebuild(server_id=server_id, image_ref=image,
440 preserve_ephemeral=preserve_ephemeral,
441 **rebuild_kwargs)
442 if wait:
443 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
444
Steven Hardyda2a8352014-10-02 12:52:20 +0100445 def ping_ip_address(self, ip_address, should_succeed=True,
446 ping_timeout=None):
447 timeout = ping_timeout or CONF.compute.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700448 cmd = ['ping', '-c1', '-w1', ip_address]
449
450 def ping():
451 proc = subprocess.Popen(cmd,
452 stdout=subprocess.PIPE,
453 stderr=subprocess.PIPE)
454 proc.communicate()
455 return (proc.returncode == 0) == should_succeed
456
Steven Hardyda2a8352014-10-02 12:52:20 +0100457 return tempest.test.call_until_true(ping, timeout, 1)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700458
Yair Friedae0e73d2014-11-24 11:56:26 +0200459 def check_vm_connectivity(self, ip_address,
460 username=None,
461 private_key=None,
462 should_connect=True):
463 """
464 :param ip_address: server to test against
465 :param username: server's ssh username
466 :param private_key: server's ssh private key to be used
467 :param should_connect: True/False indicates positive/negative test
468 positive - attempt ping and ssh
469 negative - attempt ping and fail if succeed
470
471 :raises: AssertError if the result of the connectivity check does
472 not match the value of the should_connect param
473 """
474 if should_connect:
475 msg = "Timed out waiting for %s to become reachable" % ip_address
476 else:
477 msg = "ip address %s is reachable" % ip_address
478 self.assertTrue(self.ping_ip_address(ip_address,
479 should_succeed=should_connect),
480 msg=msg)
481 if should_connect:
482 # no need to check ssh for negative connectivity
483 self.get_remote_client(ip_address, username, private_key)
484
485 def check_public_network_connectivity(self, ip_address, username,
486 private_key, should_connect=True,
487 msg=None, servers=None):
488 # The target login is assumed to have been configured for
489 # key-based authentication by cloud-init.
490 LOG.debug('checking network connections to IP %s with user: %s' %
491 (ip_address, username))
492 try:
493 self.check_vm_connectivity(ip_address,
494 username,
495 private_key,
496 should_connect=should_connect)
497 except Exception as e:
498 ex_msg = 'Public network connectivity check failed'
499 if msg:
500 ex_msg += ": " + msg
501 LOG.exception(ex_msg)
502 self._log_console_output(servers)
503 # network debug is called as part of ssh init
504 if not isinstance(e, exceptions.SSHTimeout):
505 debug.log_net_debug()
506 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
513 _, floating_ip = self.floating_ips_client.create_floating_ip(pool_name)
514 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
533 @classmethod
534 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100535 if not CONF.service_available.neutron:
536 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300537
538 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100539 def resource_setup(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +0900540 cls.check_preconditions()
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100541 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300542 cls.tenant_id = cls.manager.identity_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300543
Yair Frieddb6c9e92014-08-06 08:53:13 +0300544 def _create_network(self, client=None, tenant_id=None,
545 namestart='network-smoke-'):
546 if not client:
547 client = self.network_client
548 if not tenant_id:
549 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300550 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300551 _, result = client.create_network(name=name, tenant_id=tenant_id)
552 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300553 **result['network'])
554 self.assertEqual(network.name, name)
555 self.addCleanup(self.delete_wrapper, network.delete)
556 return network
557
558 def _list_networks(self, *args, **kwargs):
559 """List networks using admin creds """
560 return self._admin_lister('networks')(*args, **kwargs)
561
562 def _list_subnets(self, *args, **kwargs):
563 """List subnets using admin creds """
564 return self._admin_lister('subnets')(*args, **kwargs)
565
566 def _list_routers(self, *args, **kwargs):
567 """List routers using admin creds """
568 return self._admin_lister('routers')(*args, **kwargs)
569
570 def _list_ports(self, *args, **kwargs):
571 """List ports using admin creds """
572 return self._admin_lister('ports')(*args, **kwargs)
573
574 def _admin_lister(self, resource_type):
575 def temp(*args, **kwargs):
576 temp_method = self.admin_manager.network_client.__getattr__(
577 'list_%s' % resource_type)
578 _, resource_list = temp_method(*args, **kwargs)
579 return resource_list[resource_type]
580 return temp
581
Yair Frieddb6c9e92014-08-06 08:53:13 +0300582 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
583 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300584 """
585 Create a subnet for the given network within the cidr block
586 configured for tenant networks.
587 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300588 if not client:
589 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300590
591 def cidr_in_use(cidr, tenant_id):
592 """
593 :return True if subnet with cidr already exist in tenant
594 False else
595 """
596 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
597 return len(cidr_in_use) != 0
598
Kirill Shileev14113572014-11-21 16:58:02 +0300599 ip_version = kwargs.pop('ip_version', 4)
600
601 if ip_version == 6:
602 tenant_cidr = netaddr.IPNetwork(
603 CONF.network.tenant_network_v6_cidr)
604 num_bits = CONF.network.tenant_network_v6_mask_bits
605 else:
606 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
607 num_bits = CONF.network.tenant_network_mask_bits
608
Yair Fried1fc32a12014-08-04 09:11:30 +0300609 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300610 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300611 # Repeatedly attempt subnet creation with sequential cidr
612 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300613 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300614 str_cidr = str(subnet_cidr)
615 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
616 continue
617
618 subnet = dict(
619 name=data_utils.rand_name(namestart),
Yair Fried1fc32a12014-08-04 09:11:30 +0300620 network_id=network.id,
621 tenant_id=network.tenant_id,
622 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300623 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300624 **kwargs
625 )
626 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300627 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300628 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300629 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300630 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
631 if not is_overlapping_cidr:
632 raise
633 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300634 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300635 **result['subnet'])
636 self.assertEqual(subnet.cidr, str_cidr)
637 self.addCleanup(self.delete_wrapper, subnet.delete)
638 return subnet
639
Yair Frieddb6c9e92014-08-06 08:53:13 +0300640 def _create_port(self, network, client=None, namestart='port-quotatest'):
641 if not client:
642 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300643 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300644 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300645 name=name,
646 network_id=network.id,
647 tenant_id=network.tenant_id)
648 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300649 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300650 **result['port'])
651 self.addCleanup(self.delete_wrapper, port.delete)
652 return port
653
Kirill Shileev14113572014-11-21 16:58:02 +0300654 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300655 ports = self._list_ports(device_id=server['id'],
656 fixed_ip=ip_addr)
657 self.assertEqual(len(ports), 1,
658 "Unable to determine which port to target.")
Kirill Shileev14113572014-11-21 16:58:02 +0300659 # it might happen here that this port has more then one ip address
660 # as in case of dual stack- when this port is created on 2 subnets
661 for ip46 in ports[0]['fixed_ips']:
662 ip = ip46['ip_address']
663 if netaddr.valid_ipv4(ip):
664 return ports[0]['id'], ip
Yair Fried1fc32a12014-08-04 09:11:30 +0300665
David Shrewsbury9bac3662014-08-07 15:07:01 -0400666 def _get_network_by_name(self, network_name):
667 net = self._list_networks(name=network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300668 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400669
Yair Friedae0e73d2014-11-24 11:56:26 +0200670 def create_floating_ip(self, thing, external_network_id=None,
671 port_id=None, client=None):
672 """Creates a floating IP and associates to a resource/port using
673 Neutron client
674 """
675 if not external_network_id:
676 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300677 if not client:
678 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300679 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300680 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
681 else:
682 ip4 = None
Yair Frieddb6c9e92014-08-06 08:53:13 +0300683 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300684 floating_network_id=external_network_id,
685 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300686 tenant_id=thing['tenant_id'],
687 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300688 )
689 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300690 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300691 **result['floatingip'])
692 self.addCleanup(self.delete_wrapper, floating_ip.delete)
693 return floating_ip
694
695 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300696 port_id, _ = self._get_server_port_id_and_ip4(server)
Yair Fried1fc32a12014-08-04 09:11:30 +0300697 floating_ip.update(port_id=port_id)
698 self.assertEqual(port_id, floating_ip.port_id)
699 return floating_ip
700
701 def _disassociate_floating_ip(self, floating_ip):
702 """
703 :param floating_ip: type DeletableFloatingIp
704 """
705 floating_ip.update(port_id=None)
706 self.assertIsNone(floating_ip.port_id)
707 return floating_ip
708
Yair Fried45f92952014-06-26 05:19:19 +0300709 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000710 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300711
712 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
713 to check status
714 :param status: target status
715 :raises: AssertionError if status doesn't match
716 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000717 def refresh():
718 floating_ip.refresh()
719 return status == floating_ip.status
720
721 tempest.test.call_until_true(refresh,
722 CONF.network.build_timeout,
723 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300724 self.assertEqual(status, floating_ip.status,
725 message="FloatingIP: {fp} is at status: {cst}. "
726 "failed to reach status: {st}"
727 .format(fp=floating_ip, cst=floating_ip.status,
728 st=status))
729 LOG.info("FloatingIP: {fp} is at status: {st}"
730 .format(fp=floating_ip, st=status))
731
Yair Fried1fc32a12014-08-04 09:11:30 +0300732 def _check_tenant_network_connectivity(self, server,
733 username,
734 private_key,
735 should_connect=True,
736 servers_for_debug=None):
737 if not CONF.network.tenant_networks_reachable:
738 msg = 'Tenant networks not configured to be reachable.'
739 LOG.info(msg)
740 return
741 # The target login is assumed to have been configured for
742 # key-based authentication by cloud-init.
743 try:
744 for net_name, ip_addresses in server['networks'].iteritems():
745 for ip_address in ip_addresses:
Yair Friedae0e73d2014-11-24 11:56:26 +0200746 self.check_vm_connectivity(ip_address,
747 username,
748 private_key,
749 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300750 except Exception as e:
751 LOG.exception('Tenant network connectivity check failed')
752 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000753 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300754 raise
755
756 def _check_remote_connectivity(self, source, dest, should_succeed=True):
757 """
758 check ping server via source ssh connection
759
760 :param source: RemoteClient: an ssh connection from which to ping
761 :param dest: and IP to ping against
762 :param should_succeed: boolean should ping succeed or not
763 :returns: boolean -- should_succeed == ping
764 :returns: ping is false if ping failed
765 """
766 def ping_remote():
767 try:
768 source.ping_host(dest)
769 except exceptions.SSHExecCommandFailed:
770 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
771 % (dest, source.ssh_client.host))
772 return not should_succeed
773 return should_succeed
774
775 return tempest.test.call_until_true(ping_remote,
776 CONF.compute.ping_timeout,
777 1)
778
Yair Frieddb6c9e92014-08-06 08:53:13 +0300779 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300780 namestart='secgroup-smoke'):
781 if client is None:
782 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300783 if tenant_id is None:
784 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 secgroup = self._create_empty_security_group(namestart=namestart,
786 client=client,
787 tenant_id=tenant_id)
788
789 # Add rules to the security group
790 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
791 for rule in rules:
792 self.assertEqual(tenant_id, rule.tenant_id)
793 self.assertEqual(secgroup.id, rule.security_group_id)
794 return secgroup
795
Yair Frieddb6c9e92014-08-06 08:53:13 +0300796 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300797 namestart='secgroup-smoke'):
798 """Create a security group without rules.
799
800 Default rules will be created:
801 - IPv4 egress to any
802 - IPv6 egress to any
803
804 :param tenant_id: secgroup will be created in this tenant
805 :returns: DeletableSecurityGroup -- containing the secgroup created
806 """
807 if client is None:
808 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300809 if not tenant_id:
810 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300811 sg_name = data_utils.rand_name(namestart)
812 sg_desc = sg_name + " description"
813 sg_dict = dict(name=sg_name,
814 description=sg_desc)
815 sg_dict['tenant_id'] = tenant_id
816 _, result = client.create_security_group(**sg_dict)
817 secgroup = net_resources.DeletableSecurityGroup(
818 client=client,
819 **result['security_group']
820 )
821 self.assertEqual(secgroup.name, sg_name)
822 self.assertEqual(tenant_id, secgroup.tenant_id)
823 self.assertEqual(secgroup.description, sg_desc)
824 self.addCleanup(self.delete_wrapper, secgroup.delete)
825 return secgroup
826
Yair Frieddb6c9e92014-08-06 08:53:13 +0300827 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300828 """Get default secgroup for given tenant_id.
829
830 :returns: DeletableSecurityGroup -- default secgroup for given tenant
831 """
832 if client is None:
833 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300834 if not tenant_id:
835 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300836 sgs = [
837 sg for sg in client.list_security_groups().values()[0]
838 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
839 ]
840 msg = "No default security group for tenant %s." % (tenant_id)
841 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300842 return net_resources.DeletableSecurityGroup(client=client,
843 **sgs[0])
844
Yair Frieddb6c9e92014-08-06 08:53:13 +0300845 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300846 tenant_id=None, **kwargs):
847 """Create a rule from a dictionary of rule parameters.
848
849 Create a rule in a secgroup. if secgroup not defined will search for
850 default secgroup in tenant_id.
851
852 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300853 :param tenant_id: if secgroup not passed -- the tenant in which to
854 search for default secgroup
855 :param kwargs: a dictionary containing rule parameters:
856 for example, to allow incoming ssh:
857 rule = {
858 direction: 'ingress'
859 protocol:'tcp',
860 port_range_min: 22,
861 port_range_max: 22
862 }
863 """
864 if client is None:
865 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300866 if not tenant_id:
867 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300868 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300869 secgroup = self._default_security_group(client=client,
870 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300871
872 ruleset = dict(security_group_id=secgroup.id,
873 tenant_id=secgroup.tenant_id)
874 ruleset.update(kwargs)
875
876 _, sg_rule = client.create_security_group_rule(**ruleset)
877 sg_rule = net_resources.DeletableSecurityGroupRule(
878 client=client,
879 **sg_rule['security_group_rule']
880 )
881 self.addCleanup(self.delete_wrapper, sg_rule.delete)
882 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
883 self.assertEqual(secgroup.id, sg_rule.security_group_id)
884
885 return sg_rule
886
887 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
888 """These rules are intended to permit inbound ssh and icmp
889 traffic from all sources, so no group_id is provided.
890 Setting a group_id would only permit traffic from ports
891 belonging to the same security group.
892 """
893
894 if client is None:
895 client = self.network_client
896 rules = []
897 rulesets = [
898 dict(
899 # ssh
900 protocol='tcp',
901 port_range_min=22,
902 port_range_max=22,
903 ),
904 dict(
905 # ping
906 protocol='icmp',
907 )
908 ]
909 for ruleset in rulesets:
910 for r_direction in ['ingress', 'egress']:
911 ruleset['direction'] = r_direction
912 try:
913 sg_rule = self._create_security_group_rule(
914 client=client, secgroup=secgroup, **ruleset)
915 except exceptions.Conflict as ex:
916 # if rule already exist - skip rule and continue
917 msg = 'Security group rule already exists'
918 if msg not in ex._error_string:
919 raise ex
920 else:
921 self.assertEqual(r_direction, sg_rule.direction)
922 rules.append(sg_rule)
923
924 return rules
925
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500926 def _create_pool(self, lb_method, protocol, subnet_id):
927 """Wrapper utility that returns a test pool."""
928 client = self.network_client
929 name = data_utils.rand_name('pool')
930 _, resp_pool = client.create_pool(protocol=protocol, name=name,
931 subnet_id=subnet_id,
932 lb_method=lb_method)
933 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
934 self.assertEqual(pool['name'], name)
935 self.addCleanup(self.delete_wrapper, pool.delete)
936 return pool
937
938 def _create_member(self, address, protocol_port, pool_id):
939 """Wrapper utility that returns a test member."""
940 client = self.network_client
941 _, resp_member = client.create_member(protocol_port=protocol_port,
942 pool_id=pool_id,
943 address=address)
944 member = net_resources.DeletableMember(client=client,
945 **resp_member['member'])
946 self.addCleanup(self.delete_wrapper, member.delete)
947 return member
948
949 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
950 """Wrapper utility that returns a test vip."""
951 client = self.network_client
952 name = data_utils.rand_name('vip')
953 _, resp_vip = client.create_vip(protocol=protocol, name=name,
954 subnet_id=subnet_id, pool_id=pool_id,
955 protocol_port=protocol_port)
956 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
957 self.assertEqual(vip['name'], name)
958 self.addCleanup(self.delete_wrapper, vip.delete)
959 return vip
960
Yair Fried1fc32a12014-08-04 09:11:30 +0300961 def _ssh_to_server(self, server, private_key):
962 ssh_login = CONF.compute.image_ssh_user
963 return self.get_remote_client(server,
964 username=ssh_login,
965 private_key=private_key)
966
Yair Frieddb6c9e92014-08-06 08:53:13 +0300967 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300968 """Retrieve a router for the given tenant id.
969
970 If a public router has been configured, it will be returned.
971
972 If a public router has not been configured, but a public
973 network has, a tenant router will be created and returned that
974 routes traffic to the public network.
975 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300976 if not client:
977 client = self.network_client
978 if not tenant_id:
979 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300980 router_id = CONF.network.public_router_id
981 network_id = CONF.network.public_network_id
982 if router_id:
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +0400983 resp, body = client.show_router(router_id)
984 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300985 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300986 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300987 router.set_gateway(network_id)
988 return router
989 else:
990 raise Exception("Neither of 'public_router_id' or "
991 "'public_network_id' has been defined.")
992
Yair Frieddb6c9e92014-08-06 08:53:13 +0300993 def _create_router(self, client=None, tenant_id=None,
994 namestart='router-smoke'):
995 if not client:
996 client = self.network_client
997 if not tenant_id:
998 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300999 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001000 _, result = client.create_router(name=name,
1001 admin_state_up=True,
1002 tenant_id=tenant_id)
1003 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +03001004 **result['router'])
1005 self.assertEqual(router.name, name)
1006 self.addCleanup(self.delete_wrapper, router.delete)
1007 return router
1008
Yair Frieddb6c9e92014-08-06 08:53:13 +03001009 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001010 """Create a network with a subnet connected to a router.
1011
David Shrewsbury9bac3662014-08-07 15:07:01 -04001012 The baremetal driver is a special case since all nodes are
1013 on the same shared network.
1014
Yair Fried1fc32a12014-08-04 09:11:30 +03001015 :returns: network, subnet, router
1016 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001017 if CONF.baremetal.driver_enabled:
1018 # NOTE(Shrews): This exception is for environments where tenant
1019 # credential isolation is available, but network separation is
1020 # not (the current baremetal case). Likely can be removed when
1021 # test account mgmt is reworked:
1022 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
1023 network = self._get_network_by_name(
1024 CONF.compute.fixed_network_name)
1025 router = None
1026 subnet = None
1027 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001028 network = self._create_network(client=client, tenant_id=tenant_id)
1029 router = self._get_router(client=client, tenant_id=tenant_id)
1030 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001031 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001032 return network, subnet, router
1033
1034
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001035# power/provision states as of icehouse
1036class BaremetalPowerStates(object):
1037 """Possible power states of an Ironic node."""
1038 POWER_ON = 'power on'
1039 POWER_OFF = 'power off'
1040 REBOOT = 'rebooting'
1041 SUSPEND = 'suspended'
1042
1043
1044class BaremetalProvisionStates(object):
1045 """Possible provision states of an Ironic node."""
1046 NOSTATE = None
1047 INIT = 'initializing'
1048 ACTIVE = 'active'
1049 BUILDING = 'building'
1050 DEPLOYWAIT = 'wait call-back'
1051 DEPLOYING = 'deploying'
1052 DEPLOYFAIL = 'deploy failed'
1053 DEPLOYDONE = 'deploy complete'
1054 DELETING = 'deleting'
1055 DELETED = 'deleted'
1056 ERROR = 'error'
1057
1058
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001059class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001060 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001061 def resource_setup(cls):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001062 if (not CONF.service_available.ironic or
1063 not CONF.baremetal.driver_enabled):
1064 msg = 'Ironic not available or Ironic compute driver not enabled'
1065 raise cls.skipException(msg)
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001066 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001067
1068 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001069 manager = clients.Manager(
1070 credentials=cls.admin_credentials()
1071 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001072 cls.baremetal_client = manager.baremetal_client
1073
1074 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001075 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001076
1077 def _node_state_timeout(self, node_id, state_attr,
1078 target_states, timeout=10, interval=1):
1079 if not isinstance(target_states, list):
1080 target_states = [target_states]
1081
1082 def check_state():
1083 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001084 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001085 return True
1086 return False
1087
1088 if not tempest.test.call_until_true(
1089 check_state, timeout, interval):
1090 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1091 (node_id, state_attr, target_states))
1092 raise exceptions.TimeoutException(msg)
1093
1094 def wait_provisioning_state(self, node_id, state, timeout):
1095 self._node_state_timeout(
1096 node_id=node_id, state_attr='provision_state',
1097 target_states=state, timeout=timeout)
1098
1099 def wait_power_state(self, node_id, state):
1100 self._node_state_timeout(
1101 node_id=node_id, state_attr='power_state',
1102 target_states=state, timeout=CONF.baremetal.power_timeout)
1103
1104 def wait_node(self, instance_id):
1105 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001106
Adam Gandelman4a48a602014-03-20 18:23:18 -07001107 def _get_node():
1108 node = None
1109 try:
1110 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001111 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001112 pass
1113 return node is not None
1114
1115 if not tempest.test.call_until_true(
1116 _get_node, CONF.baremetal.association_timeout, 1):
1117 msg = ('Timed out waiting to get Ironic node by instance id %s'
1118 % instance_id)
1119 raise exceptions.TimeoutException(msg)
1120
1121 def get_node(self, node_id=None, instance_id=None):
1122 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001123 _, body = self.baremetal_client.show_node(node_id)
1124 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001125 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001126 _, body = self.baremetal_client.show_node_by_instance_uuid(
1127 instance_id)
1128 if body['nodes']:
1129 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001130
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001131 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001132 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001133 _, body = self.baremetal_client.list_node_ports(node_uuid)
1134 for port in body['ports']:
1135 _, p = self.baremetal_client.show_port(port['uuid'])
1136 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001137 return ports
1138
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001139 def add_keypair(self):
1140 self.keypair = self.create_keypair()
1141
1142 def verify_connectivity(self, ip=None):
1143 if ip:
1144 dest = self.get_remote_client(ip)
1145 else:
1146 dest = self.get_remote_client(self.instance)
1147 dest.validate_authentication()
1148
1149 def boot_instance(self):
1150 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001151 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001152 }
1153 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001154 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001155
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001156 self.wait_node(self.instance['id'])
1157 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001158
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001159 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001160
1161 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001162 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001163 [BaremetalProvisionStates.DEPLOYWAIT,
1164 BaremetalProvisionStates.ACTIVE],
1165 timeout=15)
1166
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001167 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001168 BaremetalProvisionStates.ACTIVE,
1169 timeout=CONF.baremetal.active_timeout)
1170
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001171 self.servers_client.wait_for_server_status(self.instance['id'],
1172 'ACTIVE')
1173 self.node = self.get_node(instance_id=self.instance['id'])
1174 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001175
1176 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001177 self.servers_client.delete_server(self.instance['id'])
1178 self.wait_power_state(self.node['uuid'],
1179 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001180 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001181 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001182 BaremetalProvisionStates.NOSTATE,
1183 timeout=CONF.baremetal.unprovision_timeout)
1184
Adam Gandelman4a48a602014-03-20 18:23:18 -07001185
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001186class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001187 """
1188 Base class for encryption scenario tests
1189 """
1190
1191 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001192 def resource_setup(cls):
1193 super(EncryptionScenarioTest, cls).resource_setup()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001194 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001195
1196 def _wait_for_volume_status(self, status):
1197 self.status_timeout(
1198 self.volume_client.volumes, self.volume.id, status)
1199
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001200 def nova_boot(self):
1201 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001202 create_kwargs = {'key_name': self.keypair['name']}
1203 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001204 create_kwargs=create_kwargs)
1205
1206 def create_volume_type(self, client=None, name=None):
1207 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001208 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001209 if not name:
1210 name = 'generic'
1211 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1212 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001213 _, body = client.create_volume_type(
1214 randomized_name)
1215 self.assertIn('id', body)
1216 self.addCleanup(client.delete_volume_type, body['id'])
1217 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001218
1219 def create_encryption_type(self, client=None, type_id=None, provider=None,
1220 key_size=None, cipher=None,
1221 control_location=None):
1222 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001223 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001224 if not type_id:
1225 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001226 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001227 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001228 client.create_encryption_type(
1229 type_id, provider=provider, key_size=key_size, cipher=cipher,
1230 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001231
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001232
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001233class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001234 """
1235 Base class for orchestration scenario tests
1236 """
1237
1238 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001239 def resource_setup(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001240 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001241 raise cls.skipException("Heat support is required")
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001242 super(OrchestrationScenarioTest, cls).resource_setup()
Matt Riedemann11c5b642013-08-24 08:45:38 -07001243
1244 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001245 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001246 admin_creds = auth.get_default_credentials('identity_admin')
1247 creds = auth.get_default_credentials('user')
1248 admin_creds.tenant_name = creds.tenant_name
1249 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001250
1251 def _load_template(self, base_file, file_name):
1252 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1253 file_name)
1254 with open(filepath) as f:
1255 return f.read()
1256
1257 @classmethod
1258 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001259 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001260
1261 @classmethod
1262 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001263 _, networks = cls.networks_client.list_networks()
1264 for net in networks:
1265 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001266 return net
Steve Baker22c16602014-05-05 13:34:19 +12001267
1268 @staticmethod
1269 def _stack_output(stack, output_key):
1270 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001271 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001272 if o['output_key'] == output_key), None)
1273
Chris Dent0d494112014-08-26 13:48:30 +01001274
1275class SwiftScenarioTest(ScenarioTest):
1276 """
1277 Provide harness to do Swift scenario tests.
1278
1279 Subclasses implement the tests that use the methods provided by this
1280 class.
1281 """
1282
1283 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001284 def resource_setup(cls):
Chris Dent0d494112014-08-26 13:48:30 +01001285 if not CONF.service_available.swift:
1286 skip_msg = ("%s skipped as swift is not available" %
1287 cls.__name__)
1288 raise cls.skipException(skip_msg)
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001289 cls.set_network_resources()
1290 super(SwiftScenarioTest, cls).resource_setup()
Chris Dent0d494112014-08-26 13:48:30 +01001291 # Clients for Swift
1292 cls.account_client = cls.manager.account_client
1293 cls.container_client = cls.manager.container_client
1294 cls.object_client = cls.manager.object_client
1295
Chris Dentde456a12014-09-10 12:41:15 +01001296 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001297 """get swift status for our user account."""
1298 self.account_client.list_account_containers()
1299 LOG.debug('Swift status information obtained successfully')
1300
Chris Dentde456a12014-09-10 12:41:15 +01001301 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001302 name = container_name or data_utils.rand_name(
1303 'swift-scenario-container')
1304 self.container_client.create_container(name)
1305 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001306 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001307 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001308 self.addCleanup(self.delete_wrapper,
1309 self.container_client.delete_container,
1310 name)
Chris Dent0d494112014-08-26 13:48:30 +01001311 return name
1312
Chris Dentde456a12014-09-10 12:41:15 +01001313 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001314 self.container_client.delete_container(container_name)
1315 LOG.debug('Container %s deleted' % (container_name))
1316
Chris Dentde456a12014-09-10 12:41:15 +01001317 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001318 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1319 obj_data = data_utils.arbitrary_string()
1320 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001321 self.addCleanup(self.delete_wrapper,
1322 self.object_client.delete_object,
1323 container_name,
1324 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001325 return obj_name, obj_data
1326
Chris Dentde456a12014-09-10 12:41:15 +01001327 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001328 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001329 self.list_and_check_container_objects(container_name,
1330 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001331
Chris Dentde456a12014-09-10 12:41:15 +01001332 def list_and_check_container_objects(self, container_name,
1333 present_obj=None,
1334 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001335 """
1336 List objects for a given container and assert which are present and
1337 which are not.
1338 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001339 if present_obj is None:
1340 present_obj = []
1341 if not_present_obj is None:
1342 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001343 _, object_list = self.container_client.list_container_contents(
1344 container_name)
1345 if present_obj:
1346 for obj in present_obj:
1347 self.assertIn(obj, object_list)
1348 if not_present_obj:
1349 for obj in not_present_obj:
1350 self.assertNotIn(obj, object_list)
1351
Chris Dentde456a12014-09-10 12:41:15 +01001352 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001353 metadata_param = {'metadata_prefix': 'x-container-',
1354 'metadata': {'read': acl}}
1355 self.container_client.update_container_metadata(container_name,
1356 **metadata_param)
1357 resp, _ = self.container_client.list_container_metadata(container_name)
1358 self.assertEqual(resp['x-container-read'], acl)
1359
Chris Dentde456a12014-09-10 12:41:15 +01001360 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001361 _, obj = self.object_client.get_object(container_name, obj_name)
1362 self.assertEqual(obj, expected_data)