blob: baa7186587c025fa165e4668ac90e0d3769b332b [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
Matt Riedemann343305f2014-05-27 09:55:03 -070026from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040027from tempest.common import isolated_creds
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 Frittoli247058f2014-07-16 16:09:22 +010054 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010055 cls.isolated_creds = isolated_creds.IsolatedCreds(
Andrea Frittoliae9aca02014-09-25 11:43:11 +010056 cls.__name__, network_resources=cls.network_resources)
Andrea Frittoli2e733b52014-07-16 14:12:11 +010057 cls.manager = clients.Manager(
58 credentials=cls.credentials()
59 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010060 cls.admin_manager = clients.Manager(cls.admin_credentials())
61 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070062 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010063 cls.floating_ips_client = cls.manager.floating_ips_client
64 # Glance image client v1
65 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000066 # Compute image client
67 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010068 cls.keypairs_client = cls.manager.keypairs_client
69 cls.networks_client = cls.admin_manager.networks_client
70 # Nova security groups client
71 cls.security_groups_client = cls.manager.security_groups_client
72 cls.servers_client = cls.manager.servers_client
73 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000074 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030075 cls.interface_client = cls.manager.interfaces_client
76 # Neutron network client
77 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090078 # Heat client
79 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010080
81 @classmethod
82 def _get_credentials(cls, get_creds, ctype):
83 if CONF.compute.allow_tenant_isolation:
84 creds = get_creds()
85 else:
86 creds = auth.get_default_credentials(ctype)
87 return creds
88
89 @classmethod
90 def credentials(cls):
91 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
92 'user')
93
Masayuki Igawaccd66592014-07-17 00:42:42 +090094 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +030095 def alt_credentials(cls):
96 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
97 'alt_user')
98
99 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900100 def admin_credentials(cls):
101 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
102 'identity_admin')
103
Andrea Frittoli247058f2014-07-16 16:09:22 +0100104 # ## Methods to handle sync and async deletes
105
106 def setUp(self):
107 super(ScenarioTest, self).setUp()
108 self.cleanup_waits = []
109 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
110 # because scenario tests in the same test class should not share
111 # resources. If resources were shared between test cases then it
112 # should be a single scenario test instead of multiples.
113
114 # NOTE(yfried): this list is cleaned at the end of test_methods and
115 # not at the end of the class
116 self.addCleanup(self._wait_for_cleanups)
117
Yair Fried1fc32a12014-08-04 09:11:30 +0300118 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100119 """Ignores NotFound exceptions for delete operations.
120
Yair Fried1fc32a12014-08-04 09:11:30 +0300121 @param delete_thing: delete method of a resource. method will be
122 executed as delete_thing(*args, **kwargs)
123
Andrea Frittoli247058f2014-07-16 16:09:22 +0100124 """
125 try:
126 # Tempest clients return dicts, so there is no common delete
127 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300128 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100129 except exceptions.NotFound:
130 # If the resource is already missing, mission accomplished.
131 pass
132
133 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900134 cleanup_callable, cleanup_args=None,
135 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700136 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100137
138 @param waiter_callable: callable to wait for the resource to delete
139 @param thing_id: the id of the resource to be cleaned-up
140 @param thing_id_param: the name of the id param in the waiter
141 @param cleanup_callable: method to load pass to self.addCleanup with
142 the following *cleanup_args, **cleanup_kwargs.
143 usually a delete method.
144 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900145 if cleanup_args is None:
146 cleanup_args = []
147 if cleanup_kwargs is None:
148 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100149 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
150 wait_dict = {
151 'waiter_callable': waiter_callable,
152 thing_id_param: thing_id
153 }
154 self.cleanup_waits.append(wait_dict)
155
156 def _wait_for_cleanups(self):
157 """To handle async delete actions, a list of waits is added
158 which will be iterated over as the last step of clearing the
159 cleanup queue. That way all the delete calls are made up front
160 and the tests won't succeed unless the deletes are eventually
161 successful. This is the same basic approach used in the api tests to
162 limit cleanup execution time except here it is multi-resource,
163 because of the nature of the scenario tests.
164 """
165 for wait in self.cleanup_waits:
166 waiter_callable = wait.pop('waiter_callable')
167 waiter_callable(**wait)
168
169 # ## Test functions library
170 #
171 # The create_[resource] functions only return body and discard the
172 # resp part which is not used in scenario tests
173
Yair Frieddb6c9e92014-08-06 08:53:13 +0300174 def create_keypair(self, client=None):
175 if not client:
176 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100177 name = data_utils.rand_name(self.__class__.__name__)
178 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300179 resp, body = client.create_keypair(name)
180 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100181 return body
182
183 def create_server(self, name=None, image=None, flavor=None,
184 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900185 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100186 """Creates VM instance.
187
188 @param image: image from which to create the instance
189 @param wait_on_boot: wait for status ACTIVE before continue
190 @param wait_on_delete: force synchronous delete on cleanup
191 @param create_kwargs: additional details for instance creation
192 @return: server dict
193 """
194 if name is None:
195 name = data_utils.rand_name(self.__class__.__name__)
196 if image is None:
197 image = CONF.compute.image_ref
198 if flavor is None:
199 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900200 if create_kwargs is None:
201 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100202
Andrea Frittoli247058f2014-07-16 16:09:22 +0100203 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
204 name, image, flavor)
205 _, server = self.servers_client.create_server(name, image, flavor,
206 **create_kwargs)
207 if wait_on_delete:
208 self.addCleanup(self.servers_client.wait_for_server_termination,
209 server['id'])
210 self.addCleanup_with_wait(
211 waiter_callable=self.servers_client.wait_for_server_termination,
212 thing_id=server['id'], thing_id_param='server_id',
213 cleanup_callable=self.delete_wrapper,
214 cleanup_args=[self.servers_client.delete_server, server['id']])
215 if wait_on_boot:
216 self.servers_client.wait_for_server_status(server_id=server['id'],
217 status='ACTIVE')
218 # The instance retrieved on creation is missing network
219 # details, necessitating retrieval after it becomes active to
220 # ensure correct details.
221 _, server = self.servers_client.get_server(server['id'])
222 self.assertEqual(server['name'], name)
223 return server
224
225 def create_volume(self, size=1, name=None, snapshot_id=None,
226 imageRef=None, volume_type=None, wait_on_delete=True):
227 if name is None:
228 name = data_utils.rand_name(self.__class__.__name__)
229 _, volume = self.volumes_client.create_volume(
230 size=size, display_name=name, snapshot_id=snapshot_id,
231 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700232
Andrea Frittoli247058f2014-07-16 16:09:22 +0100233 if wait_on_delete:
234 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
235 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700236 self.addCleanup(self.delete_wrapper,
237 self.volumes_client.delete_volume, volume['id'])
238 else:
239 self.addCleanup_with_wait(
240 waiter_callable=self.volumes_client.wait_for_resource_deletion,
241 thing_id=volume['id'], thing_id_param='id',
242 cleanup_callable=self.delete_wrapper,
243 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100244
245 self.assertEqual(name, volume['display_name'])
246 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
247 # The volume retrieved on creation has a non-up-to-date status.
248 # Retrieval after it becomes active ensures correct details.
249 _, volume = self.volumes_client.get_volume(volume['id'])
250 return volume
251
Yair Fried1fc32a12014-08-04 09:11:30 +0300252 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100253 _client = self.security_groups_client
254 if secgroup_id is None:
255 _, sgs = _client.list_security_groups()
256 for sg in sgs:
257 if sg['name'] == 'default':
258 secgroup_id = sg['id']
259
260 # These rules are intended to permit inbound ssh and icmp
261 # traffic from all sources, so no group_id is provided.
262 # Setting a group_id would only permit traffic from ports
263 # belonging to the same security group.
264 rulesets = [
265 {
266 # ssh
267 'ip_proto': 'tcp',
268 'from_port': 22,
269 'to_port': 22,
270 'cidr': '0.0.0.0/0',
271 },
272 {
273 # ping
274 'ip_proto': 'icmp',
275 'from_port': -1,
276 'to_port': -1,
277 'cidr': '0.0.0.0/0',
278 }
279 ]
280 rules = list()
281 for ruleset in rulesets:
282 _, sg_rule = _client.create_security_group_rule(secgroup_id,
283 **ruleset)
284 self.addCleanup(self.delete_wrapper,
285 _client.delete_security_group_rule,
286 sg_rule['id'])
287 rules.append(sg_rule)
288 return rules
289
Yair Fried1fc32a12014-08-04 09:11:30 +0300290 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100291 # Create security group
292 sg_name = data_utils.rand_name(self.__class__.__name__)
293 sg_desc = sg_name + " description"
294 _, secgroup = self.security_groups_client.create_security_group(
295 sg_name, sg_desc)
296 self.assertEqual(secgroup['name'], sg_name)
297 self.assertEqual(secgroup['description'], sg_desc)
298 self.addCleanup(self.delete_wrapper,
299 self.security_groups_client.delete_security_group,
300 secgroup['id'])
301
302 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300303 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100304
305 return secgroup
306
307 def get_remote_client(self, server_or_ip, username=None, private_key=None):
308 if isinstance(server_or_ip, six.string_types):
309 ip = server_or_ip
310 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700311 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
312 ip = addr['addr']
313
Andrea Frittoli247058f2014-07-16 16:09:22 +0100314 if username is None:
315 username = CONF.scenario.ssh_user
316 if private_key is None:
317 private_key = self.keypair['private_key']
318 linux_client = remote_client.RemoteClient(ip, username,
319 pkey=private_key)
320 try:
321 linux_client.validate_authentication()
322 except exceptions.SSHTimeout:
323 LOG.exception('ssh connection to %s failed' % ip)
324 debug.log_net_debug()
325 raise
326
327 return linux_client
328
Ghanshyam2a180b82014-06-16 13:54:22 +0900329 def _image_create(self, name, fmt, path, properties=None):
330 if properties is None:
331 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100332 name = data_utils.rand_name('%s-' % name)
333 image_file = open(path, 'rb')
334 self.addCleanup(image_file.close)
335 params = {
336 'name': name,
337 'container_format': fmt,
338 'disk_format': fmt,
339 'is_public': 'False',
340 }
341 params.update(properties)
342 _, image = self.image_client.create_image(**params)
343 self.addCleanup(self.image_client.delete_image, image['id'])
344 self.assertEqual("queued", image['status'])
345 self.image_client.update_image(image['id'], data=image_file)
346 return image['id']
347
348 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300349 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100350 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
351 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
352 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300353 img_container_format = CONF.scenario.img_container_format
354 img_disk_format = CONF.scenario.img_disk_format
355 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
356 "ami: %s, ari: %s, aki: %s" %
357 (img_path, img_container_format, img_disk_format,
358 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100359 try:
360 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300361 img_container_format,
362 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100363 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300364 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100365 except IOError:
366 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
367 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
368 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
369 properties = {
370 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
371 }
372 self.image = self._image_create('scenario-ami', 'ami',
373 path=ami_img_path,
374 properties=properties)
375 LOG.debug("image:%s" % self.image)
376
377 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400378 if not CONF.compute_feature_enabled.console_output:
379 LOG.debug('Console output not supported, cannot log')
380 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100381 if not servers:
382 _, servers = self.servers_client.list_servers()
383 servers = servers['servers']
384 for server in servers:
385 LOG.debug('Console output for %s', server['id'])
386 LOG.debug(self.servers_client.get_console_output(server['id'],
387 length=None))
388
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000389 def _log_net_info(self, exc):
390 # network debug is called as part of ssh init
391 if not isinstance(exc, exceptions.SSHTimeout):
392 LOG.debug('Network information on a devstack host')
393 debug.log_net_debug()
394
nithya-ganesan882595e2014-07-29 18:51:07 +0000395 def create_server_snapshot(self, server, name=None):
396 # Glance client
397 _image_client = self.image_client
398 # Compute client
399 _images_client = self.images_client
400 if name is None:
401 name = data_utils.rand_name('scenario-snapshot-')
402 LOG.debug("Creating a snapshot image for server: %s", server['name'])
403 resp, image = _images_client.create_image(server['id'], name)
404 image_id = resp['location'].split('images/')[1]
405 _image_client.wait_for_image_status(image_id, 'active')
406 self.addCleanup_with_wait(
407 waiter_callable=_image_client.wait_for_resource_deletion,
408 thing_id=image_id, thing_id_param='id',
409 cleanup_callable=self.delete_wrapper,
410 cleanup_args=[_image_client.delete_image, image_id])
411 _, snapshot_image = _image_client.get_image_meta(image_id)
412 image_name = snapshot_image['name']
413 self.assertEqual(name, image_name)
414 LOG.debug("Created snapshot image %s for server %s",
415 image_name, server['name'])
416 return snapshot_image
417
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900418 def nova_volume_attach(self):
419 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
420 _, volume_attachment = self.servers_client.attach_volume(
421 self.server['id'], self.volume['id'], '/dev/vdb')
422 volume = volume_attachment['volumeAttachment']
423 self.assertEqual(self.volume['id'], volume['id'])
424 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
425 # Refresh the volume after the attachment
426 _, self.volume = self.volumes_client.get_volume(volume['id'])
427
428 def nova_volume_detach(self):
429 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
430 self.volumes_client.wait_for_volume_status(self.volume['id'],
431 'available')
432
433 _, volume = self.volumes_client.get_volume(self.volume['id'])
434 self.assertEqual('available', volume['status'])
435
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700436 def rebuild_server(self, server_id, image=None,
437 preserve_ephemeral=False, wait=True,
438 rebuild_kwargs=None):
439 if image is None:
440 image = CONF.compute.image_ref
441
442 rebuild_kwargs = rebuild_kwargs or {}
443
444 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
445 server_id, image, preserve_ephemeral)
446 self.servers_client.rebuild(server_id=server_id, image_ref=image,
447 preserve_ephemeral=preserve_ephemeral,
448 **rebuild_kwargs)
449 if wait:
450 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
451
Aaron Rosena7df13b2014-09-23 09:45:45 -0700452 def ping_ip_address(self, ip_address, should_succeed=True):
453 cmd = ['ping', '-c1', '-w1', ip_address]
454
455 def ping():
456 proc = subprocess.Popen(cmd,
457 stdout=subprocess.PIPE,
458 stderr=subprocess.PIPE)
459 proc.communicate()
460 return (proc.returncode == 0) == should_succeed
461
462 return tempest.test.call_until_true(
463 ping, CONF.compute.ping_timeout, 1)
464
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100465
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100466class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300467 """Base class for network scenario tests.
468 This class provide helpers for network scenario tests, using the neutron
469 API. Helpers from ancestor which use the nova network API are overridden
470 with the neutron API.
471
472 This Class also enforces using Neutron instead of novanetwork.
473 Subclassed tests will be skipped if Neutron is not enabled
474
475 """
476
477 @classmethod
478 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100479 if not CONF.service_available.neutron:
480 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300481
482 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100483 def resource_setup(cls):
484 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300485 cls.tenant_id = cls.manager.identity_client.tenant_id
486 cls.check_preconditions()
487
Yair Frieddb6c9e92014-08-06 08:53:13 +0300488 def _create_network(self, client=None, tenant_id=None,
489 namestart='network-smoke-'):
490 if not client:
491 client = self.network_client
492 if not tenant_id:
493 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300494 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300495 _, result = client.create_network(name=name, tenant_id=tenant_id)
496 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300497 **result['network'])
498 self.assertEqual(network.name, name)
499 self.addCleanup(self.delete_wrapper, network.delete)
500 return network
501
502 def _list_networks(self, *args, **kwargs):
503 """List networks using admin creds """
504 return self._admin_lister('networks')(*args, **kwargs)
505
506 def _list_subnets(self, *args, **kwargs):
507 """List subnets using admin creds """
508 return self._admin_lister('subnets')(*args, **kwargs)
509
510 def _list_routers(self, *args, **kwargs):
511 """List routers using admin creds """
512 return self._admin_lister('routers')(*args, **kwargs)
513
514 def _list_ports(self, *args, **kwargs):
515 """List ports using admin creds """
516 return self._admin_lister('ports')(*args, **kwargs)
517
518 def _admin_lister(self, resource_type):
519 def temp(*args, **kwargs):
520 temp_method = self.admin_manager.network_client.__getattr__(
521 'list_%s' % resource_type)
522 _, resource_list = temp_method(*args, **kwargs)
523 return resource_list[resource_type]
524 return temp
525
Yair Frieddb6c9e92014-08-06 08:53:13 +0300526 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
527 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300528 """
529 Create a subnet for the given network within the cidr block
530 configured for tenant networks.
531 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300532 if not client:
533 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300534
535 def cidr_in_use(cidr, tenant_id):
536 """
537 :return True if subnet with cidr already exist in tenant
538 False else
539 """
540 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
541 return len(cidr_in_use) != 0
542
543 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
544 result = None
545 # Repeatedly attempt subnet creation with sequential cidr
546 # blocks until an unallocated block is found.
547 for subnet_cidr in tenant_cidr.subnet(
548 CONF.network.tenant_network_mask_bits):
549 str_cidr = str(subnet_cidr)
550 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
551 continue
552
553 subnet = dict(
554 name=data_utils.rand_name(namestart),
555 ip_version=4,
556 network_id=network.id,
557 tenant_id=network.tenant_id,
558 cidr=str_cidr,
559 **kwargs
560 )
561 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300562 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300563 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300564 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300565 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
566 if not is_overlapping_cidr:
567 raise
568 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300569 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300570 **result['subnet'])
571 self.assertEqual(subnet.cidr, str_cidr)
572 self.addCleanup(self.delete_wrapper, subnet.delete)
573 return subnet
574
Yair Frieddb6c9e92014-08-06 08:53:13 +0300575 def _create_port(self, network, client=None, namestart='port-quotatest'):
576 if not client:
577 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300578 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300579 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300580 name=name,
581 network_id=network.id,
582 tenant_id=network.tenant_id)
583 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300584 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300585 **result['port'])
586 self.addCleanup(self.delete_wrapper, port.delete)
587 return port
588
589 def _get_server_port_id(self, server, ip_addr=None):
590 ports = self._list_ports(device_id=server['id'],
591 fixed_ip=ip_addr)
592 self.assertEqual(len(ports), 1,
593 "Unable to determine which port to target.")
594 return ports[0]['id']
595
David Shrewsbury9bac3662014-08-07 15:07:01 -0400596 def _get_network_by_name(self, network_name):
597 net = self._list_networks(name=network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300598 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400599
Yair Frieddb6c9e92014-08-06 08:53:13 +0300600 def _create_floating_ip(self, thing, external_network_id, port_id=None,
601 client=None):
602 if not client:
603 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300604 if not port_id:
605 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300606 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300607 floating_network_id=external_network_id,
608 port_id=port_id,
609 tenant_id=thing['tenant_id']
610 )
611 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300612 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300613 **result['floatingip'])
614 self.addCleanup(self.delete_wrapper, floating_ip.delete)
615 return floating_ip
616
617 def _associate_floating_ip(self, floating_ip, server):
618 port_id = self._get_server_port_id(server)
619 floating_ip.update(port_id=port_id)
620 self.assertEqual(port_id, floating_ip.port_id)
621 return floating_ip
622
623 def _disassociate_floating_ip(self, floating_ip):
624 """
625 :param floating_ip: type DeletableFloatingIp
626 """
627 floating_ip.update(port_id=None)
628 self.assertIsNone(floating_ip.port_id)
629 return floating_ip
630
Yair Fried1fc32a12014-08-04 09:11:30 +0300631 def _check_vm_connectivity(self, ip_address,
632 username=None,
633 private_key=None,
634 should_connect=True):
635 """
636 :param ip_address: server to test against
637 :param username: server's ssh username
638 :param private_key: server's ssh private key to be used
639 :param should_connect: True/False indicates positive/negative test
640 positive - attempt ping and ssh
641 negative - attempt ping and fail if succeed
642
643 :raises: AssertError if the result of the connectivity check does
644 not match the value of the should_connect param
645 """
646 if should_connect:
647 msg = "Timed out waiting for %s to become reachable" % ip_address
648 else:
649 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700650 self.assertTrue(self.ping_ip_address(ip_address,
651 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300652 msg=msg)
653 if should_connect:
654 # no need to check ssh for negative connectivity
655 self.get_remote_client(ip_address, username, private_key)
656
657 def _check_public_network_connectivity(self, ip_address, username,
658 private_key, should_connect=True,
659 msg=None, servers=None):
660 # The target login is assumed to have been configured for
661 # key-based authentication by cloud-init.
662 LOG.debug('checking network connections to IP %s with user: %s' %
663 (ip_address, username))
664 try:
665 self._check_vm_connectivity(ip_address,
666 username,
667 private_key,
668 should_connect=should_connect)
669 except Exception as e:
670 ex_msg = 'Public network connectivity check failed'
671 if msg:
672 ex_msg += ": " + msg
673 LOG.exception(ex_msg)
674 self._log_console_output(servers)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000675 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300676 raise
677
678 def _check_tenant_network_connectivity(self, server,
679 username,
680 private_key,
681 should_connect=True,
682 servers_for_debug=None):
683 if not CONF.network.tenant_networks_reachable:
684 msg = 'Tenant networks not configured to be reachable.'
685 LOG.info(msg)
686 return
687 # The target login is assumed to have been configured for
688 # key-based authentication by cloud-init.
689 try:
690 for net_name, ip_addresses in server['networks'].iteritems():
691 for ip_address in ip_addresses:
692 self._check_vm_connectivity(ip_address,
693 username,
694 private_key,
695 should_connect=should_connect)
696 except Exception as e:
697 LOG.exception('Tenant network connectivity check failed')
698 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000699 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300700 raise
701
702 def _check_remote_connectivity(self, source, dest, should_succeed=True):
703 """
704 check ping server via source ssh connection
705
706 :param source: RemoteClient: an ssh connection from which to ping
707 :param dest: and IP to ping against
708 :param should_succeed: boolean should ping succeed or not
709 :returns: boolean -- should_succeed == ping
710 :returns: ping is false if ping failed
711 """
712 def ping_remote():
713 try:
714 source.ping_host(dest)
715 except exceptions.SSHExecCommandFailed:
716 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
717 % (dest, source.ssh_client.host))
718 return not should_succeed
719 return should_succeed
720
721 return tempest.test.call_until_true(ping_remote,
722 CONF.compute.ping_timeout,
723 1)
724
Yair Frieddb6c9e92014-08-06 08:53:13 +0300725 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300726 namestart='secgroup-smoke'):
727 if client is None:
728 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300729 if tenant_id is None:
730 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300731 secgroup = self._create_empty_security_group(namestart=namestart,
732 client=client,
733 tenant_id=tenant_id)
734
735 # Add rules to the security group
736 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
737 for rule in rules:
738 self.assertEqual(tenant_id, rule.tenant_id)
739 self.assertEqual(secgroup.id, rule.security_group_id)
740 return secgroup
741
Yair Frieddb6c9e92014-08-06 08:53:13 +0300742 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300743 namestart='secgroup-smoke'):
744 """Create a security group without rules.
745
746 Default rules will be created:
747 - IPv4 egress to any
748 - IPv6 egress to any
749
750 :param tenant_id: secgroup will be created in this tenant
751 :returns: DeletableSecurityGroup -- containing the secgroup created
752 """
753 if client is None:
754 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300755 if not tenant_id:
756 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300757 sg_name = data_utils.rand_name(namestart)
758 sg_desc = sg_name + " description"
759 sg_dict = dict(name=sg_name,
760 description=sg_desc)
761 sg_dict['tenant_id'] = tenant_id
762 _, result = client.create_security_group(**sg_dict)
763 secgroup = net_resources.DeletableSecurityGroup(
764 client=client,
765 **result['security_group']
766 )
767 self.assertEqual(secgroup.name, sg_name)
768 self.assertEqual(tenant_id, secgroup.tenant_id)
769 self.assertEqual(secgroup.description, sg_desc)
770 self.addCleanup(self.delete_wrapper, secgroup.delete)
771 return secgroup
772
Yair Frieddb6c9e92014-08-06 08:53:13 +0300773 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300774 """Get default secgroup for given tenant_id.
775
776 :returns: DeletableSecurityGroup -- default secgroup for given tenant
777 """
778 if client is None:
779 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300780 if not tenant_id:
781 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300782 sgs = [
783 sg for sg in client.list_security_groups().values()[0]
784 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
785 ]
786 msg = "No default security group for tenant %s." % (tenant_id)
787 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300788 return net_resources.DeletableSecurityGroup(client=client,
789 **sgs[0])
790
Yair Frieddb6c9e92014-08-06 08:53:13 +0300791 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300792 tenant_id=None, **kwargs):
793 """Create a rule from a dictionary of rule parameters.
794
795 Create a rule in a secgroup. if secgroup not defined will search for
796 default secgroup in tenant_id.
797
798 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300799 :param tenant_id: if secgroup not passed -- the tenant in which to
800 search for default secgroup
801 :param kwargs: a dictionary containing rule parameters:
802 for example, to allow incoming ssh:
803 rule = {
804 direction: 'ingress'
805 protocol:'tcp',
806 port_range_min: 22,
807 port_range_max: 22
808 }
809 """
810 if client is None:
811 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300812 if not tenant_id:
813 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300814 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300815 secgroup = self._default_security_group(client=client,
816 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300817
818 ruleset = dict(security_group_id=secgroup.id,
819 tenant_id=secgroup.tenant_id)
820 ruleset.update(kwargs)
821
822 _, sg_rule = client.create_security_group_rule(**ruleset)
823 sg_rule = net_resources.DeletableSecurityGroupRule(
824 client=client,
825 **sg_rule['security_group_rule']
826 )
827 self.addCleanup(self.delete_wrapper, sg_rule.delete)
828 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
829 self.assertEqual(secgroup.id, sg_rule.security_group_id)
830
831 return sg_rule
832
833 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
834 """These rules are intended to permit inbound ssh and icmp
835 traffic from all sources, so no group_id is provided.
836 Setting a group_id would only permit traffic from ports
837 belonging to the same security group.
838 """
839
840 if client is None:
841 client = self.network_client
842 rules = []
843 rulesets = [
844 dict(
845 # ssh
846 protocol='tcp',
847 port_range_min=22,
848 port_range_max=22,
849 ),
850 dict(
851 # ping
852 protocol='icmp',
853 )
854 ]
855 for ruleset in rulesets:
856 for r_direction in ['ingress', 'egress']:
857 ruleset['direction'] = r_direction
858 try:
859 sg_rule = self._create_security_group_rule(
860 client=client, secgroup=secgroup, **ruleset)
861 except exceptions.Conflict as ex:
862 # if rule already exist - skip rule and continue
863 msg = 'Security group rule already exists'
864 if msg not in ex._error_string:
865 raise ex
866 else:
867 self.assertEqual(r_direction, sg_rule.direction)
868 rules.append(sg_rule)
869
870 return rules
871
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500872 def _create_pool(self, lb_method, protocol, subnet_id):
873 """Wrapper utility that returns a test pool."""
874 client = self.network_client
875 name = data_utils.rand_name('pool')
876 _, resp_pool = client.create_pool(protocol=protocol, name=name,
877 subnet_id=subnet_id,
878 lb_method=lb_method)
879 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
880 self.assertEqual(pool['name'], name)
881 self.addCleanup(self.delete_wrapper, pool.delete)
882 return pool
883
884 def _create_member(self, address, protocol_port, pool_id):
885 """Wrapper utility that returns a test member."""
886 client = self.network_client
887 _, resp_member = client.create_member(protocol_port=protocol_port,
888 pool_id=pool_id,
889 address=address)
890 member = net_resources.DeletableMember(client=client,
891 **resp_member['member'])
892 self.addCleanup(self.delete_wrapper, member.delete)
893 return member
894
895 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
896 """Wrapper utility that returns a test vip."""
897 client = self.network_client
898 name = data_utils.rand_name('vip')
899 _, resp_vip = client.create_vip(protocol=protocol, name=name,
900 subnet_id=subnet_id, pool_id=pool_id,
901 protocol_port=protocol_port)
902 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
903 self.assertEqual(vip['name'], name)
904 self.addCleanup(self.delete_wrapper, vip.delete)
905 return vip
906
Yair Fried1fc32a12014-08-04 09:11:30 +0300907 def _ssh_to_server(self, server, private_key):
908 ssh_login = CONF.compute.image_ssh_user
909 return self.get_remote_client(server,
910 username=ssh_login,
911 private_key=private_key)
912
Yair Frieddb6c9e92014-08-06 08:53:13 +0300913 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300914 """Retrieve a router for the given tenant id.
915
916 If a public router has been configured, it will be returned.
917
918 If a public router has not been configured, but a public
919 network has, a tenant router will be created and returned that
920 routes traffic to the public network.
921 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300922 if not client:
923 client = self.network_client
924 if not tenant_id:
925 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 router_id = CONF.network.public_router_id
927 network_id = CONF.network.public_network_id
928 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300929 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300930 return net_resources.AttributeDict(**result['router'])
931 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300932 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300933 router.set_gateway(network_id)
934 return router
935 else:
936 raise Exception("Neither of 'public_router_id' or "
937 "'public_network_id' has been defined.")
938
Yair Frieddb6c9e92014-08-06 08:53:13 +0300939 def _create_router(self, client=None, tenant_id=None,
940 namestart='router-smoke'):
941 if not client:
942 client = self.network_client
943 if not tenant_id:
944 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300945 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300946 _, result = client.create_router(name=name,
947 admin_state_up=True,
948 tenant_id=tenant_id)
949 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300950 **result['router'])
951 self.assertEqual(router.name, name)
952 self.addCleanup(self.delete_wrapper, router.delete)
953 return router
954
Yair Frieddb6c9e92014-08-06 08:53:13 +0300955 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300956 """Create a network with a subnet connected to a router.
957
David Shrewsbury9bac3662014-08-07 15:07:01 -0400958 The baremetal driver is a special case since all nodes are
959 on the same shared network.
960
Yair Fried1fc32a12014-08-04 09:11:30 +0300961 :returns: network, subnet, router
962 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400963 if CONF.baremetal.driver_enabled:
964 # NOTE(Shrews): This exception is for environments where tenant
965 # credential isolation is available, but network separation is
966 # not (the current baremetal case). Likely can be removed when
967 # test account mgmt is reworked:
968 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
969 network = self._get_network_by_name(
970 CONF.compute.fixed_network_name)
971 router = None
972 subnet = None
973 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300974 network = self._create_network(client=client, tenant_id=tenant_id)
975 router = self._get_router(client=client, tenant_id=tenant_id)
976 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400977 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300978 return network, subnet, router
979
980
David Shrewsbury06f7f8a2014-05-20 13:55:57 -0400981# power/provision states as of icehouse
982class BaremetalPowerStates(object):
983 """Possible power states of an Ironic node."""
984 POWER_ON = 'power on'
985 POWER_OFF = 'power off'
986 REBOOT = 'rebooting'
987 SUSPEND = 'suspended'
988
989
990class BaremetalProvisionStates(object):
991 """Possible provision states of an Ironic node."""
992 NOSTATE = None
993 INIT = 'initializing'
994 ACTIVE = 'active'
995 BUILDING = 'building'
996 DEPLOYWAIT = 'wait call-back'
997 DEPLOYING = 'deploying'
998 DEPLOYFAIL = 'deploy failed'
999 DEPLOYDONE = 'deploy complete'
1000 DELETING = 'deleting'
1001 DELETED = 'deleted'
1002 ERROR = 'error'
1003
1004
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001005class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001006 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001007 def resource_setup(cls):
1008 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001009
1010 if (not CONF.service_available.ironic or
1011 not CONF.baremetal.driver_enabled):
1012 msg = 'Ironic not available or Ironic compute driver not enabled'
1013 raise cls.skipException(msg)
1014
1015 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001016 manager = clients.Manager(
1017 credentials=cls.admin_credentials()
1018 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001019 cls.baremetal_client = manager.baremetal_client
1020
1021 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001022 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001023
1024 def _node_state_timeout(self, node_id, state_attr,
1025 target_states, timeout=10, interval=1):
1026 if not isinstance(target_states, list):
1027 target_states = [target_states]
1028
1029 def check_state():
1030 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001031 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001032 return True
1033 return False
1034
1035 if not tempest.test.call_until_true(
1036 check_state, timeout, interval):
1037 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1038 (node_id, state_attr, target_states))
1039 raise exceptions.TimeoutException(msg)
1040
1041 def wait_provisioning_state(self, node_id, state, timeout):
1042 self._node_state_timeout(
1043 node_id=node_id, state_attr='provision_state',
1044 target_states=state, timeout=timeout)
1045
1046 def wait_power_state(self, node_id, state):
1047 self._node_state_timeout(
1048 node_id=node_id, state_attr='power_state',
1049 target_states=state, timeout=CONF.baremetal.power_timeout)
1050
1051 def wait_node(self, instance_id):
1052 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001053
Adam Gandelman4a48a602014-03-20 18:23:18 -07001054 def _get_node():
1055 node = None
1056 try:
1057 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001058 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001059 pass
1060 return node is not None
1061
1062 if not tempest.test.call_until_true(
1063 _get_node, CONF.baremetal.association_timeout, 1):
1064 msg = ('Timed out waiting to get Ironic node by instance id %s'
1065 % instance_id)
1066 raise exceptions.TimeoutException(msg)
1067
1068 def get_node(self, node_id=None, instance_id=None):
1069 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001070 _, body = self.baremetal_client.show_node(node_id)
1071 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001072 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001073 _, body = self.baremetal_client.show_node_by_instance_uuid(
1074 instance_id)
1075 if body['nodes']:
1076 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001077
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001078 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001079 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001080 _, body = self.baremetal_client.list_node_ports(node_uuid)
1081 for port in body['ports']:
1082 _, p = self.baremetal_client.show_port(port['uuid'])
1083 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001084 return ports
1085
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001086 def add_keypair(self):
1087 self.keypair = self.create_keypair()
1088
1089 def verify_connectivity(self, ip=None):
1090 if ip:
1091 dest = self.get_remote_client(ip)
1092 else:
1093 dest = self.get_remote_client(self.instance)
1094 dest.validate_authentication()
1095
1096 def boot_instance(self):
1097 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001098 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001099 }
1100 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001101 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001102
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001103 self.wait_node(self.instance['id'])
1104 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001105
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001106 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001107
1108 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001109 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001110 [BaremetalProvisionStates.DEPLOYWAIT,
1111 BaremetalProvisionStates.ACTIVE],
1112 timeout=15)
1113
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001114 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001115 BaremetalProvisionStates.ACTIVE,
1116 timeout=CONF.baremetal.active_timeout)
1117
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001118 self.servers_client.wait_for_server_status(self.instance['id'],
1119 'ACTIVE')
1120 self.node = self.get_node(instance_id=self.instance['id'])
1121 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001122
1123 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001124 self.servers_client.delete_server(self.instance['id'])
1125 self.wait_power_state(self.node['uuid'],
1126 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001127 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001128 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001129 BaremetalProvisionStates.NOSTATE,
1130 timeout=CONF.baremetal.unprovision_timeout)
1131
Adam Gandelman4a48a602014-03-20 18:23:18 -07001132
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001133class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001134 """
1135 Base class for encryption scenario tests
1136 """
1137
1138 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001139 def resource_setup(cls):
1140 super(EncryptionScenarioTest, cls).resource_setup()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001141 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001142
1143 def _wait_for_volume_status(self, status):
1144 self.status_timeout(
1145 self.volume_client.volumes, self.volume.id, status)
1146
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001147 def nova_boot(self):
1148 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001149 create_kwargs = {'key_name': self.keypair['name']}
1150 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001151 create_kwargs=create_kwargs)
1152
1153 def create_volume_type(self, client=None, name=None):
1154 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001155 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001156 if not name:
1157 name = 'generic'
1158 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1159 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001160 _, body = client.create_volume_type(
1161 randomized_name)
1162 self.assertIn('id', body)
1163 self.addCleanup(client.delete_volume_type, body['id'])
1164 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001165
1166 def create_encryption_type(self, client=None, type_id=None, provider=None,
1167 key_size=None, cipher=None,
1168 control_location=None):
1169 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001170 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001171 if not type_id:
1172 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001173 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001174 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001175 client.create_encryption_type(
1176 type_id, provider=provider, key_size=key_size, cipher=cipher,
1177 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001178
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001179
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001180class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001181 """
1182 Base class for orchestration scenario tests
1183 """
1184
1185 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001186 def resource_setup(cls):
1187 super(OrchestrationScenarioTest, cls).resource_setup()
Matthew Treinish6c072292014-01-29 19:15:52 +00001188 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001189 raise cls.skipException("Heat support is required")
1190
1191 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001192 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001193 admin_creds = auth.get_default_credentials('identity_admin')
1194 creds = auth.get_default_credentials('user')
1195 admin_creds.tenant_name = creds.tenant_name
1196 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001197
1198 def _load_template(self, base_file, file_name):
1199 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1200 file_name)
1201 with open(filepath) as f:
1202 return f.read()
1203
1204 @classmethod
1205 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001206 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001207
1208 @classmethod
1209 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001210 _, networks = cls.networks_client.list_networks()
1211 for net in networks:
1212 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001213 return net
Steve Baker22c16602014-05-05 13:34:19 +12001214
1215 @staticmethod
1216 def _stack_output(stack, output_key):
1217 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001218 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001219 if o['output_key'] == output_key), None)
1220
Chris Dent0d494112014-08-26 13:48:30 +01001221
1222class SwiftScenarioTest(ScenarioTest):
1223 """
1224 Provide harness to do Swift scenario tests.
1225
1226 Subclasses implement the tests that use the methods provided by this
1227 class.
1228 """
1229
1230 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001231 def resource_setup(cls):
Chris Dent0d494112014-08-26 13:48:30 +01001232 cls.set_network_resources()
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001233 super(SwiftScenarioTest, cls).resource_setup()
Chris Dent0d494112014-08-26 13:48:30 +01001234 if not CONF.service_available.swift:
1235 skip_msg = ("%s skipped as swift is not available" %
1236 cls.__name__)
1237 raise cls.skipException(skip_msg)
1238 # Clients for Swift
1239 cls.account_client = cls.manager.account_client
1240 cls.container_client = cls.manager.container_client
1241 cls.object_client = cls.manager.object_client
1242
Chris Dentde456a12014-09-10 12:41:15 +01001243 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001244 """get swift status for our user account."""
1245 self.account_client.list_account_containers()
1246 LOG.debug('Swift status information obtained successfully')
1247
Chris Dentde456a12014-09-10 12:41:15 +01001248 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001249 name = container_name or data_utils.rand_name(
1250 'swift-scenario-container')
1251 self.container_client.create_container(name)
1252 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001253 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001254 LOG.debug('Container %s created' % (name))
1255 return name
1256
Chris Dentde456a12014-09-10 12:41:15 +01001257 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001258 self.container_client.delete_container(container_name)
1259 LOG.debug('Container %s deleted' % (container_name))
1260
Chris Dentde456a12014-09-10 12:41:15 +01001261 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001262 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1263 obj_data = data_utils.arbitrary_string()
1264 self.object_client.create_object(container_name, obj_name, obj_data)
1265 return obj_name, obj_data
1266
Chris Dentde456a12014-09-10 12:41:15 +01001267 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001268 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001269 self.list_and_check_container_objects(container_name,
1270 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001271
Chris Dentde456a12014-09-10 12:41:15 +01001272 def list_and_check_container_objects(self, container_name,
1273 present_obj=None,
1274 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001275 """
1276 List objects for a given container and assert which are present and
1277 which are not.
1278 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001279 if present_obj is None:
1280 present_obj = []
1281 if not_present_obj is None:
1282 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001283 _, object_list = self.container_client.list_container_contents(
1284 container_name)
1285 if present_obj:
1286 for obj in present_obj:
1287 self.assertIn(obj, object_list)
1288 if not_present_obj:
1289 for obj in not_present_obj:
1290 self.assertNotIn(obj, object_list)
1291
Chris Dentde456a12014-09-10 12:41:15 +01001292 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001293 metadata_param = {'metadata_prefix': 'x-container-',
1294 'metadata': {'read': acl}}
1295 self.container_client.update_container_metadata(container_name,
1296 **metadata_param)
1297 resp, _ = self.container_client.list_container_metadata(container_name)
1298 self.assertEqual(resp['x-container-read'], acl)
1299
Chris Dentde456a12014-09-10 12:41:15 +01001300 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001301 _, obj = self.object_client.get_object(container_name, obj_name)
1302 self.assertEqual(obj, expected_data)