blob: 429d0e35258d17f7c8844b02d7174889a5288840 [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
Andrea Frittoliac20b5e2014-09-15 13:31:14 +010061 def resource_setup(cls):
62 super(ScenarioTest, cls).resource_setup()
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(
Andrea Frittoliae9aca02014-09-25 11:43:11 +010065 cls.__name__, network_resources=cls.network_resources)
Andrea Frittoli2e733b52014-07-16 14:12:11 +010066 cls.manager = clients.Manager(
67 credentials=cls.credentials()
68 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010069 cls.admin_manager = clients.Manager(cls.admin_credentials())
70 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070071 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010072 cls.floating_ips_client = cls.manager.floating_ips_client
73 # Glance image client v1
74 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000075 # Compute image client
76 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010077 cls.keypairs_client = cls.manager.keypairs_client
78 cls.networks_client = cls.admin_manager.networks_client
79 # Nova security groups client
80 cls.security_groups_client = cls.manager.security_groups_client
81 cls.servers_client = cls.manager.servers_client
82 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000083 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030084 cls.interface_client = cls.manager.interfaces_client
85 # Neutron network client
86 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090087 # Heat client
88 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010089
90 @classmethod
91 def _get_credentials(cls, get_creds, ctype):
92 if CONF.compute.allow_tenant_isolation:
93 creds = get_creds()
94 else:
95 creds = auth.get_default_credentials(ctype)
96 return creds
97
98 @classmethod
99 def credentials(cls):
100 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
101 'user')
102
Masayuki Igawaccd66592014-07-17 00:42:42 +0900103 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +0300104 def alt_credentials(cls):
105 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
106 'alt_user')
107
108 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900109 def admin_credentials(cls):
110 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
111 'identity_admin')
112
Andrea Frittoli247058f2014-07-16 16:09:22 +0100113 # ## Methods to handle sync and async deletes
114
115 def setUp(self):
116 super(ScenarioTest, self).setUp()
117 self.cleanup_waits = []
118 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
119 # because scenario tests in the same test class should not share
120 # resources. If resources were shared between test cases then it
121 # should be a single scenario test instead of multiples.
122
123 # NOTE(yfried): this list is cleaned at the end of test_methods and
124 # not at the end of the class
125 self.addCleanup(self._wait_for_cleanups)
126
Yair Fried1fc32a12014-08-04 09:11:30 +0300127 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100128 """Ignores NotFound exceptions for delete operations.
129
Yair Fried1fc32a12014-08-04 09:11:30 +0300130 @param delete_thing: delete method of a resource. method will be
131 executed as delete_thing(*args, **kwargs)
132
Andrea Frittoli247058f2014-07-16 16:09:22 +0100133 """
134 try:
135 # Tempest clients return dicts, so there is no common delete
136 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300137 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100138 except exceptions.NotFound:
139 # If the resource is already missing, mission accomplished.
140 pass
141
142 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900143 cleanup_callable, cleanup_args=None,
144 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700145 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100146
147 @param waiter_callable: callable to wait for the resource to delete
148 @param thing_id: the id of the resource to be cleaned-up
149 @param thing_id_param: the name of the id param in the waiter
150 @param cleanup_callable: method to load pass to self.addCleanup with
151 the following *cleanup_args, **cleanup_kwargs.
152 usually a delete method.
153 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900154 if cleanup_args is None:
155 cleanup_args = []
156 if cleanup_kwargs is None:
157 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100158 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
159 wait_dict = {
160 'waiter_callable': waiter_callable,
161 thing_id_param: thing_id
162 }
163 self.cleanup_waits.append(wait_dict)
164
165 def _wait_for_cleanups(self):
166 """To handle async delete actions, a list of waits is added
167 which will be iterated over as the last step of clearing the
168 cleanup queue. That way all the delete calls are made up front
169 and the tests won't succeed unless the deletes are eventually
170 successful. This is the same basic approach used in the api tests to
171 limit cleanup execution time except here it is multi-resource,
172 because of the nature of the scenario tests.
173 """
174 for wait in self.cleanup_waits:
175 waiter_callable = wait.pop('waiter_callable')
176 waiter_callable(**wait)
177
178 # ## Test functions library
179 #
180 # The create_[resource] functions only return body and discard the
181 # resp part which is not used in scenario tests
182
Yair Frieddb6c9e92014-08-06 08:53:13 +0300183 def create_keypair(self, client=None):
184 if not client:
185 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100186 name = data_utils.rand_name(self.__class__.__name__)
187 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300188 resp, body = client.create_keypair(name)
189 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100190 return body
191
192 def create_server(self, name=None, image=None, flavor=None,
193 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900194 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100195 """Creates VM instance.
196
197 @param image: image from which to create the instance
198 @param wait_on_boot: wait for status ACTIVE before continue
199 @param wait_on_delete: force synchronous delete on cleanup
200 @param create_kwargs: additional details for instance creation
201 @return: server dict
202 """
203 if name is None:
204 name = data_utils.rand_name(self.__class__.__name__)
205 if image is None:
206 image = CONF.compute.image_ref
207 if flavor is None:
208 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900209 if create_kwargs is None:
210 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100211
Andrea Frittoli247058f2014-07-16 16:09:22 +0100212 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
213 name, image, flavor)
214 _, server = self.servers_client.create_server(name, image, flavor,
215 **create_kwargs)
216 if wait_on_delete:
217 self.addCleanup(self.servers_client.wait_for_server_termination,
218 server['id'])
219 self.addCleanup_with_wait(
220 waiter_callable=self.servers_client.wait_for_server_termination,
221 thing_id=server['id'], thing_id_param='server_id',
222 cleanup_callable=self.delete_wrapper,
223 cleanup_args=[self.servers_client.delete_server, server['id']])
224 if wait_on_boot:
225 self.servers_client.wait_for_server_status(server_id=server['id'],
226 status='ACTIVE')
227 # The instance retrieved on creation is missing network
228 # details, necessitating retrieval after it becomes active to
229 # ensure correct details.
230 _, server = self.servers_client.get_server(server['id'])
231 self.assertEqual(server['name'], name)
232 return server
233
234 def create_volume(self, size=1, name=None, snapshot_id=None,
235 imageRef=None, volume_type=None, wait_on_delete=True):
236 if name is None:
237 name = data_utils.rand_name(self.__class__.__name__)
238 _, volume = self.volumes_client.create_volume(
239 size=size, display_name=name, snapshot_id=snapshot_id,
240 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700241
Andrea Frittoli247058f2014-07-16 16:09:22 +0100242 if wait_on_delete:
243 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
244 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700245 self.addCleanup(self.delete_wrapper,
246 self.volumes_client.delete_volume, volume['id'])
247 else:
248 self.addCleanup_with_wait(
249 waiter_callable=self.volumes_client.wait_for_resource_deletion,
250 thing_id=volume['id'], thing_id_param='id',
251 cleanup_callable=self.delete_wrapper,
252 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100253
254 self.assertEqual(name, volume['display_name'])
255 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
256 # The volume retrieved on creation has a non-up-to-date status.
257 # Retrieval after it becomes active ensures correct details.
258 _, volume = self.volumes_client.get_volume(volume['id'])
259 return volume
260
Yair Fried1fc32a12014-08-04 09:11:30 +0300261 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100262 _client = self.security_groups_client
263 if secgroup_id is None:
264 _, sgs = _client.list_security_groups()
265 for sg in sgs:
266 if sg['name'] == 'default':
267 secgroup_id = sg['id']
268
269 # These rules are intended to permit inbound ssh and icmp
270 # traffic from all sources, so no group_id is provided.
271 # Setting a group_id would only permit traffic from ports
272 # belonging to the same security group.
273 rulesets = [
274 {
275 # ssh
276 'ip_proto': 'tcp',
277 'from_port': 22,
278 'to_port': 22,
279 'cidr': '0.0.0.0/0',
280 },
281 {
282 # ping
283 'ip_proto': 'icmp',
284 'from_port': -1,
285 'to_port': -1,
286 'cidr': '0.0.0.0/0',
287 }
288 ]
289 rules = list()
290 for ruleset in rulesets:
291 _, sg_rule = _client.create_security_group_rule(secgroup_id,
292 **ruleset)
293 self.addCleanup(self.delete_wrapper,
294 _client.delete_security_group_rule,
295 sg_rule['id'])
296 rules.append(sg_rule)
297 return rules
298
Yair Fried1fc32a12014-08-04 09:11:30 +0300299 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100300 # Create security group
301 sg_name = data_utils.rand_name(self.__class__.__name__)
302 sg_desc = sg_name + " description"
303 _, secgroup = self.security_groups_client.create_security_group(
304 sg_name, sg_desc)
305 self.assertEqual(secgroup['name'], sg_name)
306 self.assertEqual(secgroup['description'], sg_desc)
307 self.addCleanup(self.delete_wrapper,
308 self.security_groups_client.delete_security_group,
309 secgroup['id'])
310
311 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300312 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100313
314 return secgroup
315
316 def get_remote_client(self, server_or_ip, username=None, private_key=None):
317 if isinstance(server_or_ip, six.string_types):
318 ip = server_or_ip
319 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700320 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
321 ip = addr['addr']
322
Andrea Frittoli247058f2014-07-16 16:09:22 +0100323 if username is None:
324 username = CONF.scenario.ssh_user
325 if private_key is None:
326 private_key = self.keypair['private_key']
327 linux_client = remote_client.RemoteClient(ip, username,
328 pkey=private_key)
329 try:
330 linux_client.validate_authentication()
331 except exceptions.SSHTimeout:
332 LOG.exception('ssh connection to %s failed' % ip)
333 debug.log_net_debug()
334 raise
335
336 return linux_client
337
Ghanshyam2a180b82014-06-16 13:54:22 +0900338 def _image_create(self, name, fmt, path, properties=None):
339 if properties is None:
340 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100341 name = data_utils.rand_name('%s-' % name)
342 image_file = open(path, 'rb')
343 self.addCleanup(image_file.close)
344 params = {
345 'name': name,
346 'container_format': fmt,
347 'disk_format': fmt,
348 'is_public': 'False',
349 }
350 params.update(properties)
351 _, image = self.image_client.create_image(**params)
352 self.addCleanup(self.image_client.delete_image, image['id'])
353 self.assertEqual("queued", image['status'])
354 self.image_client.update_image(image['id'], data=image_file)
355 return image['id']
356
357 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300358 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100359 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
360 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
361 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300362 img_container_format = CONF.scenario.img_container_format
363 img_disk_format = CONF.scenario.img_disk_format
364 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
365 "ami: %s, ari: %s, aki: %s" %
366 (img_path, img_container_format, img_disk_format,
367 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100368 try:
369 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300370 img_container_format,
371 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100372 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300373 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100374 except IOError:
375 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
376 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
377 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
378 properties = {
379 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
380 }
381 self.image = self._image_create('scenario-ami', 'ami',
382 path=ami_img_path,
383 properties=properties)
384 LOG.debug("image:%s" % self.image)
385
386 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400387 if not CONF.compute_feature_enabled.console_output:
388 LOG.debug('Console output not supported, cannot log')
389 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100390 if not servers:
391 _, servers = self.servers_client.list_servers()
392 servers = servers['servers']
393 for server in servers:
394 LOG.debug('Console output for %s', server['id'])
395 LOG.debug(self.servers_client.get_console_output(server['id'],
396 length=None))
397
nithya-ganesan882595e2014-07-29 18:51:07 +0000398 def create_server_snapshot(self, server, name=None):
399 # Glance client
400 _image_client = self.image_client
401 # Compute client
402 _images_client = self.images_client
403 if name is None:
404 name = data_utils.rand_name('scenario-snapshot-')
405 LOG.debug("Creating a snapshot image for server: %s", server['name'])
406 resp, image = _images_client.create_image(server['id'], name)
407 image_id = resp['location'].split('images/')[1]
408 _image_client.wait_for_image_status(image_id, 'active')
409 self.addCleanup_with_wait(
410 waiter_callable=_image_client.wait_for_resource_deletion,
411 thing_id=image_id, thing_id_param='id',
412 cleanup_callable=self.delete_wrapper,
413 cleanup_args=[_image_client.delete_image, image_id])
414 _, snapshot_image = _image_client.get_image_meta(image_id)
415 image_name = snapshot_image['name']
416 self.assertEqual(name, image_name)
417 LOG.debug("Created snapshot image %s for server %s",
418 image_name, server['name'])
419 return snapshot_image
420
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900421 def nova_volume_attach(self):
422 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
423 _, volume_attachment = self.servers_client.attach_volume(
424 self.server['id'], self.volume['id'], '/dev/vdb')
425 volume = volume_attachment['volumeAttachment']
426 self.assertEqual(self.volume['id'], volume['id'])
427 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
428 # Refresh the volume after the attachment
429 _, self.volume = self.volumes_client.get_volume(volume['id'])
430
431 def nova_volume_detach(self):
432 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
433 self.volumes_client.wait_for_volume_status(self.volume['id'],
434 'available')
435
436 _, volume = self.volumes_client.get_volume(self.volume['id'])
437 self.assertEqual('available', volume['status'])
438
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700439 def rebuild_server(self, server_id, image=None,
440 preserve_ephemeral=False, wait=True,
441 rebuild_kwargs=None):
442 if image is None:
443 image = CONF.compute.image_ref
444
445 rebuild_kwargs = rebuild_kwargs or {}
446
447 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
448 server_id, image, preserve_ephemeral)
449 self.servers_client.rebuild(server_id=server_id, image_ref=image,
450 preserve_ephemeral=preserve_ephemeral,
451 **rebuild_kwargs)
452 if wait:
453 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
454
Aaron Rosena7df13b2014-09-23 09:45:45 -0700455 def ping_ip_address(self, ip_address, should_succeed=True):
456 cmd = ['ping', '-c1', '-w1', ip_address]
457
458 def ping():
459 proc = subprocess.Popen(cmd,
460 stdout=subprocess.PIPE,
461 stderr=subprocess.PIPE)
462 proc.communicate()
463 return (proc.returncode == 0) == should_succeed
464
465 return tempest.test.call_until_true(
466 ping, CONF.compute.ping_timeout, 1)
467
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100468
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100469class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300470 """Base class for network scenario tests.
471 This class provide helpers for network scenario tests, using the neutron
472 API. Helpers from ancestor which use the nova network API are overridden
473 with the neutron API.
474
475 This Class also enforces using Neutron instead of novanetwork.
476 Subclassed tests will be skipped if Neutron is not enabled
477
478 """
479
480 @classmethod
481 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100482 if not CONF.service_available.neutron:
483 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300484
485 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100486 def resource_setup(cls):
487 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300488 cls.tenant_id = cls.manager.identity_client.tenant_id
489 cls.check_preconditions()
490
Yair Frieddb6c9e92014-08-06 08:53:13 +0300491 def _create_network(self, client=None, tenant_id=None,
492 namestart='network-smoke-'):
493 if not client:
494 client = self.network_client
495 if not tenant_id:
496 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300497 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300498 _, result = client.create_network(name=name, tenant_id=tenant_id)
499 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300500 **result['network'])
501 self.assertEqual(network.name, name)
502 self.addCleanup(self.delete_wrapper, network.delete)
503 return network
504
505 def _list_networks(self, *args, **kwargs):
506 """List networks using admin creds """
507 return self._admin_lister('networks')(*args, **kwargs)
508
509 def _list_subnets(self, *args, **kwargs):
510 """List subnets using admin creds """
511 return self._admin_lister('subnets')(*args, **kwargs)
512
513 def _list_routers(self, *args, **kwargs):
514 """List routers using admin creds """
515 return self._admin_lister('routers')(*args, **kwargs)
516
517 def _list_ports(self, *args, **kwargs):
518 """List ports using admin creds """
519 return self._admin_lister('ports')(*args, **kwargs)
520
521 def _admin_lister(self, resource_type):
522 def temp(*args, **kwargs):
523 temp_method = self.admin_manager.network_client.__getattr__(
524 'list_%s' % resource_type)
525 _, resource_list = temp_method(*args, **kwargs)
526 return resource_list[resource_type]
527 return temp
528
Yair Frieddb6c9e92014-08-06 08:53:13 +0300529 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
530 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300531 """
532 Create a subnet for the given network within the cidr block
533 configured for tenant networks.
534 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300535 if not client:
536 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300537
538 def cidr_in_use(cidr, tenant_id):
539 """
540 :return True if subnet with cidr already exist in tenant
541 False else
542 """
543 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
544 return len(cidr_in_use) != 0
545
546 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
547 result = None
548 # Repeatedly attempt subnet creation with sequential cidr
549 # blocks until an unallocated block is found.
550 for subnet_cidr in tenant_cidr.subnet(
551 CONF.network.tenant_network_mask_bits):
552 str_cidr = str(subnet_cidr)
553 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
554 continue
555
556 subnet = dict(
557 name=data_utils.rand_name(namestart),
558 ip_version=4,
559 network_id=network.id,
560 tenant_id=network.tenant_id,
561 cidr=str_cidr,
562 **kwargs
563 )
564 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300565 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300566 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300567 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300568 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
569 if not is_overlapping_cidr:
570 raise
571 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300572 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300573 **result['subnet'])
574 self.assertEqual(subnet.cidr, str_cidr)
575 self.addCleanup(self.delete_wrapper, subnet.delete)
576 return subnet
577
Yair Frieddb6c9e92014-08-06 08:53:13 +0300578 def _create_port(self, network, client=None, namestart='port-quotatest'):
579 if not client:
580 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300581 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300582 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300583 name=name,
584 network_id=network.id,
585 tenant_id=network.tenant_id)
586 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300587 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300588 **result['port'])
589 self.addCleanup(self.delete_wrapper, port.delete)
590 return port
591
592 def _get_server_port_id(self, server, ip_addr=None):
593 ports = self._list_ports(device_id=server['id'],
594 fixed_ip=ip_addr)
595 self.assertEqual(len(ports), 1,
596 "Unable to determine which port to target.")
597 return ports[0]['id']
598
David Shrewsbury9bac3662014-08-07 15:07:01 -0400599 def _get_network_by_name(self, network_name):
600 net = self._list_networks(name=network_name)
601 return net_common.AttributeDict(net[0])
602
Yair Frieddb6c9e92014-08-06 08:53:13 +0300603 def _create_floating_ip(self, thing, external_network_id, port_id=None,
604 client=None):
605 if not client:
606 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300607 if not port_id:
608 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300609 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300610 floating_network_id=external_network_id,
611 port_id=port_id,
612 tenant_id=thing['tenant_id']
613 )
614 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300615 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300616 **result['floatingip'])
617 self.addCleanup(self.delete_wrapper, floating_ip.delete)
618 return floating_ip
619
620 def _associate_floating_ip(self, floating_ip, server):
621 port_id = self._get_server_port_id(server)
622 floating_ip.update(port_id=port_id)
623 self.assertEqual(port_id, floating_ip.port_id)
624 return floating_ip
625
626 def _disassociate_floating_ip(self, floating_ip):
627 """
628 :param floating_ip: type DeletableFloatingIp
629 """
630 floating_ip.update(port_id=None)
631 self.assertIsNone(floating_ip.port_id)
632 return floating_ip
633
Yair Fried1fc32a12014-08-04 09:11:30 +0300634 def _check_vm_connectivity(self, ip_address,
635 username=None,
636 private_key=None,
637 should_connect=True):
638 """
639 :param ip_address: server to test against
640 :param username: server's ssh username
641 :param private_key: server's ssh private key to be used
642 :param should_connect: True/False indicates positive/negative test
643 positive - attempt ping and ssh
644 negative - attempt ping and fail if succeed
645
646 :raises: AssertError if the result of the connectivity check does
647 not match the value of the should_connect param
648 """
649 if should_connect:
650 msg = "Timed out waiting for %s to become reachable" % ip_address
651 else:
652 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700653 self.assertTrue(self.ping_ip_address(ip_address,
654 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300655 msg=msg)
656 if should_connect:
657 # no need to check ssh for negative connectivity
658 self.get_remote_client(ip_address, username, private_key)
659
660 def _check_public_network_connectivity(self, ip_address, username,
661 private_key, should_connect=True,
662 msg=None, servers=None):
663 # The target login is assumed to have been configured for
664 # key-based authentication by cloud-init.
665 LOG.debug('checking network connections to IP %s with user: %s' %
666 (ip_address, username))
667 try:
668 self._check_vm_connectivity(ip_address,
669 username,
670 private_key,
671 should_connect=should_connect)
672 except Exception as e:
673 ex_msg = 'Public network connectivity check failed'
674 if msg:
675 ex_msg += ": " + msg
676 LOG.exception(ex_msg)
677 self._log_console_output(servers)
678 # network debug is called as part of ssh init
679 if not isinstance(e, exceptions.SSHTimeout):
680 debug.log_net_debug()
681 raise
682
683 def _check_tenant_network_connectivity(self, server,
684 username,
685 private_key,
686 should_connect=True,
687 servers_for_debug=None):
688 if not CONF.network.tenant_networks_reachable:
689 msg = 'Tenant networks not configured to be reachable.'
690 LOG.info(msg)
691 return
692 # The target login is assumed to have been configured for
693 # key-based authentication by cloud-init.
694 try:
695 for net_name, ip_addresses in server['networks'].iteritems():
696 for ip_address in ip_addresses:
697 self._check_vm_connectivity(ip_address,
698 username,
699 private_key,
700 should_connect=should_connect)
701 except Exception as e:
702 LOG.exception('Tenant network connectivity check failed')
703 self._log_console_output(servers_for_debug)
704 # network debug is called as part of ssh init
705 if not isinstance(e, exceptions.SSHTimeout):
706 debug.log_net_debug()
707 raise
708
709 def _check_remote_connectivity(self, source, dest, should_succeed=True):
710 """
711 check ping server via source ssh connection
712
713 :param source: RemoteClient: an ssh connection from which to ping
714 :param dest: and IP to ping against
715 :param should_succeed: boolean should ping succeed or not
716 :returns: boolean -- should_succeed == ping
717 :returns: ping is false if ping failed
718 """
719 def ping_remote():
720 try:
721 source.ping_host(dest)
722 except exceptions.SSHExecCommandFailed:
723 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
724 % (dest, source.ssh_client.host))
725 return not should_succeed
726 return should_succeed
727
728 return tempest.test.call_until_true(ping_remote,
729 CONF.compute.ping_timeout,
730 1)
731
Yair Frieddb6c9e92014-08-06 08:53:13 +0300732 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300733 namestart='secgroup-smoke'):
734 if client is None:
735 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300736 if tenant_id is None:
737 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300738 secgroup = self._create_empty_security_group(namestart=namestart,
739 client=client,
740 tenant_id=tenant_id)
741
742 # Add rules to the security group
743 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
744 for rule in rules:
745 self.assertEqual(tenant_id, rule.tenant_id)
746 self.assertEqual(secgroup.id, rule.security_group_id)
747 return secgroup
748
Yair Frieddb6c9e92014-08-06 08:53:13 +0300749 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300750 namestart='secgroup-smoke'):
751 """Create a security group without rules.
752
753 Default rules will be created:
754 - IPv4 egress to any
755 - IPv6 egress to any
756
757 :param tenant_id: secgroup will be created in this tenant
758 :returns: DeletableSecurityGroup -- containing the secgroup created
759 """
760 if client is None:
761 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300762 if not tenant_id:
763 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300764 sg_name = data_utils.rand_name(namestart)
765 sg_desc = sg_name + " description"
766 sg_dict = dict(name=sg_name,
767 description=sg_desc)
768 sg_dict['tenant_id'] = tenant_id
769 _, result = client.create_security_group(**sg_dict)
770 secgroup = net_resources.DeletableSecurityGroup(
771 client=client,
772 **result['security_group']
773 )
774 self.assertEqual(secgroup.name, sg_name)
775 self.assertEqual(tenant_id, secgroup.tenant_id)
776 self.assertEqual(secgroup.description, sg_desc)
777 self.addCleanup(self.delete_wrapper, secgroup.delete)
778 return secgroup
779
Yair Frieddb6c9e92014-08-06 08:53:13 +0300780 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300781 """Get default secgroup for given tenant_id.
782
783 :returns: DeletableSecurityGroup -- default secgroup for given tenant
784 """
785 if client is None:
786 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300787 if not tenant_id:
788 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300789 sgs = [
790 sg for sg in client.list_security_groups().values()[0]
791 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
792 ]
793 msg = "No default security group for tenant %s." % (tenant_id)
794 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300795 return net_resources.DeletableSecurityGroup(client=client,
796 **sgs[0])
797
Yair Frieddb6c9e92014-08-06 08:53:13 +0300798 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300799 tenant_id=None, **kwargs):
800 """Create a rule from a dictionary of rule parameters.
801
802 Create a rule in a secgroup. if secgroup not defined will search for
803 default secgroup in tenant_id.
804
805 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300806 :param tenant_id: if secgroup not passed -- the tenant in which to
807 search for default secgroup
808 :param kwargs: a dictionary containing rule parameters:
809 for example, to allow incoming ssh:
810 rule = {
811 direction: 'ingress'
812 protocol:'tcp',
813 port_range_min: 22,
814 port_range_max: 22
815 }
816 """
817 if client is None:
818 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300819 if not tenant_id:
820 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300821 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300822 secgroup = self._default_security_group(client=client,
823 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300824
825 ruleset = dict(security_group_id=secgroup.id,
826 tenant_id=secgroup.tenant_id)
827 ruleset.update(kwargs)
828
829 _, sg_rule = client.create_security_group_rule(**ruleset)
830 sg_rule = net_resources.DeletableSecurityGroupRule(
831 client=client,
832 **sg_rule['security_group_rule']
833 )
834 self.addCleanup(self.delete_wrapper, sg_rule.delete)
835 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
836 self.assertEqual(secgroup.id, sg_rule.security_group_id)
837
838 return sg_rule
839
840 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
841 """These rules are intended to permit inbound ssh and icmp
842 traffic from all sources, so no group_id is provided.
843 Setting a group_id would only permit traffic from ports
844 belonging to the same security group.
845 """
846
847 if client is None:
848 client = self.network_client
849 rules = []
850 rulesets = [
851 dict(
852 # ssh
853 protocol='tcp',
854 port_range_min=22,
855 port_range_max=22,
856 ),
857 dict(
858 # ping
859 protocol='icmp',
860 )
861 ]
862 for ruleset in rulesets:
863 for r_direction in ['ingress', 'egress']:
864 ruleset['direction'] = r_direction
865 try:
866 sg_rule = self._create_security_group_rule(
867 client=client, secgroup=secgroup, **ruleset)
868 except exceptions.Conflict as ex:
869 # if rule already exist - skip rule and continue
870 msg = 'Security group rule already exists'
871 if msg not in ex._error_string:
872 raise ex
873 else:
874 self.assertEqual(r_direction, sg_rule.direction)
875 rules.append(sg_rule)
876
877 return rules
878
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500879 def _create_pool(self, lb_method, protocol, subnet_id):
880 """Wrapper utility that returns a test pool."""
881 client = self.network_client
882 name = data_utils.rand_name('pool')
883 _, resp_pool = client.create_pool(protocol=protocol, name=name,
884 subnet_id=subnet_id,
885 lb_method=lb_method)
886 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
887 self.assertEqual(pool['name'], name)
888 self.addCleanup(self.delete_wrapper, pool.delete)
889 return pool
890
891 def _create_member(self, address, protocol_port, pool_id):
892 """Wrapper utility that returns a test member."""
893 client = self.network_client
894 _, resp_member = client.create_member(protocol_port=protocol_port,
895 pool_id=pool_id,
896 address=address)
897 member = net_resources.DeletableMember(client=client,
898 **resp_member['member'])
899 self.addCleanup(self.delete_wrapper, member.delete)
900 return member
901
902 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
903 """Wrapper utility that returns a test vip."""
904 client = self.network_client
905 name = data_utils.rand_name('vip')
906 _, resp_vip = client.create_vip(protocol=protocol, name=name,
907 subnet_id=subnet_id, pool_id=pool_id,
908 protocol_port=protocol_port)
909 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
910 self.assertEqual(vip['name'], name)
911 self.addCleanup(self.delete_wrapper, vip.delete)
912 return vip
913
Yair Fried1fc32a12014-08-04 09:11:30 +0300914 def _ssh_to_server(self, server, private_key):
915 ssh_login = CONF.compute.image_ssh_user
916 return self.get_remote_client(server,
917 username=ssh_login,
918 private_key=private_key)
919
Yair Frieddb6c9e92014-08-06 08:53:13 +0300920 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300921 """Retrieve a router for the given tenant id.
922
923 If a public router has been configured, it will be returned.
924
925 If a public router has not been configured, but a public
926 network has, a tenant router will be created and returned that
927 routes traffic to the public network.
928 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300929 if not client:
930 client = self.network_client
931 if not tenant_id:
932 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300933 router_id = CONF.network.public_router_id
934 network_id = CONF.network.public_network_id
935 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300936 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300937 return net_resources.AttributeDict(**result['router'])
938 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300939 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300940 router.set_gateway(network_id)
941 return router
942 else:
943 raise Exception("Neither of 'public_router_id' or "
944 "'public_network_id' has been defined.")
945
Yair Frieddb6c9e92014-08-06 08:53:13 +0300946 def _create_router(self, client=None, tenant_id=None,
947 namestart='router-smoke'):
948 if not client:
949 client = self.network_client
950 if not tenant_id:
951 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300952 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300953 _, result = client.create_router(name=name,
954 admin_state_up=True,
955 tenant_id=tenant_id)
956 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300957 **result['router'])
958 self.assertEqual(router.name, name)
959 self.addCleanup(self.delete_wrapper, router.delete)
960 return router
961
Yair Frieddb6c9e92014-08-06 08:53:13 +0300962 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300963 """Create a network with a subnet connected to a router.
964
David Shrewsbury9bac3662014-08-07 15:07:01 -0400965 The baremetal driver is a special case since all nodes are
966 on the same shared network.
967
Yair Fried1fc32a12014-08-04 09:11:30 +0300968 :returns: network, subnet, router
969 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400970 if CONF.baremetal.driver_enabled:
971 # NOTE(Shrews): This exception is for environments where tenant
972 # credential isolation is available, but network separation is
973 # not (the current baremetal case). Likely can be removed when
974 # test account mgmt is reworked:
975 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
976 network = self._get_network_by_name(
977 CONF.compute.fixed_network_name)
978 router = None
979 subnet = None
980 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300981 network = self._create_network(client=client, tenant_id=tenant_id)
982 router = self._get_router(client=client, tenant_id=tenant_id)
983 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400984 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300985 return network, subnet, router
986
987
David Shrewsbury06f7f8a2014-05-20 13:55:57 -0400988# power/provision states as of icehouse
989class BaremetalPowerStates(object):
990 """Possible power states of an Ironic node."""
991 POWER_ON = 'power on'
992 POWER_OFF = 'power off'
993 REBOOT = 'rebooting'
994 SUSPEND = 'suspended'
995
996
997class BaremetalProvisionStates(object):
998 """Possible provision states of an Ironic node."""
999 NOSTATE = None
1000 INIT = 'initializing'
1001 ACTIVE = 'active'
1002 BUILDING = 'building'
1003 DEPLOYWAIT = 'wait call-back'
1004 DEPLOYING = 'deploying'
1005 DEPLOYFAIL = 'deploy failed'
1006 DEPLOYDONE = 'deploy complete'
1007 DELETING = 'deleting'
1008 DELETED = 'deleted'
1009 ERROR = 'error'
1010
1011
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001012class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001013 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001014 def resource_setup(cls):
1015 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001016
1017 if (not CONF.service_available.ironic or
1018 not CONF.baremetal.driver_enabled):
1019 msg = 'Ironic not available or Ironic compute driver not enabled'
1020 raise cls.skipException(msg)
1021
1022 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001023 manager = clients.Manager(
1024 credentials=cls.admin_credentials()
1025 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001026 cls.baremetal_client = manager.baremetal_client
1027
1028 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001029 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001030
1031 def _node_state_timeout(self, node_id, state_attr,
1032 target_states, timeout=10, interval=1):
1033 if not isinstance(target_states, list):
1034 target_states = [target_states]
1035
1036 def check_state():
1037 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001038 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001039 return True
1040 return False
1041
1042 if not tempest.test.call_until_true(
1043 check_state, timeout, interval):
1044 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1045 (node_id, state_attr, target_states))
1046 raise exceptions.TimeoutException(msg)
1047
1048 def wait_provisioning_state(self, node_id, state, timeout):
1049 self._node_state_timeout(
1050 node_id=node_id, state_attr='provision_state',
1051 target_states=state, timeout=timeout)
1052
1053 def wait_power_state(self, node_id, state):
1054 self._node_state_timeout(
1055 node_id=node_id, state_attr='power_state',
1056 target_states=state, timeout=CONF.baremetal.power_timeout)
1057
1058 def wait_node(self, instance_id):
1059 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001060
Adam Gandelman4a48a602014-03-20 18:23:18 -07001061 def _get_node():
1062 node = None
1063 try:
1064 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001065 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001066 pass
1067 return node is not None
1068
1069 if not tempest.test.call_until_true(
1070 _get_node, CONF.baremetal.association_timeout, 1):
1071 msg = ('Timed out waiting to get Ironic node by instance id %s'
1072 % instance_id)
1073 raise exceptions.TimeoutException(msg)
1074
1075 def get_node(self, node_id=None, instance_id=None):
1076 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001077 _, body = self.baremetal_client.show_node(node_id)
1078 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001079 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001080 _, body = self.baremetal_client.show_node_by_instance_uuid(
1081 instance_id)
1082 if body['nodes']:
1083 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001084
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001085 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001086 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001087 _, body = self.baremetal_client.list_node_ports(node_uuid)
1088 for port in body['ports']:
1089 _, p = self.baremetal_client.show_port(port['uuid'])
1090 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001091 return ports
1092
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001093 def add_keypair(self):
1094 self.keypair = self.create_keypair()
1095
1096 def verify_connectivity(self, ip=None):
1097 if ip:
1098 dest = self.get_remote_client(ip)
1099 else:
1100 dest = self.get_remote_client(self.instance)
1101 dest.validate_authentication()
1102
1103 def boot_instance(self):
1104 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001105 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001106 }
1107 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001108 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001109
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001110 self.wait_node(self.instance['id'])
1111 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001112
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001113 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001114
1115 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001116 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001117 [BaremetalProvisionStates.DEPLOYWAIT,
1118 BaremetalProvisionStates.ACTIVE],
1119 timeout=15)
1120
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001121 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001122 BaremetalProvisionStates.ACTIVE,
1123 timeout=CONF.baremetal.active_timeout)
1124
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001125 self.servers_client.wait_for_server_status(self.instance['id'],
1126 'ACTIVE')
1127 self.node = self.get_node(instance_id=self.instance['id'])
1128 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001129
1130 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001131 self.servers_client.delete_server(self.instance['id'])
1132 self.wait_power_state(self.node['uuid'],
1133 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001134 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001135 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001136 BaremetalProvisionStates.NOSTATE,
1137 timeout=CONF.baremetal.unprovision_timeout)
1138
Adam Gandelman4a48a602014-03-20 18:23:18 -07001139
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001140class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001141 """
1142 Base class for encryption scenario tests
1143 """
1144
1145 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001146 def resource_setup(cls):
1147 super(EncryptionScenarioTest, cls).resource_setup()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001148 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001149
1150 def _wait_for_volume_status(self, status):
1151 self.status_timeout(
1152 self.volume_client.volumes, self.volume.id, status)
1153
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001154 def nova_boot(self):
1155 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001156 create_kwargs = {'key_name': self.keypair['name']}
1157 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001158 create_kwargs=create_kwargs)
1159
1160 def create_volume_type(self, client=None, name=None):
1161 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001162 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001163 if not name:
1164 name = 'generic'
1165 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1166 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001167 _, body = client.create_volume_type(
1168 randomized_name)
1169 self.assertIn('id', body)
1170 self.addCleanup(client.delete_volume_type, body['id'])
1171 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001172
1173 def create_encryption_type(self, client=None, type_id=None, provider=None,
1174 key_size=None, cipher=None,
1175 control_location=None):
1176 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001177 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001178 if not type_id:
1179 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001180 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001181 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001182 client.create_encryption_type(
1183 type_id, provider=provider, key_size=key_size, cipher=cipher,
1184 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001185
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001186
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001187class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001188 """
1189 Base class for orchestration scenario tests
1190 """
1191
1192 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001193 def resource_setup(cls):
1194 super(OrchestrationScenarioTest, cls).resource_setup()
Matthew Treinish6c072292014-01-29 19:15:52 +00001195 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001196 raise cls.skipException("Heat support is required")
1197
1198 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001199 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001200 admin_creds = auth.get_default_credentials('identity_admin')
1201 creds = auth.get_default_credentials('user')
1202 admin_creds.tenant_name = creds.tenant_name
1203 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001204
1205 def _load_template(self, base_file, file_name):
1206 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1207 file_name)
1208 with open(filepath) as f:
1209 return f.read()
1210
1211 @classmethod
1212 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001213 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001214
1215 @classmethod
1216 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001217 _, networks = cls.networks_client.list_networks()
1218 for net in networks:
1219 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001220 return net
Steve Baker22c16602014-05-05 13:34:19 +12001221
1222 @staticmethod
1223 def _stack_output(stack, output_key):
1224 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001225 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001226 if o['output_key'] == output_key), None)
1227
Chris Dent0d494112014-08-26 13:48:30 +01001228
1229class SwiftScenarioTest(ScenarioTest):
1230 """
1231 Provide harness to do Swift scenario tests.
1232
1233 Subclasses implement the tests that use the methods provided by this
1234 class.
1235 """
1236
1237 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001238 def resource_setup(cls):
Chris Dent0d494112014-08-26 13:48:30 +01001239 cls.set_network_resources()
Andrea Frittoliac20b5e2014-09-15 13:31:14 +01001240 super(SwiftScenarioTest, cls).resource_setup()
Chris Dent0d494112014-08-26 13:48:30 +01001241 if not CONF.service_available.swift:
1242 skip_msg = ("%s skipped as swift is not available" %
1243 cls.__name__)
1244 raise cls.skipException(skip_msg)
1245 # Clients for Swift
1246 cls.account_client = cls.manager.account_client
1247 cls.container_client = cls.manager.container_client
1248 cls.object_client = cls.manager.object_client
1249
Chris Dentde456a12014-09-10 12:41:15 +01001250 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001251 """get swift status for our user account."""
1252 self.account_client.list_account_containers()
1253 LOG.debug('Swift status information obtained successfully')
1254
Chris Dentde456a12014-09-10 12:41:15 +01001255 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001256 name = container_name or data_utils.rand_name(
1257 'swift-scenario-container')
1258 self.container_client.create_container(name)
1259 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001260 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001261 LOG.debug('Container %s created' % (name))
1262 return name
1263
Chris Dentde456a12014-09-10 12:41:15 +01001264 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001265 self.container_client.delete_container(container_name)
1266 LOG.debug('Container %s deleted' % (container_name))
1267
Chris Dentde456a12014-09-10 12:41:15 +01001268 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001269 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1270 obj_data = data_utils.arbitrary_string()
1271 self.object_client.create_object(container_name, obj_name, obj_data)
1272 return obj_name, obj_data
1273
Chris Dentde456a12014-09-10 12:41:15 +01001274 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001275 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001276 self.list_and_check_container_objects(container_name,
1277 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001278
Chris Dentde456a12014-09-10 12:41:15 +01001279 def list_and_check_container_objects(self, container_name,
1280 present_obj=None,
1281 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001282 """
1283 List objects for a given container and assert which are present and
1284 which are not.
1285 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001286 if present_obj is None:
1287 present_obj = []
1288 if not_present_obj is None:
1289 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001290 _, object_list = self.container_client.list_container_contents(
1291 container_name)
1292 if present_obj:
1293 for obj in present_obj:
1294 self.assertIn(obj, object_list)
1295 if not_present_obj:
1296 for obj in not_present_obj:
1297 self.assertNotIn(obj, object_list)
1298
Chris Dentde456a12014-09-10 12:41:15 +01001299 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001300 metadata_param = {'metadata_prefix': 'x-container-',
1301 'metadata': {'read': acl}}
1302 self.container_client.update_container_metadata(container_name,
1303 **metadata_param)
1304 resp, _ = self.container_client.list_container_metadata(container_name)
1305 self.assertEqual(resp['x-container-read'], acl)
1306
Chris Dentde456a12014-09-10 12:41:15 +01001307 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001308 _, obj = self.object_client.get_object(container_name, obj_name)
1309 self.assertEqual(obj, expected_data)