blob: eb5bdbcd19f7e754f22eac3aee6d53c1be4d9c12 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Attila Fazekasfb7552a2013-08-27 13:02:26 +020017import logging
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120018import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import subprocess
20
Kaitlin Farr366a51f2014-04-21 12:43:54 -040021from cinderclient import exceptions as cinder_exceptions
Matthew Treinishb7144eb2013-12-13 22:57:35 +000022import glanceclient
Sean Dague6dbc6da2013-05-08 17:49:46 -040023import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040024from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090025from novaclient import exceptions as nova_exceptions
Matthew Treinish96e9e882014-06-09 18:37:19 -040026import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040027
Sean Dague1937d092013-05-17 16:36:38 -040028from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000029from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000030from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070031from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040032from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090033from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090034from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000035from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020036from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020037from tempest.openstack.common import log
Yair Fried1fc32a12014-08-04 09:11:30 +030038from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040039import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040040
Matthew Treinish6c072292014-01-29 19:15:52 +000041CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040042
Attila Fazekasfb7552a2013-08-27 13:02:26 +020043LOG = log.getLogger(__name__)
44
45# NOTE(afazekas): Workaround for the stdout logging
46LOG_nova_client = logging.getLogger('novaclient.client')
47LOG_nova_client.addHandler(log.NullHandler())
48
49LOG_cinder_client = logging.getLogger('cinderclient.client')
50LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040051
52
Andrea Frittoli2e733b52014-07-16 14:12:11 +010053class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli247058f2014-07-16 16:09:22 +010054 """Replaces the OfficialClientTest base class.
55
56 Uses tempest own clients as opposed to OfficialClients.
57
58 Common differences:
59 - replace resource.attribute with resource['attribute']
60 - replace resouce.delete with delete_callable(resource['id'])
61 - replace local waiters with common / rest_client waiters
62 """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010063
64 @classmethod
65 def setUpClass(cls):
66 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010067 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010068 cls.isolated_creds = isolated_creds.IsolatedCreds(
69 cls.__name__, tempest_client=True,
70 network_resources=cls.network_resources)
71 cls.manager = clients.Manager(
72 credentials=cls.credentials()
73 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010074 cls.admin_manager = clients.Manager(cls.admin_credentials())
75 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070076 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010077 cls.floating_ips_client = cls.manager.floating_ips_client
78 # Glance image client v1
79 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000080 # Compute image client
81 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010082 cls.keypairs_client = cls.manager.keypairs_client
83 cls.networks_client = cls.admin_manager.networks_client
84 # Nova security groups client
85 cls.security_groups_client = cls.manager.security_groups_client
86 cls.servers_client = cls.manager.servers_client
87 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000088 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030089 cls.interface_client = cls.manager.interfaces_client
90 # Neutron network client
91 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090092 # Heat client
93 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010094
95 @classmethod
96 def _get_credentials(cls, get_creds, ctype):
97 if CONF.compute.allow_tenant_isolation:
98 creds = get_creds()
99 else:
100 creds = auth.get_default_credentials(ctype)
101 return creds
102
103 @classmethod
104 def credentials(cls):
105 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
106 'user')
107
Masayuki Igawaccd66592014-07-17 00:42:42 +0900108 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +0300109 def alt_credentials(cls):
110 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
111 'alt_user')
112
113 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900114 def admin_credentials(cls):
115 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
116 'identity_admin')
117
Andrea Frittoli247058f2014-07-16 16:09:22 +0100118 # ## Methods to handle sync and async deletes
119
120 def setUp(self):
121 super(ScenarioTest, self).setUp()
122 self.cleanup_waits = []
123 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
124 # because scenario tests in the same test class should not share
125 # resources. If resources were shared between test cases then it
126 # should be a single scenario test instead of multiples.
127
128 # NOTE(yfried): this list is cleaned at the end of test_methods and
129 # not at the end of the class
130 self.addCleanup(self._wait_for_cleanups)
131
Yair Fried1fc32a12014-08-04 09:11:30 +0300132 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100133 """Ignores NotFound exceptions for delete operations.
134
Yair Fried1fc32a12014-08-04 09:11:30 +0300135 @param delete_thing: delete method of a resource. method will be
136 executed as delete_thing(*args, **kwargs)
137
Andrea Frittoli247058f2014-07-16 16:09:22 +0100138 """
139 try:
140 # Tempest clients return dicts, so there is no common delete
141 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300142 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100143 except exceptions.NotFound:
144 # If the resource is already missing, mission accomplished.
145 pass
146
147 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900148 cleanup_callable, cleanup_args=None,
149 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700150 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100151
152 @param waiter_callable: callable to wait for the resource to delete
153 @param thing_id: the id of the resource to be cleaned-up
154 @param thing_id_param: the name of the id param in the waiter
155 @param cleanup_callable: method to load pass to self.addCleanup with
156 the following *cleanup_args, **cleanup_kwargs.
157 usually a delete method.
158 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900159 if cleanup_args is None:
160 cleanup_args = []
161 if cleanup_kwargs is None:
162 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100163 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
164 wait_dict = {
165 'waiter_callable': waiter_callable,
166 thing_id_param: thing_id
167 }
168 self.cleanup_waits.append(wait_dict)
169
170 def _wait_for_cleanups(self):
171 """To handle async delete actions, a list of waits is added
172 which will be iterated over as the last step of clearing the
173 cleanup queue. That way all the delete calls are made up front
174 and the tests won't succeed unless the deletes are eventually
175 successful. This is the same basic approach used in the api tests to
176 limit cleanup execution time except here it is multi-resource,
177 because of the nature of the scenario tests.
178 """
179 for wait in self.cleanup_waits:
180 waiter_callable = wait.pop('waiter_callable')
181 waiter_callable(**wait)
182
183 # ## Test functions library
184 #
185 # The create_[resource] functions only return body and discard the
186 # resp part which is not used in scenario tests
187
Yair Frieddb6c9e92014-08-06 08:53:13 +0300188 def create_keypair(self, client=None):
189 if not client:
190 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100191 name = data_utils.rand_name(self.__class__.__name__)
192 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300193 resp, body = client.create_keypair(name)
194 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100195 return body
196
197 def create_server(self, name=None, image=None, flavor=None,
198 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900199 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100200 """Creates VM instance.
201
202 @param image: image from which to create the instance
203 @param wait_on_boot: wait for status ACTIVE before continue
204 @param wait_on_delete: force synchronous delete on cleanup
205 @param create_kwargs: additional details for instance creation
206 @return: server dict
207 """
208 if name is None:
209 name = data_utils.rand_name(self.__class__.__name__)
210 if image is None:
211 image = CONF.compute.image_ref
212 if flavor is None:
213 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900214 if create_kwargs is None:
215 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100216
Andrea Frittoli247058f2014-07-16 16:09:22 +0100217 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
218 name, image, flavor)
219 _, server = self.servers_client.create_server(name, image, flavor,
220 **create_kwargs)
221 if wait_on_delete:
222 self.addCleanup(self.servers_client.wait_for_server_termination,
223 server['id'])
224 self.addCleanup_with_wait(
225 waiter_callable=self.servers_client.wait_for_server_termination,
226 thing_id=server['id'], thing_id_param='server_id',
227 cleanup_callable=self.delete_wrapper,
228 cleanup_args=[self.servers_client.delete_server, server['id']])
229 if wait_on_boot:
230 self.servers_client.wait_for_server_status(server_id=server['id'],
231 status='ACTIVE')
232 # The instance retrieved on creation is missing network
233 # details, necessitating retrieval after it becomes active to
234 # ensure correct details.
235 _, server = self.servers_client.get_server(server['id'])
236 self.assertEqual(server['name'], name)
237 return server
238
239 def create_volume(self, size=1, name=None, snapshot_id=None,
240 imageRef=None, volume_type=None, wait_on_delete=True):
241 if name is None:
242 name = data_utils.rand_name(self.__class__.__name__)
243 _, volume = self.volumes_client.create_volume(
244 size=size, display_name=name, snapshot_id=snapshot_id,
245 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700246
Andrea Frittoli247058f2014-07-16 16:09:22 +0100247 if wait_on_delete:
248 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
249 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700250 self.addCleanup(self.delete_wrapper,
251 self.volumes_client.delete_volume, volume['id'])
252 else:
253 self.addCleanup_with_wait(
254 waiter_callable=self.volumes_client.wait_for_resource_deletion,
255 thing_id=volume['id'], thing_id_param='id',
256 cleanup_callable=self.delete_wrapper,
257 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100258
259 self.assertEqual(name, volume['display_name'])
260 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
261 # The volume retrieved on creation has a non-up-to-date status.
262 # Retrieval after it becomes active ensures correct details.
263 _, volume = self.volumes_client.get_volume(volume['id'])
264 return volume
265
Yair Fried1fc32a12014-08-04 09:11:30 +0300266 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100267 _client = self.security_groups_client
268 if secgroup_id is None:
269 _, sgs = _client.list_security_groups()
270 for sg in sgs:
271 if sg['name'] == 'default':
272 secgroup_id = sg['id']
273
274 # These rules are intended to permit inbound ssh and icmp
275 # traffic from all sources, so no group_id is provided.
276 # Setting a group_id would only permit traffic from ports
277 # belonging to the same security group.
278 rulesets = [
279 {
280 # ssh
281 'ip_proto': 'tcp',
282 'from_port': 22,
283 'to_port': 22,
284 'cidr': '0.0.0.0/0',
285 },
286 {
287 # ping
288 'ip_proto': 'icmp',
289 'from_port': -1,
290 'to_port': -1,
291 'cidr': '0.0.0.0/0',
292 }
293 ]
294 rules = list()
295 for ruleset in rulesets:
296 _, sg_rule = _client.create_security_group_rule(secgroup_id,
297 **ruleset)
298 self.addCleanup(self.delete_wrapper,
299 _client.delete_security_group_rule,
300 sg_rule['id'])
301 rules.append(sg_rule)
302 return rules
303
Yair Fried1fc32a12014-08-04 09:11:30 +0300304 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100305 # Create security group
306 sg_name = data_utils.rand_name(self.__class__.__name__)
307 sg_desc = sg_name + " description"
308 _, secgroup = self.security_groups_client.create_security_group(
309 sg_name, sg_desc)
310 self.assertEqual(secgroup['name'], sg_name)
311 self.assertEqual(secgroup['description'], sg_desc)
312 self.addCleanup(self.delete_wrapper,
313 self.security_groups_client.delete_security_group,
314 secgroup['id'])
315
316 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300317 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100318
319 return secgroup
320
321 def get_remote_client(self, server_or_ip, username=None, private_key=None):
322 if isinstance(server_or_ip, six.string_types):
323 ip = server_or_ip
324 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700325 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
326 ip = addr['addr']
327
Andrea Frittoli247058f2014-07-16 16:09:22 +0100328 if username is None:
329 username = CONF.scenario.ssh_user
330 if private_key is None:
331 private_key = self.keypair['private_key']
332 linux_client = remote_client.RemoteClient(ip, username,
333 pkey=private_key)
334 try:
335 linux_client.validate_authentication()
336 except exceptions.SSHTimeout:
337 LOG.exception('ssh connection to %s failed' % ip)
338 debug.log_net_debug()
339 raise
340
341 return linux_client
342
Ghanshyam2a180b82014-06-16 13:54:22 +0900343 def _image_create(self, name, fmt, path, properties=None):
344 if properties is None:
345 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100346 name = data_utils.rand_name('%s-' % name)
347 image_file = open(path, 'rb')
348 self.addCleanup(image_file.close)
349 params = {
350 'name': name,
351 'container_format': fmt,
352 'disk_format': fmt,
353 'is_public': 'False',
354 }
355 params.update(properties)
356 _, image = self.image_client.create_image(**params)
357 self.addCleanup(self.image_client.delete_image, image['id'])
358 self.assertEqual("queued", image['status'])
359 self.image_client.update_image(image['id'], data=image_file)
360 return image['id']
361
362 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300363 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100364 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
365 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
366 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300367 img_container_format = CONF.scenario.img_container_format
368 img_disk_format = CONF.scenario.img_disk_format
369 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
370 "ami: %s, ari: %s, aki: %s" %
371 (img_path, img_container_format, img_disk_format,
372 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100373 try:
374 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300375 img_container_format,
376 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100377 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300378 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100379 except IOError:
380 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
381 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
382 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
383 properties = {
384 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
385 }
386 self.image = self._image_create('scenario-ami', 'ami',
387 path=ami_img_path,
388 properties=properties)
389 LOG.debug("image:%s" % self.image)
390
391 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400392 if not CONF.compute_feature_enabled.console_output:
393 LOG.debug('Console output not supported, cannot log')
394 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100395 if not servers:
396 _, servers = self.servers_client.list_servers()
397 servers = servers['servers']
398 for server in servers:
399 LOG.debug('Console output for %s', server['id'])
400 LOG.debug(self.servers_client.get_console_output(server['id'],
401 length=None))
402
nithya-ganesan882595e2014-07-29 18:51:07 +0000403 def create_server_snapshot(self, server, name=None):
404 # Glance client
405 _image_client = self.image_client
406 # Compute client
407 _images_client = self.images_client
408 if name is None:
409 name = data_utils.rand_name('scenario-snapshot-')
410 LOG.debug("Creating a snapshot image for server: %s", server['name'])
411 resp, image = _images_client.create_image(server['id'], name)
412 image_id = resp['location'].split('images/')[1]
413 _image_client.wait_for_image_status(image_id, 'active')
414 self.addCleanup_with_wait(
415 waiter_callable=_image_client.wait_for_resource_deletion,
416 thing_id=image_id, thing_id_param='id',
417 cleanup_callable=self.delete_wrapper,
418 cleanup_args=[_image_client.delete_image, image_id])
419 _, snapshot_image = _image_client.get_image_meta(image_id)
420 image_name = snapshot_image['name']
421 self.assertEqual(name, image_name)
422 LOG.debug("Created snapshot image %s for server %s",
423 image_name, server['name'])
424 return snapshot_image
425
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900426 def nova_volume_attach(self):
427 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
428 _, volume_attachment = self.servers_client.attach_volume(
429 self.server['id'], self.volume['id'], '/dev/vdb')
430 volume = volume_attachment['volumeAttachment']
431 self.assertEqual(self.volume['id'], volume['id'])
432 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
433 # Refresh the volume after the attachment
434 _, self.volume = self.volumes_client.get_volume(volume['id'])
435
436 def nova_volume_detach(self):
437 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
438 self.volumes_client.wait_for_volume_status(self.volume['id'],
439 'available')
440
441 _, volume = self.volumes_client.get_volume(self.volume['id'])
442 self.assertEqual('available', volume['status'])
443
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700444 def rebuild_server(self, server_id, image=None,
445 preserve_ephemeral=False, wait=True,
446 rebuild_kwargs=None):
447 if image is None:
448 image = CONF.compute.image_ref
449
450 rebuild_kwargs = rebuild_kwargs or {}
451
452 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
453 server_id, image, preserve_ephemeral)
454 self.servers_client.rebuild(server_id=server_id, image_ref=image,
455 preserve_ephemeral=preserve_ephemeral,
456 **rebuild_kwargs)
457 if wait:
458 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
459
Aaron Rosena7df13b2014-09-23 09:45:45 -0700460 def ping_ip_address(self, ip_address, should_succeed=True):
461 cmd = ['ping', '-c1', '-w1', ip_address]
462
463 def ping():
464 proc = subprocess.Popen(cmd,
465 stdout=subprocess.PIPE,
466 stderr=subprocess.PIPE)
467 proc.communicate()
468 return (proc.returncode == 0) == should_succeed
469
470 return tempest.test.call_until_true(
471 ping, CONF.compute.ping_timeout, 1)
472
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100473
Yair Fried1fc32a12014-08-04 09:11:30 +0300474# TODO(yfried): change this class name to NetworkScenarioTest once client
475# migration is complete
476class NeutronScenarioTest(ScenarioTest):
477 """Base class for network scenario tests.
478 This class provide helpers for network scenario tests, using the neutron
479 API. Helpers from ancestor which use the nova network API are overridden
480 with the neutron API.
481
482 This Class also enforces using Neutron instead of novanetwork.
483 Subclassed tests will be skipped if Neutron is not enabled
484
485 """
486
487 @classmethod
488 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100489 if not CONF.service_available.neutron:
490 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300491
492 @classmethod
493 def setUpClass(cls):
494 super(NeutronScenarioTest, cls).setUpClass()
495 cls.tenant_id = cls.manager.identity_client.tenant_id
496 cls.check_preconditions()
497
Yair Frieddb6c9e92014-08-06 08:53:13 +0300498 def _create_network(self, client=None, tenant_id=None,
499 namestart='network-smoke-'):
500 if not client:
501 client = self.network_client
502 if not tenant_id:
503 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300504 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300505 _, result = client.create_network(name=name, tenant_id=tenant_id)
506 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300507 **result['network'])
508 self.assertEqual(network.name, name)
509 self.addCleanup(self.delete_wrapper, network.delete)
510 return network
511
512 def _list_networks(self, *args, **kwargs):
513 """List networks using admin creds """
514 return self._admin_lister('networks')(*args, **kwargs)
515
516 def _list_subnets(self, *args, **kwargs):
517 """List subnets using admin creds """
518 return self._admin_lister('subnets')(*args, **kwargs)
519
520 def _list_routers(self, *args, **kwargs):
521 """List routers using admin creds """
522 return self._admin_lister('routers')(*args, **kwargs)
523
524 def _list_ports(self, *args, **kwargs):
525 """List ports using admin creds """
526 return self._admin_lister('ports')(*args, **kwargs)
527
528 def _admin_lister(self, resource_type):
529 def temp(*args, **kwargs):
530 temp_method = self.admin_manager.network_client.__getattr__(
531 'list_%s' % resource_type)
532 _, resource_list = temp_method(*args, **kwargs)
533 return resource_list[resource_type]
534 return temp
535
Yair Frieddb6c9e92014-08-06 08:53:13 +0300536 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
537 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300538 """
539 Create a subnet for the given network within the cidr block
540 configured for tenant networks.
541 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300542 if not client:
543 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300544
545 def cidr_in_use(cidr, tenant_id):
546 """
547 :return True if subnet with cidr already exist in tenant
548 False else
549 """
550 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
551 return len(cidr_in_use) != 0
552
553 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
554 result = None
555 # Repeatedly attempt subnet creation with sequential cidr
556 # blocks until an unallocated block is found.
557 for subnet_cidr in tenant_cidr.subnet(
558 CONF.network.tenant_network_mask_bits):
559 str_cidr = str(subnet_cidr)
560 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
561 continue
562
563 subnet = dict(
564 name=data_utils.rand_name(namestart),
565 ip_version=4,
566 network_id=network.id,
567 tenant_id=network.tenant_id,
568 cidr=str_cidr,
569 **kwargs
570 )
571 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300572 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300573 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300574 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300575 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
576 if not is_overlapping_cidr:
577 raise
578 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300579 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300580 **result['subnet'])
581 self.assertEqual(subnet.cidr, str_cidr)
582 self.addCleanup(self.delete_wrapper, subnet.delete)
583 return subnet
584
Yair Frieddb6c9e92014-08-06 08:53:13 +0300585 def _create_port(self, network, client=None, namestart='port-quotatest'):
586 if not client:
587 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300588 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300589 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300590 name=name,
591 network_id=network.id,
592 tenant_id=network.tenant_id)
593 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300594 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300595 **result['port'])
596 self.addCleanup(self.delete_wrapper, port.delete)
597 return port
598
599 def _get_server_port_id(self, server, ip_addr=None):
600 ports = self._list_ports(device_id=server['id'],
601 fixed_ip=ip_addr)
602 self.assertEqual(len(ports), 1,
603 "Unable to determine which port to target.")
604 return ports[0]['id']
605
David Shrewsbury9bac3662014-08-07 15:07:01 -0400606 def _get_network_by_name(self, network_name):
607 net = self._list_networks(name=network_name)
608 return net_common.AttributeDict(net[0])
609
Yair Frieddb6c9e92014-08-06 08:53:13 +0300610 def _create_floating_ip(self, thing, external_network_id, port_id=None,
611 client=None):
612 if not client:
613 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300614 if not port_id:
615 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300616 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300617 floating_network_id=external_network_id,
618 port_id=port_id,
619 tenant_id=thing['tenant_id']
620 )
621 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300622 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300623 **result['floatingip'])
624 self.addCleanup(self.delete_wrapper, floating_ip.delete)
625 return floating_ip
626
627 def _associate_floating_ip(self, floating_ip, server):
628 port_id = self._get_server_port_id(server)
629 floating_ip.update(port_id=port_id)
630 self.assertEqual(port_id, floating_ip.port_id)
631 return floating_ip
632
633 def _disassociate_floating_ip(self, floating_ip):
634 """
635 :param floating_ip: type DeletableFloatingIp
636 """
637 floating_ip.update(port_id=None)
638 self.assertIsNone(floating_ip.port_id)
639 return floating_ip
640
Yair Fried1fc32a12014-08-04 09:11:30 +0300641 def _check_vm_connectivity(self, ip_address,
642 username=None,
643 private_key=None,
644 should_connect=True):
645 """
646 :param ip_address: server to test against
647 :param username: server's ssh username
648 :param private_key: server's ssh private key to be used
649 :param should_connect: True/False indicates positive/negative test
650 positive - attempt ping and ssh
651 negative - attempt ping and fail if succeed
652
653 :raises: AssertError if the result of the connectivity check does
654 not match the value of the should_connect param
655 """
656 if should_connect:
657 msg = "Timed out waiting for %s to become reachable" % ip_address
658 else:
659 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700660 self.assertTrue(self.ping_ip_address(ip_address,
661 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300662 msg=msg)
663 if should_connect:
664 # no need to check ssh for negative connectivity
665 self.get_remote_client(ip_address, username, private_key)
666
667 def _check_public_network_connectivity(self, ip_address, username,
668 private_key, should_connect=True,
669 msg=None, servers=None):
670 # The target login is assumed to have been configured for
671 # key-based authentication by cloud-init.
672 LOG.debug('checking network connections to IP %s with user: %s' %
673 (ip_address, username))
674 try:
675 self._check_vm_connectivity(ip_address,
676 username,
677 private_key,
678 should_connect=should_connect)
679 except Exception as e:
680 ex_msg = 'Public network connectivity check failed'
681 if msg:
682 ex_msg += ": " + msg
683 LOG.exception(ex_msg)
684 self._log_console_output(servers)
685 # network debug is called as part of ssh init
686 if not isinstance(e, exceptions.SSHTimeout):
687 debug.log_net_debug()
688 raise
689
690 def _check_tenant_network_connectivity(self, server,
691 username,
692 private_key,
693 should_connect=True,
694 servers_for_debug=None):
695 if not CONF.network.tenant_networks_reachable:
696 msg = 'Tenant networks not configured to be reachable.'
697 LOG.info(msg)
698 return
699 # The target login is assumed to have been configured for
700 # key-based authentication by cloud-init.
701 try:
702 for net_name, ip_addresses in server['networks'].iteritems():
703 for ip_address in ip_addresses:
704 self._check_vm_connectivity(ip_address,
705 username,
706 private_key,
707 should_connect=should_connect)
708 except Exception as e:
709 LOG.exception('Tenant network connectivity check failed')
710 self._log_console_output(servers_for_debug)
711 # network debug is called as part of ssh init
712 if not isinstance(e, exceptions.SSHTimeout):
713 debug.log_net_debug()
714 raise
715
716 def _check_remote_connectivity(self, source, dest, should_succeed=True):
717 """
718 check ping server via source ssh connection
719
720 :param source: RemoteClient: an ssh connection from which to ping
721 :param dest: and IP to ping against
722 :param should_succeed: boolean should ping succeed or not
723 :returns: boolean -- should_succeed == ping
724 :returns: ping is false if ping failed
725 """
726 def ping_remote():
727 try:
728 source.ping_host(dest)
729 except exceptions.SSHExecCommandFailed:
730 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
731 % (dest, source.ssh_client.host))
732 return not should_succeed
733 return should_succeed
734
735 return tempest.test.call_until_true(ping_remote,
736 CONF.compute.ping_timeout,
737 1)
738
Yair Frieddb6c9e92014-08-06 08:53:13 +0300739 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300740 namestart='secgroup-smoke'):
741 if client is None:
742 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300743 if tenant_id is None:
744 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300745 secgroup = self._create_empty_security_group(namestart=namestart,
746 client=client,
747 tenant_id=tenant_id)
748
749 # Add rules to the security group
750 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
751 for rule in rules:
752 self.assertEqual(tenant_id, rule.tenant_id)
753 self.assertEqual(secgroup.id, rule.security_group_id)
754 return secgroup
755
Yair Frieddb6c9e92014-08-06 08:53:13 +0300756 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300757 namestart='secgroup-smoke'):
758 """Create a security group without rules.
759
760 Default rules will be created:
761 - IPv4 egress to any
762 - IPv6 egress to any
763
764 :param tenant_id: secgroup will be created in this tenant
765 :returns: DeletableSecurityGroup -- containing the secgroup created
766 """
767 if client is None:
768 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300769 if not tenant_id:
770 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300771 sg_name = data_utils.rand_name(namestart)
772 sg_desc = sg_name + " description"
773 sg_dict = dict(name=sg_name,
774 description=sg_desc)
775 sg_dict['tenant_id'] = tenant_id
776 _, result = client.create_security_group(**sg_dict)
777 secgroup = net_resources.DeletableSecurityGroup(
778 client=client,
779 **result['security_group']
780 )
781 self.assertEqual(secgroup.name, sg_name)
782 self.assertEqual(tenant_id, secgroup.tenant_id)
783 self.assertEqual(secgroup.description, sg_desc)
784 self.addCleanup(self.delete_wrapper, secgroup.delete)
785 return secgroup
786
Yair Frieddb6c9e92014-08-06 08:53:13 +0300787 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300788 """Get default secgroup for given tenant_id.
789
790 :returns: DeletableSecurityGroup -- default secgroup for given tenant
791 """
792 if client is None:
793 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300794 if not tenant_id:
795 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300796 sgs = [
797 sg for sg in client.list_security_groups().values()[0]
798 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
799 ]
800 msg = "No default security group for tenant %s." % (tenant_id)
801 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300802 return net_resources.DeletableSecurityGroup(client=client,
803 **sgs[0])
804
Yair Frieddb6c9e92014-08-06 08:53:13 +0300805 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300806 tenant_id=None, **kwargs):
807 """Create a rule from a dictionary of rule parameters.
808
809 Create a rule in a secgroup. if secgroup not defined will search for
810 default secgroup in tenant_id.
811
812 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300813 :param tenant_id: if secgroup not passed -- the tenant in which to
814 search for default secgroup
815 :param kwargs: a dictionary containing rule parameters:
816 for example, to allow incoming ssh:
817 rule = {
818 direction: 'ingress'
819 protocol:'tcp',
820 port_range_min: 22,
821 port_range_max: 22
822 }
823 """
824 if client is None:
825 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300826 if not tenant_id:
827 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300828 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300829 secgroup = self._default_security_group(client=client,
830 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300831
832 ruleset = dict(security_group_id=secgroup.id,
833 tenant_id=secgroup.tenant_id)
834 ruleset.update(kwargs)
835
836 _, sg_rule = client.create_security_group_rule(**ruleset)
837 sg_rule = net_resources.DeletableSecurityGroupRule(
838 client=client,
839 **sg_rule['security_group_rule']
840 )
841 self.addCleanup(self.delete_wrapper, sg_rule.delete)
842 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
843 self.assertEqual(secgroup.id, sg_rule.security_group_id)
844
845 return sg_rule
846
847 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
848 """These rules are intended to permit inbound ssh and icmp
849 traffic from all sources, so no group_id is provided.
850 Setting a group_id would only permit traffic from ports
851 belonging to the same security group.
852 """
853
854 if client is None:
855 client = self.network_client
856 rules = []
857 rulesets = [
858 dict(
859 # ssh
860 protocol='tcp',
861 port_range_min=22,
862 port_range_max=22,
863 ),
864 dict(
865 # ping
866 protocol='icmp',
867 )
868 ]
869 for ruleset in rulesets:
870 for r_direction in ['ingress', 'egress']:
871 ruleset['direction'] = r_direction
872 try:
873 sg_rule = self._create_security_group_rule(
874 client=client, secgroup=secgroup, **ruleset)
875 except exceptions.Conflict as ex:
876 # if rule already exist - skip rule and continue
877 msg = 'Security group rule already exists'
878 if msg not in ex._error_string:
879 raise ex
880 else:
881 self.assertEqual(r_direction, sg_rule.direction)
882 rules.append(sg_rule)
883
884 return rules
885
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500886 def _create_pool(self, lb_method, protocol, subnet_id):
887 """Wrapper utility that returns a test pool."""
888 client = self.network_client
889 name = data_utils.rand_name('pool')
890 _, resp_pool = client.create_pool(protocol=protocol, name=name,
891 subnet_id=subnet_id,
892 lb_method=lb_method)
893 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
894 self.assertEqual(pool['name'], name)
895 self.addCleanup(self.delete_wrapper, pool.delete)
896 return pool
897
898 def _create_member(self, address, protocol_port, pool_id):
899 """Wrapper utility that returns a test member."""
900 client = self.network_client
901 _, resp_member = client.create_member(protocol_port=protocol_port,
902 pool_id=pool_id,
903 address=address)
904 member = net_resources.DeletableMember(client=client,
905 **resp_member['member'])
906 self.addCleanup(self.delete_wrapper, member.delete)
907 return member
908
909 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
910 """Wrapper utility that returns a test vip."""
911 client = self.network_client
912 name = data_utils.rand_name('vip')
913 _, resp_vip = client.create_vip(protocol=protocol, name=name,
914 subnet_id=subnet_id, pool_id=pool_id,
915 protocol_port=protocol_port)
916 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
917 self.assertEqual(vip['name'], name)
918 self.addCleanup(self.delete_wrapper, vip.delete)
919 return vip
920
Yair Fried1fc32a12014-08-04 09:11:30 +0300921 def _ssh_to_server(self, server, private_key):
922 ssh_login = CONF.compute.image_ssh_user
923 return self.get_remote_client(server,
924 username=ssh_login,
925 private_key=private_key)
926
Yair Frieddb6c9e92014-08-06 08:53:13 +0300927 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300928 """Retrieve a router for the given tenant id.
929
930 If a public router has been configured, it will be returned.
931
932 If a public router has not been configured, but a public
933 network has, a tenant router will be created and returned that
934 routes traffic to the public network.
935 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300936 if not client:
937 client = self.network_client
938 if not tenant_id:
939 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300940 router_id = CONF.network.public_router_id
941 network_id = CONF.network.public_network_id
942 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300943 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300944 return net_resources.AttributeDict(**result['router'])
945 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300946 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300947 router.set_gateway(network_id)
948 return router
949 else:
950 raise Exception("Neither of 'public_router_id' or "
951 "'public_network_id' has been defined.")
952
Yair Frieddb6c9e92014-08-06 08:53:13 +0300953 def _create_router(self, client=None, tenant_id=None,
954 namestart='router-smoke'):
955 if not client:
956 client = self.network_client
957 if not tenant_id:
958 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300959 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300960 _, result = client.create_router(name=name,
961 admin_state_up=True,
962 tenant_id=tenant_id)
963 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300964 **result['router'])
965 self.assertEqual(router.name, name)
966 self.addCleanup(self.delete_wrapper, router.delete)
967 return router
968
Yair Frieddb6c9e92014-08-06 08:53:13 +0300969 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300970 """Create a network with a subnet connected to a router.
971
David Shrewsbury9bac3662014-08-07 15:07:01 -0400972 The baremetal driver is a special case since all nodes are
973 on the same shared network.
974
Yair Fried1fc32a12014-08-04 09:11:30 +0300975 :returns: network, subnet, router
976 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400977 if CONF.baremetal.driver_enabled:
978 # NOTE(Shrews): This exception is for environments where tenant
979 # credential isolation is available, but network separation is
980 # not (the current baremetal case). Likely can be removed when
981 # test account mgmt is reworked:
982 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
983 network = self._get_network_by_name(
984 CONF.compute.fixed_network_name)
985 router = None
986 subnet = None
987 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300988 network = self._create_network(client=client, tenant_id=tenant_id)
989 router = self._get_router(client=client, tenant_id=tenant_id)
990 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400991 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300992 return network, subnet, router
993
994
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400995class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400996 """
997 Official Client test base class for scenario testing.
998
999 Official Client tests are tests that have the following characteristics:
1000
1001 * Test basic operations of an API, typically in an order that
1002 a regular user would perform those operations
1003 * Test only the correct inputs and action paths -- no fuzz or
1004 random input data is sent, only valid inputs.
1005 * Use only the default client tool for calling an API
1006 """
1007
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001008 @classmethod
1009 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +02001010 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -04001011 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -05001012 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -05001013 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001014
Andrea Frittolif9cde7e2014-02-18 09:57:04 +00001015 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001016 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001017 cls.compute_client = cls.manager.compute_client
1018 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -07001019 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001020 cls.identity_client = cls.manager.identity_client
1021 cls.network_client = cls.manager.network_client
1022 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +00001023 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001024 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +04001025 cls.data_processing_client = cls.manager.data_processing_client
Artur Svechnikovc3bf9252014-05-05 16:37:37 +04001026 cls.ceilometer_client = cls.manager.ceilometer_client
Sean Dague6dbc6da2013-05-08 17:49:46 -04001027
1028 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001029 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +00001030 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001031 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +02001032 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001033 creds = auth.get_default_credentials(ctype)
1034 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001035
1036 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +02001037 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001038 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
1039 'user')
Yair Frieda71cc442013-12-18 13:32:36 +02001040
1041 @classmethod
1042 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001043 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
1044 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +02001045
1046 @classmethod
1047 def admin_credentials(cls):
1048 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001049 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +02001050
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001051 def setUp(self):
1052 super(OfficialClientTest, self).setUp()
1053 self.cleanup_waits = []
1054 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
1055 # because scenario tests in the same test class should not share
1056 # resources. If resources were shared between test cases then it
1057 # should be a single scenario test instead of multiples.
Yair Friedbf2e2c42014-01-28 12:06:38 +02001058
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001059 # NOTE(yfried): this list is cleaned at the end of test_methods and
1060 # not at the end of the class
1061 self.addCleanup(self._wait_for_cleanups)
1062
1063 @staticmethod
1064 def not_found_exception(exception):
1065 """
1066 @return: True if exception is of NotFound type
1067 """
1068 NOT_FOUND_LIST = ['NotFound', 'HTTPNotFound']
1069 return (exception.__class__.__name__ in NOT_FOUND_LIST
1070 or
1071 hasattr(exception, 'status_code') and
1072 exception.status_code == 404)
1073
1074 def delete_wrapper(self, thing):
1075 """Ignores NotFound exceptions for delete operations.
1076
1077 @param thing: object with delete() method.
1078 OpenStack resources are assumed to have a delete() method which
1079 destroys the resource
1080 """
1081
Yair Friedbf2e2c42014-01-28 12:06:38 +02001082 try:
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001083 thing.delete()
Yair Friedbf2e2c42014-01-28 12:06:38 +02001084 except Exception as e:
1085 # If the resource is already missing, mission accomplished.
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001086 if not self.not_found_exception(e):
Yair Friedbf2e2c42014-01-28 12:06:38 +02001087 raise
Yair Friedbf2e2c42014-01-28 12:06:38 +02001088
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001089 def _wait_for_cleanups(self):
1090 """To handle async delete actions, a list of waits is added
1091 which will be iterated over as the last step of clearing the
1092 cleanup queue. That way all the delete calls are made up front
1093 and the tests won't succeed unless the deletes are eventually
1094 successful. This is the same basic approach used in the api tests to
1095 limit cleanup execution time except here it is multi-resource,
1096 because of the nature of the scenario tests.
1097 """
1098 for wait in self.cleanup_waits:
1099 self.delete_timeout(**wait)
Yair Friedbf2e2c42014-01-28 12:06:38 +02001100
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001101 def addCleanup_with_wait(self, things, thing_id,
1102 error_status='ERROR',
1103 exc_type=nova_exceptions.NotFound,
Ghanshyam2a180b82014-06-16 13:54:22 +09001104 cleanup_callable=None, cleanup_args=None,
1105 cleanup_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001106 """Adds wait for ansyc resource deletion at the end of cleanups
Sean Dague6dbc6da2013-05-08 17:49:46 -04001107
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001108 @param things: type of the resource to delete
1109 @param thing_id:
1110 @param error_status: see manager.delete_timeout()
1111 @param exc_type: see manager.delete_timeout()
1112 @param cleanup_callable: method to load pass to self.addCleanup with
1113 the following *cleanup_args, **cleanup_kwargs.
1114 usually a delete method. if not used, will try to use:
1115 things.delete(thing_id)
1116 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001117 if cleanup_args is None:
1118 cleanup_args = []
1119 if cleanup_kwargs is None:
1120 cleanup_kwargs = {}
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001121 if cleanup_callable is None:
1122 LOG.debug("no delete method passed. using {rclass}.delete({id}) as"
1123 " default".format(rclass=things, id=thing_id))
1124 self.addCleanup(things.delete, thing_id)
1125 else:
1126 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
1127 wait_dict = {
1128 'things': things,
1129 'thing_id': thing_id,
1130 'error_status': error_status,
1131 'not_found_exception': exc_type,
1132 }
1133 self.cleanup_waits.append(wait_dict)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001134
Steve Bakerefde7612013-09-30 11:29:23 +13001135 def status_timeout(self, things, thing_id, expected_status,
1136 error_status='ERROR',
1137 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001138 """
1139 Given a thing and an expected status, do a loop, sleeping
1140 for a configurable amount of time, checking for the
1141 expected status to show. At any time, if the returned
1142 status of the thing is ERROR, fail out.
1143 """
Steve Bakerefde7612013-09-30 11:29:23 +13001144 self._status_timeout(things, thing_id,
1145 expected_status=expected_status,
1146 error_status=error_status,
1147 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001148
Steve Bakerefde7612013-09-30 11:29:23 +13001149 def delete_timeout(self, things, thing_id,
1150 error_status='ERROR',
1151 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001152 """
1153 Given a thing, do a loop, sleeping
1154 for a configurable amount of time, checking for the
1155 deleted status to show. At any time, if the returned
1156 status of the thing is ERROR, fail out.
1157 """
1158 self._status_timeout(things,
1159 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +13001160 allow_notfound=True,
1161 error_status=error_status,
1162 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001163
1164 def _status_timeout(self,
1165 things,
1166 thing_id,
1167 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +13001168 allow_notfound=False,
1169 error_status='ERROR',
1170 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001171
1172 log_status = expected_status if expected_status else ''
1173 if allow_notfound:
1174 log_status += ' or NotFound' if log_status != '' else 'NotFound'
1175
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001176 def check_status():
1177 # python-novaclient has resources available to its client
1178 # that all implement a get() method taking an identifier
1179 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001180 try:
1181 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +13001182 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001183 if allow_notfound:
1184 return True
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001185 raise
1186 except Exception as e:
1187 if allow_notfound and self.not_found_exception(e):
1188 return True
1189 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001190
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001191 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -05001192
1193 # Some components are reporting error status in lower case
1194 # so case sensitive comparisons can really mess things
1195 # up.
1196 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +09001197 message = ("%s failed to get to expected status (%s). "
1198 "In %s state.") % (thing, expected_status,
1199 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +09001200 raise exceptions.BuildErrorException(message,
1201 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001202 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001203 return True # All good.
1204 LOG.debug("Waiting for %s to get to %s status. "
1205 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001206 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001207 if not tempest.test.call_until_true(
1208 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +00001209 CONF.compute.build_timeout,
1210 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +09001211 message = ("Timed out waiting for thing %s "
1212 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +02001213 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001214
Yair Friedeb69f3f2013-10-10 13:18:16 +03001215 def _create_loginable_secgroup_rule_nova(self, client=None,
1216 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001217 if client is None:
1218 client = self.compute_client
1219 if secgroup_id is None:
1220 sgs = client.security_groups.list()
1221 for sg in sgs:
1222 if sg.name == 'default':
1223 secgroup_id = sg.id
1224
1225 # These rules are intended to permit inbound ssh and icmp
1226 # traffic from all sources, so no group_id is provided.
1227 # Setting a group_id would only permit traffic from ports
1228 # belonging to the same security group.
1229 rulesets = [
1230 {
1231 # ssh
1232 'ip_protocol': 'tcp',
1233 'from_port': 22,
1234 'to_port': 22,
1235 'cidr': '0.0.0.0/0',
1236 },
1237 {
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001238 # ssh -6
1239 'ip_protocol': 'tcp',
1240 'from_port': 22,
1241 'to_port': 22,
1242 'cidr': '::/0',
1243 },
1244 {
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001245 # ping
1246 'ip_protocol': 'icmp',
1247 'from_port': -1,
1248 'to_port': -1,
1249 'cidr': '0.0.0.0/0',
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001250 },
1251 {
1252 # ping6
1253 'ip_protocol': 'icmp',
1254 'from_port': -1,
1255 'to_port': -1,
1256 'cidr': '::/0',
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001257 }
1258 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +03001259 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001260 for ruleset in rulesets:
1261 sg_rule = client.security_group_rules.create(secgroup_id,
1262 **ruleset)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001263 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03001264 rules.append(sg_rule)
1265 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001266
Grishkin0f1e11c2014-05-04 20:44:52 +04001267 def _create_security_group_nova(self, client=None,
1268 namestart='secgroup-smoke-'):
1269 if client is None:
1270 client = self.compute_client
1271 # Create security group
1272 sg_name = data_utils.rand_name(namestart)
1273 sg_desc = sg_name + " description"
1274 secgroup = client.security_groups.create(sg_name, sg_desc)
1275 self.assertEqual(secgroup.name, sg_name)
1276 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001277 self.addCleanup(self.delete_wrapper, secgroup)
Grishkin0f1e11c2014-05-04 20:44:52 +04001278
1279 # Add rules to the security group
1280 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
1281
1282 return secgroup
1283
David Shrewsbury02719362014-05-20 14:10:03 -04001284 def rebuild_server(self, server, client=None, image=None,
1285 preserve_ephemeral=False, wait=True,
1286 rebuild_kwargs=None):
1287 if client is None:
1288 client = self.compute_client
1289 if image is None:
1290 image = CONF.compute.image_ref
1291 rebuild_kwargs = rebuild_kwargs or {}
1292
1293 LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
1294 server.name, image, preserve_ephemeral)
1295 server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
1296 **rebuild_kwargs)
1297 if wait:
1298 self.status_timeout(client.servers, server.id, 'ACTIVE')
1299
Giulio Fidente61cadca2013-09-24 18:33:37 +02001300 def create_server(self, client=None, name=None, image=None, flavor=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001301 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +09001302 create_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001303 """Creates VM instance.
1304
1305 @param client: compute client to create the instance
1306 @param image: image from which to create the instance
1307 @param wait_on_boot: wait for status ACTIVE before continue
1308 @param wait_on_delete: force synchronous delete on cleanup
1309 @param create_kwargs: additional details for instance creation
1310 @return: client.server object
1311 """
Giulio Fidente61cadca2013-09-24 18:33:37 +02001312 if client is None:
1313 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001314 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001315 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001316 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001317 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001318 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001319 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +09001320 if create_kwargs is None:
1321 create_kwargs = {}
JordanP9c052aa2014-01-24 13:05:00 +00001322
1323 fixed_network_name = CONF.compute.fixed_network_name
1324 if 'nics' not in create_kwargs and fixed_network_name:
1325 networks = client.networks.list()
1326 # If several networks found, set the NetID on which to connect the
1327 # server to avoid the following error "Multiple possible networks
1328 # found, use a Network ID to be more specific."
1329 # See Tempest #1250866
1330 if len(networks) > 1:
1331 for network in networks:
1332 if network.label == fixed_network_name:
1333 create_kwargs['nics'] = [{'net-id': network.id}]
1334 break
1335 # If we didn't find the network we were looking for :
1336 else:
1337 msg = ("The network on which the NIC of the server must "
1338 "be connected can not be found : "
1339 "fixed_network_name=%s. Starting instance without "
1340 "specifying a network.") % fixed_network_name
1341 LOG.info(msg)
1342
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001343 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
1344 name, image, flavor)
1345 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +02001346 self.assertEqual(server.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001347 if wait_on_delete:
1348 self.addCleanup(self.delete_timeout,
1349 self.compute_client.servers,
1350 server.id)
1351 self.addCleanup_with_wait(self.compute_client.servers, server.id,
1352 cleanup_callable=self.delete_wrapper,
1353 cleanup_args=[server])
1354 if wait_on_boot:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001355 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001356 # The instance retrieved on creation is missing network
1357 # details, necessitating retrieval after it becomes active to
1358 # ensure correct details.
1359 server = client.servers.get(server.id)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001360 LOG.debug("Created server: %s", server)
1361 return server
1362
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001363 def create_volume(self, client=None, size=1, name=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001364 snapshot_id=None, imageRef=None, volume_type=None,
1365 wait_on_delete=True):
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001366 if client is None:
1367 client = self.volume_client
1368 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001369 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -07001370 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001371 volume = client.volumes.create(size=size, display_name=name,
1372 snapshot_id=snapshot_id,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001373 imageRef=imageRef,
1374 volume_type=volume_type)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001375 if wait_on_delete:
1376 self.addCleanup(self.delete_timeout,
1377 self.volume_client.volumes,
1378 volume.id)
1379 self.addCleanup_with_wait(self.volume_client.volumes, volume.id,
1380 exc_type=cinder_exceptions.NotFound)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001381 self.assertEqual(name, volume.display_name)
1382 self.status_timeout(client.volumes, volume.id, 'available')
1383 LOG.debug("Created volume: %s", volume)
1384 return volume
1385
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001386 def create_server_snapshot(self, server, compute_client=None,
1387 image_client=None, name=None):
1388 if compute_client is None:
1389 compute_client = self.compute_client
1390 if image_client is None:
1391 image_client = self.image_client
1392 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001393 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001394 LOG.debug("Creating a snapshot image for server: %s", server.name)
1395 image_id = compute_client.servers.create_image(server, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001396 self.addCleanup_with_wait(self.image_client.images, image_id,
1397 exc_type=glanceclient.exc.HTTPNotFound)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001398 self.status_timeout(image_client.images, image_id, 'active')
1399 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -07001400 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001401 LOG.debug("Created snapshot image %s for server %s",
1402 snapshot_image.name, server.name)
1403 return snapshot_image
1404
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001405 def create_keypair(self, client=None, name=None):
1406 if client is None:
1407 client = self.compute_client
1408 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001409 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001410 keypair = client.keypairs.create(name)
1411 self.assertEqual(keypair.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001412 self.addCleanup(self.delete_wrapper, keypair)
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001413 return keypair
1414
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001415 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +08001416 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001417 ip = server_or_ip
1418 else:
Matthew Treinish6c072292014-01-29 19:15:52 +00001419 network_name_for_ssh = CONF.compute.network_for_ssh
Adam Gandelman0d9508e2014-08-22 10:58:09 -07001420 ip = server_or_ip.networks[network_name_for_ssh][0]
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001421 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001422 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001423 if private_key is None:
1424 private_key = self.keypair.private_key
Yair Fried3960c4d2014-05-07 15:20:30 +03001425 linux_client = remote_client.RemoteClient(ip, username,
1426 pkey=private_key)
1427 try:
1428 linux_client.validate_authentication()
1429 except exceptions.SSHTimeout:
1430 LOG.exception('ssh connection to %s failed' % ip)
1431 debug.log_net_debug()
1432 raise
1433
1434 return linux_client
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001435
Nachi Ueno95b41282014-01-15 06:54:21 -08001436 def _log_console_output(self, servers=None):
Adam Gandelmanc6eefb42014-07-15 16:44:08 -07001437 if not CONF.compute_feature_enabled.console_output:
1438 LOG.debug('Console output not supported, cannot log')
1439 return
Nachi Ueno95b41282014-01-15 06:54:21 -08001440 if not servers:
1441 servers = self.compute_client.servers.list()
1442 for server in servers:
1443 LOG.debug('Console output for %s', server.id)
1444 LOG.debug(server.get_console_output())
1445
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001446 def wait_for_volume_status(self, status):
1447 volume_id = self.volume.id
1448 self.status_timeout(
1449 self.volume_client.volumes, volume_id, status)
1450
Ghanshyam2a180b82014-06-16 13:54:22 +09001451 def _image_create(self, name, fmt, path, properties=None):
1452 if properties is None:
1453 properties = {}
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001454 name = data_utils.rand_name('%s-' % name)
1455 image_file = open(path, 'rb')
1456 self.addCleanup(image_file.close)
1457 params = {
1458 'name': name,
1459 'container_format': fmt,
1460 'disk_format': fmt,
Aaron Rosenc7720622014-05-20 10:38:10 -07001461 'is_public': 'False',
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001462 }
1463 params.update(properties)
1464 image = self.image_client.images.create(**params)
1465 self.addCleanup(self.image_client.images.delete, image)
1466 self.assertEqual("queued", image.status)
1467 image.update(data=image_file)
1468 return image.id
1469
1470 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001471 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001472 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
1473 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
1474 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001475 img_container_format = CONF.scenario.img_container_format
1476 img_disk_format = CONF.scenario.img_disk_format
1477 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
1478 "ami: %s, ari: %s, aki: %s" %
1479 (img_path, img_container_format, img_disk_format,
1480 ami_img_path, ari_img_path, aki_img_path))
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001481 try:
1482 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001483 img_container_format,
1484 img_path,
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001485 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001486 img_disk_format})
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001487 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +09001488 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001489 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
1490 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
1491 properties = {
1492 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
1493 }
1494 self.image = self._image_create('scenario-ami', 'ami',
1495 path=ami_img_path,
1496 properties=properties)
1497 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001498
Sean Dague6dbc6da2013-05-08 17:49:46 -04001499
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001500# power/provision states as of icehouse
1501class BaremetalPowerStates(object):
1502 """Possible power states of an Ironic node."""
1503 POWER_ON = 'power on'
1504 POWER_OFF = 'power off'
1505 REBOOT = 'rebooting'
1506 SUSPEND = 'suspended'
1507
1508
1509class BaremetalProvisionStates(object):
1510 """Possible provision states of an Ironic node."""
1511 NOSTATE = None
1512 INIT = 'initializing'
1513 ACTIVE = 'active'
1514 BUILDING = 'building'
1515 DEPLOYWAIT = 'wait call-back'
1516 DEPLOYING = 'deploying'
1517 DEPLOYFAIL = 'deploy failed'
1518 DEPLOYDONE = 'deploy complete'
1519 DELETING = 'deleting'
1520 DELETED = 'deleted'
1521 ERROR = 'error'
1522
1523
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001524class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001525 @classmethod
1526 def setUpClass(cls):
1527 super(BaremetalScenarioTest, cls).setUpClass()
1528
1529 if (not CONF.service_available.ironic or
1530 not CONF.baremetal.driver_enabled):
1531 msg = 'Ironic not available or Ironic compute driver not enabled'
1532 raise cls.skipException(msg)
1533
1534 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001535 manager = clients.Manager(
1536 credentials=cls.admin_credentials()
1537 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001538 cls.baremetal_client = manager.baremetal_client
1539
1540 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001541 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001542
1543 def _node_state_timeout(self, node_id, state_attr,
1544 target_states, timeout=10, interval=1):
1545 if not isinstance(target_states, list):
1546 target_states = [target_states]
1547
1548 def check_state():
1549 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001550 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001551 return True
1552 return False
1553
1554 if not tempest.test.call_until_true(
1555 check_state, timeout, interval):
1556 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1557 (node_id, state_attr, target_states))
1558 raise exceptions.TimeoutException(msg)
1559
1560 def wait_provisioning_state(self, node_id, state, timeout):
1561 self._node_state_timeout(
1562 node_id=node_id, state_attr='provision_state',
1563 target_states=state, timeout=timeout)
1564
1565 def wait_power_state(self, node_id, state):
1566 self._node_state_timeout(
1567 node_id=node_id, state_attr='power_state',
1568 target_states=state, timeout=CONF.baremetal.power_timeout)
1569
1570 def wait_node(self, instance_id):
1571 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001572
Adam Gandelman4a48a602014-03-20 18:23:18 -07001573 def _get_node():
1574 node = None
1575 try:
1576 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001577 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001578 pass
1579 return node is not None
1580
1581 if not tempest.test.call_until_true(
1582 _get_node, CONF.baremetal.association_timeout, 1):
1583 msg = ('Timed out waiting to get Ironic node by instance id %s'
1584 % instance_id)
1585 raise exceptions.TimeoutException(msg)
1586
1587 def get_node(self, node_id=None, instance_id=None):
1588 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001589 _, body = self.baremetal_client.show_node(node_id)
1590 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001591 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001592 _, body = self.baremetal_client.show_node_by_instance_uuid(
1593 instance_id)
1594 if body['nodes']:
1595 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001596
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001597 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001598 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001599 _, body = self.baremetal_client.list_node_ports(node_uuid)
1600 for port in body['ports']:
1601 _, p = self.baremetal_client.show_port(port['uuid'])
1602 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001603 return ports
1604
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001605 def add_keypair(self):
1606 self.keypair = self.create_keypair()
1607
1608 def verify_connectivity(self, ip=None):
1609 if ip:
1610 dest = self.get_remote_client(ip)
1611 else:
1612 dest = self.get_remote_client(self.instance)
1613 dest.validate_authentication()
1614
1615 def boot_instance(self):
1616 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001617 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001618 }
1619 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001620 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001621
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001622 self.wait_node(self.instance['id'])
1623 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001624
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001625 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001626
1627 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001628 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001629 [BaremetalProvisionStates.DEPLOYWAIT,
1630 BaremetalProvisionStates.ACTIVE],
1631 timeout=15)
1632
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001633 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001634 BaremetalProvisionStates.ACTIVE,
1635 timeout=CONF.baremetal.active_timeout)
1636
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001637 self.servers_client.wait_for_server_status(self.instance['id'],
1638 'ACTIVE')
1639 self.node = self.get_node(instance_id=self.instance['id'])
1640 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001641
1642 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001643 self.servers_client.delete_server(self.instance['id'])
1644 self.wait_power_state(self.node['uuid'],
1645 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001646 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001647 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001648 BaremetalProvisionStates.NOSTATE,
1649 timeout=CONF.baremetal.unprovision_timeout)
1650
Adam Gandelman4a48a602014-03-20 18:23:18 -07001651
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001652class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001653 """
1654 Base class for encryption scenario tests
1655 """
1656
1657 @classmethod
1658 def setUpClass(cls):
1659 super(EncryptionScenarioTest, cls).setUpClass()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001660 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001661
1662 def _wait_for_volume_status(self, status):
1663 self.status_timeout(
1664 self.volume_client.volumes, self.volume.id, status)
1665
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001666 def nova_boot(self):
1667 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001668 create_kwargs = {'key_name': self.keypair['name']}
1669 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001670 create_kwargs=create_kwargs)
1671
1672 def create_volume_type(self, client=None, name=None):
1673 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001674 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001675 if not name:
1676 name = 'generic'
1677 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1678 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001679 _, body = client.create_volume_type(
1680 randomized_name)
1681 self.assertIn('id', body)
1682 self.addCleanup(client.delete_volume_type, body['id'])
1683 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001684
1685 def create_encryption_type(self, client=None, type_id=None, provider=None,
1686 key_size=None, cipher=None,
1687 control_location=None):
1688 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001689 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001690 if not type_id:
1691 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001692 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001693 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001694 client.create_encryption_type(
1695 type_id, provider=provider, key_size=key_size, cipher=cipher,
1696 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001697
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001698
Sean Dague6dbc6da2013-05-08 17:49:46 -04001699class NetworkScenarioTest(OfficialClientTest):
1700 """
1701 Base class for network scenario tests
1702 """
1703
1704 @classmethod
1705 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001706 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001707 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +02001708 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -04001709 try:
1710 cls.network_client.list_networks()
1711 except exc.EndpointNotFound:
1712 cls.enabled = False
1713 raise
1714 else:
1715 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -04001716 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -04001717 raise cls.skipException(msg)
1718
1719 @classmethod
1720 def setUpClass(cls):
1721 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001722 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -04001723
Sean Dague6dbc6da2013-05-08 17:49:46 -04001724 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001725 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001726 body = dict(
1727 network=dict(
1728 name=name,
1729 tenant_id=tenant_id,
1730 ),
1731 )
1732 result = self.network_client.create_network(body=body)
1733 network = net_common.DeletableNetwork(client=self.network_client,
1734 **result['network'])
1735 self.assertEqual(network.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001736 self.addCleanup(self.delete_wrapper, network)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001737 return network
1738
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001739 def _list_networks(self, **kwargs):
1740 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001741 return nets['networks']
1742
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001743 def _list_subnets(self, **kwargs):
1744 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001745 return subnets['subnets']
1746
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001747 def _list_routers(self, **kwargs):
1748 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001749 return routers['routers']
1750
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001751 def _list_ports(self, **kwargs):
1752 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001753 return ports['ports']
1754
1755 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001756 nets = self._list_networks(tenant_id=tenant_id)
1757 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001758
1759 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001760 subnets = self._list_subnets(tenant_id=tenant_id)
1761 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001762
1763 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001764 ports = self._list_ports(tenant_id=tenant_id)
1765 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001766
Yair Fried3097dc12014-01-26 08:46:43 +02001767 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001768 """
1769 Create a subnet for the given network within the cidr block
1770 configured for tenant networks.
1771 """
Attila Fazekase857bd62013-10-21 21:02:44 +02001772
1773 def cidr_in_use(cidr, tenant_id):
1774 """
1775 :return True if subnet with cidr already exist in tenant
1776 False else
1777 """
1778 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
1779 return len(cidr_in_use) != 0
1780
Matthew Treinish6c072292014-01-29 19:15:52 +00001781 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001782 result = None
1783 # Repeatedly attempt subnet creation with sequential cidr
1784 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +00001785 for subnet_cidr in tenant_cidr.subnet(
1786 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +02001787 str_cidr = str(subnet_cidr)
1788 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
1789 continue
1790
Sean Dague6dbc6da2013-05-08 17:49:46 -04001791 body = dict(
1792 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +02001793 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -04001794 ip_version=4,
1795 network_id=network.id,
1796 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +02001797 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001798 ),
1799 )
Yair Fried3097dc12014-01-26 08:46:43 +02001800 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001801 try:
1802 result = self.network_client.create_subnet(body=body)
1803 break
Mark McClainf2982e82013-07-06 17:48:03 -04001804 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -04001805 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1806 if not is_overlapping_cidr:
1807 raise
1808 self.assertIsNotNone(result, 'Unable to allocate tenant network')
1809 subnet = net_common.DeletableSubnet(client=self.network_client,
1810 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +02001811 self.assertEqual(subnet.cidr, str_cidr)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001812 self.addCleanup(self.delete_wrapper, subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001813 return subnet
1814
1815 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001816 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001817 body = dict(
1818 port=dict(name=name,
1819 network_id=network.id,
1820 tenant_id=network.tenant_id))
1821 result = self.network_client.create_port(body=body)
1822 self.assertIsNotNone(result, 'Unable to allocate port')
1823 port = net_common.DeletablePort(client=self.network_client,
1824 **result['port'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001825 self.addCleanup(self.delete_wrapper, port)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001826 return port
1827
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001828 def _get_server_port_id(self, server, ip_addr=None):
1829 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001830 self.assertEqual(len(ports), 1,
1831 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +02001832 return ports[0]['id']
1833
David Shrewsbury9bac3662014-08-07 15:07:01 -04001834 def _get_network_by_name(self, network_name):
1835 net = self._list_networks(name=network_name)
1836 return net_common.AttributeDict(net[0])
1837
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001838 def _create_floating_ip(self, thing, external_network_id, port_id=None):
1839 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001840 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001841 body = dict(
1842 floatingip=dict(
1843 floating_network_id=external_network_id,
1844 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001845 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001846 )
1847 )
1848 result = self.network_client.create_floatingip(body=body)
1849 floating_ip = net_common.DeletableFloatingIp(
1850 client=self.network_client,
1851 **result['floatingip'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001852 self.addCleanup(self.delete_wrapper, floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001853 return floating_ip
1854
Yair Fried05db2522013-11-18 11:02:10 +02001855 def _associate_floating_ip(self, floating_ip, server):
1856 port_id = self._get_server_port_id(server)
1857 floating_ip.update(port_id=port_id)
1858 self.assertEqual(port_id, floating_ip.port_id)
1859 return floating_ip
1860
Yair Fried9a551c42013-12-15 14:59:34 +02001861 def _disassociate_floating_ip(self, floating_ip):
1862 """
1863 :param floating_ip: type DeletableFloatingIp
1864 """
1865 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +08001866 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +02001867 return floating_ip
1868
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001869 def _create_pool(self, lb_method, protocol, subnet_id):
1870 """Wrapper utility that returns a test pool."""
1871 name = data_utils.rand_name('pool-')
1872 body = {
1873 "pool": {
1874 "protocol": protocol,
1875 "name": name,
1876 "subnet_id": subnet_id,
1877 "lb_method": lb_method
1878 }
1879 }
1880 resp = self.network_client.create_pool(body=body)
1881 pool = net_common.DeletablePool(client=self.network_client,
1882 **resp['pool'])
1883 self.assertEqual(pool['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001884 self.addCleanup(self.delete_wrapper, pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001885 return pool
1886
1887 def _create_member(self, address, protocol_port, pool_id):
1888 """Wrapper utility that returns a test member."""
1889 body = {
1890 "member": {
1891 "protocol_port": protocol_port,
1892 "pool_id": pool_id,
1893 "address": address
1894 }
1895 }
1896 resp = self.network_client.create_member(body)
1897 member = net_common.DeletableMember(client=self.network_client,
1898 **resp['member'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001899 self.addCleanup(self.delete_wrapper, member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001900 return member
1901
1902 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
1903 """Wrapper utility that returns a test vip."""
1904 name = data_utils.rand_name('vip-')
1905 body = {
1906 "vip": {
1907 "protocol": protocol,
1908 "name": name,
1909 "subnet_id": subnet_id,
1910 "pool_id": pool_id,
1911 "protocol_port": protocol_port
1912 }
1913 }
1914 resp = self.network_client.create_vip(body)
1915 vip = net_common.DeletableVip(client=self.network_client,
1916 **resp['vip'])
1917 self.assertEqual(vip['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001918 self.addCleanup(self.delete_wrapper, vip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001919 return vip
1920
Yair Fried9a551c42013-12-15 14:59:34 +02001921 def _check_vm_connectivity(self, ip_address,
1922 username=None,
1923 private_key=None,
1924 should_connect=True):
1925 """
1926 :param ip_address: server to test against
1927 :param username: server's ssh username
1928 :param private_key: server's ssh private key to be used
1929 :param should_connect: True/False indicates positive/negative test
1930 positive - attempt ping and ssh
1931 negative - attempt ping and fail if succeed
1932
1933 :raises: AssertError if the result of the connectivity check does
1934 not match the value of the should_connect param
1935 """
1936 if should_connect:
1937 msg = "Timed out waiting for %s to become reachable" % ip_address
1938 else:
1939 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -07001940 self.assertTrue(self.ping_ip_address(ip_address,
1941 should_succeed=should_connect),
Yair Fried9a551c42013-12-15 14:59:34 +02001942 msg=msg)
1943 if should_connect:
1944 # no need to check ssh for negative connectivity
Yair Fried3960c4d2014-05-07 15:20:30 +03001945 self.get_remote_client(ip_address, username, private_key)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001946
Matt Riedemann343305f2014-05-27 09:55:03 -07001947 def _check_public_network_connectivity(self, ip_address, username,
1948 private_key, should_connect=True,
1949 msg=None, servers=None):
1950 # The target login is assumed to have been configured for
1951 # key-based authentication by cloud-init.
1952 LOG.debug('checking network connections to IP %s with user: %s' %
1953 (ip_address, username))
1954 try:
1955 self._check_vm_connectivity(ip_address,
1956 username,
1957 private_key,
1958 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001959 except Exception as e:
Matt Riedemann343305f2014-05-27 09:55:03 -07001960 ex_msg = 'Public network connectivity check failed'
1961 if msg:
1962 ex_msg += ": " + msg
1963 LOG.exception(ex_msg)
1964 self._log_console_output(servers)
Yair Fried3960c4d2014-05-07 15:20:30 +03001965 # network debug is called as part of ssh init
1966 if not isinstance(e, exceptions.SSHTimeout):
1967 debug.log_net_debug()
Matt Riedemann343305f2014-05-27 09:55:03 -07001968 raise
1969
Matt Riedemann2d005be2014-05-27 10:52:35 -07001970 def _check_tenant_network_connectivity(self, server,
1971 username,
1972 private_key,
1973 should_connect=True,
1974 servers_for_debug=None):
1975 if not CONF.network.tenant_networks_reachable:
1976 msg = 'Tenant networks not configured to be reachable.'
1977 LOG.info(msg)
1978 return
1979 # The target login is assumed to have been configured for
1980 # key-based authentication by cloud-init.
1981 try:
1982 for net_name, ip_addresses in server.networks.iteritems():
1983 for ip_address in ip_addresses:
1984 self._check_vm_connectivity(ip_address,
1985 username,
1986 private_key,
1987 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001988 except Exception as e:
Matt Riedemann2d005be2014-05-27 10:52:35 -07001989 LOG.exception('Tenant network connectivity check failed')
1990 self._log_console_output(servers_for_debug)
Yair Fried3960c4d2014-05-07 15:20:30 +03001991 # network debug is called as part of ssh init
1992 if not isinstance(e, exceptions.SSHTimeout):
1993 debug.log_net_debug()
Matt Riedemann2d005be2014-05-27 10:52:35 -07001994 raise
1995
Yair Fried3097dc12014-01-26 08:46:43 +02001996 def _check_remote_connectivity(self, source, dest, should_succeed=True):
1997 """
1998 check ping server via source ssh connection
1999
2000 :param source: RemoteClient: an ssh connection from which to ping
2001 :param dest: and IP to ping against
2002 :param should_succeed: boolean should ping succeed or not
2003 :returns: boolean -- should_succeed == ping
2004 :returns: ping is false if ping failed
2005 """
2006 def ping_remote():
2007 try:
2008 source.ping_host(dest)
2009 except exceptions.SSHExecCommandFailed:
Matthew Treinishc4131d82014-05-27 11:59:28 -04002010 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
2011 % (dest, source.ssh_client.host))
Yair Fried3097dc12014-01-26 08:46:43 +02002012 return not should_succeed
2013 return should_succeed
2014
2015 return tempest.test.call_until_true(ping_remote,
2016 CONF.compute.ping_timeout,
2017 1)
2018
Yair Friedeb69f3f2013-10-10 13:18:16 +03002019 def _create_security_group_neutron(self, tenant_id, client=None,
2020 namestart='secgroup-smoke-'):
2021 if client is None:
2022 client = self.network_client
2023 secgroup = self._create_empty_security_group(namestart=namestart,
2024 client=client,
2025 tenant_id=tenant_id)
2026
2027 # Add rules to the security group
2028 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
2029 for rule in rules:
2030 self.assertEqual(tenant_id, rule.tenant_id)
2031 self.assertEqual(secgroup.id, rule.security_group_id)
2032 return secgroup
2033
2034 def _create_empty_security_group(self, tenant_id, client=None,
2035 namestart='secgroup-smoke-'):
2036 """Create a security group without rules.
2037
2038 Default rules will be created:
2039 - IPv4 egress to any
2040 - IPv6 egress to any
2041
2042 :param tenant_id: secgroup will be created in this tenant
2043 :returns: DeletableSecurityGroup -- containing the secgroup created
2044 """
2045 if client is None:
2046 client = self.network_client
2047 sg_name = data_utils.rand_name(namestart)
2048 sg_desc = sg_name + " description"
2049 sg_dict = dict(name=sg_name,
2050 description=sg_desc)
2051 sg_dict['tenant_id'] = tenant_id
2052 body = dict(security_group=sg_dict)
2053 result = client.create_security_group(body=body)
2054 secgroup = net_common.DeletableSecurityGroup(
2055 client=client,
2056 **result['security_group']
2057 )
2058 self.assertEqual(secgroup.name, sg_name)
2059 self.assertEqual(tenant_id, secgroup.tenant_id)
2060 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002061 self.addCleanup(self.delete_wrapper, secgroup)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002062 return secgroup
2063
2064 def _default_security_group(self, tenant_id, client=None):
2065 """Get default secgroup for given tenant_id.
2066
2067 :returns: DeletableSecurityGroup -- default secgroup for given tenant
2068 """
2069 if client is None:
2070 client = self.network_client
2071 sgs = [
2072 sg for sg in client.list_security_groups().values()[0]
2073 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
2074 ]
2075 msg = "No default security group for tenant %s." % (tenant_id)
2076 self.assertTrue(len(sgs) > 0, msg)
2077 if len(sgs) > 1:
2078 msg = "Found %d default security groups" % len(sgs)
2079 raise exc.NeutronClientNoUniqueMatch(msg=msg)
2080 return net_common.DeletableSecurityGroup(client=client,
2081 **sgs[0])
2082
2083 def _create_security_group_rule(self, client=None, secgroup=None,
2084 tenant_id=None, **kwargs):
2085 """Create a rule from a dictionary of rule parameters.
2086
2087 Create a rule in a secgroup. if secgroup not defined will search for
2088 default secgroup in tenant_id.
2089
2090 :param secgroup: type DeletableSecurityGroup.
2091 :param secgroup_id: search for secgroup by id
2092 default -- choose default secgroup for given tenant_id
2093 :param tenant_id: if secgroup not passed -- the tenant in which to
2094 search for default secgroup
2095 :param kwargs: a dictionary containing rule parameters:
2096 for example, to allow incoming ssh:
2097 rule = {
2098 direction: 'ingress'
2099 protocol:'tcp',
2100 port_range_min: 22,
2101 port_range_max: 22
2102 }
2103 """
2104 if client is None:
2105 client = self.network_client
2106 if secgroup is None:
2107 secgroup = self._default_security_group(tenant_id)
2108
2109 ruleset = dict(security_group_id=secgroup.id,
2110 tenant_id=secgroup.tenant_id,
2111 )
2112 ruleset.update(kwargs)
2113
2114 body = dict(security_group_rule=dict(ruleset))
2115 sg_rule = client.create_security_group_rule(body=body)
2116 sg_rule = net_common.DeletableSecurityGroupRule(
2117 client=client,
2118 **sg_rule['security_group_rule']
2119 )
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002120 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002121 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
2122 self.assertEqual(secgroup.id, sg_rule.security_group_id)
2123
2124 return sg_rule
2125
2126 def _create_loginable_secgroup_rule_neutron(self, client=None,
2127 secgroup=None):
2128 """These rules are intended to permit inbound ssh and icmp
2129 traffic from all sources, so no group_id is provided.
2130 Setting a group_id would only permit traffic from ports
2131 belonging to the same security group.
2132 """
2133
2134 if client is None:
2135 client = self.network_client
2136 rules = []
2137 rulesets = [
2138 dict(
2139 # ssh
2140 protocol='tcp',
2141 port_range_min=22,
2142 port_range_max=22,
2143 ),
2144 dict(
2145 # ping
2146 protocol='icmp',
2147 )
2148 ]
2149 for ruleset in rulesets:
2150 for r_direction in ['ingress', 'egress']:
2151 ruleset['direction'] = r_direction
2152 try:
2153 sg_rule = self._create_security_group_rule(
2154 client=client, secgroup=secgroup, **ruleset)
2155 except exc.NeutronClientException as ex:
2156 # if rule already exist - skip rule and continue
2157 if not (ex.status_code is 409 and 'Security group rule'
2158 ' already exists' in ex.message):
2159 raise ex
2160 else:
2161 self.assertEqual(r_direction, sg_rule.direction)
2162 rules.append(sg_rule)
2163
2164 return rules
2165
Yair Fried5f670ab2013-12-09 09:26:51 +02002166 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +00002167 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +02002168 return self.get_remote_client(server,
2169 username=ssh_login,
2170 private_key=private_key)
2171
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00002172 def _show_quota_network(self, tenant_id):
2173 quota = self.network_client.show_quota(tenant_id)
2174 return quota['quota']['network']
2175
2176 def _show_quota_subnet(self, tenant_id):
2177 quota = self.network_client.show_quota(tenant_id)
2178 return quota['quota']['subnet']
2179
2180 def _show_quota_port(self, tenant_id):
2181 quota = self.network_client.show_quota(tenant_id)
2182 return quota['quota']['port']
2183
Yair Fried4d7efa62013-11-17 17:12:29 +02002184 def _get_router(self, tenant_id):
2185 """Retrieve a router for the given tenant id.
2186
2187 If a public router has been configured, it will be returned.
2188
2189 If a public router has not been configured, but a public
2190 network has, a tenant router will be created and returned that
2191 routes traffic to the public network.
2192 """
Matthew Treinish6c072292014-01-29 19:15:52 +00002193 router_id = CONF.network.public_router_id
2194 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02002195 if router_id:
2196 result = self.network_client.show_router(router_id)
2197 return net_common.AttributeDict(**result['router'])
2198 elif network_id:
2199 router = self._create_router(tenant_id)
2200 router.add_gateway(network_id)
2201 return router
2202 else:
2203 raise Exception("Neither of 'public_router_id' or "
2204 "'public_network_id' has been defined.")
2205
2206 def _create_router(self, tenant_id, namestart='router-smoke-'):
2207 name = data_utils.rand_name(namestart)
2208 body = dict(
2209 router=dict(
2210 name=name,
2211 admin_state_up=True,
2212 tenant_id=tenant_id,
2213 ),
2214 )
2215 result = self.network_client.create_router(body=body)
2216 router = net_common.DeletableRouter(client=self.network_client,
2217 **result['router'])
2218 self.assertEqual(router.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002219 self.addCleanup(self.delete_wrapper, router)
Yair Fried4d7efa62013-11-17 17:12:29 +02002220 return router
2221
David Shrewsbury9bac3662014-08-07 15:07:01 -04002222 def create_networks(self, tenant_id=None):
Yair Fried4d7efa62013-11-17 17:12:29 +02002223 """Create a network with a subnet connected to a router.
2224
David Shrewsbury9bac3662014-08-07 15:07:01 -04002225 The baremetal driver is a special case since all nodes are
2226 on the same shared network.
2227
Yair Fried4d7efa62013-11-17 17:12:29 +02002228 :returns: network, subnet, router
2229 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04002230 if CONF.baremetal.driver_enabled:
2231 # NOTE(Shrews): This exception is for environments where tenant
2232 # credential isolation is available, but network separation is
2233 # not (the current baremetal case). Likely can be removed when
2234 # test account mgmt is reworked:
2235 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
2236 network = self._get_network_by_name(
2237 CONF.compute.fixed_network_name)
2238 router = None
2239 subnet = None
2240 else:
2241 if tenant_id is None:
2242 tenant_id = self.tenant_id
2243 network = self._create_network(tenant_id)
2244 router = self._get_router(tenant_id)
2245 subnet = self._create_subnet(network)
2246 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02002247 return network, subnet, router
2248
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002249
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09002250class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002251 """
2252 Base class for orchestration scenario tests
2253 """
2254
2255 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07002256 def setUpClass(cls):
2257 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00002258 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07002259 raise cls.skipException("Heat support is required")
2260
2261 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002262 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00002263 admin_creds = auth.get_default_credentials('identity_admin')
2264 creds = auth.get_default_credentials('user')
2265 admin_creds.tenant_name = creds.tenant_name
2266 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002267
2268 def _load_template(self, base_file, file_name):
2269 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
2270 file_name)
2271 with open(filepath) as f:
2272 return f.read()
2273
2274 @classmethod
2275 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09002276 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12002277
2278 @classmethod
2279 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09002280 _, networks = cls.networks_client.list_networks()
2281 for net in networks:
2282 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12002283 return net
Steve Baker22c16602014-05-05 13:34:19 +12002284
2285 @staticmethod
2286 def _stack_output(stack, output_key):
2287 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09002288 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12002289 if o['output_key'] == output_key), None)
2290
Chris Dent0d494112014-08-26 13:48:30 +01002291
2292class SwiftScenarioTest(ScenarioTest):
2293 """
2294 Provide harness to do Swift scenario tests.
2295
2296 Subclasses implement the tests that use the methods provided by this
2297 class.
2298 """
2299
2300 @classmethod
2301 def setUpClass(cls):
2302 cls.set_network_resources()
2303 super(SwiftScenarioTest, cls).setUpClass()
2304 if not CONF.service_available.swift:
2305 skip_msg = ("%s skipped as swift is not available" %
2306 cls.__name__)
2307 raise cls.skipException(skip_msg)
2308 # Clients for Swift
2309 cls.account_client = cls.manager.account_client
2310 cls.container_client = cls.manager.container_client
2311 cls.object_client = cls.manager.object_client
2312
2313 def _get_swift_stat(self):
2314 """get swift status for our user account."""
2315 self.account_client.list_account_containers()
2316 LOG.debug('Swift status information obtained successfully')
2317
2318 def _create_container(self, container_name=None):
2319 name = container_name or data_utils.rand_name(
2320 'swift-scenario-container')
2321 self.container_client.create_container(name)
2322 # look for the container to assure it is created
2323 self._list_and_check_container_objects(name)
2324 LOG.debug('Container %s created' % (name))
2325 return name
2326
2327 def _delete_container(self, container_name):
2328 self.container_client.delete_container(container_name)
2329 LOG.debug('Container %s deleted' % (container_name))
2330
2331 def _upload_object_to_container(self, container_name, obj_name=None):
2332 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
2333 obj_data = data_utils.arbitrary_string()
2334 self.object_client.create_object(container_name, obj_name, obj_data)
2335 return obj_name, obj_data
2336
2337 def _delete_object(self, container_name, filename):
2338 self.object_client.delete_object(container_name, filename)
2339 self._list_and_check_container_objects(container_name,
2340 not_present_obj=[filename])
2341
Ghanshyam2a180b82014-06-16 13:54:22 +09002342 def _list_and_check_container_objects(self, container_name,
2343 present_obj=None,
2344 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01002345 """
2346 List objects for a given container and assert which are present and
2347 which are not.
2348 """
Ghanshyam2a180b82014-06-16 13:54:22 +09002349 if present_obj is None:
2350 present_obj = []
2351 if not_present_obj is None:
2352 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01002353 _, object_list = self.container_client.list_container_contents(
2354 container_name)
2355 if present_obj:
2356 for obj in present_obj:
2357 self.assertIn(obj, object_list)
2358 if not_present_obj:
2359 for obj in not_present_obj:
2360 self.assertNotIn(obj, object_list)
2361
2362 def _change_container_acl(self, container_name, acl):
2363 metadata_param = {'metadata_prefix': 'x-container-',
2364 'metadata': {'read': acl}}
2365 self.container_client.update_container_metadata(container_name,
2366 **metadata_param)
2367 resp, _ = self.container_client.list_container_metadata(container_name)
2368 self.assertEqual(resp['x-container-read'], acl)
2369
2370 def _download_and_verify(self, container_name, obj_name, expected_data):
2371 _, obj = self.object_client.get_object(container_name, obj_name)
2372 self.assertEqual(obj, expected_data)