blob: 99336461e6caf3beea6d3ec46008076dd3bc479e [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
Sean Dague1937d092013-05-17 16:36:38 -040024from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000025from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000026from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070027from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040028from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090029from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090030from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000031from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020032from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020033from tempest.openstack.common import log
Yair Fried1fc32a12014-08-04 09:11:30 +030034from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040035import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040036
Matthew Treinish6c072292014-01-29 19:15:52 +000037CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Attila Fazekasfb7552a2013-08-27 13:02:26 +020039LOG = log.getLogger(__name__)
40
41# NOTE(afazekas): Workaround for the stdout logging
42LOG_nova_client = logging.getLogger('novaclient.client')
43LOG_nova_client.addHandler(log.NullHandler())
44
45LOG_cinder_client = logging.getLogger('cinderclient.client')
46LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040047
48
Andrea Frittoli2e733b52014-07-16 14:12:11 +010049class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli247058f2014-07-16 16:09:22 +010050 """Replaces the OfficialClientTest base class.
51
52 Uses tempest own clients as opposed to OfficialClients.
53
54 Common differences:
55 - replace resource.attribute with resource['attribute']
56 - replace resouce.delete with delete_callable(resource['id'])
57 - replace local waiters with common / rest_client waiters
58 """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010059
60 @classmethod
61 def setUpClass(cls):
62 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010063 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010064 cls.isolated_creds = isolated_creds.IsolatedCreds(
65 cls.__name__, tempest_client=True,
66 network_resources=cls.network_resources)
67 cls.manager = clients.Manager(
68 credentials=cls.credentials()
69 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010070 cls.admin_manager = clients.Manager(cls.admin_credentials())
71 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070072 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010073 cls.floating_ips_client = cls.manager.floating_ips_client
74 # Glance image client v1
75 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000076 # Compute image client
77 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010078 cls.keypairs_client = cls.manager.keypairs_client
79 cls.networks_client = cls.admin_manager.networks_client
80 # Nova security groups client
81 cls.security_groups_client = cls.manager.security_groups_client
82 cls.servers_client = cls.manager.servers_client
83 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000084 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030085 cls.interface_client = cls.manager.interfaces_client
86 # Neutron network client
87 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090088 # Heat client
89 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010090
91 @classmethod
92 def _get_credentials(cls, get_creds, ctype):
93 if CONF.compute.allow_tenant_isolation:
94 creds = get_creds()
95 else:
96 creds = auth.get_default_credentials(ctype)
97 return creds
98
99 @classmethod
100 def credentials(cls):
101 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
102 'user')
103
Masayuki Igawaccd66592014-07-17 00:42:42 +0900104 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +0300105 def alt_credentials(cls):
106 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
107 'alt_user')
108
109 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900110 def admin_credentials(cls):
111 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
112 'identity_admin')
113
Andrea Frittoli247058f2014-07-16 16:09:22 +0100114 # ## Methods to handle sync and async deletes
115
116 def setUp(self):
117 super(ScenarioTest, self).setUp()
118 self.cleanup_waits = []
119 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
120 # because scenario tests in the same test class should not share
121 # resources. If resources were shared between test cases then it
122 # should be a single scenario test instead of multiples.
123
124 # NOTE(yfried): this list is cleaned at the end of test_methods and
125 # not at the end of the class
126 self.addCleanup(self._wait_for_cleanups)
127
Yair Fried1fc32a12014-08-04 09:11:30 +0300128 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100129 """Ignores NotFound exceptions for delete operations.
130
Yair Fried1fc32a12014-08-04 09:11:30 +0300131 @param delete_thing: delete method of a resource. method will be
132 executed as delete_thing(*args, **kwargs)
133
Andrea Frittoli247058f2014-07-16 16:09:22 +0100134 """
135 try:
136 # Tempest clients return dicts, so there is no common delete
137 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300138 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100139 except exceptions.NotFound:
140 # If the resource is already missing, mission accomplished.
141 pass
142
143 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900144 cleanup_callable, cleanup_args=None,
145 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700146 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100147
148 @param waiter_callable: callable to wait for the resource to delete
149 @param thing_id: the id of the resource to be cleaned-up
150 @param thing_id_param: the name of the id param in the waiter
151 @param cleanup_callable: method to load pass to self.addCleanup with
152 the following *cleanup_args, **cleanup_kwargs.
153 usually a delete method.
154 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900155 if cleanup_args is None:
156 cleanup_args = []
157 if cleanup_kwargs is None:
158 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100159 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
160 wait_dict = {
161 'waiter_callable': waiter_callable,
162 thing_id_param: thing_id
163 }
164 self.cleanup_waits.append(wait_dict)
165
166 def _wait_for_cleanups(self):
167 """To handle async delete actions, a list of waits is added
168 which will be iterated over as the last step of clearing the
169 cleanup queue. That way all the delete calls are made up front
170 and the tests won't succeed unless the deletes are eventually
171 successful. This is the same basic approach used in the api tests to
172 limit cleanup execution time except here it is multi-resource,
173 because of the nature of the scenario tests.
174 """
175 for wait in self.cleanup_waits:
176 waiter_callable = wait.pop('waiter_callable')
177 waiter_callable(**wait)
178
179 # ## Test functions library
180 #
181 # The create_[resource] functions only return body and discard the
182 # resp part which is not used in scenario tests
183
Yair Frieddb6c9e92014-08-06 08:53:13 +0300184 def create_keypair(self, client=None):
185 if not client:
186 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100187 name = data_utils.rand_name(self.__class__.__name__)
188 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300189 resp, body = client.create_keypair(name)
190 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100191 return body
192
193 def create_server(self, name=None, image=None, flavor=None,
194 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900195 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100196 """Creates VM instance.
197
198 @param image: image from which to create the instance
199 @param wait_on_boot: wait for status ACTIVE before continue
200 @param wait_on_delete: force synchronous delete on cleanup
201 @param create_kwargs: additional details for instance creation
202 @return: server dict
203 """
204 if name is None:
205 name = data_utils.rand_name(self.__class__.__name__)
206 if image is None:
207 image = CONF.compute.image_ref
208 if flavor is None:
209 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900210 if create_kwargs is None:
211 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100212
Andrea Frittoli247058f2014-07-16 16:09:22 +0100213 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
214 name, image, flavor)
215 _, server = self.servers_client.create_server(name, image, flavor,
216 **create_kwargs)
217 if wait_on_delete:
218 self.addCleanup(self.servers_client.wait_for_server_termination,
219 server['id'])
220 self.addCleanup_with_wait(
221 waiter_callable=self.servers_client.wait_for_server_termination,
222 thing_id=server['id'], thing_id_param='server_id',
223 cleanup_callable=self.delete_wrapper,
224 cleanup_args=[self.servers_client.delete_server, server['id']])
225 if wait_on_boot:
226 self.servers_client.wait_for_server_status(server_id=server['id'],
227 status='ACTIVE')
228 # The instance retrieved on creation is missing network
229 # details, necessitating retrieval after it becomes active to
230 # ensure correct details.
231 _, server = self.servers_client.get_server(server['id'])
232 self.assertEqual(server['name'], name)
233 return server
234
235 def create_volume(self, size=1, name=None, snapshot_id=None,
236 imageRef=None, volume_type=None, wait_on_delete=True):
237 if name is None:
238 name = data_utils.rand_name(self.__class__.__name__)
239 _, volume = self.volumes_client.create_volume(
240 size=size, display_name=name, snapshot_id=snapshot_id,
241 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700242
Andrea Frittoli247058f2014-07-16 16:09:22 +0100243 if wait_on_delete:
244 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
245 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700246 self.addCleanup(self.delete_wrapper,
247 self.volumes_client.delete_volume, volume['id'])
248 else:
249 self.addCleanup_with_wait(
250 waiter_callable=self.volumes_client.wait_for_resource_deletion,
251 thing_id=volume['id'], thing_id_param='id',
252 cleanup_callable=self.delete_wrapper,
253 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100254
255 self.assertEqual(name, volume['display_name'])
256 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
257 # The volume retrieved on creation has a non-up-to-date status.
258 # Retrieval after it becomes active ensures correct details.
259 _, volume = self.volumes_client.get_volume(volume['id'])
260 return volume
261
Yair Fried1fc32a12014-08-04 09:11:30 +0300262 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100263 _client = self.security_groups_client
264 if secgroup_id is None:
265 _, sgs = _client.list_security_groups()
266 for sg in sgs:
267 if sg['name'] == 'default':
268 secgroup_id = sg['id']
269
270 # These rules are intended to permit inbound ssh and icmp
271 # traffic from all sources, so no group_id is provided.
272 # Setting a group_id would only permit traffic from ports
273 # belonging to the same security group.
274 rulesets = [
275 {
276 # ssh
277 'ip_proto': 'tcp',
278 'from_port': 22,
279 'to_port': 22,
280 'cidr': '0.0.0.0/0',
281 },
282 {
283 # ping
284 'ip_proto': 'icmp',
285 'from_port': -1,
286 'to_port': -1,
287 'cidr': '0.0.0.0/0',
288 }
289 ]
290 rules = list()
291 for ruleset in rulesets:
292 _, sg_rule = _client.create_security_group_rule(secgroup_id,
293 **ruleset)
294 self.addCleanup(self.delete_wrapper,
295 _client.delete_security_group_rule,
296 sg_rule['id'])
297 rules.append(sg_rule)
298 return rules
299
Yair Fried1fc32a12014-08-04 09:11:30 +0300300 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100301 # Create security group
302 sg_name = data_utils.rand_name(self.__class__.__name__)
303 sg_desc = sg_name + " description"
304 _, secgroup = self.security_groups_client.create_security_group(
305 sg_name, sg_desc)
306 self.assertEqual(secgroup['name'], sg_name)
307 self.assertEqual(secgroup['description'], sg_desc)
308 self.addCleanup(self.delete_wrapper,
309 self.security_groups_client.delete_security_group,
310 secgroup['id'])
311
312 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300313 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100314
315 return secgroup
316
317 def get_remote_client(self, server_or_ip, username=None, private_key=None):
318 if isinstance(server_or_ip, six.string_types):
319 ip = server_or_ip
320 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700321 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
322 ip = addr['addr']
323
Andrea Frittoli247058f2014-07-16 16:09:22 +0100324 if username is None:
325 username = CONF.scenario.ssh_user
326 if private_key is None:
327 private_key = self.keypair['private_key']
328 linux_client = remote_client.RemoteClient(ip, username,
329 pkey=private_key)
330 try:
331 linux_client.validate_authentication()
332 except exceptions.SSHTimeout:
333 LOG.exception('ssh connection to %s failed' % ip)
334 debug.log_net_debug()
335 raise
336
337 return linux_client
338
Ghanshyam2a180b82014-06-16 13:54:22 +0900339 def _image_create(self, name, fmt, path, properties=None):
340 if properties is None:
341 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100342 name = data_utils.rand_name('%s-' % name)
343 image_file = open(path, 'rb')
344 self.addCleanup(image_file.close)
345 params = {
346 'name': name,
347 'container_format': fmt,
348 'disk_format': fmt,
349 'is_public': 'False',
350 }
351 params.update(properties)
352 _, image = self.image_client.create_image(**params)
353 self.addCleanup(self.image_client.delete_image, image['id'])
354 self.assertEqual("queued", image['status'])
355 self.image_client.update_image(image['id'], data=image_file)
356 return image['id']
357
358 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300359 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100360 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
361 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
362 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300363 img_container_format = CONF.scenario.img_container_format
364 img_disk_format = CONF.scenario.img_disk_format
365 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
366 "ami: %s, ari: %s, aki: %s" %
367 (img_path, img_container_format, img_disk_format,
368 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100369 try:
370 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300371 img_container_format,
372 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100373 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300374 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100375 except IOError:
376 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
377 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
378 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
379 properties = {
380 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
381 }
382 self.image = self._image_create('scenario-ami', 'ami',
383 path=ami_img_path,
384 properties=properties)
385 LOG.debug("image:%s" % self.image)
386
387 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400388 if not CONF.compute_feature_enabled.console_output:
389 LOG.debug('Console output not supported, cannot log')
390 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100391 if not servers:
392 _, servers = self.servers_client.list_servers()
393 servers = servers['servers']
394 for server in servers:
395 LOG.debug('Console output for %s', server['id'])
396 LOG.debug(self.servers_client.get_console_output(server['id'],
397 length=None))
398
nithya-ganesan882595e2014-07-29 18:51:07 +0000399 def create_server_snapshot(self, server, name=None):
400 # Glance client
401 _image_client = self.image_client
402 # Compute client
403 _images_client = self.images_client
404 if name is None:
405 name = data_utils.rand_name('scenario-snapshot-')
406 LOG.debug("Creating a snapshot image for server: %s", server['name'])
407 resp, image = _images_client.create_image(server['id'], name)
408 image_id = resp['location'].split('images/')[1]
409 _image_client.wait_for_image_status(image_id, 'active')
410 self.addCleanup_with_wait(
411 waiter_callable=_image_client.wait_for_resource_deletion,
412 thing_id=image_id, thing_id_param='id',
413 cleanup_callable=self.delete_wrapper,
414 cleanup_args=[_image_client.delete_image, image_id])
415 _, snapshot_image = _image_client.get_image_meta(image_id)
416 image_name = snapshot_image['name']
417 self.assertEqual(name, image_name)
418 LOG.debug("Created snapshot image %s for server %s",
419 image_name, server['name'])
420 return snapshot_image
421
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900422 def nova_volume_attach(self):
423 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
424 _, volume_attachment = self.servers_client.attach_volume(
425 self.server['id'], self.volume['id'], '/dev/vdb')
426 volume = volume_attachment['volumeAttachment']
427 self.assertEqual(self.volume['id'], volume['id'])
428 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
429 # Refresh the volume after the attachment
430 _, self.volume = self.volumes_client.get_volume(volume['id'])
431
432 def nova_volume_detach(self):
433 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
434 self.volumes_client.wait_for_volume_status(self.volume['id'],
435 'available')
436
437 _, volume = self.volumes_client.get_volume(self.volume['id'])
438 self.assertEqual('available', volume['status'])
439
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700440 def rebuild_server(self, server_id, image=None,
441 preserve_ephemeral=False, wait=True,
442 rebuild_kwargs=None):
443 if image is None:
444 image = CONF.compute.image_ref
445
446 rebuild_kwargs = rebuild_kwargs or {}
447
448 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
449 server_id, image, preserve_ephemeral)
450 self.servers_client.rebuild(server_id=server_id, image_ref=image,
451 preserve_ephemeral=preserve_ephemeral,
452 **rebuild_kwargs)
453 if wait:
454 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
455
Aaron Rosena7df13b2014-09-23 09:45:45 -0700456 def ping_ip_address(self, ip_address, should_succeed=True):
457 cmd = ['ping', '-c1', '-w1', ip_address]
458
459 def ping():
460 proc = subprocess.Popen(cmd,
461 stdout=subprocess.PIPE,
462 stderr=subprocess.PIPE)
463 proc.communicate()
464 return (proc.returncode == 0) == should_succeed
465
466 return tempest.test.call_until_true(
467 ping, CONF.compute.ping_timeout, 1)
468
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100469
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100470class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300471 """Base class for network scenario tests.
472 This class provide helpers for network scenario tests, using the neutron
473 API. Helpers from ancestor which use the nova network API are overridden
474 with the neutron API.
475
476 This Class also enforces using Neutron instead of novanetwork.
477 Subclassed tests will be skipped if Neutron is not enabled
478
479 """
480
481 @classmethod
482 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100483 if not CONF.service_available.neutron:
484 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300485
486 @classmethod
487 def setUpClass(cls):
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100488 super(NetworkScenarioTest, cls).setUpClass()
Yair Fried1fc32a12014-08-04 09:11:30 +0300489 cls.tenant_id = cls.manager.identity_client.tenant_id
490 cls.check_preconditions()
491
Yair Frieddb6c9e92014-08-06 08:53:13 +0300492 def _create_network(self, client=None, tenant_id=None,
493 namestart='network-smoke-'):
494 if not client:
495 client = self.network_client
496 if not tenant_id:
497 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300498 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300499 _, result = client.create_network(name=name, tenant_id=tenant_id)
500 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300501 **result['network'])
502 self.assertEqual(network.name, name)
503 self.addCleanup(self.delete_wrapper, network.delete)
504 return network
505
506 def _list_networks(self, *args, **kwargs):
507 """List networks using admin creds """
508 return self._admin_lister('networks')(*args, **kwargs)
509
510 def _list_subnets(self, *args, **kwargs):
511 """List subnets using admin creds """
512 return self._admin_lister('subnets')(*args, **kwargs)
513
514 def _list_routers(self, *args, **kwargs):
515 """List routers using admin creds """
516 return self._admin_lister('routers')(*args, **kwargs)
517
518 def _list_ports(self, *args, **kwargs):
519 """List ports using admin creds """
520 return self._admin_lister('ports')(*args, **kwargs)
521
522 def _admin_lister(self, resource_type):
523 def temp(*args, **kwargs):
524 temp_method = self.admin_manager.network_client.__getattr__(
525 'list_%s' % resource_type)
526 _, resource_list = temp_method(*args, **kwargs)
527 return resource_list[resource_type]
528 return temp
529
Yair Frieddb6c9e92014-08-06 08:53:13 +0300530 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
531 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300532 """
533 Create a subnet for the given network within the cidr block
534 configured for tenant networks.
535 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300536 if not client:
537 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300538
539 def cidr_in_use(cidr, tenant_id):
540 """
541 :return True if subnet with cidr already exist in tenant
542 False else
543 """
544 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
545 return len(cidr_in_use) != 0
546
547 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
548 result = None
549 # Repeatedly attempt subnet creation with sequential cidr
550 # blocks until an unallocated block is found.
551 for subnet_cidr in tenant_cidr.subnet(
552 CONF.network.tenant_network_mask_bits):
553 str_cidr = str(subnet_cidr)
554 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
555 continue
556
557 subnet = dict(
558 name=data_utils.rand_name(namestart),
559 ip_version=4,
560 network_id=network.id,
561 tenant_id=network.tenant_id,
562 cidr=str_cidr,
563 **kwargs
564 )
565 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300566 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300567 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300568 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300569 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
570 if not is_overlapping_cidr:
571 raise
572 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300573 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300574 **result['subnet'])
575 self.assertEqual(subnet.cidr, str_cidr)
576 self.addCleanup(self.delete_wrapper, subnet.delete)
577 return subnet
578
Yair Frieddb6c9e92014-08-06 08:53:13 +0300579 def _create_port(self, network, client=None, namestart='port-quotatest'):
580 if not client:
581 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300582 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300583 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300584 name=name,
585 network_id=network.id,
586 tenant_id=network.tenant_id)
587 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300588 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300589 **result['port'])
590 self.addCleanup(self.delete_wrapper, port.delete)
591 return port
592
593 def _get_server_port_id(self, server, ip_addr=None):
594 ports = self._list_ports(device_id=server['id'],
595 fixed_ip=ip_addr)
596 self.assertEqual(len(ports), 1,
597 "Unable to determine which port to target.")
598 return ports[0]['id']
599
David Shrewsbury9bac3662014-08-07 15:07:01 -0400600 def _get_network_by_name(self, network_name):
601 net = self._list_networks(name=network_name)
602 return net_common.AttributeDict(net[0])
603
Yair Frieddb6c9e92014-08-06 08:53:13 +0300604 def _create_floating_ip(self, thing, external_network_id, port_id=None,
605 client=None):
606 if not client:
607 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300608 if not port_id:
609 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300610 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300611 floating_network_id=external_network_id,
612 port_id=port_id,
613 tenant_id=thing['tenant_id']
614 )
615 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300616 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300617 **result['floatingip'])
618 self.addCleanup(self.delete_wrapper, floating_ip.delete)
619 return floating_ip
620
621 def _associate_floating_ip(self, floating_ip, server):
622 port_id = self._get_server_port_id(server)
623 floating_ip.update(port_id=port_id)
624 self.assertEqual(port_id, floating_ip.port_id)
625 return floating_ip
626
627 def _disassociate_floating_ip(self, floating_ip):
628 """
629 :param floating_ip: type DeletableFloatingIp
630 """
631 floating_ip.update(port_id=None)
632 self.assertIsNone(floating_ip.port_id)
633 return floating_ip
634
Yair Fried1fc32a12014-08-04 09:11:30 +0300635 def _check_vm_connectivity(self, ip_address,
636 username=None,
637 private_key=None,
638 should_connect=True):
639 """
640 :param ip_address: server to test against
641 :param username: server's ssh username
642 :param private_key: server's ssh private key to be used
643 :param should_connect: True/False indicates positive/negative test
644 positive - attempt ping and ssh
645 negative - attempt ping and fail if succeed
646
647 :raises: AssertError if the result of the connectivity check does
648 not match the value of the should_connect param
649 """
650 if should_connect:
651 msg = "Timed out waiting for %s to become reachable" % ip_address
652 else:
653 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700654 self.assertTrue(self.ping_ip_address(ip_address,
655 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300656 msg=msg)
657 if should_connect:
658 # no need to check ssh for negative connectivity
659 self.get_remote_client(ip_address, username, private_key)
660
661 def _check_public_network_connectivity(self, ip_address, username,
662 private_key, should_connect=True,
663 msg=None, servers=None):
664 # The target login is assumed to have been configured for
665 # key-based authentication by cloud-init.
666 LOG.debug('checking network connections to IP %s with user: %s' %
667 (ip_address, username))
668 try:
669 self._check_vm_connectivity(ip_address,
670 username,
671 private_key,
672 should_connect=should_connect)
673 except Exception as e:
674 ex_msg = 'Public network connectivity check failed'
675 if msg:
676 ex_msg += ": " + msg
677 LOG.exception(ex_msg)
678 self._log_console_output(servers)
679 # network debug is called as part of ssh init
680 if not isinstance(e, exceptions.SSHTimeout):
681 debug.log_net_debug()
682 raise
683
684 def _check_tenant_network_connectivity(self, server,
685 username,
686 private_key,
687 should_connect=True,
688 servers_for_debug=None):
689 if not CONF.network.tenant_networks_reachable:
690 msg = 'Tenant networks not configured to be reachable.'
691 LOG.info(msg)
692 return
693 # The target login is assumed to have been configured for
694 # key-based authentication by cloud-init.
695 try:
696 for net_name, ip_addresses in server['networks'].iteritems():
697 for ip_address in ip_addresses:
698 self._check_vm_connectivity(ip_address,
699 username,
700 private_key,
701 should_connect=should_connect)
702 except Exception as e:
703 LOG.exception('Tenant network connectivity check failed')
704 self._log_console_output(servers_for_debug)
705 # network debug is called as part of ssh init
706 if not isinstance(e, exceptions.SSHTimeout):
707 debug.log_net_debug()
708 raise
709
710 def _check_remote_connectivity(self, source, dest, should_succeed=True):
711 """
712 check ping server via source ssh connection
713
714 :param source: RemoteClient: an ssh connection from which to ping
715 :param dest: and IP to ping against
716 :param should_succeed: boolean should ping succeed or not
717 :returns: boolean -- should_succeed == ping
718 :returns: ping is false if ping failed
719 """
720 def ping_remote():
721 try:
722 source.ping_host(dest)
723 except exceptions.SSHExecCommandFailed:
724 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
725 % (dest, source.ssh_client.host))
726 return not should_succeed
727 return should_succeed
728
729 return tempest.test.call_until_true(ping_remote,
730 CONF.compute.ping_timeout,
731 1)
732
Yair Frieddb6c9e92014-08-06 08:53:13 +0300733 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300734 namestart='secgroup-smoke'):
735 if client is None:
736 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300737 if tenant_id is None:
738 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300739 secgroup = self._create_empty_security_group(namestart=namestart,
740 client=client,
741 tenant_id=tenant_id)
742
743 # Add rules to the security group
744 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
745 for rule in rules:
746 self.assertEqual(tenant_id, rule.tenant_id)
747 self.assertEqual(secgroup.id, rule.security_group_id)
748 return secgroup
749
Yair Frieddb6c9e92014-08-06 08:53:13 +0300750 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300751 namestart='secgroup-smoke'):
752 """Create a security group without rules.
753
754 Default rules will be created:
755 - IPv4 egress to any
756 - IPv6 egress to any
757
758 :param tenant_id: secgroup will be created in this tenant
759 :returns: DeletableSecurityGroup -- containing the secgroup created
760 """
761 if client is None:
762 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300763 if not tenant_id:
764 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300765 sg_name = data_utils.rand_name(namestart)
766 sg_desc = sg_name + " description"
767 sg_dict = dict(name=sg_name,
768 description=sg_desc)
769 sg_dict['tenant_id'] = tenant_id
770 _, result = client.create_security_group(**sg_dict)
771 secgroup = net_resources.DeletableSecurityGroup(
772 client=client,
773 **result['security_group']
774 )
775 self.assertEqual(secgroup.name, sg_name)
776 self.assertEqual(tenant_id, secgroup.tenant_id)
777 self.assertEqual(secgroup.description, sg_desc)
778 self.addCleanup(self.delete_wrapper, secgroup.delete)
779 return secgroup
780
Yair Frieddb6c9e92014-08-06 08:53:13 +0300781 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300782 """Get default secgroup for given tenant_id.
783
784 :returns: DeletableSecurityGroup -- default secgroup for given tenant
785 """
786 if client is None:
787 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300788 if not tenant_id:
789 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300790 sgs = [
791 sg for sg in client.list_security_groups().values()[0]
792 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
793 ]
794 msg = "No default security group for tenant %s." % (tenant_id)
795 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300796 return net_resources.DeletableSecurityGroup(client=client,
797 **sgs[0])
798
Yair Frieddb6c9e92014-08-06 08:53:13 +0300799 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300800 tenant_id=None, **kwargs):
801 """Create a rule from a dictionary of rule parameters.
802
803 Create a rule in a secgroup. if secgroup not defined will search for
804 default secgroup in tenant_id.
805
806 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300807 :param tenant_id: if secgroup not passed -- the tenant in which to
808 search for default secgroup
809 :param kwargs: a dictionary containing rule parameters:
810 for example, to allow incoming ssh:
811 rule = {
812 direction: 'ingress'
813 protocol:'tcp',
814 port_range_min: 22,
815 port_range_max: 22
816 }
817 """
818 if client is None:
819 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300820 if not tenant_id:
821 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300822 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300823 secgroup = self._default_security_group(client=client,
824 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300825
826 ruleset = dict(security_group_id=secgroup.id,
827 tenant_id=secgroup.tenant_id)
828 ruleset.update(kwargs)
829
830 _, sg_rule = client.create_security_group_rule(**ruleset)
831 sg_rule = net_resources.DeletableSecurityGroupRule(
832 client=client,
833 **sg_rule['security_group_rule']
834 )
835 self.addCleanup(self.delete_wrapper, sg_rule.delete)
836 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
837 self.assertEqual(secgroup.id, sg_rule.security_group_id)
838
839 return sg_rule
840
841 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
842 """These rules are intended to permit inbound ssh and icmp
843 traffic from all sources, so no group_id is provided.
844 Setting a group_id would only permit traffic from ports
845 belonging to the same security group.
846 """
847
848 if client is None:
849 client = self.network_client
850 rules = []
851 rulesets = [
852 dict(
853 # ssh
854 protocol='tcp',
855 port_range_min=22,
856 port_range_max=22,
857 ),
858 dict(
859 # ping
860 protocol='icmp',
861 )
862 ]
863 for ruleset in rulesets:
864 for r_direction in ['ingress', 'egress']:
865 ruleset['direction'] = r_direction
866 try:
867 sg_rule = self._create_security_group_rule(
868 client=client, secgroup=secgroup, **ruleset)
869 except exceptions.Conflict as ex:
870 # if rule already exist - skip rule and continue
871 msg = 'Security group rule already exists'
872 if msg not in ex._error_string:
873 raise ex
874 else:
875 self.assertEqual(r_direction, sg_rule.direction)
876 rules.append(sg_rule)
877
878 return rules
879
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500880 def _create_pool(self, lb_method, protocol, subnet_id):
881 """Wrapper utility that returns a test pool."""
882 client = self.network_client
883 name = data_utils.rand_name('pool')
884 _, resp_pool = client.create_pool(protocol=protocol, name=name,
885 subnet_id=subnet_id,
886 lb_method=lb_method)
887 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
888 self.assertEqual(pool['name'], name)
889 self.addCleanup(self.delete_wrapper, pool.delete)
890 return pool
891
892 def _create_member(self, address, protocol_port, pool_id):
893 """Wrapper utility that returns a test member."""
894 client = self.network_client
895 _, resp_member = client.create_member(protocol_port=protocol_port,
896 pool_id=pool_id,
897 address=address)
898 member = net_resources.DeletableMember(client=client,
899 **resp_member['member'])
900 self.addCleanup(self.delete_wrapper, member.delete)
901 return member
902
903 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
904 """Wrapper utility that returns a test vip."""
905 client = self.network_client
906 name = data_utils.rand_name('vip')
907 _, resp_vip = client.create_vip(protocol=protocol, name=name,
908 subnet_id=subnet_id, pool_id=pool_id,
909 protocol_port=protocol_port)
910 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
911 self.assertEqual(vip['name'], name)
912 self.addCleanup(self.delete_wrapper, vip.delete)
913 return vip
914
Yair Fried1fc32a12014-08-04 09:11:30 +0300915 def _ssh_to_server(self, server, private_key):
916 ssh_login = CONF.compute.image_ssh_user
917 return self.get_remote_client(server,
918 username=ssh_login,
919 private_key=private_key)
920
Yair Frieddb6c9e92014-08-06 08:53:13 +0300921 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300922 """Retrieve a router for the given tenant id.
923
924 If a public router has been configured, it will be returned.
925
926 If a public router has not been configured, but a public
927 network has, a tenant router will be created and returned that
928 routes traffic to the public network.
929 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300930 if not client:
931 client = self.network_client
932 if not tenant_id:
933 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300934 router_id = CONF.network.public_router_id
935 network_id = CONF.network.public_network_id
936 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300937 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300938 return net_resources.AttributeDict(**result['router'])
939 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300940 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300941 router.set_gateway(network_id)
942 return router
943 else:
944 raise Exception("Neither of 'public_router_id' or "
945 "'public_network_id' has been defined.")
946
Yair Frieddb6c9e92014-08-06 08:53:13 +0300947 def _create_router(self, client=None, tenant_id=None,
948 namestart='router-smoke'):
949 if not client:
950 client = self.network_client
951 if not tenant_id:
952 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300953 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300954 _, result = client.create_router(name=name,
955 admin_state_up=True,
956 tenant_id=tenant_id)
957 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300958 **result['router'])
959 self.assertEqual(router.name, name)
960 self.addCleanup(self.delete_wrapper, router.delete)
961 return router
962
Yair Frieddb6c9e92014-08-06 08:53:13 +0300963 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300964 """Create a network with a subnet connected to a router.
965
David Shrewsbury9bac3662014-08-07 15:07:01 -0400966 The baremetal driver is a special case since all nodes are
967 on the same shared network.
968
Yair Fried1fc32a12014-08-04 09:11:30 +0300969 :returns: network, subnet, router
970 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400971 if CONF.baremetal.driver_enabled:
972 # NOTE(Shrews): This exception is for environments where tenant
973 # credential isolation is available, but network separation is
974 # not (the current baremetal case). Likely can be removed when
975 # test account mgmt is reworked:
976 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
977 network = self._get_network_by_name(
978 CONF.compute.fixed_network_name)
979 router = None
980 subnet = None
981 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300982 network = self._create_network(client=client, tenant_id=tenant_id)
983 router = self._get_router(client=client, tenant_id=tenant_id)
984 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400985 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300986 return network, subnet, router
987
988
David Shrewsbury06f7f8a2014-05-20 13:55:57 -0400989# power/provision states as of icehouse
990class BaremetalPowerStates(object):
991 """Possible power states of an Ironic node."""
992 POWER_ON = 'power on'
993 POWER_OFF = 'power off'
994 REBOOT = 'rebooting'
995 SUSPEND = 'suspended'
996
997
998class BaremetalProvisionStates(object):
999 """Possible provision states of an Ironic node."""
1000 NOSTATE = None
1001 INIT = 'initializing'
1002 ACTIVE = 'active'
1003 BUILDING = 'building'
1004 DEPLOYWAIT = 'wait call-back'
1005 DEPLOYING = 'deploying'
1006 DEPLOYFAIL = 'deploy failed'
1007 DEPLOYDONE = 'deploy complete'
1008 DELETING = 'deleting'
1009 DELETED = 'deleted'
1010 ERROR = 'error'
1011
1012
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001013class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001014 @classmethod
1015 def setUpClass(cls):
1016 super(BaremetalScenarioTest, cls).setUpClass()
1017
1018 if (not CONF.service_available.ironic or
1019 not CONF.baremetal.driver_enabled):
1020 msg = 'Ironic not available or Ironic compute driver not enabled'
1021 raise cls.skipException(msg)
1022
1023 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001024 manager = clients.Manager(
1025 credentials=cls.admin_credentials()
1026 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001027 cls.baremetal_client = manager.baremetal_client
1028
1029 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001030 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001031
1032 def _node_state_timeout(self, node_id, state_attr,
1033 target_states, timeout=10, interval=1):
1034 if not isinstance(target_states, list):
1035 target_states = [target_states]
1036
1037 def check_state():
1038 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001039 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001040 return True
1041 return False
1042
1043 if not tempest.test.call_until_true(
1044 check_state, timeout, interval):
1045 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1046 (node_id, state_attr, target_states))
1047 raise exceptions.TimeoutException(msg)
1048
1049 def wait_provisioning_state(self, node_id, state, timeout):
1050 self._node_state_timeout(
1051 node_id=node_id, state_attr='provision_state',
1052 target_states=state, timeout=timeout)
1053
1054 def wait_power_state(self, node_id, state):
1055 self._node_state_timeout(
1056 node_id=node_id, state_attr='power_state',
1057 target_states=state, timeout=CONF.baremetal.power_timeout)
1058
1059 def wait_node(self, instance_id):
1060 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001061
Adam Gandelman4a48a602014-03-20 18:23:18 -07001062 def _get_node():
1063 node = None
1064 try:
1065 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001066 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001067 pass
1068 return node is not None
1069
1070 if not tempest.test.call_until_true(
1071 _get_node, CONF.baremetal.association_timeout, 1):
1072 msg = ('Timed out waiting to get Ironic node by instance id %s'
1073 % instance_id)
1074 raise exceptions.TimeoutException(msg)
1075
1076 def get_node(self, node_id=None, instance_id=None):
1077 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001078 _, body = self.baremetal_client.show_node(node_id)
1079 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001080 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001081 _, body = self.baremetal_client.show_node_by_instance_uuid(
1082 instance_id)
1083 if body['nodes']:
1084 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001085
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001086 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001087 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001088 _, body = self.baremetal_client.list_node_ports(node_uuid)
1089 for port in body['ports']:
1090 _, p = self.baremetal_client.show_port(port['uuid'])
1091 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001092 return ports
1093
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001094 def add_keypair(self):
1095 self.keypair = self.create_keypair()
1096
1097 def verify_connectivity(self, ip=None):
1098 if ip:
1099 dest = self.get_remote_client(ip)
1100 else:
1101 dest = self.get_remote_client(self.instance)
1102 dest.validate_authentication()
1103
1104 def boot_instance(self):
1105 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001106 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001107 }
1108 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001109 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001110
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001111 self.wait_node(self.instance['id'])
1112 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001113
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001114 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001115
1116 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001117 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001118 [BaremetalProvisionStates.DEPLOYWAIT,
1119 BaremetalProvisionStates.ACTIVE],
1120 timeout=15)
1121
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001122 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001123 BaremetalProvisionStates.ACTIVE,
1124 timeout=CONF.baremetal.active_timeout)
1125
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001126 self.servers_client.wait_for_server_status(self.instance['id'],
1127 'ACTIVE')
1128 self.node = self.get_node(instance_id=self.instance['id'])
1129 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001130
1131 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001132 self.servers_client.delete_server(self.instance['id'])
1133 self.wait_power_state(self.node['uuid'],
1134 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001135 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001136 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001137 BaremetalProvisionStates.NOSTATE,
1138 timeout=CONF.baremetal.unprovision_timeout)
1139
Adam Gandelman4a48a602014-03-20 18:23:18 -07001140
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001141class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001142 """
1143 Base class for encryption scenario tests
1144 """
1145
1146 @classmethod
1147 def setUpClass(cls):
1148 super(EncryptionScenarioTest, cls).setUpClass()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001149 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001150
1151 def _wait_for_volume_status(self, status):
1152 self.status_timeout(
1153 self.volume_client.volumes, self.volume.id, status)
1154
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001155 def nova_boot(self):
1156 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001157 create_kwargs = {'key_name': self.keypair['name']}
1158 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001159 create_kwargs=create_kwargs)
1160
1161 def create_volume_type(self, client=None, name=None):
1162 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001163 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001164 if not name:
1165 name = 'generic'
1166 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1167 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001168 _, body = client.create_volume_type(
1169 randomized_name)
1170 self.assertIn('id', body)
1171 self.addCleanup(client.delete_volume_type, body['id'])
1172 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001173
1174 def create_encryption_type(self, client=None, type_id=None, provider=None,
1175 key_size=None, cipher=None,
1176 control_location=None):
1177 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001178 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001179 if not type_id:
1180 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001181 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001182 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001183 client.create_encryption_type(
1184 type_id, provider=provider, key_size=key_size, cipher=cipher,
1185 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001186
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001187
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001188class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001189 """
1190 Base class for orchestration scenario tests
1191 """
1192
1193 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001194 def setUpClass(cls):
1195 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001196 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001197 raise cls.skipException("Heat support is required")
1198
1199 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001200 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001201 admin_creds = auth.get_default_credentials('identity_admin')
1202 creds = auth.get_default_credentials('user')
1203 admin_creds.tenant_name = creds.tenant_name
1204 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001205
1206 def _load_template(self, base_file, file_name):
1207 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1208 file_name)
1209 with open(filepath) as f:
1210 return f.read()
1211
1212 @classmethod
1213 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001214 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001215
1216 @classmethod
1217 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001218 _, networks = cls.networks_client.list_networks()
1219 for net in networks:
1220 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001221 return net
Steve Baker22c16602014-05-05 13:34:19 +12001222
1223 @staticmethod
1224 def _stack_output(stack, output_key):
1225 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001226 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001227 if o['output_key'] == output_key), None)
1228
Chris Dent0d494112014-08-26 13:48:30 +01001229
1230class SwiftScenarioTest(ScenarioTest):
1231 """
1232 Provide harness to do Swift scenario tests.
1233
1234 Subclasses implement the tests that use the methods provided by this
1235 class.
1236 """
1237
1238 @classmethod
1239 def setUpClass(cls):
1240 cls.set_network_resources()
1241 super(SwiftScenarioTest, cls).setUpClass()
1242 if not CONF.service_available.swift:
1243 skip_msg = ("%s skipped as swift is not available" %
1244 cls.__name__)
1245 raise cls.skipException(skip_msg)
1246 # Clients for Swift
1247 cls.account_client = cls.manager.account_client
1248 cls.container_client = cls.manager.container_client
1249 cls.object_client = cls.manager.object_client
1250
Chris Dentde456a12014-09-10 12:41:15 +01001251 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001252 """get swift status for our user account."""
1253 self.account_client.list_account_containers()
1254 LOG.debug('Swift status information obtained successfully')
1255
Chris Dentde456a12014-09-10 12:41:15 +01001256 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001257 name = container_name or data_utils.rand_name(
1258 'swift-scenario-container')
1259 self.container_client.create_container(name)
1260 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001261 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001262 LOG.debug('Container %s created' % (name))
1263 return name
1264
Chris Dentde456a12014-09-10 12:41:15 +01001265 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001266 self.container_client.delete_container(container_name)
1267 LOG.debug('Container %s deleted' % (container_name))
1268
Chris Dentde456a12014-09-10 12:41:15 +01001269 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001270 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1271 obj_data = data_utils.arbitrary_string()
1272 self.object_client.create_object(container_name, obj_name, obj_data)
1273 return obj_name, obj_data
1274
Chris Dentde456a12014-09-10 12:41:15 +01001275 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001276 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001277 self.list_and_check_container_objects(container_name,
1278 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001279
Chris Dentde456a12014-09-10 12:41:15 +01001280 def list_and_check_container_objects(self, container_name,
1281 present_obj=None,
1282 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001283 """
1284 List objects for a given container and assert which are present and
1285 which are not.
1286 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001287 if present_obj is None:
1288 present_obj = []
1289 if not_present_obj is None:
1290 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001291 _, object_list = self.container_client.list_container_contents(
1292 container_name)
1293 if present_obj:
1294 for obj in present_obj:
1295 self.assertIn(obj, object_list)
1296 if not_present_obj:
1297 for obj in not_present_obj:
1298 self.assertNotIn(obj, object_list)
1299
Chris Dentde456a12014-09-10 12:41:15 +01001300 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001301 metadata_param = {'metadata_prefix': 'x-container-',
1302 'metadata': {'read': acl}}
1303 self.container_client.update_container_metadata(container_name,
1304 **metadata_param)
1305 resp, _ = self.container_client.list_container_metadata(container_name)
1306 self.assertEqual(resp['x-container-read'], acl)
1307
Chris Dentde456a12014-09-10 12:41:15 +01001308 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001309 _, obj = self.object_client.get_object(container_name, obj_name)
1310 self.assertEqual(obj, expected_data)