blob: 2d7bc247e4477d161a27ec330dbd64c84fa5c05a [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):
489 if CONF.service_available.neutron:
490 cls.enabled = True
491 # verify that neutron_available is telling the truth
492 try:
493 cls.network_client.list_networks()
494 except exc.EndpointNotFound:
495 cls.enabled = False
496 raise
497 else:
498 cls.enabled = False
499 msg = 'Neutron not available'
500 raise cls.skipException(msg)
501
502 @classmethod
503 def setUpClass(cls):
504 super(NeutronScenarioTest, cls).setUpClass()
505 cls.tenant_id = cls.manager.identity_client.tenant_id
506 cls.check_preconditions()
507
Yair Frieddb6c9e92014-08-06 08:53:13 +0300508 def _create_network(self, client=None, tenant_id=None,
509 namestart='network-smoke-'):
510 if not client:
511 client = self.network_client
512 if not tenant_id:
513 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300514 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300515 _, result = client.create_network(name=name, tenant_id=tenant_id)
516 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300517 **result['network'])
518 self.assertEqual(network.name, name)
519 self.addCleanup(self.delete_wrapper, network.delete)
520 return network
521
522 def _list_networks(self, *args, **kwargs):
523 """List networks using admin creds """
524 return self._admin_lister('networks')(*args, **kwargs)
525
526 def _list_subnets(self, *args, **kwargs):
527 """List subnets using admin creds """
528 return self._admin_lister('subnets')(*args, **kwargs)
529
530 def _list_routers(self, *args, **kwargs):
531 """List routers using admin creds """
532 return self._admin_lister('routers')(*args, **kwargs)
533
534 def _list_ports(self, *args, **kwargs):
535 """List ports using admin creds """
536 return self._admin_lister('ports')(*args, **kwargs)
537
538 def _admin_lister(self, resource_type):
539 def temp(*args, **kwargs):
540 temp_method = self.admin_manager.network_client.__getattr__(
541 'list_%s' % resource_type)
542 _, resource_list = temp_method(*args, **kwargs)
543 return resource_list[resource_type]
544 return temp
545
Yair Frieddb6c9e92014-08-06 08:53:13 +0300546 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
547 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300548 """
549 Create a subnet for the given network within the cidr block
550 configured for tenant networks.
551 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300552 if not client:
553 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300554
555 def cidr_in_use(cidr, tenant_id):
556 """
557 :return True if subnet with cidr already exist in tenant
558 False else
559 """
560 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
561 return len(cidr_in_use) != 0
562
563 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
564 result = None
565 # Repeatedly attempt subnet creation with sequential cidr
566 # blocks until an unallocated block is found.
567 for subnet_cidr in tenant_cidr.subnet(
568 CONF.network.tenant_network_mask_bits):
569 str_cidr = str(subnet_cidr)
570 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
571 continue
572
573 subnet = dict(
574 name=data_utils.rand_name(namestart),
575 ip_version=4,
576 network_id=network.id,
577 tenant_id=network.tenant_id,
578 cidr=str_cidr,
579 **kwargs
580 )
581 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300582 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300583 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300584 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300585 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
586 if not is_overlapping_cidr:
587 raise
588 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300589 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300590 **result['subnet'])
591 self.assertEqual(subnet.cidr, str_cidr)
592 self.addCleanup(self.delete_wrapper, subnet.delete)
593 return subnet
594
Yair Frieddb6c9e92014-08-06 08:53:13 +0300595 def _create_port(self, network, client=None, namestart='port-quotatest'):
596 if not client:
597 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300598 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300599 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300600 name=name,
601 network_id=network.id,
602 tenant_id=network.tenant_id)
603 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300604 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300605 **result['port'])
606 self.addCleanup(self.delete_wrapper, port.delete)
607 return port
608
609 def _get_server_port_id(self, server, ip_addr=None):
610 ports = self._list_ports(device_id=server['id'],
611 fixed_ip=ip_addr)
612 self.assertEqual(len(ports), 1,
613 "Unable to determine which port to target.")
614 return ports[0]['id']
615
David Shrewsbury9bac3662014-08-07 15:07:01 -0400616 def _get_network_by_name(self, network_name):
617 net = self._list_networks(name=network_name)
618 return net_common.AttributeDict(net[0])
619
Yair Frieddb6c9e92014-08-06 08:53:13 +0300620 def _create_floating_ip(self, thing, external_network_id, port_id=None,
621 client=None):
622 if not client:
623 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300624 if not port_id:
625 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300626 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300627 floating_network_id=external_network_id,
628 port_id=port_id,
629 tenant_id=thing['tenant_id']
630 )
631 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300632 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300633 **result['floatingip'])
634 self.addCleanup(self.delete_wrapper, floating_ip.delete)
635 return floating_ip
636
637 def _associate_floating_ip(self, floating_ip, server):
638 port_id = self._get_server_port_id(server)
639 floating_ip.update(port_id=port_id)
640 self.assertEqual(port_id, floating_ip.port_id)
641 return floating_ip
642
643 def _disassociate_floating_ip(self, floating_ip):
644 """
645 :param floating_ip: type DeletableFloatingIp
646 """
647 floating_ip.update(port_id=None)
648 self.assertIsNone(floating_ip.port_id)
649 return floating_ip
650
Yair Fried1fc32a12014-08-04 09:11:30 +0300651 def _check_vm_connectivity(self, ip_address,
652 username=None,
653 private_key=None,
654 should_connect=True):
655 """
656 :param ip_address: server to test against
657 :param username: server's ssh username
658 :param private_key: server's ssh private key to be used
659 :param should_connect: True/False indicates positive/negative test
660 positive - attempt ping and ssh
661 negative - attempt ping and fail if succeed
662
663 :raises: AssertError if the result of the connectivity check does
664 not match the value of the should_connect param
665 """
666 if should_connect:
667 msg = "Timed out waiting for %s to become reachable" % ip_address
668 else:
669 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700670 self.assertTrue(self.ping_ip_address(ip_address,
671 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300672 msg=msg)
673 if should_connect:
674 # no need to check ssh for negative connectivity
675 self.get_remote_client(ip_address, username, private_key)
676
677 def _check_public_network_connectivity(self, ip_address, username,
678 private_key, should_connect=True,
679 msg=None, servers=None):
680 # The target login is assumed to have been configured for
681 # key-based authentication by cloud-init.
682 LOG.debug('checking network connections to IP %s with user: %s' %
683 (ip_address, username))
684 try:
685 self._check_vm_connectivity(ip_address,
686 username,
687 private_key,
688 should_connect=should_connect)
689 except Exception as e:
690 ex_msg = 'Public network connectivity check failed'
691 if msg:
692 ex_msg += ": " + msg
693 LOG.exception(ex_msg)
694 self._log_console_output(servers)
695 # network debug is called as part of ssh init
696 if not isinstance(e, exceptions.SSHTimeout):
697 debug.log_net_debug()
698 raise
699
700 def _check_tenant_network_connectivity(self, server,
701 username,
702 private_key,
703 should_connect=True,
704 servers_for_debug=None):
705 if not CONF.network.tenant_networks_reachable:
706 msg = 'Tenant networks not configured to be reachable.'
707 LOG.info(msg)
708 return
709 # The target login is assumed to have been configured for
710 # key-based authentication by cloud-init.
711 try:
712 for net_name, ip_addresses in server['networks'].iteritems():
713 for ip_address in ip_addresses:
714 self._check_vm_connectivity(ip_address,
715 username,
716 private_key,
717 should_connect=should_connect)
718 except Exception as e:
719 LOG.exception('Tenant network connectivity check failed')
720 self._log_console_output(servers_for_debug)
721 # network debug is called as part of ssh init
722 if not isinstance(e, exceptions.SSHTimeout):
723 debug.log_net_debug()
724 raise
725
726 def _check_remote_connectivity(self, source, dest, should_succeed=True):
727 """
728 check ping server via source ssh connection
729
730 :param source: RemoteClient: an ssh connection from which to ping
731 :param dest: and IP to ping against
732 :param should_succeed: boolean should ping succeed or not
733 :returns: boolean -- should_succeed == ping
734 :returns: ping is false if ping failed
735 """
736 def ping_remote():
737 try:
738 source.ping_host(dest)
739 except exceptions.SSHExecCommandFailed:
740 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
741 % (dest, source.ssh_client.host))
742 return not should_succeed
743 return should_succeed
744
745 return tempest.test.call_until_true(ping_remote,
746 CONF.compute.ping_timeout,
747 1)
748
Yair Frieddb6c9e92014-08-06 08:53:13 +0300749 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300750 namestart='secgroup-smoke'):
751 if client is None:
752 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300753 if tenant_id is None:
754 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300755 secgroup = self._create_empty_security_group(namestart=namestart,
756 client=client,
757 tenant_id=tenant_id)
758
759 # Add rules to the security group
760 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
761 for rule in rules:
762 self.assertEqual(tenant_id, rule.tenant_id)
763 self.assertEqual(secgroup.id, rule.security_group_id)
764 return secgroup
765
Yair Frieddb6c9e92014-08-06 08:53:13 +0300766 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300767 namestart='secgroup-smoke'):
768 """Create a security group without rules.
769
770 Default rules will be created:
771 - IPv4 egress to any
772 - IPv6 egress to any
773
774 :param tenant_id: secgroup will be created in this tenant
775 :returns: DeletableSecurityGroup -- containing the secgroup created
776 """
777 if client is None:
778 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300779 if not tenant_id:
780 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300781 sg_name = data_utils.rand_name(namestart)
782 sg_desc = sg_name + " description"
783 sg_dict = dict(name=sg_name,
784 description=sg_desc)
785 sg_dict['tenant_id'] = tenant_id
786 _, result = client.create_security_group(**sg_dict)
787 secgroup = net_resources.DeletableSecurityGroup(
788 client=client,
789 **result['security_group']
790 )
791 self.assertEqual(secgroup.name, sg_name)
792 self.assertEqual(tenant_id, secgroup.tenant_id)
793 self.assertEqual(secgroup.description, sg_desc)
794 self.addCleanup(self.delete_wrapper, secgroup.delete)
795 return secgroup
796
Yair Frieddb6c9e92014-08-06 08:53:13 +0300797 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300798 """Get default secgroup for given tenant_id.
799
800 :returns: DeletableSecurityGroup -- default secgroup for given tenant
801 """
802 if client is None:
803 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300804 if not tenant_id:
805 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300806 sgs = [
807 sg for sg in client.list_security_groups().values()[0]
808 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
809 ]
810 msg = "No default security group for tenant %s." % (tenant_id)
811 self.assertTrue(len(sgs) > 0, msg)
812 if len(sgs) > 1:
813 msg = "Found %d default security groups" % len(sgs)
814 raise exc.NeutronClientNoUniqueMatch(msg=msg)
815 return net_resources.DeletableSecurityGroup(client=client,
816 **sgs[0])
817
Yair Frieddb6c9e92014-08-06 08:53:13 +0300818 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300819 tenant_id=None, **kwargs):
820 """Create a rule from a dictionary of rule parameters.
821
822 Create a rule in a secgroup. if secgroup not defined will search for
823 default secgroup in tenant_id.
824
825 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300826 :param tenant_id: if secgroup not passed -- the tenant in which to
827 search for default secgroup
828 :param kwargs: a dictionary containing rule parameters:
829 for example, to allow incoming ssh:
830 rule = {
831 direction: 'ingress'
832 protocol:'tcp',
833 port_range_min: 22,
834 port_range_max: 22
835 }
836 """
837 if client is None:
838 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300839 if not tenant_id:
840 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300841 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300842 secgroup = self._default_security_group(client=client,
843 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300844
845 ruleset = dict(security_group_id=secgroup.id,
846 tenant_id=secgroup.tenant_id)
847 ruleset.update(kwargs)
848
849 _, sg_rule = client.create_security_group_rule(**ruleset)
850 sg_rule = net_resources.DeletableSecurityGroupRule(
851 client=client,
852 **sg_rule['security_group_rule']
853 )
854 self.addCleanup(self.delete_wrapper, sg_rule.delete)
855 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
856 self.assertEqual(secgroup.id, sg_rule.security_group_id)
857
858 return sg_rule
859
860 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
861 """These rules are intended to permit inbound ssh and icmp
862 traffic from all sources, so no group_id is provided.
863 Setting a group_id would only permit traffic from ports
864 belonging to the same security group.
865 """
866
867 if client is None:
868 client = self.network_client
869 rules = []
870 rulesets = [
871 dict(
872 # ssh
873 protocol='tcp',
874 port_range_min=22,
875 port_range_max=22,
876 ),
877 dict(
878 # ping
879 protocol='icmp',
880 )
881 ]
882 for ruleset in rulesets:
883 for r_direction in ['ingress', 'egress']:
884 ruleset['direction'] = r_direction
885 try:
886 sg_rule = self._create_security_group_rule(
887 client=client, secgroup=secgroup, **ruleset)
888 except exceptions.Conflict as ex:
889 # if rule already exist - skip rule and continue
890 msg = 'Security group rule already exists'
891 if msg not in ex._error_string:
892 raise ex
893 else:
894 self.assertEqual(r_direction, sg_rule.direction)
895 rules.append(sg_rule)
896
897 return rules
898
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500899 def _create_pool(self, lb_method, protocol, subnet_id):
900 """Wrapper utility that returns a test pool."""
901 client = self.network_client
902 name = data_utils.rand_name('pool')
903 _, resp_pool = client.create_pool(protocol=protocol, name=name,
904 subnet_id=subnet_id,
905 lb_method=lb_method)
906 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
907 self.assertEqual(pool['name'], name)
908 self.addCleanup(self.delete_wrapper, pool.delete)
909 return pool
910
911 def _create_member(self, address, protocol_port, pool_id):
912 """Wrapper utility that returns a test member."""
913 client = self.network_client
914 _, resp_member = client.create_member(protocol_port=protocol_port,
915 pool_id=pool_id,
916 address=address)
917 member = net_resources.DeletableMember(client=client,
918 **resp_member['member'])
919 self.addCleanup(self.delete_wrapper, member.delete)
920 return member
921
922 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
923 """Wrapper utility that returns a test vip."""
924 client = self.network_client
925 name = data_utils.rand_name('vip')
926 _, resp_vip = client.create_vip(protocol=protocol, name=name,
927 subnet_id=subnet_id, pool_id=pool_id,
928 protocol_port=protocol_port)
929 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
930 self.assertEqual(vip['name'], name)
931 self.addCleanup(self.delete_wrapper, vip.delete)
932 return vip
933
Yair Fried1fc32a12014-08-04 09:11:30 +0300934 def _ssh_to_server(self, server, private_key):
935 ssh_login = CONF.compute.image_ssh_user
936 return self.get_remote_client(server,
937 username=ssh_login,
938 private_key=private_key)
939
Yair Frieddb6c9e92014-08-06 08:53:13 +0300940 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300941 """Retrieve a router for the given tenant id.
942
943 If a public router has been configured, it will be returned.
944
945 If a public router has not been configured, but a public
946 network has, a tenant router will be created and returned that
947 routes traffic to the public network.
948 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300949 if not client:
950 client = self.network_client
951 if not tenant_id:
952 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300953 router_id = CONF.network.public_router_id
954 network_id = CONF.network.public_network_id
955 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300956 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300957 return net_resources.AttributeDict(**result['router'])
958 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300959 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300960 router.set_gateway(network_id)
961 return router
962 else:
963 raise Exception("Neither of 'public_router_id' or "
964 "'public_network_id' has been defined.")
965
Yair Frieddb6c9e92014-08-06 08:53:13 +0300966 def _create_router(self, client=None, tenant_id=None,
967 namestart='router-smoke'):
968 if not client:
969 client = self.network_client
970 if not tenant_id:
971 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300972 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300973 _, result = client.create_router(name=name,
974 admin_state_up=True,
975 tenant_id=tenant_id)
976 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300977 **result['router'])
978 self.assertEqual(router.name, name)
979 self.addCleanup(self.delete_wrapper, router.delete)
980 return router
981
Yair Frieddb6c9e92014-08-06 08:53:13 +0300982 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300983 """Create a network with a subnet connected to a router.
984
David Shrewsbury9bac3662014-08-07 15:07:01 -0400985 The baremetal driver is a special case since all nodes are
986 on the same shared network.
987
Yair Fried1fc32a12014-08-04 09:11:30 +0300988 :returns: network, subnet, router
989 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400990 if CONF.baremetal.driver_enabled:
991 # NOTE(Shrews): This exception is for environments where tenant
992 # credential isolation is available, but network separation is
993 # not (the current baremetal case). Likely can be removed when
994 # test account mgmt is reworked:
995 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
996 network = self._get_network_by_name(
997 CONF.compute.fixed_network_name)
998 router = None
999 subnet = None
1000 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001001 network = self._create_network(client=client, tenant_id=tenant_id)
1002 router = self._get_router(client=client, tenant_id=tenant_id)
1003 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001004 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001005 return network, subnet, router
1006
1007
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001008class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001009 """
1010 Official Client test base class for scenario testing.
1011
1012 Official Client tests are tests that have the following characteristics:
1013
1014 * Test basic operations of an API, typically in an order that
1015 a regular user would perform those operations
1016 * Test only the correct inputs and action paths -- no fuzz or
1017 random input data is sent, only valid inputs.
1018 * Use only the default client tool for calling an API
1019 """
1020
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001021 @classmethod
1022 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +02001023 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -04001024 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -05001025 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -05001026 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001027
Andrea Frittolif9cde7e2014-02-18 09:57:04 +00001028 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001029 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001030 cls.compute_client = cls.manager.compute_client
1031 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -07001032 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001033 cls.identity_client = cls.manager.identity_client
1034 cls.network_client = cls.manager.network_client
1035 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +00001036 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001037 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +04001038 cls.data_processing_client = cls.manager.data_processing_client
Artur Svechnikovc3bf9252014-05-05 16:37:37 +04001039 cls.ceilometer_client = cls.manager.ceilometer_client
Sean Dague6dbc6da2013-05-08 17:49:46 -04001040
1041 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001042 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +00001043 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001044 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +02001045 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001046 creds = auth.get_default_credentials(ctype)
1047 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001048
1049 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +02001050 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001051 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
1052 'user')
Yair Frieda71cc442013-12-18 13:32:36 +02001053
1054 @classmethod
1055 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001056 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
1057 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +02001058
1059 @classmethod
1060 def admin_credentials(cls):
1061 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001062 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +02001063
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001064 def setUp(self):
1065 super(OfficialClientTest, self).setUp()
1066 self.cleanup_waits = []
1067 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
1068 # because scenario tests in the same test class should not share
1069 # resources. If resources were shared between test cases then it
1070 # should be a single scenario test instead of multiples.
Yair Friedbf2e2c42014-01-28 12:06:38 +02001071
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001072 # NOTE(yfried): this list is cleaned at the end of test_methods and
1073 # not at the end of the class
1074 self.addCleanup(self._wait_for_cleanups)
1075
1076 @staticmethod
1077 def not_found_exception(exception):
1078 """
1079 @return: True if exception is of NotFound type
1080 """
1081 NOT_FOUND_LIST = ['NotFound', 'HTTPNotFound']
1082 return (exception.__class__.__name__ in NOT_FOUND_LIST
1083 or
1084 hasattr(exception, 'status_code') and
1085 exception.status_code == 404)
1086
1087 def delete_wrapper(self, thing):
1088 """Ignores NotFound exceptions for delete operations.
1089
1090 @param thing: object with delete() method.
1091 OpenStack resources are assumed to have a delete() method which
1092 destroys the resource
1093 """
1094
Yair Friedbf2e2c42014-01-28 12:06:38 +02001095 try:
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001096 thing.delete()
Yair Friedbf2e2c42014-01-28 12:06:38 +02001097 except Exception as e:
1098 # If the resource is already missing, mission accomplished.
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001099 if not self.not_found_exception(e):
Yair Friedbf2e2c42014-01-28 12:06:38 +02001100 raise
Yair Friedbf2e2c42014-01-28 12:06:38 +02001101
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001102 def _wait_for_cleanups(self):
1103 """To handle async delete actions, a list of waits is added
1104 which will be iterated over as the last step of clearing the
1105 cleanup queue. That way all the delete calls are made up front
1106 and the tests won't succeed unless the deletes are eventually
1107 successful. This is the same basic approach used in the api tests to
1108 limit cleanup execution time except here it is multi-resource,
1109 because of the nature of the scenario tests.
1110 """
1111 for wait in self.cleanup_waits:
1112 self.delete_timeout(**wait)
Yair Friedbf2e2c42014-01-28 12:06:38 +02001113
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001114 def addCleanup_with_wait(self, things, thing_id,
1115 error_status='ERROR',
1116 exc_type=nova_exceptions.NotFound,
Ghanshyam2a180b82014-06-16 13:54:22 +09001117 cleanup_callable=None, cleanup_args=None,
1118 cleanup_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001119 """Adds wait for ansyc resource deletion at the end of cleanups
Sean Dague6dbc6da2013-05-08 17:49:46 -04001120
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001121 @param things: type of the resource to delete
1122 @param thing_id:
1123 @param error_status: see manager.delete_timeout()
1124 @param exc_type: see manager.delete_timeout()
1125 @param cleanup_callable: method to load pass to self.addCleanup with
1126 the following *cleanup_args, **cleanup_kwargs.
1127 usually a delete method. if not used, will try to use:
1128 things.delete(thing_id)
1129 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001130 if cleanup_args is None:
1131 cleanup_args = []
1132 if cleanup_kwargs is None:
1133 cleanup_kwargs = {}
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001134 if cleanup_callable is None:
1135 LOG.debug("no delete method passed. using {rclass}.delete({id}) as"
1136 " default".format(rclass=things, id=thing_id))
1137 self.addCleanup(things.delete, thing_id)
1138 else:
1139 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
1140 wait_dict = {
1141 'things': things,
1142 'thing_id': thing_id,
1143 'error_status': error_status,
1144 'not_found_exception': exc_type,
1145 }
1146 self.cleanup_waits.append(wait_dict)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001147
Steve Bakerefde7612013-09-30 11:29:23 +13001148 def status_timeout(self, things, thing_id, expected_status,
1149 error_status='ERROR',
1150 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001151 """
1152 Given a thing and an expected status, do a loop, sleeping
1153 for a configurable amount of time, checking for the
1154 expected status to show. At any time, if the returned
1155 status of the thing is ERROR, fail out.
1156 """
Steve Bakerefde7612013-09-30 11:29:23 +13001157 self._status_timeout(things, thing_id,
1158 expected_status=expected_status,
1159 error_status=error_status,
1160 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001161
Steve Bakerefde7612013-09-30 11:29:23 +13001162 def delete_timeout(self, things, thing_id,
1163 error_status='ERROR',
1164 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001165 """
1166 Given a thing, do a loop, sleeping
1167 for a configurable amount of time, checking for the
1168 deleted status to show. At any time, if the returned
1169 status of the thing is ERROR, fail out.
1170 """
1171 self._status_timeout(things,
1172 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +13001173 allow_notfound=True,
1174 error_status=error_status,
1175 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001176
1177 def _status_timeout(self,
1178 things,
1179 thing_id,
1180 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +13001181 allow_notfound=False,
1182 error_status='ERROR',
1183 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001184
1185 log_status = expected_status if expected_status else ''
1186 if allow_notfound:
1187 log_status += ' or NotFound' if log_status != '' else 'NotFound'
1188
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001189 def check_status():
1190 # python-novaclient has resources available to its client
1191 # that all implement a get() method taking an identifier
1192 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001193 try:
1194 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +13001195 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001196 if allow_notfound:
1197 return True
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001198 raise
1199 except Exception as e:
1200 if allow_notfound and self.not_found_exception(e):
1201 return True
1202 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001203
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001204 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -05001205
1206 # Some components are reporting error status in lower case
1207 # so case sensitive comparisons can really mess things
1208 # up.
1209 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +09001210 message = ("%s failed to get to expected status (%s). "
1211 "In %s state.") % (thing, expected_status,
1212 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +09001213 raise exceptions.BuildErrorException(message,
1214 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001215 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001216 return True # All good.
1217 LOG.debug("Waiting for %s to get to %s status. "
1218 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001219 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001220 if not tempest.test.call_until_true(
1221 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +00001222 CONF.compute.build_timeout,
1223 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +09001224 message = ("Timed out waiting for thing %s "
1225 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +02001226 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001227
Yair Friedeb69f3f2013-10-10 13:18:16 +03001228 def _create_loginable_secgroup_rule_nova(self, client=None,
1229 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001230 if client is None:
1231 client = self.compute_client
1232 if secgroup_id is None:
1233 sgs = client.security_groups.list()
1234 for sg in sgs:
1235 if sg.name == 'default':
1236 secgroup_id = sg.id
1237
1238 # These rules are intended to permit inbound ssh and icmp
1239 # traffic from all sources, so no group_id is provided.
1240 # Setting a group_id would only permit traffic from ports
1241 # belonging to the same security group.
1242 rulesets = [
1243 {
1244 # ssh
1245 'ip_protocol': 'tcp',
1246 'from_port': 22,
1247 'to_port': 22,
1248 'cidr': '0.0.0.0/0',
1249 },
1250 {
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001251 # ssh -6
1252 'ip_protocol': 'tcp',
1253 'from_port': 22,
1254 'to_port': 22,
1255 'cidr': '::/0',
1256 },
1257 {
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001258 # ping
1259 'ip_protocol': 'icmp',
1260 'from_port': -1,
1261 'to_port': -1,
1262 'cidr': '0.0.0.0/0',
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001263 },
1264 {
1265 # ping6
1266 'ip_protocol': 'icmp',
1267 'from_port': -1,
1268 'to_port': -1,
1269 'cidr': '::/0',
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001270 }
1271 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +03001272 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001273 for ruleset in rulesets:
1274 sg_rule = client.security_group_rules.create(secgroup_id,
1275 **ruleset)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001276 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03001277 rules.append(sg_rule)
1278 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001279
Grishkin0f1e11c2014-05-04 20:44:52 +04001280 def _create_security_group_nova(self, client=None,
1281 namestart='secgroup-smoke-'):
1282 if client is None:
1283 client = self.compute_client
1284 # Create security group
1285 sg_name = data_utils.rand_name(namestart)
1286 sg_desc = sg_name + " description"
1287 secgroup = client.security_groups.create(sg_name, sg_desc)
1288 self.assertEqual(secgroup.name, sg_name)
1289 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001290 self.addCleanup(self.delete_wrapper, secgroup)
Grishkin0f1e11c2014-05-04 20:44:52 +04001291
1292 # Add rules to the security group
1293 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
1294
1295 return secgroup
1296
David Shrewsbury02719362014-05-20 14:10:03 -04001297 def rebuild_server(self, server, client=None, image=None,
1298 preserve_ephemeral=False, wait=True,
1299 rebuild_kwargs=None):
1300 if client is None:
1301 client = self.compute_client
1302 if image is None:
1303 image = CONF.compute.image_ref
1304 rebuild_kwargs = rebuild_kwargs or {}
1305
1306 LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
1307 server.name, image, preserve_ephemeral)
1308 server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
1309 **rebuild_kwargs)
1310 if wait:
1311 self.status_timeout(client.servers, server.id, 'ACTIVE')
1312
Giulio Fidente61cadca2013-09-24 18:33:37 +02001313 def create_server(self, client=None, name=None, image=None, flavor=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001314 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +09001315 create_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001316 """Creates VM instance.
1317
1318 @param client: compute client to create the instance
1319 @param image: image from which to create the instance
1320 @param wait_on_boot: wait for status ACTIVE before continue
1321 @param wait_on_delete: force synchronous delete on cleanup
1322 @param create_kwargs: additional details for instance creation
1323 @return: client.server object
1324 """
Giulio Fidente61cadca2013-09-24 18:33:37 +02001325 if client is None:
1326 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001327 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001328 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001329 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001330 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001331 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001332 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +09001333 if create_kwargs is None:
1334 create_kwargs = {}
JordanP9c052aa2014-01-24 13:05:00 +00001335
1336 fixed_network_name = CONF.compute.fixed_network_name
1337 if 'nics' not in create_kwargs and fixed_network_name:
1338 networks = client.networks.list()
1339 # If several networks found, set the NetID on which to connect the
1340 # server to avoid the following error "Multiple possible networks
1341 # found, use a Network ID to be more specific."
1342 # See Tempest #1250866
1343 if len(networks) > 1:
1344 for network in networks:
1345 if network.label == fixed_network_name:
1346 create_kwargs['nics'] = [{'net-id': network.id}]
1347 break
1348 # If we didn't find the network we were looking for :
1349 else:
1350 msg = ("The network on which the NIC of the server must "
1351 "be connected can not be found : "
1352 "fixed_network_name=%s. Starting instance without "
1353 "specifying a network.") % fixed_network_name
1354 LOG.info(msg)
1355
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001356 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
1357 name, image, flavor)
1358 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +02001359 self.assertEqual(server.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001360 if wait_on_delete:
1361 self.addCleanup(self.delete_timeout,
1362 self.compute_client.servers,
1363 server.id)
1364 self.addCleanup_with_wait(self.compute_client.servers, server.id,
1365 cleanup_callable=self.delete_wrapper,
1366 cleanup_args=[server])
1367 if wait_on_boot:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001368 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001369 # The instance retrieved on creation is missing network
1370 # details, necessitating retrieval after it becomes active to
1371 # ensure correct details.
1372 server = client.servers.get(server.id)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001373 LOG.debug("Created server: %s", server)
1374 return server
1375
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001376 def create_volume(self, client=None, size=1, name=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001377 snapshot_id=None, imageRef=None, volume_type=None,
1378 wait_on_delete=True):
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001379 if client is None:
1380 client = self.volume_client
1381 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001382 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -07001383 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001384 volume = client.volumes.create(size=size, display_name=name,
1385 snapshot_id=snapshot_id,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001386 imageRef=imageRef,
1387 volume_type=volume_type)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001388 if wait_on_delete:
1389 self.addCleanup(self.delete_timeout,
1390 self.volume_client.volumes,
1391 volume.id)
1392 self.addCleanup_with_wait(self.volume_client.volumes, volume.id,
1393 exc_type=cinder_exceptions.NotFound)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001394 self.assertEqual(name, volume.display_name)
1395 self.status_timeout(client.volumes, volume.id, 'available')
1396 LOG.debug("Created volume: %s", volume)
1397 return volume
1398
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001399 def create_server_snapshot(self, server, compute_client=None,
1400 image_client=None, name=None):
1401 if compute_client is None:
1402 compute_client = self.compute_client
1403 if image_client is None:
1404 image_client = self.image_client
1405 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001406 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001407 LOG.debug("Creating a snapshot image for server: %s", server.name)
1408 image_id = compute_client.servers.create_image(server, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001409 self.addCleanup_with_wait(self.image_client.images, image_id,
1410 exc_type=glanceclient.exc.HTTPNotFound)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001411 self.status_timeout(image_client.images, image_id, 'active')
1412 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -07001413 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001414 LOG.debug("Created snapshot image %s for server %s",
1415 snapshot_image.name, server.name)
1416 return snapshot_image
1417
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001418 def create_keypair(self, client=None, name=None):
1419 if client is None:
1420 client = self.compute_client
1421 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001422 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001423 keypair = client.keypairs.create(name)
1424 self.assertEqual(keypair.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001425 self.addCleanup(self.delete_wrapper, keypair)
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001426 return keypair
1427
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001428 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +08001429 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001430 ip = server_or_ip
1431 else:
Matthew Treinish6c072292014-01-29 19:15:52 +00001432 network_name_for_ssh = CONF.compute.network_for_ssh
Adam Gandelman0d9508e2014-08-22 10:58:09 -07001433 ip = server_or_ip.networks[network_name_for_ssh][0]
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001434 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001435 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001436 if private_key is None:
1437 private_key = self.keypair.private_key
Yair Fried3960c4d2014-05-07 15:20:30 +03001438 linux_client = remote_client.RemoteClient(ip, username,
1439 pkey=private_key)
1440 try:
1441 linux_client.validate_authentication()
1442 except exceptions.SSHTimeout:
1443 LOG.exception('ssh connection to %s failed' % ip)
1444 debug.log_net_debug()
1445 raise
1446
1447 return linux_client
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001448
Nachi Ueno95b41282014-01-15 06:54:21 -08001449 def _log_console_output(self, servers=None):
Adam Gandelmanc6eefb42014-07-15 16:44:08 -07001450 if not CONF.compute_feature_enabled.console_output:
1451 LOG.debug('Console output not supported, cannot log')
1452 return
Nachi Ueno95b41282014-01-15 06:54:21 -08001453 if not servers:
1454 servers = self.compute_client.servers.list()
1455 for server in servers:
1456 LOG.debug('Console output for %s', server.id)
1457 LOG.debug(server.get_console_output())
1458
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001459 def wait_for_volume_status(self, status):
1460 volume_id = self.volume.id
1461 self.status_timeout(
1462 self.volume_client.volumes, volume_id, status)
1463
Ghanshyam2a180b82014-06-16 13:54:22 +09001464 def _image_create(self, name, fmt, path, properties=None):
1465 if properties is None:
1466 properties = {}
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001467 name = data_utils.rand_name('%s-' % name)
1468 image_file = open(path, 'rb')
1469 self.addCleanup(image_file.close)
1470 params = {
1471 'name': name,
1472 'container_format': fmt,
1473 'disk_format': fmt,
Aaron Rosenc7720622014-05-20 10:38:10 -07001474 'is_public': 'False',
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001475 }
1476 params.update(properties)
1477 image = self.image_client.images.create(**params)
1478 self.addCleanup(self.image_client.images.delete, image)
1479 self.assertEqual("queued", image.status)
1480 image.update(data=image_file)
1481 return image.id
1482
1483 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001484 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001485 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
1486 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
1487 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001488 img_container_format = CONF.scenario.img_container_format
1489 img_disk_format = CONF.scenario.img_disk_format
1490 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
1491 "ami: %s, ari: %s, aki: %s" %
1492 (img_path, img_container_format, img_disk_format,
1493 ami_img_path, ari_img_path, aki_img_path))
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001494 try:
1495 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001496 img_container_format,
1497 img_path,
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001498 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001499 img_disk_format})
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001500 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +09001501 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001502 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
1503 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
1504 properties = {
1505 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
1506 }
1507 self.image = self._image_create('scenario-ami', 'ami',
1508 path=ami_img_path,
1509 properties=properties)
1510 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001511
Sean Dague6dbc6da2013-05-08 17:49:46 -04001512
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001513# power/provision states as of icehouse
1514class BaremetalPowerStates(object):
1515 """Possible power states of an Ironic node."""
1516 POWER_ON = 'power on'
1517 POWER_OFF = 'power off'
1518 REBOOT = 'rebooting'
1519 SUSPEND = 'suspended'
1520
1521
1522class BaremetalProvisionStates(object):
1523 """Possible provision states of an Ironic node."""
1524 NOSTATE = None
1525 INIT = 'initializing'
1526 ACTIVE = 'active'
1527 BUILDING = 'building'
1528 DEPLOYWAIT = 'wait call-back'
1529 DEPLOYING = 'deploying'
1530 DEPLOYFAIL = 'deploy failed'
1531 DEPLOYDONE = 'deploy complete'
1532 DELETING = 'deleting'
1533 DELETED = 'deleted'
1534 ERROR = 'error'
1535
1536
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001537class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001538 @classmethod
1539 def setUpClass(cls):
1540 super(BaremetalScenarioTest, cls).setUpClass()
1541
1542 if (not CONF.service_available.ironic or
1543 not CONF.baremetal.driver_enabled):
1544 msg = 'Ironic not available or Ironic compute driver not enabled'
1545 raise cls.skipException(msg)
1546
1547 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001548 manager = clients.Manager(
1549 credentials=cls.admin_credentials()
1550 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001551 cls.baremetal_client = manager.baremetal_client
1552
1553 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001554 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001555
1556 def _node_state_timeout(self, node_id, state_attr,
1557 target_states, timeout=10, interval=1):
1558 if not isinstance(target_states, list):
1559 target_states = [target_states]
1560
1561 def check_state():
1562 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001563 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001564 return True
1565 return False
1566
1567 if not tempest.test.call_until_true(
1568 check_state, timeout, interval):
1569 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1570 (node_id, state_attr, target_states))
1571 raise exceptions.TimeoutException(msg)
1572
1573 def wait_provisioning_state(self, node_id, state, timeout):
1574 self._node_state_timeout(
1575 node_id=node_id, state_attr='provision_state',
1576 target_states=state, timeout=timeout)
1577
1578 def wait_power_state(self, node_id, state):
1579 self._node_state_timeout(
1580 node_id=node_id, state_attr='power_state',
1581 target_states=state, timeout=CONF.baremetal.power_timeout)
1582
1583 def wait_node(self, instance_id):
1584 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001585 from ironicclient import exc as ironic_exceptions
1586
Adam Gandelman4a48a602014-03-20 18:23:18 -07001587 def _get_node():
1588 node = None
1589 try:
1590 node = self.get_node(instance_id=instance_id)
1591 except ironic_exceptions.HTTPNotFound:
1592 pass
1593 return node is not None
1594
1595 if not tempest.test.call_until_true(
1596 _get_node, CONF.baremetal.association_timeout, 1):
1597 msg = ('Timed out waiting to get Ironic node by instance id %s'
1598 % instance_id)
1599 raise exceptions.TimeoutException(msg)
1600
1601 def get_node(self, node_id=None, instance_id=None):
1602 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001603 _, body = self.baremetal_client.show_node(node_id)
1604 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001605 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001606 _, body = self.baremetal_client.show_node_by_instance_uuid(
1607 instance_id)
1608 if body['nodes']:
1609 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001610
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001611 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001612 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001613 _, body = self.baremetal_client.list_node_ports(node_uuid)
1614 for port in body['ports']:
1615 _, p = self.baremetal_client.show_port(port['uuid'])
1616 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001617 return ports
1618
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001619 def add_keypair(self):
1620 self.keypair = self.create_keypair()
1621
1622 def verify_connectivity(self, ip=None):
1623 if ip:
1624 dest = self.get_remote_client(ip)
1625 else:
1626 dest = self.get_remote_client(self.instance)
1627 dest.validate_authentication()
1628
1629 def boot_instance(self):
1630 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001631 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001632 }
1633 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001634 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001635
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001636 self.wait_node(self.instance['id'])
1637 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001638
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001639 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001640
1641 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001642 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001643 [BaremetalProvisionStates.DEPLOYWAIT,
1644 BaremetalProvisionStates.ACTIVE],
1645 timeout=15)
1646
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001647 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001648 BaremetalProvisionStates.ACTIVE,
1649 timeout=CONF.baremetal.active_timeout)
1650
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001651 self.servers_client.wait_for_server_status(self.instance['id'],
1652 'ACTIVE')
1653 self.node = self.get_node(instance_id=self.instance['id'])
1654 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001655
1656 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001657 self.servers_client.delete_server(self.instance['id'])
1658 self.wait_power_state(self.node['uuid'],
1659 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001660 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001661 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001662 BaremetalProvisionStates.NOSTATE,
1663 timeout=CONF.baremetal.unprovision_timeout)
1664
Adam Gandelman4a48a602014-03-20 18:23:18 -07001665
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001666class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001667 """
1668 Base class for encryption scenario tests
1669 """
1670
1671 @classmethod
1672 def setUpClass(cls):
1673 super(EncryptionScenarioTest, cls).setUpClass()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001674 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001675
1676 def _wait_for_volume_status(self, status):
1677 self.status_timeout(
1678 self.volume_client.volumes, self.volume.id, status)
1679
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001680 def nova_boot(self):
1681 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001682 create_kwargs = {'key_name': self.keypair['name']}
1683 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001684 create_kwargs=create_kwargs)
1685
1686 def create_volume_type(self, client=None, name=None):
1687 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001688 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001689 if not name:
1690 name = 'generic'
1691 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1692 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001693 _, body = client.create_volume_type(
1694 randomized_name)
1695 self.assertIn('id', body)
1696 self.addCleanup(client.delete_volume_type, body['id'])
1697 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001698
1699 def create_encryption_type(self, client=None, type_id=None, provider=None,
1700 key_size=None, cipher=None,
1701 control_location=None):
1702 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001703 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001704 if not type_id:
1705 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001706 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001707 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001708 client.create_encryption_type(
1709 type_id, provider=provider, key_size=key_size, cipher=cipher,
1710 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001711
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001712
Sean Dague6dbc6da2013-05-08 17:49:46 -04001713class NetworkScenarioTest(OfficialClientTest):
1714 """
1715 Base class for network scenario tests
1716 """
1717
1718 @classmethod
1719 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001720 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001721 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +02001722 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -04001723 try:
1724 cls.network_client.list_networks()
1725 except exc.EndpointNotFound:
1726 cls.enabled = False
1727 raise
1728 else:
1729 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -04001730 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -04001731 raise cls.skipException(msg)
1732
1733 @classmethod
1734 def setUpClass(cls):
1735 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001736 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -04001737
Sean Dague6dbc6da2013-05-08 17:49:46 -04001738 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001739 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001740 body = dict(
1741 network=dict(
1742 name=name,
1743 tenant_id=tenant_id,
1744 ),
1745 )
1746 result = self.network_client.create_network(body=body)
1747 network = net_common.DeletableNetwork(client=self.network_client,
1748 **result['network'])
1749 self.assertEqual(network.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001750 self.addCleanup(self.delete_wrapper, network)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001751 return network
1752
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001753 def _list_networks(self, **kwargs):
1754 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001755 return nets['networks']
1756
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001757 def _list_subnets(self, **kwargs):
1758 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001759 return subnets['subnets']
1760
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001761 def _list_routers(self, **kwargs):
1762 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001763 return routers['routers']
1764
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001765 def _list_ports(self, **kwargs):
1766 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001767 return ports['ports']
1768
1769 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001770 nets = self._list_networks(tenant_id=tenant_id)
1771 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001772
1773 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001774 subnets = self._list_subnets(tenant_id=tenant_id)
1775 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001776
1777 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001778 ports = self._list_ports(tenant_id=tenant_id)
1779 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001780
Yair Fried3097dc12014-01-26 08:46:43 +02001781 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001782 """
1783 Create a subnet for the given network within the cidr block
1784 configured for tenant networks.
1785 """
Attila Fazekase857bd62013-10-21 21:02:44 +02001786
1787 def cidr_in_use(cidr, tenant_id):
1788 """
1789 :return True if subnet with cidr already exist in tenant
1790 False else
1791 """
1792 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
1793 return len(cidr_in_use) != 0
1794
Matthew Treinish6c072292014-01-29 19:15:52 +00001795 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001796 result = None
1797 # Repeatedly attempt subnet creation with sequential cidr
1798 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +00001799 for subnet_cidr in tenant_cidr.subnet(
1800 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +02001801 str_cidr = str(subnet_cidr)
1802 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
1803 continue
1804
Sean Dague6dbc6da2013-05-08 17:49:46 -04001805 body = dict(
1806 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +02001807 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -04001808 ip_version=4,
1809 network_id=network.id,
1810 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +02001811 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001812 ),
1813 )
Yair Fried3097dc12014-01-26 08:46:43 +02001814 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001815 try:
1816 result = self.network_client.create_subnet(body=body)
1817 break
Mark McClainf2982e82013-07-06 17:48:03 -04001818 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -04001819 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1820 if not is_overlapping_cidr:
1821 raise
1822 self.assertIsNotNone(result, 'Unable to allocate tenant network')
1823 subnet = net_common.DeletableSubnet(client=self.network_client,
1824 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +02001825 self.assertEqual(subnet.cidr, str_cidr)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001826 self.addCleanup(self.delete_wrapper, subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001827 return subnet
1828
1829 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001830 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001831 body = dict(
1832 port=dict(name=name,
1833 network_id=network.id,
1834 tenant_id=network.tenant_id))
1835 result = self.network_client.create_port(body=body)
1836 self.assertIsNotNone(result, 'Unable to allocate port')
1837 port = net_common.DeletablePort(client=self.network_client,
1838 **result['port'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001839 self.addCleanup(self.delete_wrapper, port)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001840 return port
1841
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001842 def _get_server_port_id(self, server, ip_addr=None):
1843 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001844 self.assertEqual(len(ports), 1,
1845 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +02001846 return ports[0]['id']
1847
David Shrewsbury9bac3662014-08-07 15:07:01 -04001848 def _get_network_by_name(self, network_name):
1849 net = self._list_networks(name=network_name)
1850 return net_common.AttributeDict(net[0])
1851
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001852 def _create_floating_ip(self, thing, external_network_id, port_id=None):
1853 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001854 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001855 body = dict(
1856 floatingip=dict(
1857 floating_network_id=external_network_id,
1858 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001859 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001860 )
1861 )
1862 result = self.network_client.create_floatingip(body=body)
1863 floating_ip = net_common.DeletableFloatingIp(
1864 client=self.network_client,
1865 **result['floatingip'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001866 self.addCleanup(self.delete_wrapper, floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001867 return floating_ip
1868
Yair Fried05db2522013-11-18 11:02:10 +02001869 def _associate_floating_ip(self, floating_ip, server):
1870 port_id = self._get_server_port_id(server)
1871 floating_ip.update(port_id=port_id)
1872 self.assertEqual(port_id, floating_ip.port_id)
1873 return floating_ip
1874
Yair Fried9a551c42013-12-15 14:59:34 +02001875 def _disassociate_floating_ip(self, floating_ip):
1876 """
1877 :param floating_ip: type DeletableFloatingIp
1878 """
1879 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +08001880 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +02001881 return floating_ip
1882
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001883 def _create_pool(self, lb_method, protocol, subnet_id):
1884 """Wrapper utility that returns a test pool."""
1885 name = data_utils.rand_name('pool-')
1886 body = {
1887 "pool": {
1888 "protocol": protocol,
1889 "name": name,
1890 "subnet_id": subnet_id,
1891 "lb_method": lb_method
1892 }
1893 }
1894 resp = self.network_client.create_pool(body=body)
1895 pool = net_common.DeletablePool(client=self.network_client,
1896 **resp['pool'])
1897 self.assertEqual(pool['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001898 self.addCleanup(self.delete_wrapper, pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001899 return pool
1900
1901 def _create_member(self, address, protocol_port, pool_id):
1902 """Wrapper utility that returns a test member."""
1903 body = {
1904 "member": {
1905 "protocol_port": protocol_port,
1906 "pool_id": pool_id,
1907 "address": address
1908 }
1909 }
1910 resp = self.network_client.create_member(body)
1911 member = net_common.DeletableMember(client=self.network_client,
1912 **resp['member'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001913 self.addCleanup(self.delete_wrapper, member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001914 return member
1915
1916 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
1917 """Wrapper utility that returns a test vip."""
1918 name = data_utils.rand_name('vip-')
1919 body = {
1920 "vip": {
1921 "protocol": protocol,
1922 "name": name,
1923 "subnet_id": subnet_id,
1924 "pool_id": pool_id,
1925 "protocol_port": protocol_port
1926 }
1927 }
1928 resp = self.network_client.create_vip(body)
1929 vip = net_common.DeletableVip(client=self.network_client,
1930 **resp['vip'])
1931 self.assertEqual(vip['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001932 self.addCleanup(self.delete_wrapper, vip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001933 return vip
1934
Yair Fried9a551c42013-12-15 14:59:34 +02001935 def _check_vm_connectivity(self, ip_address,
1936 username=None,
1937 private_key=None,
1938 should_connect=True):
1939 """
1940 :param ip_address: server to test against
1941 :param username: server's ssh username
1942 :param private_key: server's ssh private key to be used
1943 :param should_connect: True/False indicates positive/negative test
1944 positive - attempt ping and ssh
1945 negative - attempt ping and fail if succeed
1946
1947 :raises: AssertError if the result of the connectivity check does
1948 not match the value of the should_connect param
1949 """
1950 if should_connect:
1951 msg = "Timed out waiting for %s to become reachable" % ip_address
1952 else:
1953 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -07001954 self.assertTrue(self.ping_ip_address(ip_address,
1955 should_succeed=should_connect),
Yair Fried9a551c42013-12-15 14:59:34 +02001956 msg=msg)
1957 if should_connect:
1958 # no need to check ssh for negative connectivity
Yair Fried3960c4d2014-05-07 15:20:30 +03001959 self.get_remote_client(ip_address, username, private_key)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001960
Matt Riedemann343305f2014-05-27 09:55:03 -07001961 def _check_public_network_connectivity(self, ip_address, username,
1962 private_key, should_connect=True,
1963 msg=None, servers=None):
1964 # The target login is assumed to have been configured for
1965 # key-based authentication by cloud-init.
1966 LOG.debug('checking network connections to IP %s with user: %s' %
1967 (ip_address, username))
1968 try:
1969 self._check_vm_connectivity(ip_address,
1970 username,
1971 private_key,
1972 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001973 except Exception as e:
Matt Riedemann343305f2014-05-27 09:55:03 -07001974 ex_msg = 'Public network connectivity check failed'
1975 if msg:
1976 ex_msg += ": " + msg
1977 LOG.exception(ex_msg)
1978 self._log_console_output(servers)
Yair Fried3960c4d2014-05-07 15:20:30 +03001979 # network debug is called as part of ssh init
1980 if not isinstance(e, exceptions.SSHTimeout):
1981 debug.log_net_debug()
Matt Riedemann343305f2014-05-27 09:55:03 -07001982 raise
1983
Matt Riedemann2d005be2014-05-27 10:52:35 -07001984 def _check_tenant_network_connectivity(self, server,
1985 username,
1986 private_key,
1987 should_connect=True,
1988 servers_for_debug=None):
1989 if not CONF.network.tenant_networks_reachable:
1990 msg = 'Tenant networks not configured to be reachable.'
1991 LOG.info(msg)
1992 return
1993 # The target login is assumed to have been configured for
1994 # key-based authentication by cloud-init.
1995 try:
1996 for net_name, ip_addresses in server.networks.iteritems():
1997 for ip_address in ip_addresses:
1998 self._check_vm_connectivity(ip_address,
1999 username,
2000 private_key,
2001 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03002002 except Exception as e:
Matt Riedemann2d005be2014-05-27 10:52:35 -07002003 LOG.exception('Tenant network connectivity check failed')
2004 self._log_console_output(servers_for_debug)
Yair Fried3960c4d2014-05-07 15:20:30 +03002005 # network debug is called as part of ssh init
2006 if not isinstance(e, exceptions.SSHTimeout):
2007 debug.log_net_debug()
Matt Riedemann2d005be2014-05-27 10:52:35 -07002008 raise
2009
Yair Fried3097dc12014-01-26 08:46:43 +02002010 def _check_remote_connectivity(self, source, dest, should_succeed=True):
2011 """
2012 check ping server via source ssh connection
2013
2014 :param source: RemoteClient: an ssh connection from which to ping
2015 :param dest: and IP to ping against
2016 :param should_succeed: boolean should ping succeed or not
2017 :returns: boolean -- should_succeed == ping
2018 :returns: ping is false if ping failed
2019 """
2020 def ping_remote():
2021 try:
2022 source.ping_host(dest)
2023 except exceptions.SSHExecCommandFailed:
Matthew Treinishc4131d82014-05-27 11:59:28 -04002024 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
2025 % (dest, source.ssh_client.host))
Yair Fried3097dc12014-01-26 08:46:43 +02002026 return not should_succeed
2027 return should_succeed
2028
2029 return tempest.test.call_until_true(ping_remote,
2030 CONF.compute.ping_timeout,
2031 1)
2032
Yair Friedeb69f3f2013-10-10 13:18:16 +03002033 def _create_security_group_neutron(self, tenant_id, client=None,
2034 namestart='secgroup-smoke-'):
2035 if client is None:
2036 client = self.network_client
2037 secgroup = self._create_empty_security_group(namestart=namestart,
2038 client=client,
2039 tenant_id=tenant_id)
2040
2041 # Add rules to the security group
2042 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
2043 for rule in rules:
2044 self.assertEqual(tenant_id, rule.tenant_id)
2045 self.assertEqual(secgroup.id, rule.security_group_id)
2046 return secgroup
2047
2048 def _create_empty_security_group(self, tenant_id, client=None,
2049 namestart='secgroup-smoke-'):
2050 """Create a security group without rules.
2051
2052 Default rules will be created:
2053 - IPv4 egress to any
2054 - IPv6 egress to any
2055
2056 :param tenant_id: secgroup will be created in this tenant
2057 :returns: DeletableSecurityGroup -- containing the secgroup created
2058 """
2059 if client is None:
2060 client = self.network_client
2061 sg_name = data_utils.rand_name(namestart)
2062 sg_desc = sg_name + " description"
2063 sg_dict = dict(name=sg_name,
2064 description=sg_desc)
2065 sg_dict['tenant_id'] = tenant_id
2066 body = dict(security_group=sg_dict)
2067 result = client.create_security_group(body=body)
2068 secgroup = net_common.DeletableSecurityGroup(
2069 client=client,
2070 **result['security_group']
2071 )
2072 self.assertEqual(secgroup.name, sg_name)
2073 self.assertEqual(tenant_id, secgroup.tenant_id)
2074 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002075 self.addCleanup(self.delete_wrapper, secgroup)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002076 return secgroup
2077
2078 def _default_security_group(self, tenant_id, client=None):
2079 """Get default secgroup for given tenant_id.
2080
2081 :returns: DeletableSecurityGroup -- default secgroup for given tenant
2082 """
2083 if client is None:
2084 client = self.network_client
2085 sgs = [
2086 sg for sg in client.list_security_groups().values()[0]
2087 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
2088 ]
2089 msg = "No default security group for tenant %s." % (tenant_id)
2090 self.assertTrue(len(sgs) > 0, msg)
2091 if len(sgs) > 1:
2092 msg = "Found %d default security groups" % len(sgs)
2093 raise exc.NeutronClientNoUniqueMatch(msg=msg)
2094 return net_common.DeletableSecurityGroup(client=client,
2095 **sgs[0])
2096
2097 def _create_security_group_rule(self, client=None, secgroup=None,
2098 tenant_id=None, **kwargs):
2099 """Create a rule from a dictionary of rule parameters.
2100
2101 Create a rule in a secgroup. if secgroup not defined will search for
2102 default secgroup in tenant_id.
2103
2104 :param secgroup: type DeletableSecurityGroup.
2105 :param secgroup_id: search for secgroup by id
2106 default -- choose default secgroup for given tenant_id
2107 :param tenant_id: if secgroup not passed -- the tenant in which to
2108 search for default secgroup
2109 :param kwargs: a dictionary containing rule parameters:
2110 for example, to allow incoming ssh:
2111 rule = {
2112 direction: 'ingress'
2113 protocol:'tcp',
2114 port_range_min: 22,
2115 port_range_max: 22
2116 }
2117 """
2118 if client is None:
2119 client = self.network_client
2120 if secgroup is None:
2121 secgroup = self._default_security_group(tenant_id)
2122
2123 ruleset = dict(security_group_id=secgroup.id,
2124 tenant_id=secgroup.tenant_id,
2125 )
2126 ruleset.update(kwargs)
2127
2128 body = dict(security_group_rule=dict(ruleset))
2129 sg_rule = client.create_security_group_rule(body=body)
2130 sg_rule = net_common.DeletableSecurityGroupRule(
2131 client=client,
2132 **sg_rule['security_group_rule']
2133 )
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002134 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002135 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
2136 self.assertEqual(secgroup.id, sg_rule.security_group_id)
2137
2138 return sg_rule
2139
2140 def _create_loginable_secgroup_rule_neutron(self, client=None,
2141 secgroup=None):
2142 """These rules are intended to permit inbound ssh and icmp
2143 traffic from all sources, so no group_id is provided.
2144 Setting a group_id would only permit traffic from ports
2145 belonging to the same security group.
2146 """
2147
2148 if client is None:
2149 client = self.network_client
2150 rules = []
2151 rulesets = [
2152 dict(
2153 # ssh
2154 protocol='tcp',
2155 port_range_min=22,
2156 port_range_max=22,
2157 ),
2158 dict(
2159 # ping
2160 protocol='icmp',
2161 )
2162 ]
2163 for ruleset in rulesets:
2164 for r_direction in ['ingress', 'egress']:
2165 ruleset['direction'] = r_direction
2166 try:
2167 sg_rule = self._create_security_group_rule(
2168 client=client, secgroup=secgroup, **ruleset)
2169 except exc.NeutronClientException as ex:
2170 # if rule already exist - skip rule and continue
2171 if not (ex.status_code is 409 and 'Security group rule'
2172 ' already exists' in ex.message):
2173 raise ex
2174 else:
2175 self.assertEqual(r_direction, sg_rule.direction)
2176 rules.append(sg_rule)
2177
2178 return rules
2179
Yair Fried5f670ab2013-12-09 09:26:51 +02002180 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +00002181 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +02002182 return self.get_remote_client(server,
2183 username=ssh_login,
2184 private_key=private_key)
2185
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00002186 def _show_quota_network(self, tenant_id):
2187 quota = self.network_client.show_quota(tenant_id)
2188 return quota['quota']['network']
2189
2190 def _show_quota_subnet(self, tenant_id):
2191 quota = self.network_client.show_quota(tenant_id)
2192 return quota['quota']['subnet']
2193
2194 def _show_quota_port(self, tenant_id):
2195 quota = self.network_client.show_quota(tenant_id)
2196 return quota['quota']['port']
2197
Yair Fried4d7efa62013-11-17 17:12:29 +02002198 def _get_router(self, tenant_id):
2199 """Retrieve a router for the given tenant id.
2200
2201 If a public router has been configured, it will be returned.
2202
2203 If a public router has not been configured, but a public
2204 network has, a tenant router will be created and returned that
2205 routes traffic to the public network.
2206 """
Matthew Treinish6c072292014-01-29 19:15:52 +00002207 router_id = CONF.network.public_router_id
2208 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02002209 if router_id:
2210 result = self.network_client.show_router(router_id)
2211 return net_common.AttributeDict(**result['router'])
2212 elif network_id:
2213 router = self._create_router(tenant_id)
2214 router.add_gateway(network_id)
2215 return router
2216 else:
2217 raise Exception("Neither of 'public_router_id' or "
2218 "'public_network_id' has been defined.")
2219
2220 def _create_router(self, tenant_id, namestart='router-smoke-'):
2221 name = data_utils.rand_name(namestart)
2222 body = dict(
2223 router=dict(
2224 name=name,
2225 admin_state_up=True,
2226 tenant_id=tenant_id,
2227 ),
2228 )
2229 result = self.network_client.create_router(body=body)
2230 router = net_common.DeletableRouter(client=self.network_client,
2231 **result['router'])
2232 self.assertEqual(router.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002233 self.addCleanup(self.delete_wrapper, router)
Yair Fried4d7efa62013-11-17 17:12:29 +02002234 return router
2235
David Shrewsbury9bac3662014-08-07 15:07:01 -04002236 def create_networks(self, tenant_id=None):
Yair Fried4d7efa62013-11-17 17:12:29 +02002237 """Create a network with a subnet connected to a router.
2238
David Shrewsbury9bac3662014-08-07 15:07:01 -04002239 The baremetal driver is a special case since all nodes are
2240 on the same shared network.
2241
Yair Fried4d7efa62013-11-17 17:12:29 +02002242 :returns: network, subnet, router
2243 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04002244 if CONF.baremetal.driver_enabled:
2245 # NOTE(Shrews): This exception is for environments where tenant
2246 # credential isolation is available, but network separation is
2247 # not (the current baremetal case). Likely can be removed when
2248 # test account mgmt is reworked:
2249 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
2250 network = self._get_network_by_name(
2251 CONF.compute.fixed_network_name)
2252 router = None
2253 subnet = None
2254 else:
2255 if tenant_id is None:
2256 tenant_id = self.tenant_id
2257 network = self._create_network(tenant_id)
2258 router = self._get_router(tenant_id)
2259 subnet = self._create_subnet(network)
2260 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02002261 return network, subnet, router
2262
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002263
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09002264class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002265 """
2266 Base class for orchestration scenario tests
2267 """
2268
2269 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07002270 def setUpClass(cls):
2271 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00002272 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07002273 raise cls.skipException("Heat support is required")
2274
2275 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002276 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00002277 admin_creds = auth.get_default_credentials('identity_admin')
2278 creds = auth.get_default_credentials('user')
2279 admin_creds.tenant_name = creds.tenant_name
2280 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002281
2282 def _load_template(self, base_file, file_name):
2283 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
2284 file_name)
2285 with open(filepath) as f:
2286 return f.read()
2287
2288 @classmethod
2289 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09002290 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12002291
2292 @classmethod
2293 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09002294 _, networks = cls.networks_client.list_networks()
2295 for net in networks:
2296 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12002297 return net
Steve Baker22c16602014-05-05 13:34:19 +12002298
2299 @staticmethod
2300 def _stack_output(stack, output_key):
2301 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09002302 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12002303 if o['output_key'] == output_key), None)
2304
Chris Dent0d494112014-08-26 13:48:30 +01002305
2306class SwiftScenarioTest(ScenarioTest):
2307 """
2308 Provide harness to do Swift scenario tests.
2309
2310 Subclasses implement the tests that use the methods provided by this
2311 class.
2312 """
2313
2314 @classmethod
2315 def setUpClass(cls):
2316 cls.set_network_resources()
2317 super(SwiftScenarioTest, cls).setUpClass()
2318 if not CONF.service_available.swift:
2319 skip_msg = ("%s skipped as swift is not available" %
2320 cls.__name__)
2321 raise cls.skipException(skip_msg)
2322 # Clients for Swift
2323 cls.account_client = cls.manager.account_client
2324 cls.container_client = cls.manager.container_client
2325 cls.object_client = cls.manager.object_client
2326
2327 def _get_swift_stat(self):
2328 """get swift status for our user account."""
2329 self.account_client.list_account_containers()
2330 LOG.debug('Swift status information obtained successfully')
2331
2332 def _create_container(self, container_name=None):
2333 name = container_name or data_utils.rand_name(
2334 'swift-scenario-container')
2335 self.container_client.create_container(name)
2336 # look for the container to assure it is created
2337 self._list_and_check_container_objects(name)
2338 LOG.debug('Container %s created' % (name))
2339 return name
2340
2341 def _delete_container(self, container_name):
2342 self.container_client.delete_container(container_name)
2343 LOG.debug('Container %s deleted' % (container_name))
2344
2345 def _upload_object_to_container(self, container_name, obj_name=None):
2346 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
2347 obj_data = data_utils.arbitrary_string()
2348 self.object_client.create_object(container_name, obj_name, obj_data)
2349 return obj_name, obj_data
2350
2351 def _delete_object(self, container_name, filename):
2352 self.object_client.delete_object(container_name, filename)
2353 self._list_and_check_container_objects(container_name,
2354 not_present_obj=[filename])
2355
Ghanshyam2a180b82014-06-16 13:54:22 +09002356 def _list_and_check_container_objects(self, container_name,
2357 present_obj=None,
2358 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01002359 """
2360 List objects for a given container and assert which are present and
2361 which are not.
2362 """
Ghanshyam2a180b82014-06-16 13:54:22 +09002363 if present_obj is None:
2364 present_obj = []
2365 if not_present_obj is None:
2366 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01002367 _, object_list = self.container_client.list_container_contents(
2368 container_name)
2369 if present_obj:
2370 for obj in present_obj:
2371 self.assertIn(obj, object_list)
2372 if not_present_obj:
2373 for obj in not_present_obj:
2374 self.assertNotIn(obj, object_list)
2375
2376 def _change_container_acl(self, container_name, acl):
2377 metadata_param = {'metadata_prefix': 'x-container-',
2378 'metadata': {'read': acl}}
2379 self.container_client.update_container_metadata(container_name,
2380 **metadata_param)
2381 resp, _ = self.container_client.list_container_metadata(container_name)
2382 self.assertEqual(resp['x-container-read'], acl)
2383
2384 def _download_and_verify(self, container_name, obj_name, expected_data):
2385 _, obj = self.object_client.get_object(container_name, obj_name)
2386 self.assertEqual(obj, expected_data)