blob: 6e35a31a9e8dce502ca6df8137dc238c8d453a9f [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
Steve Baker22c16602014-05-05 13:34:19 +120019import re
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import subprocess
Steve Baker22c16602014-05-05 13:34:19 +120021import time
Sean Dague6dbc6da2013-05-08 17:49:46 -040022
Kaitlin Farr366a51f2014-04-21 12:43:54 -040023from cinderclient import exceptions as cinder_exceptions
Matthew Treinishb7144eb2013-12-13 22:57:35 +000024import glanceclient
Steve Baker22c16602014-05-05 13:34:19 +120025from heatclient import exc as heat_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040026import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040027from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090028from novaclient import exceptions as nova_exceptions
Matthew Treinish96e9e882014-06-09 18:37:19 -040029import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040030
Sean Dague1937d092013-05-17 16:36:38 -040031from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000032from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000033from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070034from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040035from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090036from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090037from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000038from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020039from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020040from tempest.openstack.common import log
Steve Baker22c16602014-05-05 13:34:19 +120041from tempest.openstack.common import timeutils
Yair Fried1fc32a12014-08-04 09:11:30 +030042from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040043import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040044
Matthew Treinish6c072292014-01-29 19:15:52 +000045CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
Attila Fazekasfb7552a2013-08-27 13:02:26 +020047LOG = log.getLogger(__name__)
48
49# NOTE(afazekas): Workaround for the stdout logging
50LOG_nova_client = logging.getLogger('novaclient.client')
51LOG_nova_client.addHandler(log.NullHandler())
52
53LOG_cinder_client = logging.getLogger('cinderclient.client')
54LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040055
56
Andrea Frittoli2e733b52014-07-16 14:12:11 +010057class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli247058f2014-07-16 16:09:22 +010058 """Replaces the OfficialClientTest base class.
59
60 Uses tempest own clients as opposed to OfficialClients.
61
62 Common differences:
63 - replace resource.attribute with resource['attribute']
64 - replace resouce.delete with delete_callable(resource['id'])
65 - replace local waiters with common / rest_client waiters
66 """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010067
68 @classmethod
69 def setUpClass(cls):
70 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010071 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010072 cls.isolated_creds = isolated_creds.IsolatedCreds(
73 cls.__name__, tempest_client=True,
74 network_resources=cls.network_resources)
75 cls.manager = clients.Manager(
76 credentials=cls.credentials()
77 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010078 cls.admin_manager = clients.Manager(cls.admin_credentials())
79 # Clients (in alphabetical order)
80 cls.floating_ips_client = cls.manager.floating_ips_client
81 # Glance image client v1
82 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000083 # Compute image client
84 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010085 cls.keypairs_client = cls.manager.keypairs_client
86 cls.networks_client = cls.admin_manager.networks_client
87 # Nova security groups client
88 cls.security_groups_client = cls.manager.security_groups_client
89 cls.servers_client = cls.manager.servers_client
90 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000091 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030092 cls.interface_client = cls.manager.interfaces_client
93 # Neutron network client
94 cls.network_client = cls.manager.network_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010095
96 @classmethod
97 def _get_credentials(cls, get_creds, ctype):
98 if CONF.compute.allow_tenant_isolation:
99 creds = get_creds()
100 else:
101 creds = auth.get_default_credentials(ctype)
102 return creds
103
104 @classmethod
105 def credentials(cls):
106 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
107 'user')
108
Masayuki Igawaccd66592014-07-17 00:42:42 +0900109 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +0300110 def alt_credentials(cls):
111 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
112 'alt_user')
113
114 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900115 def admin_credentials(cls):
116 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
117 'identity_admin')
118
Andrea Frittoli247058f2014-07-16 16:09:22 +0100119 # ## Methods to handle sync and async deletes
120
121 def setUp(self):
122 super(ScenarioTest, self).setUp()
123 self.cleanup_waits = []
124 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
125 # because scenario tests in the same test class should not share
126 # resources. If resources were shared between test cases then it
127 # should be a single scenario test instead of multiples.
128
129 # NOTE(yfried): this list is cleaned at the end of test_methods and
130 # not at the end of the class
131 self.addCleanup(self._wait_for_cleanups)
132
Yair Fried1fc32a12014-08-04 09:11:30 +0300133 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100134 """Ignores NotFound exceptions for delete operations.
135
Yair Fried1fc32a12014-08-04 09:11:30 +0300136 @param delete_thing: delete method of a resource. method will be
137 executed as delete_thing(*args, **kwargs)
138
Andrea Frittoli247058f2014-07-16 16:09:22 +0100139 """
140 try:
141 # Tempest clients return dicts, so there is no common delete
142 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300143 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100144 except exceptions.NotFound:
145 # If the resource is already missing, mission accomplished.
146 pass
147
148 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900149 cleanup_callable, cleanup_args=None,
150 cleanup_kwargs=None, ignore_error=True):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100151 """Adds wait for ansyc resource deletion at the end of cleanups
152
153 @param waiter_callable: callable to wait for the resource to delete
154 @param thing_id: the id of the resource to be cleaned-up
155 @param thing_id_param: the name of the id param in the waiter
156 @param cleanup_callable: method to load pass to self.addCleanup with
157 the following *cleanup_args, **cleanup_kwargs.
158 usually a delete method.
159 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900160 if cleanup_args is None:
161 cleanup_args = []
162 if cleanup_kwargs is None:
163 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100164 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
165 wait_dict = {
166 'waiter_callable': waiter_callable,
167 thing_id_param: thing_id
168 }
169 self.cleanup_waits.append(wait_dict)
170
171 def _wait_for_cleanups(self):
172 """To handle async delete actions, a list of waits is added
173 which will be iterated over as the last step of clearing the
174 cleanup queue. That way all the delete calls are made up front
175 and the tests won't succeed unless the deletes are eventually
176 successful. This is the same basic approach used in the api tests to
177 limit cleanup execution time except here it is multi-resource,
178 because of the nature of the scenario tests.
179 """
180 for wait in self.cleanup_waits:
181 waiter_callable = wait.pop('waiter_callable')
182 waiter_callable(**wait)
183
184 # ## Test functions library
185 #
186 # The create_[resource] functions only return body and discard the
187 # resp part which is not used in scenario tests
188
Yair Frieddb6c9e92014-08-06 08:53:13 +0300189 def create_keypair(self, client=None):
190 if not client:
191 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100192 name = data_utils.rand_name(self.__class__.__name__)
193 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300194 resp, body = client.create_keypair(name)
195 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100196 return body
197
198 def create_server(self, name=None, image=None, flavor=None,
199 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900200 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100201 """Creates VM instance.
202
203 @param image: image from which to create the instance
204 @param wait_on_boot: wait for status ACTIVE before continue
205 @param wait_on_delete: force synchronous delete on cleanup
206 @param create_kwargs: additional details for instance creation
207 @return: server dict
208 """
209 if name is None:
210 name = data_utils.rand_name(self.__class__.__name__)
211 if image is None:
212 image = CONF.compute.image_ref
213 if flavor is None:
214 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900215 if create_kwargs is None:
216 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100217
218 fixed_network_name = CONF.compute.fixed_network_name
219 if 'nics' not in create_kwargs and fixed_network_name:
220 _, networks = self.networks_client.list_networks()
221 # If several networks found, set the NetID on which to connect the
222 # server to avoid the following error "Multiple possible networks
223 # found, use a Network ID to be more specific."
224 # See Tempest #1250866
225 if len(networks) > 1:
226 for network in networks:
227 if network['label'] == fixed_network_name:
228 create_kwargs['nics'] = [{'net-id': network['id']}]
229 break
230 # If we didn't find the network we were looking for :
231 else:
232 msg = ("The network on which the NIC of the server must "
233 "be connected can not be found : "
234 "fixed_network_name=%s. Starting instance without "
235 "specifying a network.") % fixed_network_name
236 LOG.info(msg)
237
238 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
239 name, image, flavor)
240 _, server = self.servers_client.create_server(name, image, flavor,
241 **create_kwargs)
242 if wait_on_delete:
243 self.addCleanup(self.servers_client.wait_for_server_termination,
244 server['id'])
245 self.addCleanup_with_wait(
246 waiter_callable=self.servers_client.wait_for_server_termination,
247 thing_id=server['id'], thing_id_param='server_id',
248 cleanup_callable=self.delete_wrapper,
249 cleanup_args=[self.servers_client.delete_server, server['id']])
250 if wait_on_boot:
251 self.servers_client.wait_for_server_status(server_id=server['id'],
252 status='ACTIVE')
253 # The instance retrieved on creation is missing network
254 # details, necessitating retrieval after it becomes active to
255 # ensure correct details.
256 _, server = self.servers_client.get_server(server['id'])
257 self.assertEqual(server['name'], name)
258 return server
259
260 def create_volume(self, size=1, name=None, snapshot_id=None,
261 imageRef=None, volume_type=None, wait_on_delete=True):
262 if name is None:
263 name = data_utils.rand_name(self.__class__.__name__)
264 _, volume = self.volumes_client.create_volume(
265 size=size, display_name=name, snapshot_id=snapshot_id,
266 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700267
Andrea Frittoli247058f2014-07-16 16:09:22 +0100268 if wait_on_delete:
269 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
270 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700271 self.addCleanup(self.delete_wrapper,
272 self.volumes_client.delete_volume, volume['id'])
273 else:
274 self.addCleanup_with_wait(
275 waiter_callable=self.volumes_client.wait_for_resource_deletion,
276 thing_id=volume['id'], thing_id_param='id',
277 cleanup_callable=self.delete_wrapper,
278 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100279
280 self.assertEqual(name, volume['display_name'])
281 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
282 # The volume retrieved on creation has a non-up-to-date status.
283 # Retrieval after it becomes active ensures correct details.
284 _, volume = self.volumes_client.get_volume(volume['id'])
285 return volume
286
Yair Fried1fc32a12014-08-04 09:11:30 +0300287 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100288 _client = self.security_groups_client
289 if secgroup_id is None:
290 _, sgs = _client.list_security_groups()
291 for sg in sgs:
292 if sg['name'] == 'default':
293 secgroup_id = sg['id']
294
295 # These rules are intended to permit inbound ssh and icmp
296 # traffic from all sources, so no group_id is provided.
297 # Setting a group_id would only permit traffic from ports
298 # belonging to the same security group.
299 rulesets = [
300 {
301 # ssh
302 'ip_proto': 'tcp',
303 'from_port': 22,
304 'to_port': 22,
305 'cidr': '0.0.0.0/0',
306 },
307 {
308 # ping
309 'ip_proto': 'icmp',
310 'from_port': -1,
311 'to_port': -1,
312 'cidr': '0.0.0.0/0',
313 }
314 ]
315 rules = list()
316 for ruleset in rulesets:
317 _, sg_rule = _client.create_security_group_rule(secgroup_id,
318 **ruleset)
319 self.addCleanup(self.delete_wrapper,
320 _client.delete_security_group_rule,
321 sg_rule['id'])
322 rules.append(sg_rule)
323 return rules
324
Yair Fried1fc32a12014-08-04 09:11:30 +0300325 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100326 # Create security group
327 sg_name = data_utils.rand_name(self.__class__.__name__)
328 sg_desc = sg_name + " description"
329 _, secgroup = self.security_groups_client.create_security_group(
330 sg_name, sg_desc)
331 self.assertEqual(secgroup['name'], sg_name)
332 self.assertEqual(secgroup['description'], sg_desc)
333 self.addCleanup(self.delete_wrapper,
334 self.security_groups_client.delete_security_group,
335 secgroup['id'])
336
337 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300338 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100339
340 return secgroup
341
342 def get_remote_client(self, server_or_ip, username=None, private_key=None):
343 if isinstance(server_or_ip, six.string_types):
344 ip = server_or_ip
345 else:
346 network_name_for_ssh = CONF.compute.network_for_ssh
347 ip = server_or_ip.networks[network_name_for_ssh][0]
348 if username is None:
349 username = CONF.scenario.ssh_user
350 if private_key is None:
351 private_key = self.keypair['private_key']
352 linux_client = remote_client.RemoteClient(ip, username,
353 pkey=private_key)
354 try:
355 linux_client.validate_authentication()
356 except exceptions.SSHTimeout:
357 LOG.exception('ssh connection to %s failed' % ip)
358 debug.log_net_debug()
359 raise
360
361 return linux_client
362
Ghanshyam2a180b82014-06-16 13:54:22 +0900363 def _image_create(self, name, fmt, path, properties=None):
364 if properties is None:
365 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100366 name = data_utils.rand_name('%s-' % name)
367 image_file = open(path, 'rb')
368 self.addCleanup(image_file.close)
369 params = {
370 'name': name,
371 'container_format': fmt,
372 'disk_format': fmt,
373 'is_public': 'False',
374 }
375 params.update(properties)
376 _, image = self.image_client.create_image(**params)
377 self.addCleanup(self.image_client.delete_image, image['id'])
378 self.assertEqual("queued", image['status'])
379 self.image_client.update_image(image['id'], data=image_file)
380 return image['id']
381
382 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300383 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100384 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
385 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
386 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300387 img_container_format = CONF.scenario.img_container_format
388 img_disk_format = CONF.scenario.img_disk_format
389 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
390 "ami: %s, ari: %s, aki: %s" %
391 (img_path, img_container_format, img_disk_format,
392 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100393 try:
394 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300395 img_container_format,
396 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100397 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300398 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100399 except IOError:
400 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
401 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
402 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
403 properties = {
404 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
405 }
406 self.image = self._image_create('scenario-ami', 'ami',
407 path=ami_img_path,
408 properties=properties)
409 LOG.debug("image:%s" % self.image)
410
411 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400412 if not CONF.compute_feature_enabled.console_output:
413 LOG.debug('Console output not supported, cannot log')
414 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100415 if not servers:
416 _, servers = self.servers_client.list_servers()
417 servers = servers['servers']
418 for server in servers:
419 LOG.debug('Console output for %s', server['id'])
420 LOG.debug(self.servers_client.get_console_output(server['id'],
421 length=None))
422
nithya-ganesan882595e2014-07-29 18:51:07 +0000423 def create_server_snapshot(self, server, name=None):
424 # Glance client
425 _image_client = self.image_client
426 # Compute client
427 _images_client = self.images_client
428 if name is None:
429 name = data_utils.rand_name('scenario-snapshot-')
430 LOG.debug("Creating a snapshot image for server: %s", server['name'])
431 resp, image = _images_client.create_image(server['id'], name)
432 image_id = resp['location'].split('images/')[1]
433 _image_client.wait_for_image_status(image_id, 'active')
434 self.addCleanup_with_wait(
435 waiter_callable=_image_client.wait_for_resource_deletion,
436 thing_id=image_id, thing_id_param='id',
437 cleanup_callable=self.delete_wrapper,
438 cleanup_args=[_image_client.delete_image, image_id])
439 _, snapshot_image = _image_client.get_image_meta(image_id)
440 image_name = snapshot_image['name']
441 self.assertEqual(name, image_name)
442 LOG.debug("Created snapshot image %s for server %s",
443 image_name, server['name'])
444 return snapshot_image
445
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900446 def nova_volume_attach(self):
447 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
448 _, volume_attachment = self.servers_client.attach_volume(
449 self.server['id'], self.volume['id'], '/dev/vdb')
450 volume = volume_attachment['volumeAttachment']
451 self.assertEqual(self.volume['id'], volume['id'])
452 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
453 # Refresh the volume after the attachment
454 _, self.volume = self.volumes_client.get_volume(volume['id'])
455
456 def nova_volume_detach(self):
457 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
458 self.volumes_client.wait_for_volume_status(self.volume['id'],
459 'available')
460
461 _, volume = self.volumes_client.get_volume(self.volume['id'])
462 self.assertEqual('available', volume['status'])
463
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100464
Yair Fried1fc32a12014-08-04 09:11:30 +0300465# TODO(yfried): change this class name to NetworkScenarioTest once client
466# migration is complete
467class NeutronScenarioTest(ScenarioTest):
468 """Base class for network scenario tests.
469 This class provide helpers for network scenario tests, using the neutron
470 API. Helpers from ancestor which use the nova network API are overridden
471 with the neutron API.
472
473 This Class also enforces using Neutron instead of novanetwork.
474 Subclassed tests will be skipped if Neutron is not enabled
475
476 """
477
478 @classmethod
479 def check_preconditions(cls):
480 if CONF.service_available.neutron:
481 cls.enabled = True
482 # verify that neutron_available is telling the truth
483 try:
484 cls.network_client.list_networks()
485 except exc.EndpointNotFound:
486 cls.enabled = False
487 raise
488 else:
489 cls.enabled = False
490 msg = 'Neutron not available'
491 raise cls.skipException(msg)
492
493 @classmethod
494 def setUpClass(cls):
495 super(NeutronScenarioTest, cls).setUpClass()
496 cls.tenant_id = cls.manager.identity_client.tenant_id
497 cls.check_preconditions()
498
Yair Frieddb6c9e92014-08-06 08:53:13 +0300499 def _create_network(self, client=None, tenant_id=None,
500 namestart='network-smoke-'):
501 if not client:
502 client = self.network_client
503 if not tenant_id:
504 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300505 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300506 _, result = client.create_network(name=name, tenant_id=tenant_id)
507 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300508 **result['network'])
509 self.assertEqual(network.name, name)
510 self.addCleanup(self.delete_wrapper, network.delete)
511 return network
512
513 def _list_networks(self, *args, **kwargs):
514 """List networks using admin creds """
515 return self._admin_lister('networks')(*args, **kwargs)
516
517 def _list_subnets(self, *args, **kwargs):
518 """List subnets using admin creds """
519 return self._admin_lister('subnets')(*args, **kwargs)
520
521 def _list_routers(self, *args, **kwargs):
522 """List routers using admin creds """
523 return self._admin_lister('routers')(*args, **kwargs)
524
525 def _list_ports(self, *args, **kwargs):
526 """List ports using admin creds """
527 return self._admin_lister('ports')(*args, **kwargs)
528
529 def _admin_lister(self, resource_type):
530 def temp(*args, **kwargs):
531 temp_method = self.admin_manager.network_client.__getattr__(
532 'list_%s' % resource_type)
533 _, resource_list = temp_method(*args, **kwargs)
534 return resource_list[resource_type]
535 return temp
536
Yair Frieddb6c9e92014-08-06 08:53:13 +0300537 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
538 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300539 """
540 Create a subnet for the given network within the cidr block
541 configured for tenant networks.
542 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300543 if not client:
544 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300545
546 def cidr_in_use(cidr, tenant_id):
547 """
548 :return True if subnet with cidr already exist in tenant
549 False else
550 """
551 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
552 return len(cidr_in_use) != 0
553
554 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
555 result = None
556 # Repeatedly attempt subnet creation with sequential cidr
557 # blocks until an unallocated block is found.
558 for subnet_cidr in tenant_cidr.subnet(
559 CONF.network.tenant_network_mask_bits):
560 str_cidr = str(subnet_cidr)
561 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
562 continue
563
564 subnet = dict(
565 name=data_utils.rand_name(namestart),
566 ip_version=4,
567 network_id=network.id,
568 tenant_id=network.tenant_id,
569 cidr=str_cidr,
570 **kwargs
571 )
572 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300573 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300574 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300575 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300576 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
577 if not is_overlapping_cidr:
578 raise
579 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300580 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300581 **result['subnet'])
582 self.assertEqual(subnet.cidr, str_cidr)
583 self.addCleanup(self.delete_wrapper, subnet.delete)
584 return subnet
585
Yair Frieddb6c9e92014-08-06 08:53:13 +0300586 def _create_port(self, network, client=None, namestart='port-quotatest'):
587 if not client:
588 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300589 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300590 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300591 name=name,
592 network_id=network.id,
593 tenant_id=network.tenant_id)
594 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300595 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300596 **result['port'])
597 self.addCleanup(self.delete_wrapper, port.delete)
598 return port
599
600 def _get_server_port_id(self, server, ip_addr=None):
601 ports = self._list_ports(device_id=server['id'],
602 fixed_ip=ip_addr)
603 self.assertEqual(len(ports), 1,
604 "Unable to determine which port to target.")
605 return ports[0]['id']
606
David Shrewsbury9bac3662014-08-07 15:07:01 -0400607 def _get_network_by_name(self, network_name):
608 net = self._list_networks(name=network_name)
609 return net_common.AttributeDict(net[0])
610
Yair Frieddb6c9e92014-08-06 08:53:13 +0300611 def _create_floating_ip(self, thing, external_network_id, port_id=None,
612 client=None):
613 if not client:
614 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300615 if not port_id:
616 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300617 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300618 floating_network_id=external_network_id,
619 port_id=port_id,
620 tenant_id=thing['tenant_id']
621 )
622 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300623 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300624 **result['floatingip'])
625 self.addCleanup(self.delete_wrapper, floating_ip.delete)
626 return floating_ip
627
628 def _associate_floating_ip(self, floating_ip, server):
629 port_id = self._get_server_port_id(server)
630 floating_ip.update(port_id=port_id)
631 self.assertEqual(port_id, floating_ip.port_id)
632 return floating_ip
633
634 def _disassociate_floating_ip(self, floating_ip):
635 """
636 :param floating_ip: type DeletableFloatingIp
637 """
638 floating_ip.update(port_id=None)
639 self.assertIsNone(floating_ip.port_id)
640 return floating_ip
641
642 def _ping_ip_address(self, ip_address, should_succeed=True):
643 cmd = ['ping', '-c1', '-w1', ip_address]
644
645 def ping():
646 proc = subprocess.Popen(cmd,
647 stdout=subprocess.PIPE,
648 stderr=subprocess.PIPE)
649 proc.wait()
650 return (proc.returncode == 0) == should_succeed
651
652 return tempest.test.call_until_true(
653 ping, CONF.compute.ping_timeout, 1)
654
655 def _check_vm_connectivity(self, ip_address,
656 username=None,
657 private_key=None,
658 should_connect=True):
659 """
660 :param ip_address: server to test against
661 :param username: server's ssh username
662 :param private_key: server's ssh private key to be used
663 :param should_connect: True/False indicates positive/negative test
664 positive - attempt ping and ssh
665 negative - attempt ping and fail if succeed
666
667 :raises: AssertError if the result of the connectivity check does
668 not match the value of the should_connect param
669 """
670 if should_connect:
671 msg = "Timed out waiting for %s to become reachable" % ip_address
672 else:
673 msg = "ip address %s is reachable" % ip_address
674 self.assertTrue(self._ping_ip_address(ip_address,
675 should_succeed=should_connect),
676 msg=msg)
677 if should_connect:
678 # no need to check ssh for negative connectivity
679 self.get_remote_client(ip_address, username, private_key)
680
681 def _check_public_network_connectivity(self, ip_address, username,
682 private_key, should_connect=True,
683 msg=None, servers=None):
684 # The target login is assumed to have been configured for
685 # key-based authentication by cloud-init.
686 LOG.debug('checking network connections to IP %s with user: %s' %
687 (ip_address, username))
688 try:
689 self._check_vm_connectivity(ip_address,
690 username,
691 private_key,
692 should_connect=should_connect)
693 except Exception as e:
694 ex_msg = 'Public network connectivity check failed'
695 if msg:
696 ex_msg += ": " + msg
697 LOG.exception(ex_msg)
698 self._log_console_output(servers)
699 # network debug is called as part of ssh init
700 if not isinstance(e, exceptions.SSHTimeout):
701 debug.log_net_debug()
702 raise
703
704 def _check_tenant_network_connectivity(self, server,
705 username,
706 private_key,
707 should_connect=True,
708 servers_for_debug=None):
709 if not CONF.network.tenant_networks_reachable:
710 msg = 'Tenant networks not configured to be reachable.'
711 LOG.info(msg)
712 return
713 # The target login is assumed to have been configured for
714 # key-based authentication by cloud-init.
715 try:
716 for net_name, ip_addresses in server['networks'].iteritems():
717 for ip_address in ip_addresses:
718 self._check_vm_connectivity(ip_address,
719 username,
720 private_key,
721 should_connect=should_connect)
722 except Exception as e:
723 LOG.exception('Tenant network connectivity check failed')
724 self._log_console_output(servers_for_debug)
725 # network debug is called as part of ssh init
726 if not isinstance(e, exceptions.SSHTimeout):
727 debug.log_net_debug()
728 raise
729
730 def _check_remote_connectivity(self, source, dest, should_succeed=True):
731 """
732 check ping server via source ssh connection
733
734 :param source: RemoteClient: an ssh connection from which to ping
735 :param dest: and IP to ping against
736 :param should_succeed: boolean should ping succeed or not
737 :returns: boolean -- should_succeed == ping
738 :returns: ping is false if ping failed
739 """
740 def ping_remote():
741 try:
742 source.ping_host(dest)
743 except exceptions.SSHExecCommandFailed:
744 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
745 % (dest, source.ssh_client.host))
746 return not should_succeed
747 return should_succeed
748
749 return tempest.test.call_until_true(ping_remote,
750 CONF.compute.ping_timeout,
751 1)
752
Yair Frieddb6c9e92014-08-06 08:53:13 +0300753 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300754 namestart='secgroup-smoke'):
755 if client is None:
756 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300757 if tenant_id is None:
758 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300759 secgroup = self._create_empty_security_group(namestart=namestart,
760 client=client,
761 tenant_id=tenant_id)
762
763 # Add rules to the security group
764 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
765 for rule in rules:
766 self.assertEqual(tenant_id, rule.tenant_id)
767 self.assertEqual(secgroup.id, rule.security_group_id)
768 return secgroup
769
Yair Frieddb6c9e92014-08-06 08:53:13 +0300770 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300771 namestart='secgroup-smoke'):
772 """Create a security group without rules.
773
774 Default rules will be created:
775 - IPv4 egress to any
776 - IPv6 egress to any
777
778 :param tenant_id: secgroup will be created in this tenant
779 :returns: DeletableSecurityGroup -- containing the secgroup created
780 """
781 if client is None:
782 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300783 if not tenant_id:
784 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 sg_name = data_utils.rand_name(namestart)
786 sg_desc = sg_name + " description"
787 sg_dict = dict(name=sg_name,
788 description=sg_desc)
789 sg_dict['tenant_id'] = tenant_id
790 _, result = client.create_security_group(**sg_dict)
791 secgroup = net_resources.DeletableSecurityGroup(
792 client=client,
793 **result['security_group']
794 )
795 self.assertEqual(secgroup.name, sg_name)
796 self.assertEqual(tenant_id, secgroup.tenant_id)
797 self.assertEqual(secgroup.description, sg_desc)
798 self.addCleanup(self.delete_wrapper, secgroup.delete)
799 return secgroup
800
Yair Frieddb6c9e92014-08-06 08:53:13 +0300801 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300802 """Get default secgroup for given tenant_id.
803
804 :returns: DeletableSecurityGroup -- default secgroup for given tenant
805 """
806 if client is None:
807 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300808 if not tenant_id:
809 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300810 sgs = [
811 sg for sg in client.list_security_groups().values()[0]
812 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
813 ]
814 msg = "No default security group for tenant %s." % (tenant_id)
815 self.assertTrue(len(sgs) > 0, msg)
816 if len(sgs) > 1:
817 msg = "Found %d default security groups" % len(sgs)
818 raise exc.NeutronClientNoUniqueMatch(msg=msg)
819 return net_resources.DeletableSecurityGroup(client=client,
820 **sgs[0])
821
Yair Frieddb6c9e92014-08-06 08:53:13 +0300822 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300823 tenant_id=None, **kwargs):
824 """Create a rule from a dictionary of rule parameters.
825
826 Create a rule in a secgroup. if secgroup not defined will search for
827 default secgroup in tenant_id.
828
829 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300830 :param tenant_id: if secgroup not passed -- the tenant in which to
831 search for default secgroup
832 :param kwargs: a dictionary containing rule parameters:
833 for example, to allow incoming ssh:
834 rule = {
835 direction: 'ingress'
836 protocol:'tcp',
837 port_range_min: 22,
838 port_range_max: 22
839 }
840 """
841 if client is None:
842 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300843 if not tenant_id:
844 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300845 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300846 secgroup = self._default_security_group(client=client,
847 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300848
849 ruleset = dict(security_group_id=secgroup.id,
850 tenant_id=secgroup.tenant_id)
851 ruleset.update(kwargs)
852
853 _, sg_rule = client.create_security_group_rule(**ruleset)
854 sg_rule = net_resources.DeletableSecurityGroupRule(
855 client=client,
856 **sg_rule['security_group_rule']
857 )
858 self.addCleanup(self.delete_wrapper, sg_rule.delete)
859 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
860 self.assertEqual(secgroup.id, sg_rule.security_group_id)
861
862 return sg_rule
863
864 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
865 """These rules are intended to permit inbound ssh and icmp
866 traffic from all sources, so no group_id is provided.
867 Setting a group_id would only permit traffic from ports
868 belonging to the same security group.
869 """
870
871 if client is None:
872 client = self.network_client
873 rules = []
874 rulesets = [
875 dict(
876 # ssh
877 protocol='tcp',
878 port_range_min=22,
879 port_range_max=22,
880 ),
881 dict(
882 # ping
883 protocol='icmp',
884 )
885 ]
886 for ruleset in rulesets:
887 for r_direction in ['ingress', 'egress']:
888 ruleset['direction'] = r_direction
889 try:
890 sg_rule = self._create_security_group_rule(
891 client=client, secgroup=secgroup, **ruleset)
892 except exceptions.Conflict as ex:
893 # if rule already exist - skip rule and continue
894 msg = 'Security group rule already exists'
895 if msg not in ex._error_string:
896 raise ex
897 else:
898 self.assertEqual(r_direction, sg_rule.direction)
899 rules.append(sg_rule)
900
901 return rules
902
903 def _ssh_to_server(self, server, private_key):
904 ssh_login = CONF.compute.image_ssh_user
905 return self.get_remote_client(server,
906 username=ssh_login,
907 private_key=private_key)
908
Yair Frieddb6c9e92014-08-06 08:53:13 +0300909 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300910 """Retrieve a router for the given tenant id.
911
912 If a public router has been configured, it will be returned.
913
914 If a public router has not been configured, but a public
915 network has, a tenant router will be created and returned that
916 routes traffic to the public network.
917 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300918 if not client:
919 client = self.network_client
920 if not tenant_id:
921 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300922 router_id = CONF.network.public_router_id
923 network_id = CONF.network.public_network_id
924 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300925 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 return net_resources.AttributeDict(**result['router'])
927 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300928 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300929 router.set_gateway(network_id)
930 return router
931 else:
932 raise Exception("Neither of 'public_router_id' or "
933 "'public_network_id' has been defined.")
934
Yair Frieddb6c9e92014-08-06 08:53:13 +0300935 def _create_router(self, client=None, tenant_id=None,
936 namestart='router-smoke'):
937 if not client:
938 client = self.network_client
939 if not tenant_id:
940 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300941 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300942 _, result = client.create_router(name=name,
943 admin_state_up=True,
944 tenant_id=tenant_id)
945 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300946 **result['router'])
947 self.assertEqual(router.name, name)
948 self.addCleanup(self.delete_wrapper, router.delete)
949 return router
950
Yair Frieddb6c9e92014-08-06 08:53:13 +0300951 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300952 """Create a network with a subnet connected to a router.
953
David Shrewsbury9bac3662014-08-07 15:07:01 -0400954 The baremetal driver is a special case since all nodes are
955 on the same shared network.
956
Yair Fried1fc32a12014-08-04 09:11:30 +0300957 :returns: network, subnet, router
958 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400959 if CONF.baremetal.driver_enabled:
960 # NOTE(Shrews): This exception is for environments where tenant
961 # credential isolation is available, but network separation is
962 # not (the current baremetal case). Likely can be removed when
963 # test account mgmt is reworked:
964 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
965 network = self._get_network_by_name(
966 CONF.compute.fixed_network_name)
967 router = None
968 subnet = None
969 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300970 network = self._create_network(client=client, tenant_id=tenant_id)
971 router = self._get_router(client=client, tenant_id=tenant_id)
972 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400973 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300974 return network, subnet, router
975
976
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400977class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400978 """
979 Official Client test base class for scenario testing.
980
981 Official Client tests are tests that have the following characteristics:
982
983 * Test basic operations of an API, typically in an order that
984 a regular user would perform those operations
985 * Test only the correct inputs and action paths -- no fuzz or
986 random input data is sent, only valid inputs.
987 * Use only the default client tool for calling an API
988 """
989
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400990 @classmethod
991 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200992 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -0400993 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -0500994 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500995 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200996
Andrea Frittolif9cde7e2014-02-18 09:57:04 +0000997 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000998 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400999 cls.compute_client = cls.manager.compute_client
1000 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -07001001 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001002 cls.identity_client = cls.manager.identity_client
1003 cls.network_client = cls.manager.network_client
1004 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +00001005 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001006 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +04001007 cls.data_processing_client = cls.manager.data_processing_client
Artur Svechnikovc3bf9252014-05-05 16:37:37 +04001008 cls.ceilometer_client = cls.manager.ceilometer_client
Sean Dague6dbc6da2013-05-08 17:49:46 -04001009
1010 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001011 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +00001012 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001013 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +02001014 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001015 creds = auth.get_default_credentials(ctype)
1016 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001017
1018 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +02001019 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001020 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
1021 'user')
Yair Frieda71cc442013-12-18 13:32:36 +02001022
1023 @classmethod
1024 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001025 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
1026 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +02001027
1028 @classmethod
1029 def admin_credentials(cls):
1030 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001031 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +02001032
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001033 def setUp(self):
1034 super(OfficialClientTest, self).setUp()
1035 self.cleanup_waits = []
1036 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
1037 # because scenario tests in the same test class should not share
1038 # resources. If resources were shared between test cases then it
1039 # should be a single scenario test instead of multiples.
Yair Friedbf2e2c42014-01-28 12:06:38 +02001040
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001041 # NOTE(yfried): this list is cleaned at the end of test_methods and
1042 # not at the end of the class
1043 self.addCleanup(self._wait_for_cleanups)
1044
1045 @staticmethod
1046 def not_found_exception(exception):
1047 """
1048 @return: True if exception is of NotFound type
1049 """
1050 NOT_FOUND_LIST = ['NotFound', 'HTTPNotFound']
1051 return (exception.__class__.__name__ in NOT_FOUND_LIST
1052 or
1053 hasattr(exception, 'status_code') and
1054 exception.status_code == 404)
1055
1056 def delete_wrapper(self, thing):
1057 """Ignores NotFound exceptions for delete operations.
1058
1059 @param thing: object with delete() method.
1060 OpenStack resources are assumed to have a delete() method which
1061 destroys the resource
1062 """
1063
Yair Friedbf2e2c42014-01-28 12:06:38 +02001064 try:
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001065 thing.delete()
Yair Friedbf2e2c42014-01-28 12:06:38 +02001066 except Exception as e:
1067 # If the resource is already missing, mission accomplished.
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001068 if not self.not_found_exception(e):
Yair Friedbf2e2c42014-01-28 12:06:38 +02001069 raise
Yair Friedbf2e2c42014-01-28 12:06:38 +02001070
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001071 def _wait_for_cleanups(self):
1072 """To handle async delete actions, a list of waits is added
1073 which will be iterated over as the last step of clearing the
1074 cleanup queue. That way all the delete calls are made up front
1075 and the tests won't succeed unless the deletes are eventually
1076 successful. This is the same basic approach used in the api tests to
1077 limit cleanup execution time except here it is multi-resource,
1078 because of the nature of the scenario tests.
1079 """
1080 for wait in self.cleanup_waits:
1081 self.delete_timeout(**wait)
Yair Friedbf2e2c42014-01-28 12:06:38 +02001082
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001083 def addCleanup_with_wait(self, things, thing_id,
1084 error_status='ERROR',
1085 exc_type=nova_exceptions.NotFound,
Ghanshyam2a180b82014-06-16 13:54:22 +09001086 cleanup_callable=None, cleanup_args=None,
1087 cleanup_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001088 """Adds wait for ansyc resource deletion at the end of cleanups
Sean Dague6dbc6da2013-05-08 17:49:46 -04001089
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001090 @param things: type of the resource to delete
1091 @param thing_id:
1092 @param error_status: see manager.delete_timeout()
1093 @param exc_type: see manager.delete_timeout()
1094 @param cleanup_callable: method to load pass to self.addCleanup with
1095 the following *cleanup_args, **cleanup_kwargs.
1096 usually a delete method. if not used, will try to use:
1097 things.delete(thing_id)
1098 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001099 if cleanup_args is None:
1100 cleanup_args = []
1101 if cleanup_kwargs is None:
1102 cleanup_kwargs = {}
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001103 if cleanup_callable is None:
1104 LOG.debug("no delete method passed. using {rclass}.delete({id}) as"
1105 " default".format(rclass=things, id=thing_id))
1106 self.addCleanup(things.delete, thing_id)
1107 else:
1108 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
1109 wait_dict = {
1110 'things': things,
1111 'thing_id': thing_id,
1112 'error_status': error_status,
1113 'not_found_exception': exc_type,
1114 }
1115 self.cleanup_waits.append(wait_dict)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001116
Steve Bakerefde7612013-09-30 11:29:23 +13001117 def status_timeout(self, things, thing_id, expected_status,
1118 error_status='ERROR',
1119 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001120 """
1121 Given a thing and an expected status, do a loop, sleeping
1122 for a configurable amount of time, checking for the
1123 expected status to show. At any time, if the returned
1124 status of the thing is ERROR, fail out.
1125 """
Steve Bakerefde7612013-09-30 11:29:23 +13001126 self._status_timeout(things, thing_id,
1127 expected_status=expected_status,
1128 error_status=error_status,
1129 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001130
Steve Bakerefde7612013-09-30 11:29:23 +13001131 def delete_timeout(self, things, thing_id,
1132 error_status='ERROR',
1133 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001134 """
1135 Given a thing, do a loop, sleeping
1136 for a configurable amount of time, checking for the
1137 deleted status to show. At any time, if the returned
1138 status of the thing is ERROR, fail out.
1139 """
1140 self._status_timeout(things,
1141 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +13001142 allow_notfound=True,
1143 error_status=error_status,
1144 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001145
1146 def _status_timeout(self,
1147 things,
1148 thing_id,
1149 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +13001150 allow_notfound=False,
1151 error_status='ERROR',
1152 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001153
1154 log_status = expected_status if expected_status else ''
1155 if allow_notfound:
1156 log_status += ' or NotFound' if log_status != '' else 'NotFound'
1157
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001158 def check_status():
1159 # python-novaclient has resources available to its client
1160 # that all implement a get() method taking an identifier
1161 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001162 try:
1163 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +13001164 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001165 if allow_notfound:
1166 return True
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001167 raise
1168 except Exception as e:
1169 if allow_notfound and self.not_found_exception(e):
1170 return True
1171 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001172
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001173 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -05001174
1175 # Some components are reporting error status in lower case
1176 # so case sensitive comparisons can really mess things
1177 # up.
1178 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +09001179 message = ("%s failed to get to expected status (%s). "
1180 "In %s state.") % (thing, expected_status,
1181 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +09001182 raise exceptions.BuildErrorException(message,
1183 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001184 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001185 return True # All good.
1186 LOG.debug("Waiting for %s to get to %s status. "
1187 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001188 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001189 if not tempest.test.call_until_true(
1190 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +00001191 CONF.compute.build_timeout,
1192 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +09001193 message = ("Timed out waiting for thing %s "
1194 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +02001195 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001196
Yair Friedeb69f3f2013-10-10 13:18:16 +03001197 def _create_loginable_secgroup_rule_nova(self, client=None,
1198 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001199 if client is None:
1200 client = self.compute_client
1201 if secgroup_id is None:
1202 sgs = client.security_groups.list()
1203 for sg in sgs:
1204 if sg.name == 'default':
1205 secgroup_id = sg.id
1206
1207 # These rules are intended to permit inbound ssh and icmp
1208 # traffic from all sources, so no group_id is provided.
1209 # Setting a group_id would only permit traffic from ports
1210 # belonging to the same security group.
1211 rulesets = [
1212 {
1213 # ssh
1214 'ip_protocol': 'tcp',
1215 'from_port': 22,
1216 'to_port': 22,
1217 'cidr': '0.0.0.0/0',
1218 },
1219 {
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001220 # ssh -6
1221 'ip_protocol': 'tcp',
1222 'from_port': 22,
1223 'to_port': 22,
1224 'cidr': '::/0',
1225 },
1226 {
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001227 # ping
1228 'ip_protocol': 'icmp',
1229 'from_port': -1,
1230 'to_port': -1,
1231 'cidr': '0.0.0.0/0',
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001232 },
1233 {
1234 # ping6
1235 'ip_protocol': 'icmp',
1236 'from_port': -1,
1237 'to_port': -1,
1238 'cidr': '::/0',
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001239 }
1240 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +03001241 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001242 for ruleset in rulesets:
1243 sg_rule = client.security_group_rules.create(secgroup_id,
1244 **ruleset)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001245 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03001246 rules.append(sg_rule)
1247 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001248
Grishkin0f1e11c2014-05-04 20:44:52 +04001249 def _create_security_group_nova(self, client=None,
1250 namestart='secgroup-smoke-'):
1251 if client is None:
1252 client = self.compute_client
1253 # Create security group
1254 sg_name = data_utils.rand_name(namestart)
1255 sg_desc = sg_name + " description"
1256 secgroup = client.security_groups.create(sg_name, sg_desc)
1257 self.assertEqual(secgroup.name, sg_name)
1258 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001259 self.addCleanup(self.delete_wrapper, secgroup)
Grishkin0f1e11c2014-05-04 20:44:52 +04001260
1261 # Add rules to the security group
1262 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
1263
1264 return secgroup
1265
David Shrewsbury02719362014-05-20 14:10:03 -04001266 def rebuild_server(self, server, client=None, image=None,
1267 preserve_ephemeral=False, wait=True,
1268 rebuild_kwargs=None):
1269 if client is None:
1270 client = self.compute_client
1271 if image is None:
1272 image = CONF.compute.image_ref
1273 rebuild_kwargs = rebuild_kwargs or {}
1274
1275 LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
1276 server.name, image, preserve_ephemeral)
1277 server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
1278 **rebuild_kwargs)
1279 if wait:
1280 self.status_timeout(client.servers, server.id, 'ACTIVE')
1281
Giulio Fidente61cadca2013-09-24 18:33:37 +02001282 def create_server(self, client=None, name=None, image=None, flavor=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001283 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +09001284 create_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001285 """Creates VM instance.
1286
1287 @param client: compute client to create the instance
1288 @param image: image from which to create the instance
1289 @param wait_on_boot: wait for status ACTIVE before continue
1290 @param wait_on_delete: force synchronous delete on cleanup
1291 @param create_kwargs: additional details for instance creation
1292 @return: client.server object
1293 """
Giulio Fidente61cadca2013-09-24 18:33:37 +02001294 if client is None:
1295 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001296 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001297 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001298 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001299 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001300 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001301 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +09001302 if create_kwargs is None:
1303 create_kwargs = {}
JordanP9c052aa2014-01-24 13:05:00 +00001304
1305 fixed_network_name = CONF.compute.fixed_network_name
1306 if 'nics' not in create_kwargs and fixed_network_name:
1307 networks = client.networks.list()
1308 # If several networks found, set the NetID on which to connect the
1309 # server to avoid the following error "Multiple possible networks
1310 # found, use a Network ID to be more specific."
1311 # See Tempest #1250866
1312 if len(networks) > 1:
1313 for network in networks:
1314 if network.label == fixed_network_name:
1315 create_kwargs['nics'] = [{'net-id': network.id}]
1316 break
1317 # If we didn't find the network we were looking for :
1318 else:
1319 msg = ("The network on which the NIC of the server must "
1320 "be connected can not be found : "
1321 "fixed_network_name=%s. Starting instance without "
1322 "specifying a network.") % fixed_network_name
1323 LOG.info(msg)
1324
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001325 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
1326 name, image, flavor)
1327 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +02001328 self.assertEqual(server.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001329 if wait_on_delete:
1330 self.addCleanup(self.delete_timeout,
1331 self.compute_client.servers,
1332 server.id)
1333 self.addCleanup_with_wait(self.compute_client.servers, server.id,
1334 cleanup_callable=self.delete_wrapper,
1335 cleanup_args=[server])
1336 if wait_on_boot:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001337 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001338 # The instance retrieved on creation is missing network
1339 # details, necessitating retrieval after it becomes active to
1340 # ensure correct details.
1341 server = client.servers.get(server.id)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001342 LOG.debug("Created server: %s", server)
1343 return server
1344
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001345 def create_volume(self, client=None, size=1, name=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001346 snapshot_id=None, imageRef=None, volume_type=None,
1347 wait_on_delete=True):
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001348 if client is None:
1349 client = self.volume_client
1350 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001351 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -07001352 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001353 volume = client.volumes.create(size=size, display_name=name,
1354 snapshot_id=snapshot_id,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001355 imageRef=imageRef,
1356 volume_type=volume_type)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001357 if wait_on_delete:
1358 self.addCleanup(self.delete_timeout,
1359 self.volume_client.volumes,
1360 volume.id)
1361 self.addCleanup_with_wait(self.volume_client.volumes, volume.id,
1362 exc_type=cinder_exceptions.NotFound)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001363 self.assertEqual(name, volume.display_name)
1364 self.status_timeout(client.volumes, volume.id, 'available')
1365 LOG.debug("Created volume: %s", volume)
1366 return volume
1367
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001368 def create_server_snapshot(self, server, compute_client=None,
1369 image_client=None, name=None):
1370 if compute_client is None:
1371 compute_client = self.compute_client
1372 if image_client is None:
1373 image_client = self.image_client
1374 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001375 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001376 LOG.debug("Creating a snapshot image for server: %s", server.name)
1377 image_id = compute_client.servers.create_image(server, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001378 self.addCleanup_with_wait(self.image_client.images, image_id,
1379 exc_type=glanceclient.exc.HTTPNotFound)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001380 self.status_timeout(image_client.images, image_id, 'active')
1381 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -07001382 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001383 LOG.debug("Created snapshot image %s for server %s",
1384 snapshot_image.name, server.name)
1385 return snapshot_image
1386
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001387 def create_keypair(self, client=None, name=None):
1388 if client is None:
1389 client = self.compute_client
1390 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001391 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001392 keypair = client.keypairs.create(name)
1393 self.assertEqual(keypair.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001394 self.addCleanup(self.delete_wrapper, keypair)
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001395 return keypair
1396
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001397 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +08001398 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001399 ip = server_or_ip
1400 else:
Matthew Treinish6c072292014-01-29 19:15:52 +00001401 network_name_for_ssh = CONF.compute.network_for_ssh
Adam Gandelman0d9508e2014-08-22 10:58:09 -07001402 ip = server_or_ip.networks[network_name_for_ssh][0]
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001403 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001404 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001405 if private_key is None:
1406 private_key = self.keypair.private_key
Yair Fried3960c4d2014-05-07 15:20:30 +03001407 linux_client = remote_client.RemoteClient(ip, username,
1408 pkey=private_key)
1409 try:
1410 linux_client.validate_authentication()
1411 except exceptions.SSHTimeout:
1412 LOG.exception('ssh connection to %s failed' % ip)
1413 debug.log_net_debug()
1414 raise
1415
1416 return linux_client
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001417
Nachi Ueno95b41282014-01-15 06:54:21 -08001418 def _log_console_output(self, servers=None):
Adam Gandelmanc6eefb42014-07-15 16:44:08 -07001419 if not CONF.compute_feature_enabled.console_output:
1420 LOG.debug('Console output not supported, cannot log')
1421 return
Nachi Ueno95b41282014-01-15 06:54:21 -08001422 if not servers:
1423 servers = self.compute_client.servers.list()
1424 for server in servers:
1425 LOG.debug('Console output for %s', server.id)
1426 LOG.debug(server.get_console_output())
1427
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001428 def wait_for_volume_status(self, status):
1429 volume_id = self.volume.id
1430 self.status_timeout(
1431 self.volume_client.volumes, volume_id, status)
1432
Ghanshyam2a180b82014-06-16 13:54:22 +09001433 def _image_create(self, name, fmt, path, properties=None):
1434 if properties is None:
1435 properties = {}
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001436 name = data_utils.rand_name('%s-' % name)
1437 image_file = open(path, 'rb')
1438 self.addCleanup(image_file.close)
1439 params = {
1440 'name': name,
1441 'container_format': fmt,
1442 'disk_format': fmt,
Aaron Rosenc7720622014-05-20 10:38:10 -07001443 'is_public': 'False',
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001444 }
1445 params.update(properties)
1446 image = self.image_client.images.create(**params)
1447 self.addCleanup(self.image_client.images.delete, image)
1448 self.assertEqual("queued", image.status)
1449 image.update(data=image_file)
1450 return image.id
1451
1452 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001453 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001454 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
1455 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
1456 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001457 img_container_format = CONF.scenario.img_container_format
1458 img_disk_format = CONF.scenario.img_disk_format
1459 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
1460 "ami: %s, ari: %s, aki: %s" %
1461 (img_path, img_container_format, img_disk_format,
1462 ami_img_path, ari_img_path, aki_img_path))
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001463 try:
1464 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001465 img_container_format,
1466 img_path,
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001467 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001468 img_disk_format})
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001469 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +09001470 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001471 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
1472 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
1473 properties = {
1474 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
1475 }
1476 self.image = self._image_create('scenario-ami', 'ami',
1477 path=ami_img_path,
1478 properties=properties)
1479 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001480
Sean Dague6dbc6da2013-05-08 17:49:46 -04001481
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001482# power/provision states as of icehouse
1483class BaremetalPowerStates(object):
1484 """Possible power states of an Ironic node."""
1485 POWER_ON = 'power on'
1486 POWER_OFF = 'power off'
1487 REBOOT = 'rebooting'
1488 SUSPEND = 'suspended'
1489
1490
1491class BaremetalProvisionStates(object):
1492 """Possible provision states of an Ironic node."""
1493 NOSTATE = None
1494 INIT = 'initializing'
1495 ACTIVE = 'active'
1496 BUILDING = 'building'
1497 DEPLOYWAIT = 'wait call-back'
1498 DEPLOYING = 'deploying'
1499 DEPLOYFAIL = 'deploy failed'
1500 DEPLOYDONE = 'deploy complete'
1501 DELETING = 'deleting'
1502 DELETED = 'deleted'
1503 ERROR = 'error'
1504
1505
Adam Gandelman4a48a602014-03-20 18:23:18 -07001506class BaremetalScenarioTest(OfficialClientTest):
1507 @classmethod
1508 def setUpClass(cls):
1509 super(BaremetalScenarioTest, cls).setUpClass()
1510
1511 if (not CONF.service_available.ironic or
1512 not CONF.baremetal.driver_enabled):
1513 msg = 'Ironic not available or Ironic compute driver not enabled'
1514 raise cls.skipException(msg)
1515
1516 # use an admin client manager for baremetal client
Adam Gandelmanacc13e62014-05-08 11:12:47 -07001517 admin_creds = cls.admin_credentials()
1518 manager = clients.OfficialClientManager(credentials=admin_creds)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001519 cls.baremetal_client = manager.baremetal_client
1520
1521 # allow any issues obtaining the node list to raise early
1522 cls.baremetal_client.node.list()
1523
1524 def _node_state_timeout(self, node_id, state_attr,
1525 target_states, timeout=10, interval=1):
1526 if not isinstance(target_states, list):
1527 target_states = [target_states]
1528
1529 def check_state():
1530 node = self.get_node(node_id=node_id)
1531 if getattr(node, state_attr) in target_states:
1532 return True
1533 return False
1534
1535 if not tempest.test.call_until_true(
1536 check_state, timeout, interval):
1537 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1538 (node_id, state_attr, target_states))
1539 raise exceptions.TimeoutException(msg)
1540
1541 def wait_provisioning_state(self, node_id, state, timeout):
1542 self._node_state_timeout(
1543 node_id=node_id, state_attr='provision_state',
1544 target_states=state, timeout=timeout)
1545
1546 def wait_power_state(self, node_id, state):
1547 self._node_state_timeout(
1548 node_id=node_id, state_attr='power_state',
1549 target_states=state, timeout=CONF.baremetal.power_timeout)
1550
1551 def wait_node(self, instance_id):
1552 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001553 from ironicclient import exc as ironic_exceptions
1554
Adam Gandelman4a48a602014-03-20 18:23:18 -07001555 def _get_node():
1556 node = None
1557 try:
1558 node = self.get_node(instance_id=instance_id)
1559 except ironic_exceptions.HTTPNotFound:
1560 pass
1561 return node is not None
1562
1563 if not tempest.test.call_until_true(
1564 _get_node, CONF.baremetal.association_timeout, 1):
1565 msg = ('Timed out waiting to get Ironic node by instance id %s'
1566 % instance_id)
1567 raise exceptions.TimeoutException(msg)
1568
1569 def get_node(self, node_id=None, instance_id=None):
1570 if node_id:
1571 return self.baremetal_client.node.get(node_id)
1572 elif instance_id:
1573 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
1574
1575 def get_ports(self, node_id):
1576 ports = []
1577 for port in self.baremetal_client.node.list_ports(node_id):
1578 ports.append(self.baremetal_client.port.get(port.uuid))
1579 return ports
1580
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001581 def add_keypair(self):
1582 self.keypair = self.create_keypair()
1583
1584 def verify_connectivity(self, ip=None):
1585 if ip:
1586 dest = self.get_remote_client(ip)
1587 else:
1588 dest = self.get_remote_client(self.instance)
1589 dest.validate_authentication()
1590
1591 def boot_instance(self):
1592 create_kwargs = {
1593 'key_name': self.keypair.id
1594 }
1595 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001596 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001597
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001598 self.addCleanup_with_wait(self.compute_client.servers,
1599 self.instance.id,
1600 cleanup_callable=self.delete_wrapper,
1601 cleanup_args=[self.instance])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001602
1603 self.wait_node(self.instance.id)
1604 self.node = self.get_node(instance_id=self.instance.id)
1605
1606 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_ON)
1607
1608 self.wait_provisioning_state(
1609 self.node.uuid,
1610 [BaremetalProvisionStates.DEPLOYWAIT,
1611 BaremetalProvisionStates.ACTIVE],
1612 timeout=15)
1613
1614 self.wait_provisioning_state(self.node.uuid,
1615 BaremetalProvisionStates.ACTIVE,
1616 timeout=CONF.baremetal.active_timeout)
1617
1618 self.status_timeout(
1619 self.compute_client.servers, self.instance.id, 'ACTIVE')
1620
1621 self.node = self.get_node(instance_id=self.instance.id)
1622 self.instance = self.compute_client.servers.get(self.instance.id)
1623
1624 def terminate_instance(self):
1625 self.instance.delete()
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001626 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_OFF)
1627 self.wait_provisioning_state(
1628 self.node.uuid,
1629 BaremetalProvisionStates.NOSTATE,
1630 timeout=CONF.baremetal.unprovision_timeout)
1631
Adam Gandelman4a48a602014-03-20 18:23:18 -07001632
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001633class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001634 """
1635 Base class for encryption scenario tests
1636 """
1637
1638 @classmethod
1639 def setUpClass(cls):
1640 super(EncryptionScenarioTest, cls).setUpClass()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001641 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001642
1643 def _wait_for_volume_status(self, status):
1644 self.status_timeout(
1645 self.volume_client.volumes, self.volume.id, status)
1646
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001647 def nova_boot(self):
1648 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001649 create_kwargs = {'key_name': self.keypair['name']}
1650 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001651 create_kwargs=create_kwargs)
1652
1653 def create_volume_type(self, client=None, name=None):
1654 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001655 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001656 if not name:
1657 name = 'generic'
1658 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1659 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001660 _, body = client.create_volume_type(
1661 randomized_name)
1662 self.assertIn('id', body)
1663 self.addCleanup(client.delete_volume_type, body['id'])
1664 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001665
1666 def create_encryption_type(self, client=None, type_id=None, provider=None,
1667 key_size=None, cipher=None,
1668 control_location=None):
1669 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001670 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001671 if not type_id:
1672 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001673 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001674 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001675 client.create_encryption_type(
1676 type_id, provider=provider, key_size=key_size, cipher=cipher,
1677 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001678
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001679
Sean Dague6dbc6da2013-05-08 17:49:46 -04001680class NetworkScenarioTest(OfficialClientTest):
1681 """
1682 Base class for network scenario tests
1683 """
1684
1685 @classmethod
1686 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001687 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001688 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +02001689 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -04001690 try:
1691 cls.network_client.list_networks()
1692 except exc.EndpointNotFound:
1693 cls.enabled = False
1694 raise
1695 else:
1696 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -04001697 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -04001698 raise cls.skipException(msg)
1699
1700 @classmethod
1701 def setUpClass(cls):
1702 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001703 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -04001704
Sean Dague6dbc6da2013-05-08 17:49:46 -04001705 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001706 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001707 body = dict(
1708 network=dict(
1709 name=name,
1710 tenant_id=tenant_id,
1711 ),
1712 )
1713 result = self.network_client.create_network(body=body)
1714 network = net_common.DeletableNetwork(client=self.network_client,
1715 **result['network'])
1716 self.assertEqual(network.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001717 self.addCleanup(self.delete_wrapper, network)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001718 return network
1719
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001720 def _list_networks(self, **kwargs):
1721 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001722 return nets['networks']
1723
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001724 def _list_subnets(self, **kwargs):
1725 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001726 return subnets['subnets']
1727
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001728 def _list_routers(self, **kwargs):
1729 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001730 return routers['routers']
1731
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001732 def _list_ports(self, **kwargs):
1733 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001734 return ports['ports']
1735
1736 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001737 nets = self._list_networks(tenant_id=tenant_id)
1738 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001739
1740 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001741 subnets = self._list_subnets(tenant_id=tenant_id)
1742 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001743
1744 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001745 ports = self._list_ports(tenant_id=tenant_id)
1746 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001747
Yair Fried3097dc12014-01-26 08:46:43 +02001748 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001749 """
1750 Create a subnet for the given network within the cidr block
1751 configured for tenant networks.
1752 """
Attila Fazekase857bd62013-10-21 21:02:44 +02001753
1754 def cidr_in_use(cidr, tenant_id):
1755 """
1756 :return True if subnet with cidr already exist in tenant
1757 False else
1758 """
1759 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
1760 return len(cidr_in_use) != 0
1761
Matthew Treinish6c072292014-01-29 19:15:52 +00001762 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001763 result = None
1764 # Repeatedly attempt subnet creation with sequential cidr
1765 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +00001766 for subnet_cidr in tenant_cidr.subnet(
1767 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +02001768 str_cidr = str(subnet_cidr)
1769 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
1770 continue
1771
Sean Dague6dbc6da2013-05-08 17:49:46 -04001772 body = dict(
1773 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +02001774 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -04001775 ip_version=4,
1776 network_id=network.id,
1777 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +02001778 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001779 ),
1780 )
Yair Fried3097dc12014-01-26 08:46:43 +02001781 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001782 try:
1783 result = self.network_client.create_subnet(body=body)
1784 break
Mark McClainf2982e82013-07-06 17:48:03 -04001785 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -04001786 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1787 if not is_overlapping_cidr:
1788 raise
1789 self.assertIsNotNone(result, 'Unable to allocate tenant network')
1790 subnet = net_common.DeletableSubnet(client=self.network_client,
1791 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +02001792 self.assertEqual(subnet.cidr, str_cidr)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001793 self.addCleanup(self.delete_wrapper, subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001794 return subnet
1795
1796 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001797 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001798 body = dict(
1799 port=dict(name=name,
1800 network_id=network.id,
1801 tenant_id=network.tenant_id))
1802 result = self.network_client.create_port(body=body)
1803 self.assertIsNotNone(result, 'Unable to allocate port')
1804 port = net_common.DeletablePort(client=self.network_client,
1805 **result['port'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001806 self.addCleanup(self.delete_wrapper, port)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001807 return port
1808
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001809 def _get_server_port_id(self, server, ip_addr=None):
1810 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001811 self.assertEqual(len(ports), 1,
1812 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +02001813 return ports[0]['id']
1814
David Shrewsbury9bac3662014-08-07 15:07:01 -04001815 def _get_network_by_name(self, network_name):
1816 net = self._list_networks(name=network_name)
1817 return net_common.AttributeDict(net[0])
1818
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001819 def _create_floating_ip(self, thing, external_network_id, port_id=None):
1820 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001821 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001822 body = dict(
1823 floatingip=dict(
1824 floating_network_id=external_network_id,
1825 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001826 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001827 )
1828 )
1829 result = self.network_client.create_floatingip(body=body)
1830 floating_ip = net_common.DeletableFloatingIp(
1831 client=self.network_client,
1832 **result['floatingip'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001833 self.addCleanup(self.delete_wrapper, floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001834 return floating_ip
1835
Yair Fried05db2522013-11-18 11:02:10 +02001836 def _associate_floating_ip(self, floating_ip, server):
1837 port_id = self._get_server_port_id(server)
1838 floating_ip.update(port_id=port_id)
1839 self.assertEqual(port_id, floating_ip.port_id)
1840 return floating_ip
1841
Yair Fried9a551c42013-12-15 14:59:34 +02001842 def _disassociate_floating_ip(self, floating_ip):
1843 """
1844 :param floating_ip: type DeletableFloatingIp
1845 """
1846 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +08001847 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +02001848 return floating_ip
1849
1850 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001851 cmd = ['ping', '-c1', '-w1', ip_address]
1852
1853 def ping():
1854 proc = subprocess.Popen(cmd,
1855 stdout=subprocess.PIPE,
1856 stderr=subprocess.PIPE)
1857 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +02001858 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -04001859
Nachi Ueno6d580be2013-07-24 10:58:11 -07001860 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +00001861 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +00001862
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001863 def _create_pool(self, lb_method, protocol, subnet_id):
1864 """Wrapper utility that returns a test pool."""
1865 name = data_utils.rand_name('pool-')
1866 body = {
1867 "pool": {
1868 "protocol": protocol,
1869 "name": name,
1870 "subnet_id": subnet_id,
1871 "lb_method": lb_method
1872 }
1873 }
1874 resp = self.network_client.create_pool(body=body)
1875 pool = net_common.DeletablePool(client=self.network_client,
1876 **resp['pool'])
1877 self.assertEqual(pool['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001878 self.addCleanup(self.delete_wrapper, pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001879 return pool
1880
1881 def _create_member(self, address, protocol_port, pool_id):
1882 """Wrapper utility that returns a test member."""
1883 body = {
1884 "member": {
1885 "protocol_port": protocol_port,
1886 "pool_id": pool_id,
1887 "address": address
1888 }
1889 }
1890 resp = self.network_client.create_member(body)
1891 member = net_common.DeletableMember(client=self.network_client,
1892 **resp['member'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001893 self.addCleanup(self.delete_wrapper, member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001894 return member
1895
1896 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
1897 """Wrapper utility that returns a test vip."""
1898 name = data_utils.rand_name('vip-')
1899 body = {
1900 "vip": {
1901 "protocol": protocol,
1902 "name": name,
1903 "subnet_id": subnet_id,
1904 "pool_id": pool_id,
1905 "protocol_port": protocol_port
1906 }
1907 }
1908 resp = self.network_client.create_vip(body)
1909 vip = net_common.DeletableVip(client=self.network_client,
1910 **resp['vip'])
1911 self.assertEqual(vip['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001912 self.addCleanup(self.delete_wrapper, vip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001913 return vip
1914
Yair Fried9a551c42013-12-15 14:59:34 +02001915 def _check_vm_connectivity(self, ip_address,
1916 username=None,
1917 private_key=None,
1918 should_connect=True):
1919 """
1920 :param ip_address: server to test against
1921 :param username: server's ssh username
1922 :param private_key: server's ssh private key to be used
1923 :param should_connect: True/False indicates positive/negative test
1924 positive - attempt ping and ssh
1925 negative - attempt ping and fail if succeed
1926
1927 :raises: AssertError if the result of the connectivity check does
1928 not match the value of the should_connect param
1929 """
1930 if should_connect:
1931 msg = "Timed out waiting for %s to become reachable" % ip_address
1932 else:
1933 msg = "ip address %s is reachable" % ip_address
1934 self.assertTrue(self._ping_ip_address(ip_address,
1935 should_succeed=should_connect),
1936 msg=msg)
1937 if should_connect:
1938 # no need to check ssh for negative connectivity
Yair Fried3960c4d2014-05-07 15:20:30 +03001939 self.get_remote_client(ip_address, username, private_key)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001940
Matt Riedemann343305f2014-05-27 09:55:03 -07001941 def _check_public_network_connectivity(self, ip_address, username,
1942 private_key, should_connect=True,
1943 msg=None, servers=None):
1944 # The target login is assumed to have been configured for
1945 # key-based authentication by cloud-init.
1946 LOG.debug('checking network connections to IP %s with user: %s' %
1947 (ip_address, username))
1948 try:
1949 self._check_vm_connectivity(ip_address,
1950 username,
1951 private_key,
1952 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001953 except Exception as e:
Matt Riedemann343305f2014-05-27 09:55:03 -07001954 ex_msg = 'Public network connectivity check failed'
1955 if msg:
1956 ex_msg += ": " + msg
1957 LOG.exception(ex_msg)
1958 self._log_console_output(servers)
Yair Fried3960c4d2014-05-07 15:20:30 +03001959 # network debug is called as part of ssh init
1960 if not isinstance(e, exceptions.SSHTimeout):
1961 debug.log_net_debug()
Matt Riedemann343305f2014-05-27 09:55:03 -07001962 raise
1963
Matt Riedemann2d005be2014-05-27 10:52:35 -07001964 def _check_tenant_network_connectivity(self, server,
1965 username,
1966 private_key,
1967 should_connect=True,
1968 servers_for_debug=None):
1969 if not CONF.network.tenant_networks_reachable:
1970 msg = 'Tenant networks not configured to be reachable.'
1971 LOG.info(msg)
1972 return
1973 # The target login is assumed to have been configured for
1974 # key-based authentication by cloud-init.
1975 try:
1976 for net_name, ip_addresses in server.networks.iteritems():
1977 for ip_address in ip_addresses:
1978 self._check_vm_connectivity(ip_address,
1979 username,
1980 private_key,
1981 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001982 except Exception as e:
Matt Riedemann2d005be2014-05-27 10:52:35 -07001983 LOG.exception('Tenant network connectivity check failed')
1984 self._log_console_output(servers_for_debug)
Yair Fried3960c4d2014-05-07 15:20:30 +03001985 # network debug is called as part of ssh init
1986 if not isinstance(e, exceptions.SSHTimeout):
1987 debug.log_net_debug()
Matt Riedemann2d005be2014-05-27 10:52:35 -07001988 raise
1989
Yair Fried3097dc12014-01-26 08:46:43 +02001990 def _check_remote_connectivity(self, source, dest, should_succeed=True):
1991 """
1992 check ping server via source ssh connection
1993
1994 :param source: RemoteClient: an ssh connection from which to ping
1995 :param dest: and IP to ping against
1996 :param should_succeed: boolean should ping succeed or not
1997 :returns: boolean -- should_succeed == ping
1998 :returns: ping is false if ping failed
1999 """
2000 def ping_remote():
2001 try:
2002 source.ping_host(dest)
2003 except exceptions.SSHExecCommandFailed:
Matthew Treinishc4131d82014-05-27 11:59:28 -04002004 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
2005 % (dest, source.ssh_client.host))
Yair Fried3097dc12014-01-26 08:46:43 +02002006 return not should_succeed
2007 return should_succeed
2008
2009 return tempest.test.call_until_true(ping_remote,
2010 CONF.compute.ping_timeout,
2011 1)
2012
Yair Friedeb69f3f2013-10-10 13:18:16 +03002013 def _create_security_group_neutron(self, tenant_id, client=None,
2014 namestart='secgroup-smoke-'):
2015 if client is None:
2016 client = self.network_client
2017 secgroup = self._create_empty_security_group(namestart=namestart,
2018 client=client,
2019 tenant_id=tenant_id)
2020
2021 # Add rules to the security group
2022 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
2023 for rule in rules:
2024 self.assertEqual(tenant_id, rule.tenant_id)
2025 self.assertEqual(secgroup.id, rule.security_group_id)
2026 return secgroup
2027
2028 def _create_empty_security_group(self, tenant_id, client=None,
2029 namestart='secgroup-smoke-'):
2030 """Create a security group without rules.
2031
2032 Default rules will be created:
2033 - IPv4 egress to any
2034 - IPv6 egress to any
2035
2036 :param tenant_id: secgroup will be created in this tenant
2037 :returns: DeletableSecurityGroup -- containing the secgroup created
2038 """
2039 if client is None:
2040 client = self.network_client
2041 sg_name = data_utils.rand_name(namestart)
2042 sg_desc = sg_name + " description"
2043 sg_dict = dict(name=sg_name,
2044 description=sg_desc)
2045 sg_dict['tenant_id'] = tenant_id
2046 body = dict(security_group=sg_dict)
2047 result = client.create_security_group(body=body)
2048 secgroup = net_common.DeletableSecurityGroup(
2049 client=client,
2050 **result['security_group']
2051 )
2052 self.assertEqual(secgroup.name, sg_name)
2053 self.assertEqual(tenant_id, secgroup.tenant_id)
2054 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002055 self.addCleanup(self.delete_wrapper, secgroup)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002056 return secgroup
2057
2058 def _default_security_group(self, tenant_id, client=None):
2059 """Get default secgroup for given tenant_id.
2060
2061 :returns: DeletableSecurityGroup -- default secgroup for given tenant
2062 """
2063 if client is None:
2064 client = self.network_client
2065 sgs = [
2066 sg for sg in client.list_security_groups().values()[0]
2067 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
2068 ]
2069 msg = "No default security group for tenant %s." % (tenant_id)
2070 self.assertTrue(len(sgs) > 0, msg)
2071 if len(sgs) > 1:
2072 msg = "Found %d default security groups" % len(sgs)
2073 raise exc.NeutronClientNoUniqueMatch(msg=msg)
2074 return net_common.DeletableSecurityGroup(client=client,
2075 **sgs[0])
2076
2077 def _create_security_group_rule(self, client=None, secgroup=None,
2078 tenant_id=None, **kwargs):
2079 """Create a rule from a dictionary of rule parameters.
2080
2081 Create a rule in a secgroup. if secgroup not defined will search for
2082 default secgroup in tenant_id.
2083
2084 :param secgroup: type DeletableSecurityGroup.
2085 :param secgroup_id: search for secgroup by id
2086 default -- choose default secgroup for given tenant_id
2087 :param tenant_id: if secgroup not passed -- the tenant in which to
2088 search for default secgroup
2089 :param kwargs: a dictionary containing rule parameters:
2090 for example, to allow incoming ssh:
2091 rule = {
2092 direction: 'ingress'
2093 protocol:'tcp',
2094 port_range_min: 22,
2095 port_range_max: 22
2096 }
2097 """
2098 if client is None:
2099 client = self.network_client
2100 if secgroup is None:
2101 secgroup = self._default_security_group(tenant_id)
2102
2103 ruleset = dict(security_group_id=secgroup.id,
2104 tenant_id=secgroup.tenant_id,
2105 )
2106 ruleset.update(kwargs)
2107
2108 body = dict(security_group_rule=dict(ruleset))
2109 sg_rule = client.create_security_group_rule(body=body)
2110 sg_rule = net_common.DeletableSecurityGroupRule(
2111 client=client,
2112 **sg_rule['security_group_rule']
2113 )
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002114 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002115 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
2116 self.assertEqual(secgroup.id, sg_rule.security_group_id)
2117
2118 return sg_rule
2119
2120 def _create_loginable_secgroup_rule_neutron(self, client=None,
2121 secgroup=None):
2122 """These rules are intended to permit inbound ssh and icmp
2123 traffic from all sources, so no group_id is provided.
2124 Setting a group_id would only permit traffic from ports
2125 belonging to the same security group.
2126 """
2127
2128 if client is None:
2129 client = self.network_client
2130 rules = []
2131 rulesets = [
2132 dict(
2133 # ssh
2134 protocol='tcp',
2135 port_range_min=22,
2136 port_range_max=22,
2137 ),
2138 dict(
2139 # ping
2140 protocol='icmp',
2141 )
2142 ]
2143 for ruleset in rulesets:
2144 for r_direction in ['ingress', 'egress']:
2145 ruleset['direction'] = r_direction
2146 try:
2147 sg_rule = self._create_security_group_rule(
2148 client=client, secgroup=secgroup, **ruleset)
2149 except exc.NeutronClientException as ex:
2150 # if rule already exist - skip rule and continue
2151 if not (ex.status_code is 409 and 'Security group rule'
2152 ' already exists' in ex.message):
2153 raise ex
2154 else:
2155 self.assertEqual(r_direction, sg_rule.direction)
2156 rules.append(sg_rule)
2157
2158 return rules
2159
Yair Fried5f670ab2013-12-09 09:26:51 +02002160 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +00002161 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +02002162 return self.get_remote_client(server,
2163 username=ssh_login,
2164 private_key=private_key)
2165
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00002166 def _show_quota_network(self, tenant_id):
2167 quota = self.network_client.show_quota(tenant_id)
2168 return quota['quota']['network']
2169
2170 def _show_quota_subnet(self, tenant_id):
2171 quota = self.network_client.show_quota(tenant_id)
2172 return quota['quota']['subnet']
2173
2174 def _show_quota_port(self, tenant_id):
2175 quota = self.network_client.show_quota(tenant_id)
2176 return quota['quota']['port']
2177
Yair Fried4d7efa62013-11-17 17:12:29 +02002178 def _get_router(self, tenant_id):
2179 """Retrieve a router for the given tenant id.
2180
2181 If a public router has been configured, it will be returned.
2182
2183 If a public router has not been configured, but a public
2184 network has, a tenant router will be created and returned that
2185 routes traffic to the public network.
2186 """
Matthew Treinish6c072292014-01-29 19:15:52 +00002187 router_id = CONF.network.public_router_id
2188 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02002189 if router_id:
2190 result = self.network_client.show_router(router_id)
2191 return net_common.AttributeDict(**result['router'])
2192 elif network_id:
2193 router = self._create_router(tenant_id)
2194 router.add_gateway(network_id)
2195 return router
2196 else:
2197 raise Exception("Neither of 'public_router_id' or "
2198 "'public_network_id' has been defined.")
2199
2200 def _create_router(self, tenant_id, namestart='router-smoke-'):
2201 name = data_utils.rand_name(namestart)
2202 body = dict(
2203 router=dict(
2204 name=name,
2205 admin_state_up=True,
2206 tenant_id=tenant_id,
2207 ),
2208 )
2209 result = self.network_client.create_router(body=body)
2210 router = net_common.DeletableRouter(client=self.network_client,
2211 **result['router'])
2212 self.assertEqual(router.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002213 self.addCleanup(self.delete_wrapper, router)
Yair Fried4d7efa62013-11-17 17:12:29 +02002214 return router
2215
David Shrewsbury9bac3662014-08-07 15:07:01 -04002216 def create_networks(self, tenant_id=None):
Yair Fried4d7efa62013-11-17 17:12:29 +02002217 """Create a network with a subnet connected to a router.
2218
David Shrewsbury9bac3662014-08-07 15:07:01 -04002219 The baremetal driver is a special case since all nodes are
2220 on the same shared network.
2221
Yair Fried4d7efa62013-11-17 17:12:29 +02002222 :returns: network, subnet, router
2223 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04002224 if CONF.baremetal.driver_enabled:
2225 # NOTE(Shrews): This exception is for environments where tenant
2226 # credential isolation is available, but network separation is
2227 # not (the current baremetal case). Likely can be removed when
2228 # test account mgmt is reworked:
2229 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
2230 network = self._get_network_by_name(
2231 CONF.compute.fixed_network_name)
2232 router = None
2233 subnet = None
2234 else:
2235 if tenant_id is None:
2236 tenant_id = self.tenant_id
2237 network = self._create_network(tenant_id)
2238 router = self._get_router(tenant_id)
2239 subnet = self._create_subnet(network)
2240 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02002241 return network, subnet, router
2242
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002243
2244class OrchestrationScenarioTest(OfficialClientTest):
2245 """
2246 Base class for orchestration scenario tests
2247 """
2248
2249 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07002250 def setUpClass(cls):
2251 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00002252 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07002253 raise cls.skipException("Heat support is required")
2254
2255 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002256 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00002257 admin_creds = auth.get_default_credentials('identity_admin')
2258 creds = auth.get_default_credentials('user')
2259 admin_creds.tenant_name = creds.tenant_name
2260 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002261
2262 def _load_template(self, base_file, file_name):
2263 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
2264 file_name)
2265 with open(filepath) as f:
2266 return f.read()
2267
2268 @classmethod
2269 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09002270 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12002271
2272 @classmethod
2273 def _get_default_network(cls):
2274 networks = cls.network_client.list_networks()
2275 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00002276 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12002277 return net
Steve Baker22c16602014-05-05 13:34:19 +12002278
2279 @staticmethod
2280 def _stack_output(stack, output_key):
2281 """Return a stack output value for a given key."""
2282 return next((o['output_value'] for o in stack.outputs
2283 if o['output_key'] == output_key), None)
2284
2285 def _ping_ip_address(self, ip_address, should_succeed=True):
2286 cmd = ['ping', '-c1', '-w1', ip_address]
2287
2288 def ping():
2289 proc = subprocess.Popen(cmd,
2290 stdout=subprocess.PIPE,
2291 stderr=subprocess.PIPE)
2292 proc.wait()
2293 return (proc.returncode == 0) == should_succeed
2294
2295 return tempest.test.call_until_true(
2296 ping, CONF.orchestration.build_timeout, 1)
2297
2298 def _wait_for_resource_status(self, stack_identifier, resource_name,
2299 status, failure_pattern='^.*_FAILED$'):
2300 """Waits for a Resource to reach a given status."""
2301 fail_regexp = re.compile(failure_pattern)
2302 build_timeout = CONF.orchestration.build_timeout
2303 build_interval = CONF.orchestration.build_interval
2304
2305 start = timeutils.utcnow()
2306 while timeutils.delta_seconds(start,
2307 timeutils.utcnow()) < build_timeout:
2308 try:
2309 res = self.client.resources.get(
2310 stack_identifier, resource_name)
2311 except heat_exceptions.HTTPNotFound:
2312 # ignore this, as the resource may not have
2313 # been created yet
2314 pass
2315 else:
2316 if res.resource_status == status:
2317 return
2318 if fail_regexp.search(res.resource_status):
2319 raise exceptions.StackResourceBuildErrorException(
2320 resource_name=res.resource_name,
2321 stack_identifier=stack_identifier,
2322 resource_status=res.resource_status,
2323 resource_status_reason=res.resource_status_reason)
2324 time.sleep(build_interval)
2325
2326 message = ('Resource %s failed to reach %s status within '
2327 'the required time (%s s).' %
2328 (res.resource_name, status, build_timeout))
2329 raise exceptions.TimeoutException(message)
2330
2331 def _wait_for_stack_status(self, stack_identifier, status,
2332 failure_pattern='^.*_FAILED$'):
2333 """
2334 Waits for a Stack to reach a given status.
2335
2336 Note this compares the full $action_$status, e.g
2337 CREATE_COMPLETE, not just COMPLETE which is exposed
2338 via the status property of Stack in heatclient
2339 """
2340 fail_regexp = re.compile(failure_pattern)
2341 build_timeout = CONF.orchestration.build_timeout
2342 build_interval = CONF.orchestration.build_interval
2343
2344 start = timeutils.utcnow()
2345 while timeutils.delta_seconds(start,
2346 timeutils.utcnow()) < build_timeout:
2347 try:
2348 stack = self.client.stacks.get(stack_identifier)
2349 except heat_exceptions.HTTPNotFound:
2350 # ignore this, as the stackource may not have
2351 # been created yet
2352 pass
2353 else:
2354 if stack.stack_status == status:
2355 return
2356 if fail_regexp.search(stack.stack_status):
2357 raise exceptions.StackBuildErrorException(
2358 stack_identifier=stack_identifier,
2359 stack_status=stack.stack_status,
2360 stack_status_reason=stack.stack_status_reason)
2361 time.sleep(build_interval)
2362
2363 message = ('Stack %s failed to reach %s status within '
2364 'the required time (%s s).' %
2365 (stack.stack_name, status, build_timeout))
2366 raise exceptions.TimeoutException(message)
2367
2368 def _stack_delete(self, stack_identifier):
2369 try:
2370 self.client.stacks.delete(stack_identifier)
2371 except heat_exceptions.HTTPNotFound:
2372 pass
Chris Dent0d494112014-08-26 13:48:30 +01002373
2374
2375class SwiftScenarioTest(ScenarioTest):
2376 """
2377 Provide harness to do Swift scenario tests.
2378
2379 Subclasses implement the tests that use the methods provided by this
2380 class.
2381 """
2382
2383 @classmethod
2384 def setUpClass(cls):
2385 cls.set_network_resources()
2386 super(SwiftScenarioTest, cls).setUpClass()
2387 if not CONF.service_available.swift:
2388 skip_msg = ("%s skipped as swift is not available" %
2389 cls.__name__)
2390 raise cls.skipException(skip_msg)
2391 # Clients for Swift
2392 cls.account_client = cls.manager.account_client
2393 cls.container_client = cls.manager.container_client
2394 cls.object_client = cls.manager.object_client
2395
2396 def _get_swift_stat(self):
2397 """get swift status for our user account."""
2398 self.account_client.list_account_containers()
2399 LOG.debug('Swift status information obtained successfully')
2400
2401 def _create_container(self, container_name=None):
2402 name = container_name or data_utils.rand_name(
2403 'swift-scenario-container')
2404 self.container_client.create_container(name)
2405 # look for the container to assure it is created
2406 self._list_and_check_container_objects(name)
2407 LOG.debug('Container %s created' % (name))
2408 return name
2409
2410 def _delete_container(self, container_name):
2411 self.container_client.delete_container(container_name)
2412 LOG.debug('Container %s deleted' % (container_name))
2413
2414 def _upload_object_to_container(self, container_name, obj_name=None):
2415 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
2416 obj_data = data_utils.arbitrary_string()
2417 self.object_client.create_object(container_name, obj_name, obj_data)
2418 return obj_name, obj_data
2419
2420 def _delete_object(self, container_name, filename):
2421 self.object_client.delete_object(container_name, filename)
2422 self._list_and_check_container_objects(container_name,
2423 not_present_obj=[filename])
2424
Ghanshyam2a180b82014-06-16 13:54:22 +09002425 def _list_and_check_container_objects(self, container_name,
2426 present_obj=None,
2427 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01002428 """
2429 List objects for a given container and assert which are present and
2430 which are not.
2431 """
Ghanshyam2a180b82014-06-16 13:54:22 +09002432 if present_obj is None:
2433 present_obj = []
2434 if not_present_obj is None:
2435 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01002436 _, object_list = self.container_client.list_container_contents(
2437 container_name)
2438 if present_obj:
2439 for obj in present_obj:
2440 self.assertIn(obj, object_list)
2441 if not_present_obj:
2442 for obj in not_present_obj:
2443 self.assertNotIn(obj, object_list)
2444
2445 def _change_container_acl(self, container_name, acl):
2446 metadata_param = {'metadata_prefix': 'x-container-',
2447 'metadata': {'read': acl}}
2448 self.container_client.update_container_metadata(container_name,
2449 **metadata_param)
2450 resp, _ = self.container_client.list_container_metadata(container_name)
2451 self.assertEqual(resp['x-container-read'], acl)
2452
2453 def _download_and_verify(self, container_name, obj_name, expected_data):
2454 _, obj = self.object_client.get_object(container_name, obj_name)
2455 self.assertEqual(obj, expected_data)