blob: db58d879e7a4d7525dbf50ab8128c931ad5e994a [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
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000399 def _log_net_info(self, exc):
400 # network debug is called as part of ssh init
401 if not isinstance(exc, exceptions.SSHTimeout):
402 LOG.debug('Network information on a devstack host')
403 debug.log_net_debug()
404
nithya-ganesan882595e2014-07-29 18:51:07 +0000405 def create_server_snapshot(self, server, name=None):
406 # Glance client
407 _image_client = self.image_client
408 # Compute client
409 _images_client = self.images_client
410 if name is None:
411 name = data_utils.rand_name('scenario-snapshot-')
412 LOG.debug("Creating a snapshot image for server: %s", server['name'])
413 resp, image = _images_client.create_image(server['id'], name)
414 image_id = resp['location'].split('images/')[1]
415 _image_client.wait_for_image_status(image_id, 'active')
416 self.addCleanup_with_wait(
417 waiter_callable=_image_client.wait_for_resource_deletion,
418 thing_id=image_id, thing_id_param='id',
419 cleanup_callable=self.delete_wrapper,
420 cleanup_args=[_image_client.delete_image, image_id])
421 _, snapshot_image = _image_client.get_image_meta(image_id)
422 image_name = snapshot_image['name']
423 self.assertEqual(name, image_name)
424 LOG.debug("Created snapshot image %s for server %s",
425 image_name, server['name'])
426 return snapshot_image
427
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900428 def nova_volume_attach(self):
429 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
430 _, volume_attachment = self.servers_client.attach_volume(
431 self.server['id'], self.volume['id'], '/dev/vdb')
432 volume = volume_attachment['volumeAttachment']
433 self.assertEqual(self.volume['id'], volume['id'])
434 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
435 # Refresh the volume after the attachment
436 _, self.volume = self.volumes_client.get_volume(volume['id'])
437
438 def nova_volume_detach(self):
439 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
440 self.volumes_client.wait_for_volume_status(self.volume['id'],
441 'available')
442
443 _, volume = self.volumes_client.get_volume(self.volume['id'])
444 self.assertEqual('available', volume['status'])
445
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700446 def rebuild_server(self, server_id, image=None,
447 preserve_ephemeral=False, wait=True,
448 rebuild_kwargs=None):
449 if image is None:
450 image = CONF.compute.image_ref
451
452 rebuild_kwargs = rebuild_kwargs or {}
453
454 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
455 server_id, image, preserve_ephemeral)
456 self.servers_client.rebuild(server_id=server_id, image_ref=image,
457 preserve_ephemeral=preserve_ephemeral,
458 **rebuild_kwargs)
459 if wait:
460 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
461
Aaron Rosena7df13b2014-09-23 09:45:45 -0700462 def ping_ip_address(self, ip_address, should_succeed=True):
463 cmd = ['ping', '-c1', '-w1', ip_address]
464
465 def ping():
466 proc = subprocess.Popen(cmd,
467 stdout=subprocess.PIPE,
468 stderr=subprocess.PIPE)
469 proc.communicate()
470 return (proc.returncode == 0) == should_succeed
471
472 return tempest.test.call_until_true(
473 ping, CONF.compute.ping_timeout, 1)
474
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100475
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100476class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300477 """Base class for network scenario tests.
478 This class provide helpers for network scenario tests, using the neutron
479 API. Helpers from ancestor which use the nova network API are overridden
480 with the neutron API.
481
482 This Class also enforces using Neutron instead of novanetwork.
483 Subclassed tests will be skipped if Neutron is not enabled
484
485 """
486
487 @classmethod
488 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100489 if not CONF.service_available.neutron:
490 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300491
492 @classmethod
493 def setUpClass(cls):
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100494 super(NetworkScenarioTest, cls).setUpClass()
Yair Fried1fc32a12014-08-04 09:11:30 +0300495 cls.tenant_id = cls.manager.identity_client.tenant_id
496 cls.check_preconditions()
497
Yair Frieddb6c9e92014-08-06 08:53:13 +0300498 def _create_network(self, client=None, tenant_id=None,
499 namestart='network-smoke-'):
500 if not client:
501 client = self.network_client
502 if not tenant_id:
503 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300504 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300505 _, result = client.create_network(name=name, tenant_id=tenant_id)
506 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300507 **result['network'])
508 self.assertEqual(network.name, name)
509 self.addCleanup(self.delete_wrapper, network.delete)
510 return network
511
512 def _list_networks(self, *args, **kwargs):
513 """List networks using admin creds """
514 return self._admin_lister('networks')(*args, **kwargs)
515
516 def _list_subnets(self, *args, **kwargs):
517 """List subnets using admin creds """
518 return self._admin_lister('subnets')(*args, **kwargs)
519
520 def _list_routers(self, *args, **kwargs):
521 """List routers using admin creds """
522 return self._admin_lister('routers')(*args, **kwargs)
523
524 def _list_ports(self, *args, **kwargs):
525 """List ports using admin creds """
526 return self._admin_lister('ports')(*args, **kwargs)
527
528 def _admin_lister(self, resource_type):
529 def temp(*args, **kwargs):
530 temp_method = self.admin_manager.network_client.__getattr__(
531 'list_%s' % resource_type)
532 _, resource_list = temp_method(*args, **kwargs)
533 return resource_list[resource_type]
534 return temp
535
Yair Frieddb6c9e92014-08-06 08:53:13 +0300536 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
537 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300538 """
539 Create a subnet for the given network within the cidr block
540 configured for tenant networks.
541 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300542 if not client:
543 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300544
545 def cidr_in_use(cidr, tenant_id):
546 """
547 :return True if subnet with cidr already exist in tenant
548 False else
549 """
550 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
551 return len(cidr_in_use) != 0
552
553 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
554 result = None
555 # Repeatedly attempt subnet creation with sequential cidr
556 # blocks until an unallocated block is found.
557 for subnet_cidr in tenant_cidr.subnet(
558 CONF.network.tenant_network_mask_bits):
559 str_cidr = str(subnet_cidr)
560 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
561 continue
562
563 subnet = dict(
564 name=data_utils.rand_name(namestart),
565 ip_version=4,
566 network_id=network.id,
567 tenant_id=network.tenant_id,
568 cidr=str_cidr,
569 **kwargs
570 )
571 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300572 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300573 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300574 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300575 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
576 if not is_overlapping_cidr:
577 raise
578 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300579 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300580 **result['subnet'])
581 self.assertEqual(subnet.cidr, str_cidr)
582 self.addCleanup(self.delete_wrapper, subnet.delete)
583 return subnet
584
Yair Frieddb6c9e92014-08-06 08:53:13 +0300585 def _create_port(self, network, client=None, namestart='port-quotatest'):
586 if not client:
587 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300588 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300589 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300590 name=name,
591 network_id=network.id,
592 tenant_id=network.tenant_id)
593 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300594 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300595 **result['port'])
596 self.addCleanup(self.delete_wrapper, port.delete)
597 return port
598
599 def _get_server_port_id(self, server, ip_addr=None):
600 ports = self._list_ports(device_id=server['id'],
601 fixed_ip=ip_addr)
602 self.assertEqual(len(ports), 1,
603 "Unable to determine which port to target.")
604 return ports[0]['id']
605
David Shrewsbury9bac3662014-08-07 15:07:01 -0400606 def _get_network_by_name(self, network_name):
607 net = self._list_networks(name=network_name)
608 return net_common.AttributeDict(net[0])
609
Yair Frieddb6c9e92014-08-06 08:53:13 +0300610 def _create_floating_ip(self, thing, external_network_id, port_id=None,
611 client=None):
612 if not client:
613 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300614 if not port_id:
615 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300616 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300617 floating_network_id=external_network_id,
618 port_id=port_id,
619 tenant_id=thing['tenant_id']
620 )
621 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300622 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300623 **result['floatingip'])
624 self.addCleanup(self.delete_wrapper, floating_ip.delete)
625 return floating_ip
626
627 def _associate_floating_ip(self, floating_ip, server):
628 port_id = self._get_server_port_id(server)
629 floating_ip.update(port_id=port_id)
630 self.assertEqual(port_id, floating_ip.port_id)
631 return floating_ip
632
633 def _disassociate_floating_ip(self, floating_ip):
634 """
635 :param floating_ip: type DeletableFloatingIp
636 """
637 floating_ip.update(port_id=None)
638 self.assertIsNone(floating_ip.port_id)
639 return floating_ip
640
Yair Fried1fc32a12014-08-04 09:11:30 +0300641 def _check_vm_connectivity(self, ip_address,
642 username=None,
643 private_key=None,
644 should_connect=True):
645 """
646 :param ip_address: server to test against
647 :param username: server's ssh username
648 :param private_key: server's ssh private key to be used
649 :param should_connect: True/False indicates positive/negative test
650 positive - attempt ping and ssh
651 negative - attempt ping and fail if succeed
652
653 :raises: AssertError if the result of the connectivity check does
654 not match the value of the should_connect param
655 """
656 if should_connect:
657 msg = "Timed out waiting for %s to become reachable" % ip_address
658 else:
659 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700660 self.assertTrue(self.ping_ip_address(ip_address,
661 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300662 msg=msg)
663 if should_connect:
664 # no need to check ssh for negative connectivity
665 self.get_remote_client(ip_address, username, private_key)
666
667 def _check_public_network_connectivity(self, ip_address, username,
668 private_key, should_connect=True,
669 msg=None, servers=None):
670 # The target login is assumed to have been configured for
671 # key-based authentication by cloud-init.
672 LOG.debug('checking network connections to IP %s with user: %s' %
673 (ip_address, username))
674 try:
675 self._check_vm_connectivity(ip_address,
676 username,
677 private_key,
678 should_connect=should_connect)
679 except Exception as e:
680 ex_msg = 'Public network connectivity check failed'
681 if msg:
682 ex_msg += ": " + msg
683 LOG.exception(ex_msg)
684 self._log_console_output(servers)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000685 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300686 raise
687
688 def _check_tenant_network_connectivity(self, server,
689 username,
690 private_key,
691 should_connect=True,
692 servers_for_debug=None):
693 if not CONF.network.tenant_networks_reachable:
694 msg = 'Tenant networks not configured to be reachable.'
695 LOG.info(msg)
696 return
697 # The target login is assumed to have been configured for
698 # key-based authentication by cloud-init.
699 try:
700 for net_name, ip_addresses in server['networks'].iteritems():
701 for ip_address in ip_addresses:
702 self._check_vm_connectivity(ip_address,
703 username,
704 private_key,
705 should_connect=should_connect)
706 except Exception as e:
707 LOG.exception('Tenant network connectivity check failed')
708 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000709 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300710 raise
711
712 def _check_remote_connectivity(self, source, dest, should_succeed=True):
713 """
714 check ping server via source ssh connection
715
716 :param source: RemoteClient: an ssh connection from which to ping
717 :param dest: and IP to ping against
718 :param should_succeed: boolean should ping succeed or not
719 :returns: boolean -- should_succeed == ping
720 :returns: ping is false if ping failed
721 """
722 def ping_remote():
723 try:
724 source.ping_host(dest)
725 except exceptions.SSHExecCommandFailed:
726 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
727 % (dest, source.ssh_client.host))
728 return not should_succeed
729 return should_succeed
730
731 return tempest.test.call_until_true(ping_remote,
732 CONF.compute.ping_timeout,
733 1)
734
Yair Frieddb6c9e92014-08-06 08:53:13 +0300735 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300736 namestart='secgroup-smoke'):
737 if client is None:
738 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300739 if tenant_id is None:
740 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300741 secgroup = self._create_empty_security_group(namestart=namestart,
742 client=client,
743 tenant_id=tenant_id)
744
745 # Add rules to the security group
746 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
747 for rule in rules:
748 self.assertEqual(tenant_id, rule.tenant_id)
749 self.assertEqual(secgroup.id, rule.security_group_id)
750 return secgroup
751
Yair Frieddb6c9e92014-08-06 08:53:13 +0300752 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300753 namestart='secgroup-smoke'):
754 """Create a security group without rules.
755
756 Default rules will be created:
757 - IPv4 egress to any
758 - IPv6 egress to any
759
760 :param tenant_id: secgroup will be created in this tenant
761 :returns: DeletableSecurityGroup -- containing the secgroup created
762 """
763 if client is None:
764 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300765 if not tenant_id:
766 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300767 sg_name = data_utils.rand_name(namestart)
768 sg_desc = sg_name + " description"
769 sg_dict = dict(name=sg_name,
770 description=sg_desc)
771 sg_dict['tenant_id'] = tenant_id
772 _, result = client.create_security_group(**sg_dict)
773 secgroup = net_resources.DeletableSecurityGroup(
774 client=client,
775 **result['security_group']
776 )
777 self.assertEqual(secgroup.name, sg_name)
778 self.assertEqual(tenant_id, secgroup.tenant_id)
779 self.assertEqual(secgroup.description, sg_desc)
780 self.addCleanup(self.delete_wrapper, secgroup.delete)
781 return secgroup
782
Yair Frieddb6c9e92014-08-06 08:53:13 +0300783 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300784 """Get default secgroup for given tenant_id.
785
786 :returns: DeletableSecurityGroup -- default secgroup for given tenant
787 """
788 if client is None:
789 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300790 if not tenant_id:
791 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300792 sgs = [
793 sg for sg in client.list_security_groups().values()[0]
794 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
795 ]
796 msg = "No default security group for tenant %s." % (tenant_id)
797 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300798 return net_resources.DeletableSecurityGroup(client=client,
799 **sgs[0])
800
Yair Frieddb6c9e92014-08-06 08:53:13 +0300801 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300802 tenant_id=None, **kwargs):
803 """Create a rule from a dictionary of rule parameters.
804
805 Create a rule in a secgroup. if secgroup not defined will search for
806 default secgroup in tenant_id.
807
808 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300809 :param tenant_id: if secgroup not passed -- the tenant in which to
810 search for default secgroup
811 :param kwargs: a dictionary containing rule parameters:
812 for example, to allow incoming ssh:
813 rule = {
814 direction: 'ingress'
815 protocol:'tcp',
816 port_range_min: 22,
817 port_range_max: 22
818 }
819 """
820 if client is None:
821 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300822 if not tenant_id:
823 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300824 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300825 secgroup = self._default_security_group(client=client,
826 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300827
828 ruleset = dict(security_group_id=secgroup.id,
829 tenant_id=secgroup.tenant_id)
830 ruleset.update(kwargs)
831
832 _, sg_rule = client.create_security_group_rule(**ruleset)
833 sg_rule = net_resources.DeletableSecurityGroupRule(
834 client=client,
835 **sg_rule['security_group_rule']
836 )
837 self.addCleanup(self.delete_wrapper, sg_rule.delete)
838 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
839 self.assertEqual(secgroup.id, sg_rule.security_group_id)
840
841 return sg_rule
842
843 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
844 """These rules are intended to permit inbound ssh and icmp
845 traffic from all sources, so no group_id is provided.
846 Setting a group_id would only permit traffic from ports
847 belonging to the same security group.
848 """
849
850 if client is None:
851 client = self.network_client
852 rules = []
853 rulesets = [
854 dict(
855 # ssh
856 protocol='tcp',
857 port_range_min=22,
858 port_range_max=22,
859 ),
860 dict(
861 # ping
862 protocol='icmp',
863 )
864 ]
865 for ruleset in rulesets:
866 for r_direction in ['ingress', 'egress']:
867 ruleset['direction'] = r_direction
868 try:
869 sg_rule = self._create_security_group_rule(
870 client=client, secgroup=secgroup, **ruleset)
871 except exceptions.Conflict as ex:
872 # if rule already exist - skip rule and continue
873 msg = 'Security group rule already exists'
874 if msg not in ex._error_string:
875 raise ex
876 else:
877 self.assertEqual(r_direction, sg_rule.direction)
878 rules.append(sg_rule)
879
880 return rules
881
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500882 def _create_pool(self, lb_method, protocol, subnet_id):
883 """Wrapper utility that returns a test pool."""
884 client = self.network_client
885 name = data_utils.rand_name('pool')
886 _, resp_pool = client.create_pool(protocol=protocol, name=name,
887 subnet_id=subnet_id,
888 lb_method=lb_method)
889 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
890 self.assertEqual(pool['name'], name)
891 self.addCleanup(self.delete_wrapper, pool.delete)
892 return pool
893
894 def _create_member(self, address, protocol_port, pool_id):
895 """Wrapper utility that returns a test member."""
896 client = self.network_client
897 _, resp_member = client.create_member(protocol_port=protocol_port,
898 pool_id=pool_id,
899 address=address)
900 member = net_resources.DeletableMember(client=client,
901 **resp_member['member'])
902 self.addCleanup(self.delete_wrapper, member.delete)
903 return member
904
905 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
906 """Wrapper utility that returns a test vip."""
907 client = self.network_client
908 name = data_utils.rand_name('vip')
909 _, resp_vip = client.create_vip(protocol=protocol, name=name,
910 subnet_id=subnet_id, pool_id=pool_id,
911 protocol_port=protocol_port)
912 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
913 self.assertEqual(vip['name'], name)
914 self.addCleanup(self.delete_wrapper, vip.delete)
915 return vip
916
Yair Fried1fc32a12014-08-04 09:11:30 +0300917 def _ssh_to_server(self, server, private_key):
918 ssh_login = CONF.compute.image_ssh_user
919 return self.get_remote_client(server,
920 username=ssh_login,
921 private_key=private_key)
922
Yair Frieddb6c9e92014-08-06 08:53:13 +0300923 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300924 """Retrieve a router for the given tenant id.
925
926 If a public router has been configured, it will be returned.
927
928 If a public router has not been configured, but a public
929 network has, a tenant router will be created and returned that
930 routes traffic to the public network.
931 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300932 if not client:
933 client = self.network_client
934 if not tenant_id:
935 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300936 router_id = CONF.network.public_router_id
937 network_id = CONF.network.public_network_id
938 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300939 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300940 return net_resources.AttributeDict(**result['router'])
941 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300942 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300943 router.set_gateway(network_id)
944 return router
945 else:
946 raise Exception("Neither of 'public_router_id' or "
947 "'public_network_id' has been defined.")
948
Yair Frieddb6c9e92014-08-06 08:53:13 +0300949 def _create_router(self, client=None, tenant_id=None,
950 namestart='router-smoke'):
951 if not client:
952 client = self.network_client
953 if not tenant_id:
954 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300955 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300956 _, result = client.create_router(name=name,
957 admin_state_up=True,
958 tenant_id=tenant_id)
959 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300960 **result['router'])
961 self.assertEqual(router.name, name)
962 self.addCleanup(self.delete_wrapper, router.delete)
963 return router
964
Yair Frieddb6c9e92014-08-06 08:53:13 +0300965 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300966 """Create a network with a subnet connected to a router.
967
David Shrewsbury9bac3662014-08-07 15:07:01 -0400968 The baremetal driver is a special case since all nodes are
969 on the same shared network.
970
Yair Fried1fc32a12014-08-04 09:11:30 +0300971 :returns: network, subnet, router
972 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400973 if CONF.baremetal.driver_enabled:
974 # NOTE(Shrews): This exception is for environments where tenant
975 # credential isolation is available, but network separation is
976 # not (the current baremetal case). Likely can be removed when
977 # test account mgmt is reworked:
978 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
979 network = self._get_network_by_name(
980 CONF.compute.fixed_network_name)
981 router = None
982 subnet = None
983 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300984 network = self._create_network(client=client, tenant_id=tenant_id)
985 router = self._get_router(client=client, tenant_id=tenant_id)
986 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400987 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300988 return network, subnet, router
989
990
David Shrewsbury06f7f8a2014-05-20 13:55:57 -0400991# power/provision states as of icehouse
992class BaremetalPowerStates(object):
993 """Possible power states of an Ironic node."""
994 POWER_ON = 'power on'
995 POWER_OFF = 'power off'
996 REBOOT = 'rebooting'
997 SUSPEND = 'suspended'
998
999
1000class BaremetalProvisionStates(object):
1001 """Possible provision states of an Ironic node."""
1002 NOSTATE = None
1003 INIT = 'initializing'
1004 ACTIVE = 'active'
1005 BUILDING = 'building'
1006 DEPLOYWAIT = 'wait call-back'
1007 DEPLOYING = 'deploying'
1008 DEPLOYFAIL = 'deploy failed'
1009 DEPLOYDONE = 'deploy complete'
1010 DELETING = 'deleting'
1011 DELETED = 'deleted'
1012 ERROR = 'error'
1013
1014
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001015class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001016 @classmethod
1017 def setUpClass(cls):
1018 super(BaremetalScenarioTest, cls).setUpClass()
1019
1020 if (not CONF.service_available.ironic or
1021 not CONF.baremetal.driver_enabled):
1022 msg = 'Ironic not available or Ironic compute driver not enabled'
1023 raise cls.skipException(msg)
1024
1025 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001026 manager = clients.Manager(
1027 credentials=cls.admin_credentials()
1028 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001029 cls.baremetal_client = manager.baremetal_client
1030
1031 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001032 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001033
1034 def _node_state_timeout(self, node_id, state_attr,
1035 target_states, timeout=10, interval=1):
1036 if not isinstance(target_states, list):
1037 target_states = [target_states]
1038
1039 def check_state():
1040 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001041 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001042 return True
1043 return False
1044
1045 if not tempest.test.call_until_true(
1046 check_state, timeout, interval):
1047 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1048 (node_id, state_attr, target_states))
1049 raise exceptions.TimeoutException(msg)
1050
1051 def wait_provisioning_state(self, node_id, state, timeout):
1052 self._node_state_timeout(
1053 node_id=node_id, state_attr='provision_state',
1054 target_states=state, timeout=timeout)
1055
1056 def wait_power_state(self, node_id, state):
1057 self._node_state_timeout(
1058 node_id=node_id, state_attr='power_state',
1059 target_states=state, timeout=CONF.baremetal.power_timeout)
1060
1061 def wait_node(self, instance_id):
1062 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001063
Adam Gandelman4a48a602014-03-20 18:23:18 -07001064 def _get_node():
1065 node = None
1066 try:
1067 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001068 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001069 pass
1070 return node is not None
1071
1072 if not tempest.test.call_until_true(
1073 _get_node, CONF.baremetal.association_timeout, 1):
1074 msg = ('Timed out waiting to get Ironic node by instance id %s'
1075 % instance_id)
1076 raise exceptions.TimeoutException(msg)
1077
1078 def get_node(self, node_id=None, instance_id=None):
1079 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001080 _, body = self.baremetal_client.show_node(node_id)
1081 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001082 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001083 _, body = self.baremetal_client.show_node_by_instance_uuid(
1084 instance_id)
1085 if body['nodes']:
1086 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001087
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001088 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001089 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001090 _, body = self.baremetal_client.list_node_ports(node_uuid)
1091 for port in body['ports']:
1092 _, p = self.baremetal_client.show_port(port['uuid'])
1093 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001094 return ports
1095
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001096 def add_keypair(self):
1097 self.keypair = self.create_keypair()
1098
1099 def verify_connectivity(self, ip=None):
1100 if ip:
1101 dest = self.get_remote_client(ip)
1102 else:
1103 dest = self.get_remote_client(self.instance)
1104 dest.validate_authentication()
1105
1106 def boot_instance(self):
1107 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001108 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001109 }
1110 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001111 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001112
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001113 self.wait_node(self.instance['id'])
1114 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001115
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001116 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001117
1118 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001119 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001120 [BaremetalProvisionStates.DEPLOYWAIT,
1121 BaremetalProvisionStates.ACTIVE],
1122 timeout=15)
1123
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001124 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001125 BaremetalProvisionStates.ACTIVE,
1126 timeout=CONF.baremetal.active_timeout)
1127
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001128 self.servers_client.wait_for_server_status(self.instance['id'],
1129 'ACTIVE')
1130 self.node = self.get_node(instance_id=self.instance['id'])
1131 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001132
1133 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001134 self.servers_client.delete_server(self.instance['id'])
1135 self.wait_power_state(self.node['uuid'],
1136 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001137 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001138 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001139 BaremetalProvisionStates.NOSTATE,
1140 timeout=CONF.baremetal.unprovision_timeout)
1141
Adam Gandelman4a48a602014-03-20 18:23:18 -07001142
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001143class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001144 """
1145 Base class for encryption scenario tests
1146 """
1147
1148 @classmethod
1149 def setUpClass(cls):
1150 super(EncryptionScenarioTest, cls).setUpClass()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001151 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001152
1153 def _wait_for_volume_status(self, status):
1154 self.status_timeout(
1155 self.volume_client.volumes, self.volume.id, status)
1156
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001157 def nova_boot(self):
1158 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001159 create_kwargs = {'key_name': self.keypair['name']}
1160 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001161 create_kwargs=create_kwargs)
1162
1163 def create_volume_type(self, client=None, name=None):
1164 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001165 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001166 if not name:
1167 name = 'generic'
1168 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1169 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001170 _, body = client.create_volume_type(
1171 randomized_name)
1172 self.assertIn('id', body)
1173 self.addCleanup(client.delete_volume_type, body['id'])
1174 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001175
1176 def create_encryption_type(self, client=None, type_id=None, provider=None,
1177 key_size=None, cipher=None,
1178 control_location=None):
1179 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001180 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001181 if not type_id:
1182 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001183 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001184 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001185 client.create_encryption_type(
1186 type_id, provider=provider, key_size=key_size, cipher=cipher,
1187 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001188
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001189
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001190class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001191 """
1192 Base class for orchestration scenario tests
1193 """
1194
1195 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001196 def setUpClass(cls):
1197 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001198 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001199 raise cls.skipException("Heat support is required")
1200
1201 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001202 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001203 admin_creds = auth.get_default_credentials('identity_admin')
1204 creds = auth.get_default_credentials('user')
1205 admin_creds.tenant_name = creds.tenant_name
1206 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001207
1208 def _load_template(self, base_file, file_name):
1209 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1210 file_name)
1211 with open(filepath) as f:
1212 return f.read()
1213
1214 @classmethod
1215 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001216 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001217
1218 @classmethod
1219 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001220 _, networks = cls.networks_client.list_networks()
1221 for net in networks:
1222 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001223 return net
Steve Baker22c16602014-05-05 13:34:19 +12001224
1225 @staticmethod
1226 def _stack_output(stack, output_key):
1227 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001228 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001229 if o['output_key'] == output_key), None)
1230
Chris Dent0d494112014-08-26 13:48:30 +01001231
1232class SwiftScenarioTest(ScenarioTest):
1233 """
1234 Provide harness to do Swift scenario tests.
1235
1236 Subclasses implement the tests that use the methods provided by this
1237 class.
1238 """
1239
1240 @classmethod
1241 def setUpClass(cls):
1242 cls.set_network_resources()
1243 super(SwiftScenarioTest, cls).setUpClass()
1244 if not CONF.service_available.swift:
1245 skip_msg = ("%s skipped as swift is not available" %
1246 cls.__name__)
1247 raise cls.skipException(skip_msg)
1248 # Clients for Swift
1249 cls.account_client = cls.manager.account_client
1250 cls.container_client = cls.manager.container_client
1251 cls.object_client = cls.manager.object_client
1252
Chris Dentde456a12014-09-10 12:41:15 +01001253 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001254 """get swift status for our user account."""
1255 self.account_client.list_account_containers()
1256 LOG.debug('Swift status information obtained successfully')
1257
Chris Dentde456a12014-09-10 12:41:15 +01001258 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001259 name = container_name or data_utils.rand_name(
1260 'swift-scenario-container')
1261 self.container_client.create_container(name)
1262 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001263 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001264 LOG.debug('Container %s created' % (name))
1265 return name
1266
Chris Dentde456a12014-09-10 12:41:15 +01001267 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001268 self.container_client.delete_container(container_name)
1269 LOG.debug('Container %s deleted' % (container_name))
1270
Chris Dentde456a12014-09-10 12:41:15 +01001271 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001272 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1273 obj_data = data_utils.arbitrary_string()
1274 self.object_client.create_object(container_name, obj_name, obj_data)
1275 return obj_name, obj_data
1276
Chris Dentde456a12014-09-10 12:41:15 +01001277 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001278 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001279 self.list_and_check_container_objects(container_name,
1280 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001281
Chris Dentde456a12014-09-10 12:41:15 +01001282 def list_and_check_container_objects(self, container_name,
1283 present_obj=None,
1284 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001285 """
1286 List objects for a given container and assert which are present and
1287 which are not.
1288 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001289 if present_obj is None:
1290 present_obj = []
1291 if not_present_obj is None:
1292 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001293 _, object_list = self.container_client.list_container_contents(
1294 container_name)
1295 if present_obj:
1296 for obj in present_obj:
1297 self.assertIn(obj, object_list)
1298 if not_present_obj:
1299 for obj in not_present_obj:
1300 self.assertNotIn(obj, object_list)
1301
Chris Dentde456a12014-09-10 12:41:15 +01001302 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001303 metadata_param = {'metadata_prefix': 'x-container-',
1304 'metadata': {'read': acl}}
1305 self.container_client.update_container_metadata(container_name,
1306 **metadata_param)
1307 resp, _ = self.container_client.list_container_metadata(container_name)
1308 self.assertEqual(resp['x-container-read'], acl)
1309
Chris Dentde456a12014-09-10 12:41:15 +01001310 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001311 _, obj = self.object_client.get_object(container_name, obj_name)
1312 self.assertEqual(obj, expected_data)