blob: 522aa433368e89dcf82bd9afd5623a8a69ff3e5a [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
599 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
600 result = None
601 # Repeatedly attempt subnet creation with sequential cidr
602 # blocks until an unallocated block is found.
603 for subnet_cidr in tenant_cidr.subnet(
604 CONF.network.tenant_network_mask_bits):
605 str_cidr = str(subnet_cidr)
606 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
607 continue
608
609 subnet = dict(
610 name=data_utils.rand_name(namestart),
611 ip_version=4,
612 network_id=network.id,
613 tenant_id=network.tenant_id,
614 cidr=str_cidr,
615 **kwargs
616 )
617 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300618 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300619 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300620 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300621 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
622 if not is_overlapping_cidr:
623 raise
624 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300625 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300626 **result['subnet'])
627 self.assertEqual(subnet.cidr, str_cidr)
628 self.addCleanup(self.delete_wrapper, subnet.delete)
629 return subnet
630
Yair Frieddb6c9e92014-08-06 08:53:13 +0300631 def _create_port(self, network, client=None, namestart='port-quotatest'):
632 if not client:
633 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300634 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300635 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300636 name=name,
637 network_id=network.id,
638 tenant_id=network.tenant_id)
639 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300640 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300641 **result['port'])
642 self.addCleanup(self.delete_wrapper, port.delete)
643 return port
644
645 def _get_server_port_id(self, server, ip_addr=None):
646 ports = self._list_ports(device_id=server['id'],
647 fixed_ip=ip_addr)
648 self.assertEqual(len(ports), 1,
649 "Unable to determine which port to target.")
650 return ports[0]['id']
651
David Shrewsbury9bac3662014-08-07 15:07:01 -0400652 def _get_network_by_name(self, network_name):
653 net = self._list_networks(name=network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300654 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400655
Yair Friedae0e73d2014-11-24 11:56:26 +0200656 def create_floating_ip(self, thing, external_network_id=None,
657 port_id=None, client=None):
658 """Creates a floating IP and associates to a resource/port using
659 Neutron client
660 """
661 if not external_network_id:
662 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300663 if not client:
664 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300665 if not port_id:
666 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300667 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300668 floating_network_id=external_network_id,
669 port_id=port_id,
670 tenant_id=thing['tenant_id']
671 )
672 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300673 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300674 **result['floatingip'])
675 self.addCleanup(self.delete_wrapper, floating_ip.delete)
676 return floating_ip
677
678 def _associate_floating_ip(self, floating_ip, server):
679 port_id = self._get_server_port_id(server)
680 floating_ip.update(port_id=port_id)
681 self.assertEqual(port_id, floating_ip.port_id)
682 return floating_ip
683
684 def _disassociate_floating_ip(self, floating_ip):
685 """
686 :param floating_ip: type DeletableFloatingIp
687 """
688 floating_ip.update(port_id=None)
689 self.assertIsNone(floating_ip.port_id)
690 return floating_ip
691
Yair Fried45f92952014-06-26 05:19:19 +0300692 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000693 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300694
695 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
696 to check status
697 :param status: target status
698 :raises: AssertionError if status doesn't match
699 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000700 def refresh():
701 floating_ip.refresh()
702 return status == floating_ip.status
703
704 tempest.test.call_until_true(refresh,
705 CONF.network.build_timeout,
706 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300707 self.assertEqual(status, floating_ip.status,
708 message="FloatingIP: {fp} is at status: {cst}. "
709 "failed to reach status: {st}"
710 .format(fp=floating_ip, cst=floating_ip.status,
711 st=status))
712 LOG.info("FloatingIP: {fp} is at status: {st}"
713 .format(fp=floating_ip, st=status))
714
Yair Fried1fc32a12014-08-04 09:11:30 +0300715 def _check_tenant_network_connectivity(self, server,
716 username,
717 private_key,
718 should_connect=True,
719 servers_for_debug=None):
720 if not CONF.network.tenant_networks_reachable:
721 msg = 'Tenant networks not configured to be reachable.'
722 LOG.info(msg)
723 return
724 # The target login is assumed to have been configured for
725 # key-based authentication by cloud-init.
726 try:
727 for net_name, ip_addresses in server['networks'].iteritems():
728 for ip_address in ip_addresses:
Yair Friedae0e73d2014-11-24 11:56:26 +0200729 self.check_vm_connectivity(ip_address,
730 username,
731 private_key,
732 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300733 except Exception as e:
734 LOG.exception('Tenant network connectivity check failed')
735 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000736 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300737 raise
738
739 def _check_remote_connectivity(self, source, dest, should_succeed=True):
740 """
741 check ping server via source ssh connection
742
743 :param source: RemoteClient: an ssh connection from which to ping
744 :param dest: and IP to ping against
745 :param should_succeed: boolean should ping succeed or not
746 :returns: boolean -- should_succeed == ping
747 :returns: ping is false if ping failed
748 """
749 def ping_remote():
750 try:
751 source.ping_host(dest)
752 except exceptions.SSHExecCommandFailed:
753 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
754 % (dest, source.ssh_client.host))
755 return not should_succeed
756 return should_succeed
757
758 return tempest.test.call_until_true(ping_remote,
759 CONF.compute.ping_timeout,
760 1)
761
Yair Frieddb6c9e92014-08-06 08:53:13 +0300762 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300763 namestart='secgroup-smoke'):
764 if client is None:
765 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300766 if tenant_id is None:
767 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300768 secgroup = self._create_empty_security_group(namestart=namestart,
769 client=client,
770 tenant_id=tenant_id)
771
772 # Add rules to the security group
773 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
774 for rule in rules:
775 self.assertEqual(tenant_id, rule.tenant_id)
776 self.assertEqual(secgroup.id, rule.security_group_id)
777 return secgroup
778
Yair Frieddb6c9e92014-08-06 08:53:13 +0300779 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300780 namestart='secgroup-smoke'):
781 """Create a security group without rules.
782
783 Default rules will be created:
784 - IPv4 egress to any
785 - IPv6 egress to any
786
787 :param tenant_id: secgroup will be created in this tenant
788 :returns: DeletableSecurityGroup -- containing the secgroup created
789 """
790 if client is None:
791 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300792 if not tenant_id:
793 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300794 sg_name = data_utils.rand_name(namestart)
795 sg_desc = sg_name + " description"
796 sg_dict = dict(name=sg_name,
797 description=sg_desc)
798 sg_dict['tenant_id'] = tenant_id
799 _, result = client.create_security_group(**sg_dict)
800 secgroup = net_resources.DeletableSecurityGroup(
801 client=client,
802 **result['security_group']
803 )
804 self.assertEqual(secgroup.name, sg_name)
805 self.assertEqual(tenant_id, secgroup.tenant_id)
806 self.assertEqual(secgroup.description, sg_desc)
807 self.addCleanup(self.delete_wrapper, secgroup.delete)
808 return secgroup
809
Yair Frieddb6c9e92014-08-06 08:53:13 +0300810 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300811 """Get default secgroup for given tenant_id.
812
813 :returns: DeletableSecurityGroup -- default secgroup for given tenant
814 """
815 if client is None:
816 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300817 if not tenant_id:
818 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300819 sgs = [
820 sg for sg in client.list_security_groups().values()[0]
821 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
822 ]
823 msg = "No default security group for tenant %s." % (tenant_id)
824 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300825 return net_resources.DeletableSecurityGroup(client=client,
826 **sgs[0])
827
Yair Frieddb6c9e92014-08-06 08:53:13 +0300828 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300829 tenant_id=None, **kwargs):
830 """Create a rule from a dictionary of rule parameters.
831
832 Create a rule in a secgroup. if secgroup not defined will search for
833 default secgroup in tenant_id.
834
835 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300836 :param tenant_id: if secgroup not passed -- the tenant in which to
837 search for default secgroup
838 :param kwargs: a dictionary containing rule parameters:
839 for example, to allow incoming ssh:
840 rule = {
841 direction: 'ingress'
842 protocol:'tcp',
843 port_range_min: 22,
844 port_range_max: 22
845 }
846 """
847 if client is None:
848 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300849 if not tenant_id:
850 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300851 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300852 secgroup = self._default_security_group(client=client,
853 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300854
855 ruleset = dict(security_group_id=secgroup.id,
856 tenant_id=secgroup.tenant_id)
857 ruleset.update(kwargs)
858
859 _, sg_rule = client.create_security_group_rule(**ruleset)
860 sg_rule = net_resources.DeletableSecurityGroupRule(
861 client=client,
862 **sg_rule['security_group_rule']
863 )
864 self.addCleanup(self.delete_wrapper, sg_rule.delete)
865 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
866 self.assertEqual(secgroup.id, sg_rule.security_group_id)
867
868 return sg_rule
869
870 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
871 """These rules are intended to permit inbound ssh and icmp
872 traffic from all sources, so no group_id is provided.
873 Setting a group_id would only permit traffic from ports
874 belonging to the same security group.
875 """
876
877 if client is None:
878 client = self.network_client
879 rules = []
880 rulesets = [
881 dict(
882 # ssh
883 protocol='tcp',
884 port_range_min=22,
885 port_range_max=22,
886 ),
887 dict(
888 # ping
889 protocol='icmp',
890 )
891 ]
892 for ruleset in rulesets:
893 for r_direction in ['ingress', 'egress']:
894 ruleset['direction'] = r_direction
895 try:
896 sg_rule = self._create_security_group_rule(
897 client=client, secgroup=secgroup, **ruleset)
898 except exceptions.Conflict as ex:
899 # if rule already exist - skip rule and continue
900 msg = 'Security group rule already exists'
901 if msg not in ex._error_string:
902 raise ex
903 else:
904 self.assertEqual(r_direction, sg_rule.direction)
905 rules.append(sg_rule)
906
907 return rules
908
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500909 def _create_pool(self, lb_method, protocol, subnet_id):
910 """Wrapper utility that returns a test pool."""
911 client = self.network_client
912 name = data_utils.rand_name('pool')
913 _, resp_pool = client.create_pool(protocol=protocol, name=name,
914 subnet_id=subnet_id,
915 lb_method=lb_method)
916 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
917 self.assertEqual(pool['name'], name)
918 self.addCleanup(self.delete_wrapper, pool.delete)
919 return pool
920
921 def _create_member(self, address, protocol_port, pool_id):
922 """Wrapper utility that returns a test member."""
923 client = self.network_client
924 _, resp_member = client.create_member(protocol_port=protocol_port,
925 pool_id=pool_id,
926 address=address)
927 member = net_resources.DeletableMember(client=client,
928 **resp_member['member'])
929 self.addCleanup(self.delete_wrapper, member.delete)
930 return member
931
932 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
933 """Wrapper utility that returns a test vip."""
934 client = self.network_client
935 name = data_utils.rand_name('vip')
936 _, resp_vip = client.create_vip(protocol=protocol, name=name,
937 subnet_id=subnet_id, pool_id=pool_id,
938 protocol_port=protocol_port)
939 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
940 self.assertEqual(vip['name'], name)
941 self.addCleanup(self.delete_wrapper, vip.delete)
942 return vip
943
Yair Fried1fc32a12014-08-04 09:11:30 +0300944 def _ssh_to_server(self, server, private_key):
945 ssh_login = CONF.compute.image_ssh_user
946 return self.get_remote_client(server,
947 username=ssh_login,
948 private_key=private_key)
949
Yair Frieddb6c9e92014-08-06 08:53:13 +0300950 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300951 """Retrieve a router for the given tenant id.
952
953 If a public router has been configured, it will be returned.
954
955 If a public router has not been configured, but a public
956 network has, a tenant router will be created and returned that
957 routes traffic to the public network.
958 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300959 if not client:
960 client = self.network_client
961 if not tenant_id:
962 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300963 router_id = CONF.network.public_router_id
964 network_id = CONF.network.public_network_id
965 if router_id:
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +0400966 resp, body = client.show_router(router_id)
967 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300968 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300969 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300970 router.set_gateway(network_id)
971 return router
972 else:
973 raise Exception("Neither of 'public_router_id' or "
974 "'public_network_id' has been defined.")
975
Yair Frieddb6c9e92014-08-06 08:53:13 +0300976 def _create_router(self, client=None, tenant_id=None,
977 namestart='router-smoke'):
978 if not client:
979 client = self.network_client
980 if not tenant_id:
981 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300982 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300983 _, result = client.create_router(name=name,
984 admin_state_up=True,
985 tenant_id=tenant_id)
986 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300987 **result['router'])
988 self.assertEqual(router.name, name)
989 self.addCleanup(self.delete_wrapper, router.delete)
990 return router
991
Yair Frieddb6c9e92014-08-06 08:53:13 +0300992 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300993 """Create a network with a subnet connected to a router.
994
David Shrewsbury9bac3662014-08-07 15:07:01 -0400995 The baremetal driver is a special case since all nodes are
996 on the same shared network.
997
Yair Fried1fc32a12014-08-04 09:11:30 +0300998 :returns: network, subnet, router
999 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001000 if CONF.baremetal.driver_enabled:
1001 # NOTE(Shrews): This exception is for environments where tenant
1002 # credential isolation is available, but network separation is
1003 # not (the current baremetal case). Likely can be removed when
1004 # test account mgmt is reworked:
1005 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
1006 network = self._get_network_by_name(
1007 CONF.compute.fixed_network_name)
1008 router = None
1009 subnet = None
1010 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001011 network = self._create_network(client=client, tenant_id=tenant_id)
1012 router = self._get_router(client=client, tenant_id=tenant_id)
1013 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001014 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001015 return network, subnet, router
1016
1017
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001018# power/provision states as of icehouse
1019class BaremetalPowerStates(object):
1020 """Possible power states of an Ironic node."""
1021 POWER_ON = 'power on'
1022 POWER_OFF = 'power off'
1023 REBOOT = 'rebooting'
1024 SUSPEND = 'suspended'
1025
1026
1027class BaremetalProvisionStates(object):
1028 """Possible provision states of an Ironic node."""
1029 NOSTATE = None
1030 INIT = 'initializing'
1031 ACTIVE = 'active'
1032 BUILDING = 'building'
1033 DEPLOYWAIT = 'wait call-back'
1034 DEPLOYING = 'deploying'
1035 DEPLOYFAIL = 'deploy failed'
1036 DEPLOYDONE = 'deploy complete'
1037 DELETING = 'deleting'
1038 DELETED = 'deleted'
1039 ERROR = 'error'
1040
1041
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001042class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001043 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001044 def resource_setup(cls):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001045 if (not CONF.service_available.ironic or
1046 not CONF.baremetal.driver_enabled):
1047 msg = 'Ironic not available or Ironic compute driver not enabled'
1048 raise cls.skipException(msg)
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001049 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001050
1051 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001052 manager = clients.Manager(
1053 credentials=cls.admin_credentials()
1054 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001055 cls.baremetal_client = manager.baremetal_client
1056
1057 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001058 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001059
1060 def _node_state_timeout(self, node_id, state_attr,
1061 target_states, timeout=10, interval=1):
1062 if not isinstance(target_states, list):
1063 target_states = [target_states]
1064
1065 def check_state():
1066 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001067 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001068 return True
1069 return False
1070
1071 if not tempest.test.call_until_true(
1072 check_state, timeout, interval):
1073 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1074 (node_id, state_attr, target_states))
1075 raise exceptions.TimeoutException(msg)
1076
1077 def wait_provisioning_state(self, node_id, state, timeout):
1078 self._node_state_timeout(
1079 node_id=node_id, state_attr='provision_state',
1080 target_states=state, timeout=timeout)
1081
1082 def wait_power_state(self, node_id, state):
1083 self._node_state_timeout(
1084 node_id=node_id, state_attr='power_state',
1085 target_states=state, timeout=CONF.baremetal.power_timeout)
1086
1087 def wait_node(self, instance_id):
1088 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001089
Adam Gandelman4a48a602014-03-20 18:23:18 -07001090 def _get_node():
1091 node = None
1092 try:
1093 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001094 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001095 pass
1096 return node is not None
1097
1098 if not tempest.test.call_until_true(
1099 _get_node, CONF.baremetal.association_timeout, 1):
1100 msg = ('Timed out waiting to get Ironic node by instance id %s'
1101 % instance_id)
1102 raise exceptions.TimeoutException(msg)
1103
1104 def get_node(self, node_id=None, instance_id=None):
1105 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001106 _, body = self.baremetal_client.show_node(node_id)
1107 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001108 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001109 _, body = self.baremetal_client.show_node_by_instance_uuid(
1110 instance_id)
1111 if body['nodes']:
1112 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001113
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001114 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001115 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001116 _, body = self.baremetal_client.list_node_ports(node_uuid)
1117 for port in body['ports']:
1118 _, p = self.baremetal_client.show_port(port['uuid'])
1119 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001120 return ports
1121
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001122 def add_keypair(self):
1123 self.keypair = self.create_keypair()
1124
1125 def verify_connectivity(self, ip=None):
1126 if ip:
1127 dest = self.get_remote_client(ip)
1128 else:
1129 dest = self.get_remote_client(self.instance)
1130 dest.validate_authentication()
1131
1132 def boot_instance(self):
1133 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001134 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001135 }
1136 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001137 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001138
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001139 self.wait_node(self.instance['id'])
1140 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001141
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001142 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001143
1144 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001145 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001146 [BaremetalProvisionStates.DEPLOYWAIT,
1147 BaremetalProvisionStates.ACTIVE],
1148 timeout=15)
1149
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001150 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001151 BaremetalProvisionStates.ACTIVE,
1152 timeout=CONF.baremetal.active_timeout)
1153
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001154 self.servers_client.wait_for_server_status(self.instance['id'],
1155 'ACTIVE')
1156 self.node = self.get_node(instance_id=self.instance['id'])
1157 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001158
1159 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001160 self.servers_client.delete_server(self.instance['id'])
1161 self.wait_power_state(self.node['uuid'],
1162 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001163 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001164 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001165 BaremetalProvisionStates.NOSTATE,
1166 timeout=CONF.baremetal.unprovision_timeout)
1167
Adam Gandelman4a48a602014-03-20 18:23:18 -07001168
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001169class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001170 """
1171 Base class for encryption scenario tests
1172 """
1173
1174 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001175 def resource_setup(cls):
1176 super(EncryptionScenarioTest, cls).resource_setup()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001177 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001178
1179 def _wait_for_volume_status(self, status):
1180 self.status_timeout(
1181 self.volume_client.volumes, self.volume.id, status)
1182
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001183 def nova_boot(self):
1184 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001185 create_kwargs = {'key_name': self.keypair['name']}
1186 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001187 create_kwargs=create_kwargs)
1188
1189 def create_volume_type(self, client=None, name=None):
1190 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001191 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001192 if not name:
1193 name = 'generic'
1194 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1195 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001196 _, body = client.create_volume_type(
1197 randomized_name)
1198 self.assertIn('id', body)
1199 self.addCleanup(client.delete_volume_type, body['id'])
1200 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001201
1202 def create_encryption_type(self, client=None, type_id=None, provider=None,
1203 key_size=None, cipher=None,
1204 control_location=None):
1205 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001206 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001207 if not type_id:
1208 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001209 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001210 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001211 client.create_encryption_type(
1212 type_id, provider=provider, key_size=key_size, cipher=cipher,
1213 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001214
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001215
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001216class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001217 """
1218 Base class for orchestration scenario tests
1219 """
1220
1221 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001222 def resource_setup(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001223 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001224 raise cls.skipException("Heat support is required")
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001225 super(OrchestrationScenarioTest, cls).resource_setup()
Matt Riedemann11c5b642013-08-24 08:45:38 -07001226
1227 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001228 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001229 admin_creds = auth.get_default_credentials('identity_admin')
1230 creds = auth.get_default_credentials('user')
1231 admin_creds.tenant_name = creds.tenant_name
1232 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001233
1234 def _load_template(self, base_file, file_name):
1235 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1236 file_name)
1237 with open(filepath) as f:
1238 return f.read()
1239
1240 @classmethod
1241 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001242 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001243
1244 @classmethod
1245 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001246 _, networks = cls.networks_client.list_networks()
1247 for net in networks:
1248 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001249 return net
Steve Baker22c16602014-05-05 13:34:19 +12001250
1251 @staticmethod
1252 def _stack_output(stack, output_key):
1253 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001254 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001255 if o['output_key'] == output_key), None)
1256
Chris Dent0d494112014-08-26 13:48:30 +01001257
1258class SwiftScenarioTest(ScenarioTest):
1259 """
1260 Provide harness to do Swift scenario tests.
1261
1262 Subclasses implement the tests that use the methods provided by this
1263 class.
1264 """
1265
1266 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001267 def resource_setup(cls):
Chris Dent0d494112014-08-26 13:48:30 +01001268 if not CONF.service_available.swift:
1269 skip_msg = ("%s skipped as swift is not available" %
1270 cls.__name__)
1271 raise cls.skipException(skip_msg)
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001272 cls.set_network_resources()
1273 super(SwiftScenarioTest, cls).resource_setup()
Chris Dent0d494112014-08-26 13:48:30 +01001274 # Clients for Swift
1275 cls.account_client = cls.manager.account_client
1276 cls.container_client = cls.manager.container_client
1277 cls.object_client = cls.manager.object_client
1278
Chris Dentde456a12014-09-10 12:41:15 +01001279 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001280 """get swift status for our user account."""
1281 self.account_client.list_account_containers()
1282 LOG.debug('Swift status information obtained successfully')
1283
Chris Dentde456a12014-09-10 12:41:15 +01001284 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001285 name = container_name or data_utils.rand_name(
1286 'swift-scenario-container')
1287 self.container_client.create_container(name)
1288 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001289 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001290 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001291 self.addCleanup(self.delete_wrapper,
1292 self.container_client.delete_container,
1293 name)
Chris Dent0d494112014-08-26 13:48:30 +01001294 return name
1295
Chris Dentde456a12014-09-10 12:41:15 +01001296 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001297 self.container_client.delete_container(container_name)
1298 LOG.debug('Container %s deleted' % (container_name))
1299
Chris Dentde456a12014-09-10 12:41:15 +01001300 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001301 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1302 obj_data = data_utils.arbitrary_string()
1303 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001304 self.addCleanup(self.delete_wrapper,
1305 self.object_client.delete_object,
1306 container_name,
1307 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001308 return obj_name, obj_data
1309
Chris Dentde456a12014-09-10 12:41:15 +01001310 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001311 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001312 self.list_and_check_container_objects(container_name,
1313 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001314
Chris Dentde456a12014-09-10 12:41:15 +01001315 def list_and_check_container_objects(self, container_name,
1316 present_obj=None,
1317 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001318 """
1319 List objects for a given container and assert which are present and
1320 which are not.
1321 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001322 if present_obj is None:
1323 present_obj = []
1324 if not_present_obj is None:
1325 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001326 _, object_list = self.container_client.list_container_contents(
1327 container_name)
1328 if present_obj:
1329 for obj in present_obj:
1330 self.assertIn(obj, object_list)
1331 if not_present_obj:
1332 for obj in not_present_obj:
1333 self.assertNotIn(obj, object_list)
1334
Chris Dentde456a12014-09-10 12:41:15 +01001335 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001336 metadata_param = {'metadata_prefix': 'x-container-',
1337 'metadata': {'read': acl}}
1338 self.container_client.update_container_metadata(container_name,
1339 **metadata_param)
1340 resp, _ = self.container_client.list_container_metadata(container_name)
1341 self.assertEqual(resp['x-container-read'], acl)
1342
Chris Dentde456a12014-09-10 12:41:15 +01001343 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001344 _, obj = self.object_client.get_object(container_name, obj_name)
1345 self.assertEqual(obj, expected_data)