blob: 928a8e17bc75b14cf883b8e79cf3d95c91dbefc8 [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
414 _, volume_attachment = self.servers_client.attach_volume(
415 self.server['id'], self.volume['id'], '/dev/vdb')
416 volume = volume_attachment['volumeAttachment']
417 self.assertEqual(self.volume['id'], volume['id'])
418 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
419 # Refresh the volume after the attachment
420 _, self.volume = self.volumes_client.get_volume(volume['id'])
421
422 def nova_volume_detach(self):
423 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
424 self.volumes_client.wait_for_volume_status(self.volume['id'],
425 'available')
426
427 _, volume = self.volumes_client.get_volume(self.volume['id'])
428 self.assertEqual('available', volume['status'])
429
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700430 def rebuild_server(self, server_id, image=None,
431 preserve_ephemeral=False, wait=True,
432 rebuild_kwargs=None):
433 if image is None:
434 image = CONF.compute.image_ref
435
436 rebuild_kwargs = rebuild_kwargs or {}
437
438 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
439 server_id, image, preserve_ephemeral)
440 self.servers_client.rebuild(server_id=server_id, image_ref=image,
441 preserve_ephemeral=preserve_ephemeral,
442 **rebuild_kwargs)
443 if wait:
444 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
445
Steven Hardyda2a8352014-10-02 12:52:20 +0100446 def ping_ip_address(self, ip_address, should_succeed=True,
447 ping_timeout=None):
448 timeout = ping_timeout or CONF.compute.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700449 cmd = ['ping', '-c1', '-w1', ip_address]
450
451 def ping():
452 proc = subprocess.Popen(cmd,
453 stdout=subprocess.PIPE,
454 stderr=subprocess.PIPE)
455 proc.communicate()
456 return (proc.returncode == 0) == should_succeed
457
Steven Hardyda2a8352014-10-02 12:52:20 +0100458 return tempest.test.call_until_true(ping, timeout, 1)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700459
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100460
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100461class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300462 """Base class for network scenario tests.
463 This class provide helpers for network scenario tests, using the neutron
464 API. Helpers from ancestor which use the nova network API are overridden
465 with the neutron API.
466
467 This Class also enforces using Neutron instead of novanetwork.
468 Subclassed tests will be skipped if Neutron is not enabled
469
470 """
471
472 @classmethod
473 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100474 if not CONF.service_available.neutron:
475 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300476
477 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100478 def resource_setup(cls):
479 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300480 cls.tenant_id = cls.manager.identity_client.tenant_id
481 cls.check_preconditions()
482
Yair Frieddb6c9e92014-08-06 08:53:13 +0300483 def _create_network(self, client=None, tenant_id=None,
484 namestart='network-smoke-'):
485 if not client:
486 client = self.network_client
487 if not tenant_id:
488 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300489 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300490 _, result = client.create_network(name=name, tenant_id=tenant_id)
491 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300492 **result['network'])
493 self.assertEqual(network.name, name)
494 self.addCleanup(self.delete_wrapper, network.delete)
495 return network
496
497 def _list_networks(self, *args, **kwargs):
498 """List networks using admin creds """
499 return self._admin_lister('networks')(*args, **kwargs)
500
501 def _list_subnets(self, *args, **kwargs):
502 """List subnets using admin creds """
503 return self._admin_lister('subnets')(*args, **kwargs)
504
505 def _list_routers(self, *args, **kwargs):
506 """List routers using admin creds """
507 return self._admin_lister('routers')(*args, **kwargs)
508
509 def _list_ports(self, *args, **kwargs):
510 """List ports using admin creds """
511 return self._admin_lister('ports')(*args, **kwargs)
512
513 def _admin_lister(self, resource_type):
514 def temp(*args, **kwargs):
515 temp_method = self.admin_manager.network_client.__getattr__(
516 'list_%s' % resource_type)
517 _, resource_list = temp_method(*args, **kwargs)
518 return resource_list[resource_type]
519 return temp
520
Yair Frieddb6c9e92014-08-06 08:53:13 +0300521 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
522 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300523 """
524 Create a subnet for the given network within the cidr block
525 configured for tenant networks.
526 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300527 if not client:
528 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300529
530 def cidr_in_use(cidr, tenant_id):
531 """
532 :return True if subnet with cidr already exist in tenant
533 False else
534 """
535 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
536 return len(cidr_in_use) != 0
537
538 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
539 result = None
540 # Repeatedly attempt subnet creation with sequential cidr
541 # blocks until an unallocated block is found.
542 for subnet_cidr in tenant_cidr.subnet(
543 CONF.network.tenant_network_mask_bits):
544 str_cidr = str(subnet_cidr)
545 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
546 continue
547
548 subnet = dict(
549 name=data_utils.rand_name(namestart),
550 ip_version=4,
551 network_id=network.id,
552 tenant_id=network.tenant_id,
553 cidr=str_cidr,
554 **kwargs
555 )
556 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300557 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300558 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300559 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300560 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
561 if not is_overlapping_cidr:
562 raise
563 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300564 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300565 **result['subnet'])
566 self.assertEqual(subnet.cidr, str_cidr)
567 self.addCleanup(self.delete_wrapper, subnet.delete)
568 return subnet
569
Yair Frieddb6c9e92014-08-06 08:53:13 +0300570 def _create_port(self, network, client=None, namestart='port-quotatest'):
571 if not client:
572 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300573 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300574 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300575 name=name,
576 network_id=network.id,
577 tenant_id=network.tenant_id)
578 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300579 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300580 **result['port'])
581 self.addCleanup(self.delete_wrapper, port.delete)
582 return port
583
584 def _get_server_port_id(self, server, ip_addr=None):
585 ports = self._list_ports(device_id=server['id'],
586 fixed_ip=ip_addr)
587 self.assertEqual(len(ports), 1,
588 "Unable to determine which port to target.")
589 return ports[0]['id']
590
David Shrewsbury9bac3662014-08-07 15:07:01 -0400591 def _get_network_by_name(self, network_name):
592 net = self._list_networks(name=network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300593 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400594
Yair Frieddb6c9e92014-08-06 08:53:13 +0300595 def _create_floating_ip(self, thing, external_network_id, port_id=None,
596 client=None):
597 if not client:
598 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300599 if not port_id:
600 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300601 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300602 floating_network_id=external_network_id,
603 port_id=port_id,
604 tenant_id=thing['tenant_id']
605 )
606 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300607 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300608 **result['floatingip'])
609 self.addCleanup(self.delete_wrapper, floating_ip.delete)
610 return floating_ip
611
612 def _associate_floating_ip(self, floating_ip, server):
613 port_id = self._get_server_port_id(server)
614 floating_ip.update(port_id=port_id)
615 self.assertEqual(port_id, floating_ip.port_id)
616 return floating_ip
617
618 def _disassociate_floating_ip(self, floating_ip):
619 """
620 :param floating_ip: type DeletableFloatingIp
621 """
622 floating_ip.update(port_id=None)
623 self.assertIsNone(floating_ip.port_id)
624 return floating_ip
625
Yair Fried45f92952014-06-26 05:19:19 +0300626 def check_floating_ip_status(self, floating_ip, status):
627 """Verifies floatingip has reached given status. without waiting
628
629 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
630 to check status
631 :param status: target status
632 :raises: AssertionError if status doesn't match
633 """
634 floating_ip.refresh()
635 self.assertEqual(status, floating_ip.status,
636 message="FloatingIP: {fp} is at status: {cst}. "
637 "failed to reach status: {st}"
638 .format(fp=floating_ip, cst=floating_ip.status,
639 st=status))
640 LOG.info("FloatingIP: {fp} is at status: {st}"
641 .format(fp=floating_ip, st=status))
642
Yair Fried1fc32a12014-08-04 09:11:30 +0300643 def _check_vm_connectivity(self, ip_address,
644 username=None,
645 private_key=None,
646 should_connect=True):
647 """
648 :param ip_address: server to test against
649 :param username: server's ssh username
650 :param private_key: server's ssh private key to be used
651 :param should_connect: True/False indicates positive/negative test
652 positive - attempt ping and ssh
653 negative - attempt ping and fail if succeed
654
655 :raises: AssertError if the result of the connectivity check does
656 not match the value of the should_connect param
657 """
658 if should_connect:
659 msg = "Timed out waiting for %s to become reachable" % ip_address
660 else:
661 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700662 self.assertTrue(self.ping_ip_address(ip_address,
663 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300664 msg=msg)
665 if should_connect:
666 # no need to check ssh for negative connectivity
667 self.get_remote_client(ip_address, username, private_key)
668
669 def _check_public_network_connectivity(self, ip_address, username,
670 private_key, should_connect=True,
671 msg=None, servers=None):
672 # The target login is assumed to have been configured for
673 # key-based authentication by cloud-init.
674 LOG.debug('checking network connections to IP %s with user: %s' %
675 (ip_address, username))
676 try:
677 self._check_vm_connectivity(ip_address,
678 username,
679 private_key,
680 should_connect=should_connect)
681 except Exception as e:
682 ex_msg = 'Public network connectivity check failed'
683 if msg:
684 ex_msg += ": " + msg
685 LOG.exception(ex_msg)
686 self._log_console_output(servers)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000687 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300688 raise
689
690 def _check_tenant_network_connectivity(self, server,
691 username,
692 private_key,
693 should_connect=True,
694 servers_for_debug=None):
695 if not CONF.network.tenant_networks_reachable:
696 msg = 'Tenant networks not configured to be reachable.'
697 LOG.info(msg)
698 return
699 # The target login is assumed to have been configured for
700 # key-based authentication by cloud-init.
701 try:
702 for net_name, ip_addresses in server['networks'].iteritems():
703 for ip_address in ip_addresses:
704 self._check_vm_connectivity(ip_address,
705 username,
706 private_key,
707 should_connect=should_connect)
708 except Exception as e:
709 LOG.exception('Tenant network connectivity check failed')
710 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000711 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300712 raise
713
714 def _check_remote_connectivity(self, source, dest, should_succeed=True):
715 """
716 check ping server via source ssh connection
717
718 :param source: RemoteClient: an ssh connection from which to ping
719 :param dest: and IP to ping against
720 :param should_succeed: boolean should ping succeed or not
721 :returns: boolean -- should_succeed == ping
722 :returns: ping is false if ping failed
723 """
724 def ping_remote():
725 try:
726 source.ping_host(dest)
727 except exceptions.SSHExecCommandFailed:
728 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
729 % (dest, source.ssh_client.host))
730 return not should_succeed
731 return should_succeed
732
733 return tempest.test.call_until_true(ping_remote,
734 CONF.compute.ping_timeout,
735 1)
736
Yair Frieddb6c9e92014-08-06 08:53:13 +0300737 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300738 namestart='secgroup-smoke'):
739 if client is None:
740 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300741 if tenant_id is None:
742 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300743 secgroup = self._create_empty_security_group(namestart=namestart,
744 client=client,
745 tenant_id=tenant_id)
746
747 # Add rules to the security group
748 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
749 for rule in rules:
750 self.assertEqual(tenant_id, rule.tenant_id)
751 self.assertEqual(secgroup.id, rule.security_group_id)
752 return secgroup
753
Yair Frieddb6c9e92014-08-06 08:53:13 +0300754 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300755 namestart='secgroup-smoke'):
756 """Create a security group without rules.
757
758 Default rules will be created:
759 - IPv4 egress to any
760 - IPv6 egress to any
761
762 :param tenant_id: secgroup will be created in this tenant
763 :returns: DeletableSecurityGroup -- containing the secgroup created
764 """
765 if client is None:
766 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300767 if not tenant_id:
768 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300769 sg_name = data_utils.rand_name(namestart)
770 sg_desc = sg_name + " description"
771 sg_dict = dict(name=sg_name,
772 description=sg_desc)
773 sg_dict['tenant_id'] = tenant_id
774 _, result = client.create_security_group(**sg_dict)
775 secgroup = net_resources.DeletableSecurityGroup(
776 client=client,
777 **result['security_group']
778 )
779 self.assertEqual(secgroup.name, sg_name)
780 self.assertEqual(tenant_id, secgroup.tenant_id)
781 self.assertEqual(secgroup.description, sg_desc)
782 self.addCleanup(self.delete_wrapper, secgroup.delete)
783 return secgroup
784
Yair Frieddb6c9e92014-08-06 08:53:13 +0300785 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300786 """Get default secgroup for given tenant_id.
787
788 :returns: DeletableSecurityGroup -- default secgroup for given tenant
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 sgs = [
795 sg for sg in client.list_security_groups().values()[0]
796 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
797 ]
798 msg = "No default security group for tenant %s." % (tenant_id)
799 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300800 return net_resources.DeletableSecurityGroup(client=client,
801 **sgs[0])
802
Yair Frieddb6c9e92014-08-06 08:53:13 +0300803 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300804 tenant_id=None, **kwargs):
805 """Create a rule from a dictionary of rule parameters.
806
807 Create a rule in a secgroup. if secgroup not defined will search for
808 default secgroup in tenant_id.
809
810 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300811 :param tenant_id: if secgroup not passed -- the tenant in which to
812 search for default secgroup
813 :param kwargs: a dictionary containing rule parameters:
814 for example, to allow incoming ssh:
815 rule = {
816 direction: 'ingress'
817 protocol:'tcp',
818 port_range_min: 22,
819 port_range_max: 22
820 }
821 """
822 if client is None:
823 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300824 if not tenant_id:
825 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300826 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300827 secgroup = self._default_security_group(client=client,
828 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300829
830 ruleset = dict(security_group_id=secgroup.id,
831 tenant_id=secgroup.tenant_id)
832 ruleset.update(kwargs)
833
834 _, sg_rule = client.create_security_group_rule(**ruleset)
835 sg_rule = net_resources.DeletableSecurityGroupRule(
836 client=client,
837 **sg_rule['security_group_rule']
838 )
839 self.addCleanup(self.delete_wrapper, sg_rule.delete)
840 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
841 self.assertEqual(secgroup.id, sg_rule.security_group_id)
842
843 return sg_rule
844
845 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
846 """These rules are intended to permit inbound ssh and icmp
847 traffic from all sources, so no group_id is provided.
848 Setting a group_id would only permit traffic from ports
849 belonging to the same security group.
850 """
851
852 if client is None:
853 client = self.network_client
854 rules = []
855 rulesets = [
856 dict(
857 # ssh
858 protocol='tcp',
859 port_range_min=22,
860 port_range_max=22,
861 ),
862 dict(
863 # ping
864 protocol='icmp',
865 )
866 ]
867 for ruleset in rulesets:
868 for r_direction in ['ingress', 'egress']:
869 ruleset['direction'] = r_direction
870 try:
871 sg_rule = self._create_security_group_rule(
872 client=client, secgroup=secgroup, **ruleset)
873 except exceptions.Conflict as ex:
874 # if rule already exist - skip rule and continue
875 msg = 'Security group rule already exists'
876 if msg not in ex._error_string:
877 raise ex
878 else:
879 self.assertEqual(r_direction, sg_rule.direction)
880 rules.append(sg_rule)
881
882 return rules
883
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500884 def _create_pool(self, lb_method, protocol, subnet_id):
885 """Wrapper utility that returns a test pool."""
886 client = self.network_client
887 name = data_utils.rand_name('pool')
888 _, resp_pool = client.create_pool(protocol=protocol, name=name,
889 subnet_id=subnet_id,
890 lb_method=lb_method)
891 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
892 self.assertEqual(pool['name'], name)
893 self.addCleanup(self.delete_wrapper, pool.delete)
894 return pool
895
896 def _create_member(self, address, protocol_port, pool_id):
897 """Wrapper utility that returns a test member."""
898 client = self.network_client
899 _, resp_member = client.create_member(protocol_port=protocol_port,
900 pool_id=pool_id,
901 address=address)
902 member = net_resources.DeletableMember(client=client,
903 **resp_member['member'])
904 self.addCleanup(self.delete_wrapper, member.delete)
905 return member
906
907 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
908 """Wrapper utility that returns a test vip."""
909 client = self.network_client
910 name = data_utils.rand_name('vip')
911 _, resp_vip = client.create_vip(protocol=protocol, name=name,
912 subnet_id=subnet_id, pool_id=pool_id,
913 protocol_port=protocol_port)
914 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
915 self.assertEqual(vip['name'], name)
916 self.addCleanup(self.delete_wrapper, vip.delete)
917 return vip
918
Yair Fried1fc32a12014-08-04 09:11:30 +0300919 def _ssh_to_server(self, server, private_key):
920 ssh_login = CONF.compute.image_ssh_user
921 return self.get_remote_client(server,
922 username=ssh_login,
923 private_key=private_key)
924
Yair Frieddb6c9e92014-08-06 08:53:13 +0300925 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 """Retrieve a router for the given tenant id.
927
928 If a public router has been configured, it will be returned.
929
930 If a public router has not been configured, but a public
931 network has, a tenant router will be created and returned that
932 routes traffic to the public network.
933 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300934 if not client:
935 client = self.network_client
936 if not tenant_id:
937 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300938 router_id = CONF.network.public_router_id
939 network_id = CONF.network.public_network_id
940 if router_id:
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +0400941 resp, body = client.show_router(router_id)
942 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300943 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300944 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300945 router.set_gateway(network_id)
946 return router
947 else:
948 raise Exception("Neither of 'public_router_id' or "
949 "'public_network_id' has been defined.")
950
Yair Frieddb6c9e92014-08-06 08:53:13 +0300951 def _create_router(self, client=None, tenant_id=None,
952 namestart='router-smoke'):
953 if not client:
954 client = self.network_client
955 if not tenant_id:
956 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300957 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300958 _, result = client.create_router(name=name,
959 admin_state_up=True,
960 tenant_id=tenant_id)
961 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300962 **result['router'])
963 self.assertEqual(router.name, name)
964 self.addCleanup(self.delete_wrapper, router.delete)
965 return router
966
Yair Frieddb6c9e92014-08-06 08:53:13 +0300967 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300968 """Create a network with a subnet connected to a router.
969
David Shrewsbury9bac3662014-08-07 15:07:01 -0400970 The baremetal driver is a special case since all nodes are
971 on the same shared network.
972
Yair Fried1fc32a12014-08-04 09:11:30 +0300973 :returns: network, subnet, router
974 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400975 if CONF.baremetal.driver_enabled:
976 # NOTE(Shrews): This exception is for environments where tenant
977 # credential isolation is available, but network separation is
978 # not (the current baremetal case). Likely can be removed when
979 # test account mgmt is reworked:
980 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
981 network = self._get_network_by_name(
982 CONF.compute.fixed_network_name)
983 router = None
984 subnet = None
985 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300986 network = self._create_network(client=client, tenant_id=tenant_id)
987 router = self._get_router(client=client, tenant_id=tenant_id)
988 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400989 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300990 return network, subnet, router
991
992
David Shrewsbury06f7f8a2014-05-20 13:55:57 -0400993# power/provision states as of icehouse
994class BaremetalPowerStates(object):
995 """Possible power states of an Ironic node."""
996 POWER_ON = 'power on'
997 POWER_OFF = 'power off'
998 REBOOT = 'rebooting'
999 SUSPEND = 'suspended'
1000
1001
1002class BaremetalProvisionStates(object):
1003 """Possible provision states of an Ironic node."""
1004 NOSTATE = None
1005 INIT = 'initializing'
1006 ACTIVE = 'active'
1007 BUILDING = 'building'
1008 DEPLOYWAIT = 'wait call-back'
1009 DEPLOYING = 'deploying'
1010 DEPLOYFAIL = 'deploy failed'
1011 DEPLOYDONE = 'deploy complete'
1012 DELETING = 'deleting'
1013 DELETED = 'deleted'
1014 ERROR = 'error'
1015
1016
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001017class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001018 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001019 def resource_setup(cls):
1020 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001021
1022 if (not CONF.service_available.ironic or
1023 not CONF.baremetal.driver_enabled):
1024 msg = 'Ironic not available or Ironic compute driver not enabled'
1025 raise cls.skipException(msg)
1026
1027 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001028 manager = clients.Manager(
1029 credentials=cls.admin_credentials()
1030 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001031 cls.baremetal_client = manager.baremetal_client
1032
1033 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001034 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001035
1036 def _node_state_timeout(self, node_id, state_attr,
1037 target_states, timeout=10, interval=1):
1038 if not isinstance(target_states, list):
1039 target_states = [target_states]
1040
1041 def check_state():
1042 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001043 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001044 return True
1045 return False
1046
1047 if not tempest.test.call_until_true(
1048 check_state, timeout, interval):
1049 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1050 (node_id, state_attr, target_states))
1051 raise exceptions.TimeoutException(msg)
1052
1053 def wait_provisioning_state(self, node_id, state, timeout):
1054 self._node_state_timeout(
1055 node_id=node_id, state_attr='provision_state',
1056 target_states=state, timeout=timeout)
1057
1058 def wait_power_state(self, node_id, state):
1059 self._node_state_timeout(
1060 node_id=node_id, state_attr='power_state',
1061 target_states=state, timeout=CONF.baremetal.power_timeout)
1062
1063 def wait_node(self, instance_id):
1064 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001065
Adam Gandelman4a48a602014-03-20 18:23:18 -07001066 def _get_node():
1067 node = None
1068 try:
1069 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001070 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001071 pass
1072 return node is not None
1073
1074 if not tempest.test.call_until_true(
1075 _get_node, CONF.baremetal.association_timeout, 1):
1076 msg = ('Timed out waiting to get Ironic node by instance id %s'
1077 % instance_id)
1078 raise exceptions.TimeoutException(msg)
1079
1080 def get_node(self, node_id=None, instance_id=None):
1081 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001082 _, body = self.baremetal_client.show_node(node_id)
1083 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001084 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001085 _, body = self.baremetal_client.show_node_by_instance_uuid(
1086 instance_id)
1087 if body['nodes']:
1088 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001089
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001090 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001091 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001092 _, body = self.baremetal_client.list_node_ports(node_uuid)
1093 for port in body['ports']:
1094 _, p = self.baremetal_client.show_port(port['uuid'])
1095 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001096 return ports
1097
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001098 def add_keypair(self):
1099 self.keypair = self.create_keypair()
1100
1101 def verify_connectivity(self, ip=None):
1102 if ip:
1103 dest = self.get_remote_client(ip)
1104 else:
1105 dest = self.get_remote_client(self.instance)
1106 dest.validate_authentication()
1107
1108 def boot_instance(self):
1109 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001110 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001111 }
1112 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001113 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001114
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001115 self.wait_node(self.instance['id'])
1116 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001117
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001118 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001119
1120 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001121 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001122 [BaremetalProvisionStates.DEPLOYWAIT,
1123 BaremetalProvisionStates.ACTIVE],
1124 timeout=15)
1125
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001126 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001127 BaremetalProvisionStates.ACTIVE,
1128 timeout=CONF.baremetal.active_timeout)
1129
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001130 self.servers_client.wait_for_server_status(self.instance['id'],
1131 'ACTIVE')
1132 self.node = self.get_node(instance_id=self.instance['id'])
1133 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001134
1135 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001136 self.servers_client.delete_server(self.instance['id'])
1137 self.wait_power_state(self.node['uuid'],
1138 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001139 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001140 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001141 BaremetalProvisionStates.NOSTATE,
1142 timeout=CONF.baremetal.unprovision_timeout)
1143
Adam Gandelman4a48a602014-03-20 18:23:18 -07001144
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001145class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001146 """
1147 Base class for encryption scenario tests
1148 """
1149
1150 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001151 def resource_setup(cls):
1152 super(EncryptionScenarioTest, cls).resource_setup()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001153 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001154
1155 def _wait_for_volume_status(self, status):
1156 self.status_timeout(
1157 self.volume_client.volumes, self.volume.id, status)
1158
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001159 def nova_boot(self):
1160 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001161 create_kwargs = {'key_name': self.keypair['name']}
1162 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001163 create_kwargs=create_kwargs)
1164
1165 def create_volume_type(self, client=None, name=None):
1166 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001167 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001168 if not name:
1169 name = 'generic'
1170 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1171 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001172 _, body = client.create_volume_type(
1173 randomized_name)
1174 self.assertIn('id', body)
1175 self.addCleanup(client.delete_volume_type, body['id'])
1176 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001177
1178 def create_encryption_type(self, client=None, type_id=None, provider=None,
1179 key_size=None, cipher=None,
1180 control_location=None):
1181 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001182 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001183 if not type_id:
1184 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001185 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001186 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001187 client.create_encryption_type(
1188 type_id, provider=provider, key_size=key_size, cipher=cipher,
1189 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001190
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001191
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001192class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001193 """
1194 Base class for orchestration scenario tests
1195 """
1196
1197 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001198 def resource_setup(cls):
1199 super(OrchestrationScenarioTest, cls).resource_setup()
Matthew Treinish6c072292014-01-29 19:15:52 +00001200 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001201 raise cls.skipException("Heat support is required")
1202
1203 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001204 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001205 admin_creds = auth.get_default_credentials('identity_admin')
1206 creds = auth.get_default_credentials('user')
1207 admin_creds.tenant_name = creds.tenant_name
1208 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001209
1210 def _load_template(self, base_file, file_name):
1211 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1212 file_name)
1213 with open(filepath) as f:
1214 return f.read()
1215
1216 @classmethod
1217 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001218 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001219
1220 @classmethod
1221 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001222 _, networks = cls.networks_client.list_networks()
1223 for net in networks:
1224 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001225 return net
Steve Baker22c16602014-05-05 13:34:19 +12001226
1227 @staticmethod
1228 def _stack_output(stack, output_key):
1229 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001230 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001231 if o['output_key'] == output_key), None)
1232
Chris Dent0d494112014-08-26 13:48:30 +01001233
1234class SwiftScenarioTest(ScenarioTest):
1235 """
1236 Provide harness to do Swift scenario tests.
1237
1238 Subclasses implement the tests that use the methods provided by this
1239 class.
1240 """
1241
1242 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001243 def resource_setup(cls):
Chris Dent0d494112014-08-26 13:48:30 +01001244 cls.set_network_resources()
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001245 super(SwiftScenarioTest, cls).resource_setup()
Chris Dent0d494112014-08-26 13:48:30 +01001246 if not CONF.service_available.swift:
1247 skip_msg = ("%s skipped as swift is not available" %
1248 cls.__name__)
1249 raise cls.skipException(skip_msg)
1250 # Clients for Swift
1251 cls.account_client = cls.manager.account_client
1252 cls.container_client = cls.manager.container_client
1253 cls.object_client = cls.manager.object_client
1254
Chris Dentde456a12014-09-10 12:41:15 +01001255 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001256 """get swift status for our user account."""
1257 self.account_client.list_account_containers()
1258 LOG.debug('Swift status information obtained successfully')
1259
Chris Dentde456a12014-09-10 12:41:15 +01001260 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001261 name = container_name or data_utils.rand_name(
1262 'swift-scenario-container')
1263 self.container_client.create_container(name)
1264 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001265 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001266 LOG.debug('Container %s created' % (name))
1267 return name
1268
Chris Dentde456a12014-09-10 12:41:15 +01001269 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001270 self.container_client.delete_container(container_name)
1271 LOG.debug('Container %s deleted' % (container_name))
1272
Chris Dentde456a12014-09-10 12:41:15 +01001273 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001274 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1275 obj_data = data_utils.arbitrary_string()
1276 self.object_client.create_object(container_name, obj_name, obj_data)
1277 return obj_name, obj_data
1278
Chris Dentde456a12014-09-10 12:41:15 +01001279 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001280 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001281 self.list_and_check_container_objects(container_name,
1282 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001283
Chris Dentde456a12014-09-10 12:41:15 +01001284 def list_and_check_container_objects(self, container_name,
1285 present_obj=None,
1286 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001287 """
1288 List objects for a given container and assert which are present and
1289 which are not.
1290 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001291 if present_obj is None:
1292 present_obj = []
1293 if not_present_obj is None:
1294 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001295 _, object_list = self.container_client.list_container_contents(
1296 container_name)
1297 if present_obj:
1298 for obj in present_obj:
1299 self.assertIn(obj, object_list)
1300 if not_present_obj:
1301 for obj in not_present_obj:
1302 self.assertNotIn(obj, object_list)
1303
Chris Dentde456a12014-09-10 12:41:15 +01001304 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001305 metadata_param = {'metadata_prefix': 'x-container-',
1306 'metadata': {'read': acl}}
1307 self.container_client.update_container_metadata(container_name,
1308 **metadata_param)
1309 resp, _ = self.container_client.list_container_metadata(container_name)
1310 self.assertEqual(resp['x-container-read'], acl)
1311
Chris Dentde456a12014-09-10 12:41:15 +01001312 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001313 _, obj = self.object_client.get_object(container_name, obj_name)
1314 self.assertEqual(obj, expected_data)