blob: cabefc8dbab8dbeca9c4c2625a97c66c0862f7d6 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Attila Fazekasfb7552a2013-08-27 13:02:26 +020017import logging
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120018import os
Steve Baker22c16602014-05-05 13:34:19 +120019import re
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import subprocess
Steve Baker22c16602014-05-05 13:34:19 +120021import time
Sean Dague6dbc6da2013-05-08 17:49:46 -040022
Kaitlin Farr366a51f2014-04-21 12:43:54 -040023from cinderclient import exceptions as cinder_exceptions
Matthew Treinishb7144eb2013-12-13 22:57:35 +000024import glanceclient
Steve Baker22c16602014-05-05 13:34:19 +120025from heatclient import exc as heat_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040026import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040027from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090028from novaclient import exceptions as nova_exceptions
Matthew Treinish96e9e882014-06-09 18:37:19 -040029import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040030
Sean Dague1937d092013-05-17 16:36:38 -040031from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000032from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000033from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070034from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040035from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090036from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090037from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000038from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020039from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020040from tempest.openstack.common import log
Steve Baker22c16602014-05-05 13:34:19 +120041from tempest.openstack.common import timeutils
Yair Fried1fc32a12014-08-04 09:11:30 +030042from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040043import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040044
Matthew Treinish6c072292014-01-29 19:15:52 +000045CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
Attila Fazekasfb7552a2013-08-27 13:02:26 +020047LOG = log.getLogger(__name__)
48
49# NOTE(afazekas): Workaround for the stdout logging
50LOG_nova_client = logging.getLogger('novaclient.client')
51LOG_nova_client.addHandler(log.NullHandler())
52
53LOG_cinder_client = logging.getLogger('cinderclient.client')
54LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040055
56
Andrea Frittoli2e733b52014-07-16 14:12:11 +010057class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli247058f2014-07-16 16:09:22 +010058 """Replaces the OfficialClientTest base class.
59
60 Uses tempest own clients as opposed to OfficialClients.
61
62 Common differences:
63 - replace resource.attribute with resource['attribute']
64 - replace resouce.delete with delete_callable(resource['id'])
65 - replace local waiters with common / rest_client waiters
66 """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010067
68 @classmethod
69 def setUpClass(cls):
70 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010071 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010072 cls.isolated_creds = isolated_creds.IsolatedCreds(
73 cls.__name__, tempest_client=True,
74 network_resources=cls.network_resources)
75 cls.manager = clients.Manager(
76 credentials=cls.credentials()
77 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010078 cls.admin_manager = clients.Manager(cls.admin_credentials())
79 # Clients (in alphabetical order)
80 cls.floating_ips_client = cls.manager.floating_ips_client
81 # Glance image client v1
82 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000083 # Compute image client
84 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010085 cls.keypairs_client = cls.manager.keypairs_client
86 cls.networks_client = cls.admin_manager.networks_client
87 # Nova security groups client
88 cls.security_groups_client = cls.manager.security_groups_client
89 cls.servers_client = cls.manager.servers_client
90 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000091 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030092 cls.interface_client = cls.manager.interfaces_client
93 # Neutron network client
94 cls.network_client = cls.manager.network_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010095
96 @classmethod
Salvatorecfe42f62014-09-03 23:19:12 +020097 def tearDownClass(cls):
98 # Isolated creds also manages network resources, which should
99 # be cleaned up at the end of the test case
100 cls.isolated_creds.clear_isolated_creds()
101 super(ScenarioTest, cls).tearDownClass()
102
103 @classmethod
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100104 def _get_credentials(cls, get_creds, ctype):
105 if CONF.compute.allow_tenant_isolation:
106 creds = get_creds()
107 else:
108 creds = auth.get_default_credentials(ctype)
109 return creds
110
111 @classmethod
112 def credentials(cls):
113 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
114 'user')
115
Masayuki Igawaccd66592014-07-17 00:42:42 +0900116 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +0300117 def alt_credentials(cls):
118 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
119 'alt_user')
120
121 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900122 def admin_credentials(cls):
123 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
124 'identity_admin')
125
Andrea Frittoli247058f2014-07-16 16:09:22 +0100126 # ## Methods to handle sync and async deletes
127
128 def setUp(self):
129 super(ScenarioTest, self).setUp()
130 self.cleanup_waits = []
131 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
132 # because scenario tests in the same test class should not share
133 # resources. If resources were shared between test cases then it
134 # should be a single scenario test instead of multiples.
135
136 # NOTE(yfried): this list is cleaned at the end of test_methods and
137 # not at the end of the class
138 self.addCleanup(self._wait_for_cleanups)
139
Yair Fried1fc32a12014-08-04 09:11:30 +0300140 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100141 """Ignores NotFound exceptions for delete operations.
142
Yair Fried1fc32a12014-08-04 09:11:30 +0300143 @param delete_thing: delete method of a resource. method will be
144 executed as delete_thing(*args, **kwargs)
145
Andrea Frittoli247058f2014-07-16 16:09:22 +0100146 """
147 try:
148 # Tempest clients return dicts, so there is no common delete
149 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300150 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100151 except exceptions.NotFound:
152 # If the resource is already missing, mission accomplished.
153 pass
154
155 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900156 cleanup_callable, cleanup_args=None,
157 cleanup_kwargs=None, ignore_error=True):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100158 """Adds wait for ansyc resource deletion at the end of cleanups
159
160 @param waiter_callable: callable to wait for the resource to delete
161 @param thing_id: the id of the resource to be cleaned-up
162 @param thing_id_param: the name of the id param in the waiter
163 @param cleanup_callable: method to load pass to self.addCleanup with
164 the following *cleanup_args, **cleanup_kwargs.
165 usually a delete method.
166 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900167 if cleanup_args is None:
168 cleanup_args = []
169 if cleanup_kwargs is None:
170 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100171 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
172 wait_dict = {
173 'waiter_callable': waiter_callable,
174 thing_id_param: thing_id
175 }
176 self.cleanup_waits.append(wait_dict)
177
178 def _wait_for_cleanups(self):
179 """To handle async delete actions, a list of waits is added
180 which will be iterated over as the last step of clearing the
181 cleanup queue. That way all the delete calls are made up front
182 and the tests won't succeed unless the deletes are eventually
183 successful. This is the same basic approach used in the api tests to
184 limit cleanup execution time except here it is multi-resource,
185 because of the nature of the scenario tests.
186 """
187 for wait in self.cleanup_waits:
188 waiter_callable = wait.pop('waiter_callable')
189 waiter_callable(**wait)
190
191 # ## Test functions library
192 #
193 # The create_[resource] functions only return body and discard the
194 # resp part which is not used in scenario tests
195
Yair Frieddb6c9e92014-08-06 08:53:13 +0300196 def create_keypair(self, client=None):
197 if not client:
198 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100199 name = data_utils.rand_name(self.__class__.__name__)
200 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300201 resp, body = client.create_keypair(name)
202 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100203 return body
204
205 def create_server(self, name=None, image=None, flavor=None,
206 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900207 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100208 """Creates VM instance.
209
210 @param image: image from which to create the instance
211 @param wait_on_boot: wait for status ACTIVE before continue
212 @param wait_on_delete: force synchronous delete on cleanup
213 @param create_kwargs: additional details for instance creation
214 @return: server dict
215 """
216 if name is None:
217 name = data_utils.rand_name(self.__class__.__name__)
218 if image is None:
219 image = CONF.compute.image_ref
220 if flavor is None:
221 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900222 if create_kwargs is None:
223 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100224
225 fixed_network_name = CONF.compute.fixed_network_name
226 if 'nics' not in create_kwargs and fixed_network_name:
227 _, networks = self.networks_client.list_networks()
228 # If several networks found, set the NetID on which to connect the
229 # server to avoid the following error "Multiple possible networks
230 # found, use a Network ID to be more specific."
231 # See Tempest #1250866
232 if len(networks) > 1:
233 for network in networks:
234 if network['label'] == fixed_network_name:
235 create_kwargs['nics'] = [{'net-id': network['id']}]
236 break
237 # If we didn't find the network we were looking for :
238 else:
239 msg = ("The network on which the NIC of the server must "
240 "be connected can not be found : "
241 "fixed_network_name=%s. Starting instance without "
242 "specifying a network.") % fixed_network_name
243 LOG.info(msg)
244
245 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
246 name, image, flavor)
247 _, server = self.servers_client.create_server(name, image, flavor,
248 **create_kwargs)
249 if wait_on_delete:
250 self.addCleanup(self.servers_client.wait_for_server_termination,
251 server['id'])
252 self.addCleanup_with_wait(
253 waiter_callable=self.servers_client.wait_for_server_termination,
254 thing_id=server['id'], thing_id_param='server_id',
255 cleanup_callable=self.delete_wrapper,
256 cleanup_args=[self.servers_client.delete_server, server['id']])
257 if wait_on_boot:
258 self.servers_client.wait_for_server_status(server_id=server['id'],
259 status='ACTIVE')
260 # The instance retrieved on creation is missing network
261 # details, necessitating retrieval after it becomes active to
262 # ensure correct details.
263 _, server = self.servers_client.get_server(server['id'])
264 self.assertEqual(server['name'], name)
265 return server
266
267 def create_volume(self, size=1, name=None, snapshot_id=None,
268 imageRef=None, volume_type=None, wait_on_delete=True):
269 if name is None:
270 name = data_utils.rand_name(self.__class__.__name__)
271 _, volume = self.volumes_client.create_volume(
272 size=size, display_name=name, snapshot_id=snapshot_id,
273 imageRef=imageRef, volume_type=volume_type)
274 if wait_on_delete:
275 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
276 volume['id'])
277 self.addCleanup_with_wait(
278 waiter_callable=self.volumes_client.wait_for_resource_deletion,
279 thing_id=volume['id'], thing_id_param='id',
280 cleanup_callable=self.delete_wrapper,
281 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
282
283 self.assertEqual(name, volume['display_name'])
284 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
285 # The volume retrieved on creation has a non-up-to-date status.
286 # Retrieval after it becomes active ensures correct details.
287 _, volume = self.volumes_client.get_volume(volume['id'])
288 return volume
289
Yair Fried1fc32a12014-08-04 09:11:30 +0300290 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100291 _client = self.security_groups_client
292 if secgroup_id is None:
293 _, sgs = _client.list_security_groups()
294 for sg in sgs:
295 if sg['name'] == 'default':
296 secgroup_id = sg['id']
297
298 # These rules are intended to permit inbound ssh and icmp
299 # traffic from all sources, so no group_id is provided.
300 # Setting a group_id would only permit traffic from ports
301 # belonging to the same security group.
302 rulesets = [
303 {
304 # ssh
305 'ip_proto': 'tcp',
306 'from_port': 22,
307 'to_port': 22,
308 'cidr': '0.0.0.0/0',
309 },
310 {
311 # ping
312 'ip_proto': 'icmp',
313 'from_port': -1,
314 'to_port': -1,
315 'cidr': '0.0.0.0/0',
316 }
317 ]
318 rules = list()
319 for ruleset in rulesets:
320 _, sg_rule = _client.create_security_group_rule(secgroup_id,
321 **ruleset)
322 self.addCleanup(self.delete_wrapper,
323 _client.delete_security_group_rule,
324 sg_rule['id'])
325 rules.append(sg_rule)
326 return rules
327
Yair Fried1fc32a12014-08-04 09:11:30 +0300328 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100329 # Create security group
330 sg_name = data_utils.rand_name(self.__class__.__name__)
331 sg_desc = sg_name + " description"
332 _, secgroup = self.security_groups_client.create_security_group(
333 sg_name, sg_desc)
334 self.assertEqual(secgroup['name'], sg_name)
335 self.assertEqual(secgroup['description'], sg_desc)
336 self.addCleanup(self.delete_wrapper,
337 self.security_groups_client.delete_security_group,
338 secgroup['id'])
339
340 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300341 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100342
343 return secgroup
344
345 def get_remote_client(self, server_or_ip, username=None, private_key=None):
346 if isinstance(server_or_ip, six.string_types):
347 ip = server_or_ip
348 else:
349 network_name_for_ssh = CONF.compute.network_for_ssh
350 ip = server_or_ip.networks[network_name_for_ssh][0]
351 if username is None:
352 username = CONF.scenario.ssh_user
353 if private_key is None:
354 private_key = self.keypair['private_key']
355 linux_client = remote_client.RemoteClient(ip, username,
356 pkey=private_key)
357 try:
358 linux_client.validate_authentication()
359 except exceptions.SSHTimeout:
360 LOG.exception('ssh connection to %s failed' % ip)
361 debug.log_net_debug()
362 raise
363
364 return linux_client
365
Ghanshyam2a180b82014-06-16 13:54:22 +0900366 def _image_create(self, name, fmt, path, properties=None):
367 if properties is None:
368 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100369 name = data_utils.rand_name('%s-' % name)
370 image_file = open(path, 'rb')
371 self.addCleanup(image_file.close)
372 params = {
373 'name': name,
374 'container_format': fmt,
375 'disk_format': fmt,
376 'is_public': 'False',
377 }
378 params.update(properties)
379 _, image = self.image_client.create_image(**params)
380 self.addCleanup(self.image_client.delete_image, image['id'])
381 self.assertEqual("queued", image['status'])
382 self.image_client.update_image(image['id'], data=image_file)
383 return image['id']
384
385 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300386 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100387 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
388 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
389 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300390 img_container_format = CONF.scenario.img_container_format
391 img_disk_format = CONF.scenario.img_disk_format
392 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
393 "ami: %s, ari: %s, aki: %s" %
394 (img_path, img_container_format, img_disk_format,
395 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100396 try:
397 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300398 img_container_format,
399 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100400 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300401 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100402 except IOError:
403 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
404 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
405 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
406 properties = {
407 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
408 }
409 self.image = self._image_create('scenario-ami', 'ami',
410 path=ami_img_path,
411 properties=properties)
412 LOG.debug("image:%s" % self.image)
413
414 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400415 if not CONF.compute_feature_enabled.console_output:
416 LOG.debug('Console output not supported, cannot log')
417 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100418 if not servers:
419 _, servers = self.servers_client.list_servers()
420 servers = servers['servers']
421 for server in servers:
422 LOG.debug('Console output for %s', server['id'])
423 LOG.debug(self.servers_client.get_console_output(server['id'],
424 length=None))
425
nithya-ganesan882595e2014-07-29 18:51:07 +0000426 def create_server_snapshot(self, server, name=None):
427 # Glance client
428 _image_client = self.image_client
429 # Compute client
430 _images_client = self.images_client
431 if name is None:
432 name = data_utils.rand_name('scenario-snapshot-')
433 LOG.debug("Creating a snapshot image for server: %s", server['name'])
434 resp, image = _images_client.create_image(server['id'], name)
435 image_id = resp['location'].split('images/')[1]
436 _image_client.wait_for_image_status(image_id, 'active')
437 self.addCleanup_with_wait(
438 waiter_callable=_image_client.wait_for_resource_deletion,
439 thing_id=image_id, thing_id_param='id',
440 cleanup_callable=self.delete_wrapper,
441 cleanup_args=[_image_client.delete_image, image_id])
442 _, snapshot_image = _image_client.get_image_meta(image_id)
443 image_name = snapshot_image['name']
444 self.assertEqual(name, image_name)
445 LOG.debug("Created snapshot image %s for server %s",
446 image_name, server['name'])
447 return snapshot_image
448
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100449
Yair Fried1fc32a12014-08-04 09:11:30 +0300450# TODO(yfried): change this class name to NetworkScenarioTest once client
451# migration is complete
452class NeutronScenarioTest(ScenarioTest):
453 """Base class for network scenario tests.
454 This class provide helpers for network scenario tests, using the neutron
455 API. Helpers from ancestor which use the nova network API are overridden
456 with the neutron API.
457
458 This Class also enforces using Neutron instead of novanetwork.
459 Subclassed tests will be skipped if Neutron is not enabled
460
461 """
462
463 @classmethod
464 def check_preconditions(cls):
465 if CONF.service_available.neutron:
466 cls.enabled = True
467 # verify that neutron_available is telling the truth
468 try:
469 cls.network_client.list_networks()
470 except exc.EndpointNotFound:
471 cls.enabled = False
472 raise
473 else:
474 cls.enabled = False
475 msg = 'Neutron not available'
476 raise cls.skipException(msg)
477
478 @classmethod
479 def setUpClass(cls):
480 super(NeutronScenarioTest, cls).setUpClass()
481 cls.tenant_id = cls.manager.identity_client.tenant_id
482 cls.check_preconditions()
483
Yair Frieddb6c9e92014-08-06 08:53:13 +0300484 def _create_network(self, client=None, tenant_id=None,
485 namestart='network-smoke-'):
486 if not client:
487 client = self.network_client
488 if not tenant_id:
489 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300490 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300491 _, result = client.create_network(name=name, tenant_id=tenant_id)
492 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300493 **result['network'])
494 self.assertEqual(network.name, name)
495 self.addCleanup(self.delete_wrapper, network.delete)
496 return network
497
498 def _list_networks(self, *args, **kwargs):
499 """List networks using admin creds """
500 return self._admin_lister('networks')(*args, **kwargs)
501
502 def _list_subnets(self, *args, **kwargs):
503 """List subnets using admin creds """
504 return self._admin_lister('subnets')(*args, **kwargs)
505
506 def _list_routers(self, *args, **kwargs):
507 """List routers using admin creds """
508 return self._admin_lister('routers')(*args, **kwargs)
509
510 def _list_ports(self, *args, **kwargs):
511 """List ports using admin creds """
512 return self._admin_lister('ports')(*args, **kwargs)
513
514 def _admin_lister(self, resource_type):
515 def temp(*args, **kwargs):
516 temp_method = self.admin_manager.network_client.__getattr__(
517 'list_%s' % resource_type)
518 _, resource_list = temp_method(*args, **kwargs)
519 return resource_list[resource_type]
520 return temp
521
Yair Frieddb6c9e92014-08-06 08:53:13 +0300522 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
523 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300524 """
525 Create a subnet for the given network within the cidr block
526 configured for tenant networks.
527 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300528 if not client:
529 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300530
531 def cidr_in_use(cidr, tenant_id):
532 """
533 :return True if subnet with cidr already exist in tenant
534 False else
535 """
536 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
537 return len(cidr_in_use) != 0
538
539 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
540 result = None
541 # Repeatedly attempt subnet creation with sequential cidr
542 # blocks until an unallocated block is found.
543 for subnet_cidr in tenant_cidr.subnet(
544 CONF.network.tenant_network_mask_bits):
545 str_cidr = str(subnet_cidr)
546 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
547 continue
548
549 subnet = dict(
550 name=data_utils.rand_name(namestart),
551 ip_version=4,
552 network_id=network.id,
553 tenant_id=network.tenant_id,
554 cidr=str_cidr,
555 **kwargs
556 )
557 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300558 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300559 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300560 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300561 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
562 if not is_overlapping_cidr:
563 raise
564 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300565 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300566 **result['subnet'])
567 self.assertEqual(subnet.cidr, str_cidr)
568 self.addCleanup(self.delete_wrapper, subnet.delete)
569 return subnet
570
Yair Frieddb6c9e92014-08-06 08:53:13 +0300571 def _create_port(self, network, client=None, namestart='port-quotatest'):
572 if not client:
573 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300574 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300575 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300576 name=name,
577 network_id=network.id,
578 tenant_id=network.tenant_id)
579 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300580 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300581 **result['port'])
582 self.addCleanup(self.delete_wrapper, port.delete)
583 return port
584
585 def _get_server_port_id(self, server, ip_addr=None):
586 ports = self._list_ports(device_id=server['id'],
587 fixed_ip=ip_addr)
588 self.assertEqual(len(ports), 1,
589 "Unable to determine which port to target.")
590 return ports[0]['id']
591
David Shrewsbury9bac3662014-08-07 15:07:01 -0400592 def _get_network_by_name(self, network_name):
593 net = self._list_networks(name=network_name)
594 return net_common.AttributeDict(net[0])
595
Yair Frieddb6c9e92014-08-06 08:53:13 +0300596 def _create_floating_ip(self, thing, external_network_id, port_id=None,
597 client=None):
598 if not client:
599 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300600 if not port_id:
601 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300602 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300603 floating_network_id=external_network_id,
604 port_id=port_id,
605 tenant_id=thing['tenant_id']
606 )
607 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300608 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300609 **result['floatingip'])
610 self.addCleanup(self.delete_wrapper, floating_ip.delete)
611 return floating_ip
612
613 def _associate_floating_ip(self, floating_ip, server):
614 port_id = self._get_server_port_id(server)
615 floating_ip.update(port_id=port_id)
616 self.assertEqual(port_id, floating_ip.port_id)
617 return floating_ip
618
619 def _disassociate_floating_ip(self, floating_ip):
620 """
621 :param floating_ip: type DeletableFloatingIp
622 """
623 floating_ip.update(port_id=None)
624 self.assertIsNone(floating_ip.port_id)
625 return floating_ip
626
627 def _ping_ip_address(self, ip_address, should_succeed=True):
628 cmd = ['ping', '-c1', '-w1', ip_address]
629
630 def ping():
631 proc = subprocess.Popen(cmd,
632 stdout=subprocess.PIPE,
633 stderr=subprocess.PIPE)
634 proc.wait()
635 return (proc.returncode == 0) == should_succeed
636
637 return tempest.test.call_until_true(
638 ping, CONF.compute.ping_timeout, 1)
639
640 def _check_vm_connectivity(self, ip_address,
641 username=None,
642 private_key=None,
643 should_connect=True):
644 """
645 :param ip_address: server to test against
646 :param username: server's ssh username
647 :param private_key: server's ssh private key to be used
648 :param should_connect: True/False indicates positive/negative test
649 positive - attempt ping and ssh
650 negative - attempt ping and fail if succeed
651
652 :raises: AssertError if the result of the connectivity check does
653 not match the value of the should_connect param
654 """
655 if should_connect:
656 msg = "Timed out waiting for %s to become reachable" % ip_address
657 else:
658 msg = "ip address %s is reachable" % ip_address
659 self.assertTrue(self._ping_ip_address(ip_address,
660 should_succeed=should_connect),
661 msg=msg)
662 if should_connect:
663 # no need to check ssh for negative connectivity
664 self.get_remote_client(ip_address, username, private_key)
665
666 def _check_public_network_connectivity(self, ip_address, username,
667 private_key, should_connect=True,
668 msg=None, servers=None):
669 # The target login is assumed to have been configured for
670 # key-based authentication by cloud-init.
671 LOG.debug('checking network connections to IP %s with user: %s' %
672 (ip_address, username))
673 try:
674 self._check_vm_connectivity(ip_address,
675 username,
676 private_key,
677 should_connect=should_connect)
678 except Exception as e:
679 ex_msg = 'Public network connectivity check failed'
680 if msg:
681 ex_msg += ": " + msg
682 LOG.exception(ex_msg)
683 self._log_console_output(servers)
684 # network debug is called as part of ssh init
685 if not isinstance(e, exceptions.SSHTimeout):
686 debug.log_net_debug()
687 raise
688
689 def _check_tenant_network_connectivity(self, server,
690 username,
691 private_key,
692 should_connect=True,
693 servers_for_debug=None):
694 if not CONF.network.tenant_networks_reachable:
695 msg = 'Tenant networks not configured to be reachable.'
696 LOG.info(msg)
697 return
698 # The target login is assumed to have been configured for
699 # key-based authentication by cloud-init.
700 try:
701 for net_name, ip_addresses in server['networks'].iteritems():
702 for ip_address in ip_addresses:
703 self._check_vm_connectivity(ip_address,
704 username,
705 private_key,
706 should_connect=should_connect)
707 except Exception as e:
708 LOG.exception('Tenant network connectivity check failed')
709 self._log_console_output(servers_for_debug)
710 # network debug is called as part of ssh init
711 if not isinstance(e, exceptions.SSHTimeout):
712 debug.log_net_debug()
713 raise
714
715 def _check_remote_connectivity(self, source, dest, should_succeed=True):
716 """
717 check ping server via source ssh connection
718
719 :param source: RemoteClient: an ssh connection from which to ping
720 :param dest: and IP to ping against
721 :param should_succeed: boolean should ping succeed or not
722 :returns: boolean -- should_succeed == ping
723 :returns: ping is false if ping failed
724 """
725 def ping_remote():
726 try:
727 source.ping_host(dest)
728 except exceptions.SSHExecCommandFailed:
729 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
730 % (dest, source.ssh_client.host))
731 return not should_succeed
732 return should_succeed
733
734 return tempest.test.call_until_true(ping_remote,
735 CONF.compute.ping_timeout,
736 1)
737
Yair Frieddb6c9e92014-08-06 08:53:13 +0300738 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300739 namestart='secgroup-smoke'):
740 if client is None:
741 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300742 if tenant_id is None:
743 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300744 secgroup = self._create_empty_security_group(namestart=namestart,
745 client=client,
746 tenant_id=tenant_id)
747
748 # Add rules to the security group
749 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
750 for rule in rules:
751 self.assertEqual(tenant_id, rule.tenant_id)
752 self.assertEqual(secgroup.id, rule.security_group_id)
753 return secgroup
754
Yair Frieddb6c9e92014-08-06 08:53:13 +0300755 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300756 namestart='secgroup-smoke'):
757 """Create a security group without rules.
758
759 Default rules will be created:
760 - IPv4 egress to any
761 - IPv6 egress to any
762
763 :param tenant_id: secgroup will be created in this tenant
764 :returns: DeletableSecurityGroup -- containing the secgroup created
765 """
766 if client is None:
767 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300768 if not tenant_id:
769 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300770 sg_name = data_utils.rand_name(namestart)
771 sg_desc = sg_name + " description"
772 sg_dict = dict(name=sg_name,
773 description=sg_desc)
774 sg_dict['tenant_id'] = tenant_id
775 _, result = client.create_security_group(**sg_dict)
776 secgroup = net_resources.DeletableSecurityGroup(
777 client=client,
778 **result['security_group']
779 )
780 self.assertEqual(secgroup.name, sg_name)
781 self.assertEqual(tenant_id, secgroup.tenant_id)
782 self.assertEqual(secgroup.description, sg_desc)
783 self.addCleanup(self.delete_wrapper, secgroup.delete)
784 return secgroup
785
Yair Frieddb6c9e92014-08-06 08:53:13 +0300786 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300787 """Get default secgroup for given tenant_id.
788
789 :returns: DeletableSecurityGroup -- default secgroup for given tenant
790 """
791 if client is None:
792 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300793 if not tenant_id:
794 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300795 sgs = [
796 sg for sg in client.list_security_groups().values()[0]
797 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
798 ]
799 msg = "No default security group for tenant %s." % (tenant_id)
800 self.assertTrue(len(sgs) > 0, msg)
801 if len(sgs) > 1:
802 msg = "Found %d default security groups" % len(sgs)
803 raise exc.NeutronClientNoUniqueMatch(msg=msg)
804 return net_resources.DeletableSecurityGroup(client=client,
805 **sgs[0])
806
Yair Frieddb6c9e92014-08-06 08:53:13 +0300807 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300808 tenant_id=None, **kwargs):
809 """Create a rule from a dictionary of rule parameters.
810
811 Create a rule in a secgroup. if secgroup not defined will search for
812 default secgroup in tenant_id.
813
814 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300815 :param tenant_id: if secgroup not passed -- the tenant in which to
816 search for default secgroup
817 :param kwargs: a dictionary containing rule parameters:
818 for example, to allow incoming ssh:
819 rule = {
820 direction: 'ingress'
821 protocol:'tcp',
822 port_range_min: 22,
823 port_range_max: 22
824 }
825 """
826 if client is None:
827 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300828 if not tenant_id:
829 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300830 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300831 secgroup = self._default_security_group(client=client,
832 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300833
834 ruleset = dict(security_group_id=secgroup.id,
835 tenant_id=secgroup.tenant_id)
836 ruleset.update(kwargs)
837
838 _, sg_rule = client.create_security_group_rule(**ruleset)
839 sg_rule = net_resources.DeletableSecurityGroupRule(
840 client=client,
841 **sg_rule['security_group_rule']
842 )
843 self.addCleanup(self.delete_wrapper, sg_rule.delete)
844 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
845 self.assertEqual(secgroup.id, sg_rule.security_group_id)
846
847 return sg_rule
848
849 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
850 """These rules are intended to permit inbound ssh and icmp
851 traffic from all sources, so no group_id is provided.
852 Setting a group_id would only permit traffic from ports
853 belonging to the same security group.
854 """
855
856 if client is None:
857 client = self.network_client
858 rules = []
859 rulesets = [
860 dict(
861 # ssh
862 protocol='tcp',
863 port_range_min=22,
864 port_range_max=22,
865 ),
866 dict(
867 # ping
868 protocol='icmp',
869 )
870 ]
871 for ruleset in rulesets:
872 for r_direction in ['ingress', 'egress']:
873 ruleset['direction'] = r_direction
874 try:
875 sg_rule = self._create_security_group_rule(
876 client=client, secgroup=secgroup, **ruleset)
877 except exceptions.Conflict as ex:
878 # if rule already exist - skip rule and continue
879 msg = 'Security group rule already exists'
880 if msg not in ex._error_string:
881 raise ex
882 else:
883 self.assertEqual(r_direction, sg_rule.direction)
884 rules.append(sg_rule)
885
886 return rules
887
888 def _ssh_to_server(self, server, private_key):
889 ssh_login = CONF.compute.image_ssh_user
890 return self.get_remote_client(server,
891 username=ssh_login,
892 private_key=private_key)
893
Yair Frieddb6c9e92014-08-06 08:53:13 +0300894 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300895 """Retrieve a router for the given tenant id.
896
897 If a public router has been configured, it will be returned.
898
899 If a public router has not been configured, but a public
900 network has, a tenant router will be created and returned that
901 routes traffic to the public network.
902 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300903 if not client:
904 client = self.network_client
905 if not tenant_id:
906 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300907 router_id = CONF.network.public_router_id
908 network_id = CONF.network.public_network_id
909 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300910 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300911 return net_resources.AttributeDict(**result['router'])
912 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300913 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300914 router.set_gateway(network_id)
915 return router
916 else:
917 raise Exception("Neither of 'public_router_id' or "
918 "'public_network_id' has been defined.")
919
Yair Frieddb6c9e92014-08-06 08:53:13 +0300920 def _create_router(self, client=None, tenant_id=None,
921 namestart='router-smoke'):
922 if not client:
923 client = self.network_client
924 if not tenant_id:
925 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300927 _, result = client.create_router(name=name,
928 admin_state_up=True,
929 tenant_id=tenant_id)
930 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300931 **result['router'])
932 self.assertEqual(router.name, name)
933 self.addCleanup(self.delete_wrapper, router.delete)
934 return router
935
Yair Frieddb6c9e92014-08-06 08:53:13 +0300936 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300937 """Create a network with a subnet connected to a router.
938
David Shrewsbury9bac3662014-08-07 15:07:01 -0400939 The baremetal driver is a special case since all nodes are
940 on the same shared network.
941
Yair Fried1fc32a12014-08-04 09:11:30 +0300942 :returns: network, subnet, router
943 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400944 if CONF.baremetal.driver_enabled:
945 # NOTE(Shrews): This exception is for environments where tenant
946 # credential isolation is available, but network separation is
947 # not (the current baremetal case). Likely can be removed when
948 # test account mgmt is reworked:
949 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
950 network = self._get_network_by_name(
951 CONF.compute.fixed_network_name)
952 router = None
953 subnet = None
954 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300955 network = self._create_network(client=client, tenant_id=tenant_id)
956 router = self._get_router(client=client, tenant_id=tenant_id)
957 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400958 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300959 return network, subnet, router
960
961
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400962class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400963 """
964 Official Client test base class for scenario testing.
965
966 Official Client tests are tests that have the following characteristics:
967
968 * Test basic operations of an API, typically in an order that
969 a regular user would perform those operations
970 * Test only the correct inputs and action paths -- no fuzz or
971 random input data is sent, only valid inputs.
972 * Use only the default client tool for calling an API
973 """
974
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400975 @classmethod
976 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200977 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -0400978 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -0500979 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500980 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200981
Andrea Frittolif9cde7e2014-02-18 09:57:04 +0000982 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000983 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400984 cls.compute_client = cls.manager.compute_client
985 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -0700986 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400987 cls.identity_client = cls.manager.identity_client
988 cls.network_client = cls.manager.network_client
989 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000990 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200991 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +0400992 cls.data_processing_client = cls.manager.data_processing_client
Artur Svechnikovc3bf9252014-05-05 16:37:37 +0400993 cls.ceilometer_client = cls.manager.ceilometer_client
Sean Dague6dbc6da2013-05-08 17:49:46 -0400994
995 @classmethod
Miguel Lavalleda5f7082014-07-16 19:18:22 -0500996 def tearDownClass(cls):
997 cls.isolated_creds.clear_isolated_creds()
998 super(OfficialClientTest, cls).tearDownClass()
999
1000 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001001 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +00001002 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001003 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +02001004 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001005 creds = auth.get_default_credentials(ctype)
1006 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001007
1008 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +02001009 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001010 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
1011 'user')
Yair Frieda71cc442013-12-18 13:32:36 +02001012
1013 @classmethod
1014 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001015 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
1016 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +02001017
1018 @classmethod
1019 def admin_credentials(cls):
1020 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001021 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +02001022
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001023 def setUp(self):
1024 super(OfficialClientTest, self).setUp()
1025 self.cleanup_waits = []
1026 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
1027 # because scenario tests in the same test class should not share
1028 # resources. If resources were shared between test cases then it
1029 # should be a single scenario test instead of multiples.
Yair Friedbf2e2c42014-01-28 12:06:38 +02001030
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001031 # NOTE(yfried): this list is cleaned at the end of test_methods and
1032 # not at the end of the class
1033 self.addCleanup(self._wait_for_cleanups)
1034
1035 @staticmethod
1036 def not_found_exception(exception):
1037 """
1038 @return: True if exception is of NotFound type
1039 """
1040 NOT_FOUND_LIST = ['NotFound', 'HTTPNotFound']
1041 return (exception.__class__.__name__ in NOT_FOUND_LIST
1042 or
1043 hasattr(exception, 'status_code') and
1044 exception.status_code == 404)
1045
1046 def delete_wrapper(self, thing):
1047 """Ignores NotFound exceptions for delete operations.
1048
1049 @param thing: object with delete() method.
1050 OpenStack resources are assumed to have a delete() method which
1051 destroys the resource
1052 """
1053
Yair Friedbf2e2c42014-01-28 12:06:38 +02001054 try:
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001055 thing.delete()
Yair Friedbf2e2c42014-01-28 12:06:38 +02001056 except Exception as e:
1057 # If the resource is already missing, mission accomplished.
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001058 if not self.not_found_exception(e):
Yair Friedbf2e2c42014-01-28 12:06:38 +02001059 raise
Yair Friedbf2e2c42014-01-28 12:06:38 +02001060
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001061 def _wait_for_cleanups(self):
1062 """To handle async delete actions, a list of waits is added
1063 which will be iterated over as the last step of clearing the
1064 cleanup queue. That way all the delete calls are made up front
1065 and the tests won't succeed unless the deletes are eventually
1066 successful. This is the same basic approach used in the api tests to
1067 limit cleanup execution time except here it is multi-resource,
1068 because of the nature of the scenario tests.
1069 """
1070 for wait in self.cleanup_waits:
1071 self.delete_timeout(**wait)
Yair Friedbf2e2c42014-01-28 12:06:38 +02001072
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001073 def addCleanup_with_wait(self, things, thing_id,
1074 error_status='ERROR',
1075 exc_type=nova_exceptions.NotFound,
Ghanshyam2a180b82014-06-16 13:54:22 +09001076 cleanup_callable=None, cleanup_args=None,
1077 cleanup_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001078 """Adds wait for ansyc resource deletion at the end of cleanups
Sean Dague6dbc6da2013-05-08 17:49:46 -04001079
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001080 @param things: type of the resource to delete
1081 @param thing_id:
1082 @param error_status: see manager.delete_timeout()
1083 @param exc_type: see manager.delete_timeout()
1084 @param cleanup_callable: method to load pass to self.addCleanup with
1085 the following *cleanup_args, **cleanup_kwargs.
1086 usually a delete method. if not used, will try to use:
1087 things.delete(thing_id)
1088 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001089 if cleanup_args is None:
1090 cleanup_args = []
1091 if cleanup_kwargs is None:
1092 cleanup_kwargs = {}
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001093 if cleanup_callable is None:
1094 LOG.debug("no delete method passed. using {rclass}.delete({id}) as"
1095 " default".format(rclass=things, id=thing_id))
1096 self.addCleanup(things.delete, thing_id)
1097 else:
1098 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
1099 wait_dict = {
1100 'things': things,
1101 'thing_id': thing_id,
1102 'error_status': error_status,
1103 'not_found_exception': exc_type,
1104 }
1105 self.cleanup_waits.append(wait_dict)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001106
Steve Bakerefde7612013-09-30 11:29:23 +13001107 def status_timeout(self, things, thing_id, expected_status,
1108 error_status='ERROR',
1109 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001110 """
1111 Given a thing and an expected status, do a loop, sleeping
1112 for a configurable amount of time, checking for the
1113 expected status to show. At any time, if the returned
1114 status of the thing is ERROR, fail out.
1115 """
Steve Bakerefde7612013-09-30 11:29:23 +13001116 self._status_timeout(things, thing_id,
1117 expected_status=expected_status,
1118 error_status=error_status,
1119 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001120
Steve Bakerefde7612013-09-30 11:29:23 +13001121 def delete_timeout(self, things, thing_id,
1122 error_status='ERROR',
1123 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001124 """
1125 Given a thing, do a loop, sleeping
1126 for a configurable amount of time, checking for the
1127 deleted status to show. At any time, if the returned
1128 status of the thing is ERROR, fail out.
1129 """
1130 self._status_timeout(things,
1131 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +13001132 allow_notfound=True,
1133 error_status=error_status,
1134 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001135
1136 def _status_timeout(self,
1137 things,
1138 thing_id,
1139 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +13001140 allow_notfound=False,
1141 error_status='ERROR',
1142 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001143
1144 log_status = expected_status if expected_status else ''
1145 if allow_notfound:
1146 log_status += ' or NotFound' if log_status != '' else 'NotFound'
1147
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001148 def check_status():
1149 # python-novaclient has resources available to its client
1150 # that all implement a get() method taking an identifier
1151 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001152 try:
1153 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +13001154 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001155 if allow_notfound:
1156 return True
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001157 raise
1158 except Exception as e:
1159 if allow_notfound and self.not_found_exception(e):
1160 return True
1161 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001162
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001163 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -05001164
1165 # Some components are reporting error status in lower case
1166 # so case sensitive comparisons can really mess things
1167 # up.
1168 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +09001169 message = ("%s failed to get to expected status (%s). "
1170 "In %s state.") % (thing, expected_status,
1171 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +09001172 raise exceptions.BuildErrorException(message,
1173 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001174 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001175 return True # All good.
1176 LOG.debug("Waiting for %s to get to %s status. "
1177 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001178 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001179 if not tempest.test.call_until_true(
1180 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +00001181 CONF.compute.build_timeout,
1182 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +09001183 message = ("Timed out waiting for thing %s "
1184 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +02001185 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001186
Yair Friedeb69f3f2013-10-10 13:18:16 +03001187 def _create_loginable_secgroup_rule_nova(self, client=None,
1188 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001189 if client is None:
1190 client = self.compute_client
1191 if secgroup_id is None:
1192 sgs = client.security_groups.list()
1193 for sg in sgs:
1194 if sg.name == 'default':
1195 secgroup_id = sg.id
1196
1197 # These rules are intended to permit inbound ssh and icmp
1198 # traffic from all sources, so no group_id is provided.
1199 # Setting a group_id would only permit traffic from ports
1200 # belonging to the same security group.
1201 rulesets = [
1202 {
1203 # ssh
1204 'ip_protocol': 'tcp',
1205 'from_port': 22,
1206 'to_port': 22,
1207 'cidr': '0.0.0.0/0',
1208 },
1209 {
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001210 # ssh -6
1211 'ip_protocol': 'tcp',
1212 'from_port': 22,
1213 'to_port': 22,
1214 'cidr': '::/0',
1215 },
1216 {
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001217 # ping
1218 'ip_protocol': 'icmp',
1219 'from_port': -1,
1220 'to_port': -1,
1221 'cidr': '0.0.0.0/0',
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001222 },
1223 {
1224 # ping6
1225 'ip_protocol': 'icmp',
1226 'from_port': -1,
1227 'to_port': -1,
1228 'cidr': '::/0',
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001229 }
1230 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +03001231 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001232 for ruleset in rulesets:
1233 sg_rule = client.security_group_rules.create(secgroup_id,
1234 **ruleset)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001235 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03001236 rules.append(sg_rule)
1237 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001238
Grishkin0f1e11c2014-05-04 20:44:52 +04001239 def _create_security_group_nova(self, client=None,
1240 namestart='secgroup-smoke-'):
1241 if client is None:
1242 client = self.compute_client
1243 # Create security group
1244 sg_name = data_utils.rand_name(namestart)
1245 sg_desc = sg_name + " description"
1246 secgroup = client.security_groups.create(sg_name, sg_desc)
1247 self.assertEqual(secgroup.name, sg_name)
1248 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001249 self.addCleanup(self.delete_wrapper, secgroup)
Grishkin0f1e11c2014-05-04 20:44:52 +04001250
1251 # Add rules to the security group
1252 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
1253
1254 return secgroup
1255
David Shrewsbury02719362014-05-20 14:10:03 -04001256 def rebuild_server(self, server, client=None, image=None,
1257 preserve_ephemeral=False, wait=True,
1258 rebuild_kwargs=None):
1259 if client is None:
1260 client = self.compute_client
1261 if image is None:
1262 image = CONF.compute.image_ref
1263 rebuild_kwargs = rebuild_kwargs or {}
1264
1265 LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
1266 server.name, image, preserve_ephemeral)
1267 server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
1268 **rebuild_kwargs)
1269 if wait:
1270 self.status_timeout(client.servers, server.id, 'ACTIVE')
1271
Giulio Fidente61cadca2013-09-24 18:33:37 +02001272 def create_server(self, client=None, name=None, image=None, flavor=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001273 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +09001274 create_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001275 """Creates VM instance.
1276
1277 @param client: compute client to create the instance
1278 @param image: image from which to create the instance
1279 @param wait_on_boot: wait for status ACTIVE before continue
1280 @param wait_on_delete: force synchronous delete on cleanup
1281 @param create_kwargs: additional details for instance creation
1282 @return: client.server object
1283 """
Giulio Fidente61cadca2013-09-24 18:33:37 +02001284 if client is None:
1285 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001286 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001287 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001288 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001289 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001290 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001291 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +09001292 if create_kwargs is None:
1293 create_kwargs = {}
JordanP9c052aa2014-01-24 13:05:00 +00001294
1295 fixed_network_name = CONF.compute.fixed_network_name
1296 if 'nics' not in create_kwargs and fixed_network_name:
1297 networks = client.networks.list()
1298 # If several networks found, set the NetID on which to connect the
1299 # server to avoid the following error "Multiple possible networks
1300 # found, use a Network ID to be more specific."
1301 # See Tempest #1250866
1302 if len(networks) > 1:
1303 for network in networks:
1304 if network.label == fixed_network_name:
1305 create_kwargs['nics'] = [{'net-id': network.id}]
1306 break
1307 # If we didn't find the network we were looking for :
1308 else:
1309 msg = ("The network on which the NIC of the server must "
1310 "be connected can not be found : "
1311 "fixed_network_name=%s. Starting instance without "
1312 "specifying a network.") % fixed_network_name
1313 LOG.info(msg)
1314
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001315 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
1316 name, image, flavor)
1317 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +02001318 self.assertEqual(server.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001319 if wait_on_delete:
1320 self.addCleanup(self.delete_timeout,
1321 self.compute_client.servers,
1322 server.id)
1323 self.addCleanup_with_wait(self.compute_client.servers, server.id,
1324 cleanup_callable=self.delete_wrapper,
1325 cleanup_args=[server])
1326 if wait_on_boot:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001327 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001328 # The instance retrieved on creation is missing network
1329 # details, necessitating retrieval after it becomes active to
1330 # ensure correct details.
1331 server = client.servers.get(server.id)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001332 LOG.debug("Created server: %s", server)
1333 return server
1334
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001335 def create_volume(self, client=None, size=1, name=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001336 snapshot_id=None, imageRef=None, volume_type=None,
1337 wait_on_delete=True):
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001338 if client is None:
1339 client = self.volume_client
1340 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001341 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -07001342 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001343 volume = client.volumes.create(size=size, display_name=name,
1344 snapshot_id=snapshot_id,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001345 imageRef=imageRef,
1346 volume_type=volume_type)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001347 if wait_on_delete:
1348 self.addCleanup(self.delete_timeout,
1349 self.volume_client.volumes,
1350 volume.id)
1351 self.addCleanup_with_wait(self.volume_client.volumes, volume.id,
1352 exc_type=cinder_exceptions.NotFound)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001353 self.assertEqual(name, volume.display_name)
1354 self.status_timeout(client.volumes, volume.id, 'available')
1355 LOG.debug("Created volume: %s", volume)
1356 return volume
1357
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001358 def create_server_snapshot(self, server, compute_client=None,
1359 image_client=None, name=None):
1360 if compute_client is None:
1361 compute_client = self.compute_client
1362 if image_client is None:
1363 image_client = self.image_client
1364 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001365 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001366 LOG.debug("Creating a snapshot image for server: %s", server.name)
1367 image_id = compute_client.servers.create_image(server, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001368 self.addCleanup_with_wait(self.image_client.images, image_id,
1369 exc_type=glanceclient.exc.HTTPNotFound)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001370 self.status_timeout(image_client.images, image_id, 'active')
1371 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -07001372 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001373 LOG.debug("Created snapshot image %s for server %s",
1374 snapshot_image.name, server.name)
1375 return snapshot_image
1376
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001377 def create_keypair(self, client=None, name=None):
1378 if client is None:
1379 client = self.compute_client
1380 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001381 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001382 keypair = client.keypairs.create(name)
1383 self.assertEqual(keypair.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001384 self.addCleanup(self.delete_wrapper, keypair)
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001385 return keypair
1386
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001387 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +08001388 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001389 ip = server_or_ip
1390 else:
Matthew Treinish6c072292014-01-29 19:15:52 +00001391 network_name_for_ssh = CONF.compute.network_for_ssh
Adam Gandelman0d9508e2014-08-22 10:58:09 -07001392 ip = server_or_ip.networks[network_name_for_ssh][0]
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001393 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001394 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001395 if private_key is None:
1396 private_key = self.keypair.private_key
Yair Fried3960c4d2014-05-07 15:20:30 +03001397 linux_client = remote_client.RemoteClient(ip, username,
1398 pkey=private_key)
1399 try:
1400 linux_client.validate_authentication()
1401 except exceptions.SSHTimeout:
1402 LOG.exception('ssh connection to %s failed' % ip)
1403 debug.log_net_debug()
1404 raise
1405
1406 return linux_client
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001407
Nachi Ueno95b41282014-01-15 06:54:21 -08001408 def _log_console_output(self, servers=None):
Adam Gandelmanc6eefb42014-07-15 16:44:08 -07001409 if not CONF.compute_feature_enabled.console_output:
1410 LOG.debug('Console output not supported, cannot log')
1411 return
Nachi Ueno95b41282014-01-15 06:54:21 -08001412 if not servers:
1413 servers = self.compute_client.servers.list()
1414 for server in servers:
1415 LOG.debug('Console output for %s', server.id)
1416 LOG.debug(server.get_console_output())
1417
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001418 def wait_for_volume_status(self, status):
1419 volume_id = self.volume.id
1420 self.status_timeout(
1421 self.volume_client.volumes, volume_id, status)
1422
Ghanshyam2a180b82014-06-16 13:54:22 +09001423 def _image_create(self, name, fmt, path, properties=None):
1424 if properties is None:
1425 properties = {}
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001426 name = data_utils.rand_name('%s-' % name)
1427 image_file = open(path, 'rb')
1428 self.addCleanup(image_file.close)
1429 params = {
1430 'name': name,
1431 'container_format': fmt,
1432 'disk_format': fmt,
Aaron Rosenc7720622014-05-20 10:38:10 -07001433 'is_public': 'False',
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001434 }
1435 params.update(properties)
1436 image = self.image_client.images.create(**params)
1437 self.addCleanup(self.image_client.images.delete, image)
1438 self.assertEqual("queued", image.status)
1439 image.update(data=image_file)
1440 return image.id
1441
1442 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001443 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001444 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
1445 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
1446 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001447 img_container_format = CONF.scenario.img_container_format
1448 img_disk_format = CONF.scenario.img_disk_format
1449 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
1450 "ami: %s, ari: %s, aki: %s" %
1451 (img_path, img_container_format, img_disk_format,
1452 ami_img_path, ari_img_path, aki_img_path))
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001453 try:
1454 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001455 img_container_format,
1456 img_path,
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001457 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001458 img_disk_format})
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001459 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +09001460 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001461 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
1462 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
1463 properties = {
1464 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
1465 }
1466 self.image = self._image_create('scenario-ami', 'ami',
1467 path=ami_img_path,
1468 properties=properties)
1469 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001470
Sean Dague6dbc6da2013-05-08 17:49:46 -04001471
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001472# power/provision states as of icehouse
1473class BaremetalPowerStates(object):
1474 """Possible power states of an Ironic node."""
1475 POWER_ON = 'power on'
1476 POWER_OFF = 'power off'
1477 REBOOT = 'rebooting'
1478 SUSPEND = 'suspended'
1479
1480
1481class BaremetalProvisionStates(object):
1482 """Possible provision states of an Ironic node."""
1483 NOSTATE = None
1484 INIT = 'initializing'
1485 ACTIVE = 'active'
1486 BUILDING = 'building'
1487 DEPLOYWAIT = 'wait call-back'
1488 DEPLOYING = 'deploying'
1489 DEPLOYFAIL = 'deploy failed'
1490 DEPLOYDONE = 'deploy complete'
1491 DELETING = 'deleting'
1492 DELETED = 'deleted'
1493 ERROR = 'error'
1494
1495
Adam Gandelman4a48a602014-03-20 18:23:18 -07001496class BaremetalScenarioTest(OfficialClientTest):
1497 @classmethod
1498 def setUpClass(cls):
1499 super(BaremetalScenarioTest, cls).setUpClass()
1500
1501 if (not CONF.service_available.ironic or
1502 not CONF.baremetal.driver_enabled):
1503 msg = 'Ironic not available or Ironic compute driver not enabled'
1504 raise cls.skipException(msg)
1505
1506 # use an admin client manager for baremetal client
Adam Gandelmanacc13e62014-05-08 11:12:47 -07001507 admin_creds = cls.admin_credentials()
1508 manager = clients.OfficialClientManager(credentials=admin_creds)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001509 cls.baremetal_client = manager.baremetal_client
1510
1511 # allow any issues obtaining the node list to raise early
1512 cls.baremetal_client.node.list()
1513
1514 def _node_state_timeout(self, node_id, state_attr,
1515 target_states, timeout=10, interval=1):
1516 if not isinstance(target_states, list):
1517 target_states = [target_states]
1518
1519 def check_state():
1520 node = self.get_node(node_id=node_id)
1521 if getattr(node, state_attr) in target_states:
1522 return True
1523 return False
1524
1525 if not tempest.test.call_until_true(
1526 check_state, timeout, interval):
1527 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1528 (node_id, state_attr, target_states))
1529 raise exceptions.TimeoutException(msg)
1530
1531 def wait_provisioning_state(self, node_id, state, timeout):
1532 self._node_state_timeout(
1533 node_id=node_id, state_attr='provision_state',
1534 target_states=state, timeout=timeout)
1535
1536 def wait_power_state(self, node_id, state):
1537 self._node_state_timeout(
1538 node_id=node_id, state_attr='power_state',
1539 target_states=state, timeout=CONF.baremetal.power_timeout)
1540
1541 def wait_node(self, instance_id):
1542 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001543 from ironicclient import exc as ironic_exceptions
1544
Adam Gandelman4a48a602014-03-20 18:23:18 -07001545 def _get_node():
1546 node = None
1547 try:
1548 node = self.get_node(instance_id=instance_id)
1549 except ironic_exceptions.HTTPNotFound:
1550 pass
1551 return node is not None
1552
1553 if not tempest.test.call_until_true(
1554 _get_node, CONF.baremetal.association_timeout, 1):
1555 msg = ('Timed out waiting to get Ironic node by instance id %s'
1556 % instance_id)
1557 raise exceptions.TimeoutException(msg)
1558
1559 def get_node(self, node_id=None, instance_id=None):
1560 if node_id:
1561 return self.baremetal_client.node.get(node_id)
1562 elif instance_id:
1563 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
1564
1565 def get_ports(self, node_id):
1566 ports = []
1567 for port in self.baremetal_client.node.list_ports(node_id):
1568 ports.append(self.baremetal_client.port.get(port.uuid))
1569 return ports
1570
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001571 def add_keypair(self):
1572 self.keypair = self.create_keypair()
1573
1574 def verify_connectivity(self, ip=None):
1575 if ip:
1576 dest = self.get_remote_client(ip)
1577 else:
1578 dest = self.get_remote_client(self.instance)
1579 dest.validate_authentication()
1580
1581 def boot_instance(self):
1582 create_kwargs = {
1583 'key_name': self.keypair.id
1584 }
1585 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001586 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001587
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001588 self.addCleanup_with_wait(self.compute_client.servers,
1589 self.instance.id,
1590 cleanup_callable=self.delete_wrapper,
1591 cleanup_args=[self.instance])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001592
1593 self.wait_node(self.instance.id)
1594 self.node = self.get_node(instance_id=self.instance.id)
1595
1596 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_ON)
1597
1598 self.wait_provisioning_state(
1599 self.node.uuid,
1600 [BaremetalProvisionStates.DEPLOYWAIT,
1601 BaremetalProvisionStates.ACTIVE],
1602 timeout=15)
1603
1604 self.wait_provisioning_state(self.node.uuid,
1605 BaremetalProvisionStates.ACTIVE,
1606 timeout=CONF.baremetal.active_timeout)
1607
1608 self.status_timeout(
1609 self.compute_client.servers, self.instance.id, 'ACTIVE')
1610
1611 self.node = self.get_node(instance_id=self.instance.id)
1612 self.instance = self.compute_client.servers.get(self.instance.id)
1613
1614 def terminate_instance(self):
1615 self.instance.delete()
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001616 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_OFF)
1617 self.wait_provisioning_state(
1618 self.node.uuid,
1619 BaremetalProvisionStates.NOSTATE,
1620 timeout=CONF.baremetal.unprovision_timeout)
1621
Adam Gandelman4a48a602014-03-20 18:23:18 -07001622
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001623class EncryptionScenarioTest(OfficialClientTest):
1624 """
1625 Base class for encryption scenario tests
1626 """
1627
1628 @classmethod
1629 def setUpClass(cls):
1630 super(EncryptionScenarioTest, cls).setUpClass()
1631
1632 # use admin credentials to create encrypted volume types
1633 admin_creds = cls.admin_credentials()
1634 manager = clients.OfficialClientManager(credentials=admin_creds)
1635 cls.admin_volume_client = manager.volume_client
1636
1637 def _wait_for_volume_status(self, status):
1638 self.status_timeout(
1639 self.volume_client.volumes, self.volume.id, status)
1640
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001641 def nova_boot(self):
1642 self.keypair = self.create_keypair()
1643 create_kwargs = {'key_name': self.keypair.name}
1644 self.server = self.create_server(self.compute_client,
1645 image=self.image,
1646 create_kwargs=create_kwargs)
1647
1648 def create_volume_type(self, client=None, name=None):
1649 if not client:
1650 client = self.admin_volume_client
1651 if not name:
1652 name = 'generic'
1653 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1654 LOG.debug("Creating a volume type: %s", randomized_name)
1655 volume_type = client.volume_types.create(randomized_name)
1656 self.addCleanup(client.volume_types.delete, volume_type.id)
1657 return volume_type
1658
1659 def create_encryption_type(self, client=None, type_id=None, provider=None,
1660 key_size=None, cipher=None,
1661 control_location=None):
1662 if not client:
1663 client = self.admin_volume_client
1664 if not type_id:
1665 volume_type = self.create_volume_type()
1666 type_id = volume_type.id
1667 LOG.debug("Creating an encryption type for volume type: %s", type_id)
1668 client.volume_encryption_types.create(type_id,
1669 {'provider': provider,
1670 'key_size': key_size,
1671 'cipher': cipher,
1672 'control_location':
1673 control_location})
1674
1675 def nova_volume_attach(self):
1676 attach_volume_client = self.compute_client.volumes.create_server_volume
1677 volume = attach_volume_client(self.server.id,
1678 self.volume.id,
1679 '/dev/vdb')
1680 self.assertEqual(self.volume.id, volume.id)
1681 self._wait_for_volume_status('in-use')
1682
1683 def nova_volume_detach(self):
1684 detach_volume_client = self.compute_client.volumes.delete_server_volume
1685 detach_volume_client(self.server.id, self.volume.id)
1686 self._wait_for_volume_status('available')
1687
1688 volume = self.volume_client.volumes.get(self.volume.id)
1689 self.assertEqual('available', volume.status)
1690
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001691
Sean Dague6dbc6da2013-05-08 17:49:46 -04001692class NetworkScenarioTest(OfficialClientTest):
1693 """
1694 Base class for network scenario tests
1695 """
1696
1697 @classmethod
1698 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001699 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001700 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +02001701 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -04001702 try:
1703 cls.network_client.list_networks()
1704 except exc.EndpointNotFound:
1705 cls.enabled = False
1706 raise
1707 else:
1708 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -04001709 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -04001710 raise cls.skipException(msg)
1711
1712 @classmethod
1713 def setUpClass(cls):
1714 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001715 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -04001716
Sean Dague6dbc6da2013-05-08 17:49:46 -04001717 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001718 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001719 body = dict(
1720 network=dict(
1721 name=name,
1722 tenant_id=tenant_id,
1723 ),
1724 )
1725 result = self.network_client.create_network(body=body)
1726 network = net_common.DeletableNetwork(client=self.network_client,
1727 **result['network'])
1728 self.assertEqual(network.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001729 self.addCleanup(self.delete_wrapper, network)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001730 return network
1731
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001732 def _list_networks(self, **kwargs):
1733 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001734 return nets['networks']
1735
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001736 def _list_subnets(self, **kwargs):
1737 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001738 return subnets['subnets']
1739
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001740 def _list_routers(self, **kwargs):
1741 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001742 return routers['routers']
1743
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001744 def _list_ports(self, **kwargs):
1745 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001746 return ports['ports']
1747
1748 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001749 nets = self._list_networks(tenant_id=tenant_id)
1750 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001751
1752 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001753 subnets = self._list_subnets(tenant_id=tenant_id)
1754 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001755
1756 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001757 ports = self._list_ports(tenant_id=tenant_id)
1758 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001759
Yair Fried3097dc12014-01-26 08:46:43 +02001760 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001761 """
1762 Create a subnet for the given network within the cidr block
1763 configured for tenant networks.
1764 """
Attila Fazekase857bd62013-10-21 21:02:44 +02001765
1766 def cidr_in_use(cidr, tenant_id):
1767 """
1768 :return True if subnet with cidr already exist in tenant
1769 False else
1770 """
1771 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
1772 return len(cidr_in_use) != 0
1773
Matthew Treinish6c072292014-01-29 19:15:52 +00001774 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001775 result = None
1776 # Repeatedly attempt subnet creation with sequential cidr
1777 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +00001778 for subnet_cidr in tenant_cidr.subnet(
1779 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +02001780 str_cidr = str(subnet_cidr)
1781 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
1782 continue
1783
Sean Dague6dbc6da2013-05-08 17:49:46 -04001784 body = dict(
1785 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +02001786 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -04001787 ip_version=4,
1788 network_id=network.id,
1789 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +02001790 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001791 ),
1792 )
Yair Fried3097dc12014-01-26 08:46:43 +02001793 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001794 try:
1795 result = self.network_client.create_subnet(body=body)
1796 break
Mark McClainf2982e82013-07-06 17:48:03 -04001797 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -04001798 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1799 if not is_overlapping_cidr:
1800 raise
1801 self.assertIsNotNone(result, 'Unable to allocate tenant network')
1802 subnet = net_common.DeletableSubnet(client=self.network_client,
1803 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +02001804 self.assertEqual(subnet.cidr, str_cidr)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001805 self.addCleanup(self.delete_wrapper, subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001806 return subnet
1807
1808 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001809 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001810 body = dict(
1811 port=dict(name=name,
1812 network_id=network.id,
1813 tenant_id=network.tenant_id))
1814 result = self.network_client.create_port(body=body)
1815 self.assertIsNotNone(result, 'Unable to allocate port')
1816 port = net_common.DeletablePort(client=self.network_client,
1817 **result['port'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001818 self.addCleanup(self.delete_wrapper, port)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001819 return port
1820
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001821 def _get_server_port_id(self, server, ip_addr=None):
1822 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001823 self.assertEqual(len(ports), 1,
1824 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +02001825 return ports[0]['id']
1826
David Shrewsbury9bac3662014-08-07 15:07:01 -04001827 def _get_network_by_name(self, network_name):
1828 net = self._list_networks(name=network_name)
1829 return net_common.AttributeDict(net[0])
1830
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001831 def _create_floating_ip(self, thing, external_network_id, port_id=None):
1832 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001833 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001834 body = dict(
1835 floatingip=dict(
1836 floating_network_id=external_network_id,
1837 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001838 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001839 )
1840 )
1841 result = self.network_client.create_floatingip(body=body)
1842 floating_ip = net_common.DeletableFloatingIp(
1843 client=self.network_client,
1844 **result['floatingip'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001845 self.addCleanup(self.delete_wrapper, floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001846 return floating_ip
1847
Yair Fried05db2522013-11-18 11:02:10 +02001848 def _associate_floating_ip(self, floating_ip, server):
1849 port_id = self._get_server_port_id(server)
1850 floating_ip.update(port_id=port_id)
1851 self.assertEqual(port_id, floating_ip.port_id)
1852 return floating_ip
1853
Yair Fried9a551c42013-12-15 14:59:34 +02001854 def _disassociate_floating_ip(self, floating_ip):
1855 """
1856 :param floating_ip: type DeletableFloatingIp
1857 """
1858 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +08001859 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +02001860 return floating_ip
1861
1862 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001863 cmd = ['ping', '-c1', '-w1', ip_address]
1864
1865 def ping():
1866 proc = subprocess.Popen(cmd,
1867 stdout=subprocess.PIPE,
1868 stderr=subprocess.PIPE)
1869 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +02001870 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -04001871
Nachi Ueno6d580be2013-07-24 10:58:11 -07001872 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +00001873 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +00001874
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001875 def _create_pool(self, lb_method, protocol, subnet_id):
1876 """Wrapper utility that returns a test pool."""
1877 name = data_utils.rand_name('pool-')
1878 body = {
1879 "pool": {
1880 "protocol": protocol,
1881 "name": name,
1882 "subnet_id": subnet_id,
1883 "lb_method": lb_method
1884 }
1885 }
1886 resp = self.network_client.create_pool(body=body)
1887 pool = net_common.DeletablePool(client=self.network_client,
1888 **resp['pool'])
1889 self.assertEqual(pool['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001890 self.addCleanup(self.delete_wrapper, pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001891 return pool
1892
1893 def _create_member(self, address, protocol_port, pool_id):
1894 """Wrapper utility that returns a test member."""
1895 body = {
1896 "member": {
1897 "protocol_port": protocol_port,
1898 "pool_id": pool_id,
1899 "address": address
1900 }
1901 }
1902 resp = self.network_client.create_member(body)
1903 member = net_common.DeletableMember(client=self.network_client,
1904 **resp['member'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001905 self.addCleanup(self.delete_wrapper, member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001906 return member
1907
1908 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
1909 """Wrapper utility that returns a test vip."""
1910 name = data_utils.rand_name('vip-')
1911 body = {
1912 "vip": {
1913 "protocol": protocol,
1914 "name": name,
1915 "subnet_id": subnet_id,
1916 "pool_id": pool_id,
1917 "protocol_port": protocol_port
1918 }
1919 }
1920 resp = self.network_client.create_vip(body)
1921 vip = net_common.DeletableVip(client=self.network_client,
1922 **resp['vip'])
1923 self.assertEqual(vip['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001924 self.addCleanup(self.delete_wrapper, vip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001925 return vip
1926
Yair Fried9a551c42013-12-15 14:59:34 +02001927 def _check_vm_connectivity(self, ip_address,
1928 username=None,
1929 private_key=None,
1930 should_connect=True):
1931 """
1932 :param ip_address: server to test against
1933 :param username: server's ssh username
1934 :param private_key: server's ssh private key to be used
1935 :param should_connect: True/False indicates positive/negative test
1936 positive - attempt ping and ssh
1937 negative - attempt ping and fail if succeed
1938
1939 :raises: AssertError if the result of the connectivity check does
1940 not match the value of the should_connect param
1941 """
1942 if should_connect:
1943 msg = "Timed out waiting for %s to become reachable" % ip_address
1944 else:
1945 msg = "ip address %s is reachable" % ip_address
1946 self.assertTrue(self._ping_ip_address(ip_address,
1947 should_succeed=should_connect),
1948 msg=msg)
1949 if should_connect:
1950 # no need to check ssh for negative connectivity
Yair Fried3960c4d2014-05-07 15:20:30 +03001951 self.get_remote_client(ip_address, username, private_key)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001952
Matt Riedemann343305f2014-05-27 09:55:03 -07001953 def _check_public_network_connectivity(self, ip_address, username,
1954 private_key, should_connect=True,
1955 msg=None, servers=None):
1956 # The target login is assumed to have been configured for
1957 # key-based authentication by cloud-init.
1958 LOG.debug('checking network connections to IP %s with user: %s' %
1959 (ip_address, username))
1960 try:
1961 self._check_vm_connectivity(ip_address,
1962 username,
1963 private_key,
1964 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001965 except Exception as e:
Matt Riedemann343305f2014-05-27 09:55:03 -07001966 ex_msg = 'Public network connectivity check failed'
1967 if msg:
1968 ex_msg += ": " + msg
1969 LOG.exception(ex_msg)
1970 self._log_console_output(servers)
Yair Fried3960c4d2014-05-07 15:20:30 +03001971 # network debug is called as part of ssh init
1972 if not isinstance(e, exceptions.SSHTimeout):
1973 debug.log_net_debug()
Matt Riedemann343305f2014-05-27 09:55:03 -07001974 raise
1975
Matt Riedemann2d005be2014-05-27 10:52:35 -07001976 def _check_tenant_network_connectivity(self, server,
1977 username,
1978 private_key,
1979 should_connect=True,
1980 servers_for_debug=None):
1981 if not CONF.network.tenant_networks_reachable:
1982 msg = 'Tenant networks not configured to be reachable.'
1983 LOG.info(msg)
1984 return
1985 # The target login is assumed to have been configured for
1986 # key-based authentication by cloud-init.
1987 try:
1988 for net_name, ip_addresses in server.networks.iteritems():
1989 for ip_address in ip_addresses:
1990 self._check_vm_connectivity(ip_address,
1991 username,
1992 private_key,
1993 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001994 except Exception as e:
Matt Riedemann2d005be2014-05-27 10:52:35 -07001995 LOG.exception('Tenant network connectivity check failed')
1996 self._log_console_output(servers_for_debug)
Yair Fried3960c4d2014-05-07 15:20:30 +03001997 # network debug is called as part of ssh init
1998 if not isinstance(e, exceptions.SSHTimeout):
1999 debug.log_net_debug()
Matt Riedemann2d005be2014-05-27 10:52:35 -07002000 raise
2001
Yair Fried3097dc12014-01-26 08:46:43 +02002002 def _check_remote_connectivity(self, source, dest, should_succeed=True):
2003 """
2004 check ping server via source ssh connection
2005
2006 :param source: RemoteClient: an ssh connection from which to ping
2007 :param dest: and IP to ping against
2008 :param should_succeed: boolean should ping succeed or not
2009 :returns: boolean -- should_succeed == ping
2010 :returns: ping is false if ping failed
2011 """
2012 def ping_remote():
2013 try:
2014 source.ping_host(dest)
2015 except exceptions.SSHExecCommandFailed:
Matthew Treinishc4131d82014-05-27 11:59:28 -04002016 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
2017 % (dest, source.ssh_client.host))
Yair Fried3097dc12014-01-26 08:46:43 +02002018 return not should_succeed
2019 return should_succeed
2020
2021 return tempest.test.call_until_true(ping_remote,
2022 CONF.compute.ping_timeout,
2023 1)
2024
Yair Friedeb69f3f2013-10-10 13:18:16 +03002025 def _create_security_group_neutron(self, tenant_id, client=None,
2026 namestart='secgroup-smoke-'):
2027 if client is None:
2028 client = self.network_client
2029 secgroup = self._create_empty_security_group(namestart=namestart,
2030 client=client,
2031 tenant_id=tenant_id)
2032
2033 # Add rules to the security group
2034 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
2035 for rule in rules:
2036 self.assertEqual(tenant_id, rule.tenant_id)
2037 self.assertEqual(secgroup.id, rule.security_group_id)
2038 return secgroup
2039
2040 def _create_empty_security_group(self, tenant_id, client=None,
2041 namestart='secgroup-smoke-'):
2042 """Create a security group without rules.
2043
2044 Default rules will be created:
2045 - IPv4 egress to any
2046 - IPv6 egress to any
2047
2048 :param tenant_id: secgroup will be created in this tenant
2049 :returns: DeletableSecurityGroup -- containing the secgroup created
2050 """
2051 if client is None:
2052 client = self.network_client
2053 sg_name = data_utils.rand_name(namestart)
2054 sg_desc = sg_name + " description"
2055 sg_dict = dict(name=sg_name,
2056 description=sg_desc)
2057 sg_dict['tenant_id'] = tenant_id
2058 body = dict(security_group=sg_dict)
2059 result = client.create_security_group(body=body)
2060 secgroup = net_common.DeletableSecurityGroup(
2061 client=client,
2062 **result['security_group']
2063 )
2064 self.assertEqual(secgroup.name, sg_name)
2065 self.assertEqual(tenant_id, secgroup.tenant_id)
2066 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002067 self.addCleanup(self.delete_wrapper, secgroup)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002068 return secgroup
2069
2070 def _default_security_group(self, tenant_id, client=None):
2071 """Get default secgroup for given tenant_id.
2072
2073 :returns: DeletableSecurityGroup -- default secgroup for given tenant
2074 """
2075 if client is None:
2076 client = self.network_client
2077 sgs = [
2078 sg for sg in client.list_security_groups().values()[0]
2079 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
2080 ]
2081 msg = "No default security group for tenant %s." % (tenant_id)
2082 self.assertTrue(len(sgs) > 0, msg)
2083 if len(sgs) > 1:
2084 msg = "Found %d default security groups" % len(sgs)
2085 raise exc.NeutronClientNoUniqueMatch(msg=msg)
2086 return net_common.DeletableSecurityGroup(client=client,
2087 **sgs[0])
2088
2089 def _create_security_group_rule(self, client=None, secgroup=None,
2090 tenant_id=None, **kwargs):
2091 """Create a rule from a dictionary of rule parameters.
2092
2093 Create a rule in a secgroup. if secgroup not defined will search for
2094 default secgroup in tenant_id.
2095
2096 :param secgroup: type DeletableSecurityGroup.
2097 :param secgroup_id: search for secgroup by id
2098 default -- choose default secgroup for given tenant_id
2099 :param tenant_id: if secgroup not passed -- the tenant in which to
2100 search for default secgroup
2101 :param kwargs: a dictionary containing rule parameters:
2102 for example, to allow incoming ssh:
2103 rule = {
2104 direction: 'ingress'
2105 protocol:'tcp',
2106 port_range_min: 22,
2107 port_range_max: 22
2108 }
2109 """
2110 if client is None:
2111 client = self.network_client
2112 if secgroup is None:
2113 secgroup = self._default_security_group(tenant_id)
2114
2115 ruleset = dict(security_group_id=secgroup.id,
2116 tenant_id=secgroup.tenant_id,
2117 )
2118 ruleset.update(kwargs)
2119
2120 body = dict(security_group_rule=dict(ruleset))
2121 sg_rule = client.create_security_group_rule(body=body)
2122 sg_rule = net_common.DeletableSecurityGroupRule(
2123 client=client,
2124 **sg_rule['security_group_rule']
2125 )
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002126 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002127 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
2128 self.assertEqual(secgroup.id, sg_rule.security_group_id)
2129
2130 return sg_rule
2131
2132 def _create_loginable_secgroup_rule_neutron(self, client=None,
2133 secgroup=None):
2134 """These rules are intended to permit inbound ssh and icmp
2135 traffic from all sources, so no group_id is provided.
2136 Setting a group_id would only permit traffic from ports
2137 belonging to the same security group.
2138 """
2139
2140 if client is None:
2141 client = self.network_client
2142 rules = []
2143 rulesets = [
2144 dict(
2145 # ssh
2146 protocol='tcp',
2147 port_range_min=22,
2148 port_range_max=22,
2149 ),
2150 dict(
2151 # ping
2152 protocol='icmp',
2153 )
2154 ]
2155 for ruleset in rulesets:
2156 for r_direction in ['ingress', 'egress']:
2157 ruleset['direction'] = r_direction
2158 try:
2159 sg_rule = self._create_security_group_rule(
2160 client=client, secgroup=secgroup, **ruleset)
2161 except exc.NeutronClientException as ex:
2162 # if rule already exist - skip rule and continue
2163 if not (ex.status_code is 409 and 'Security group rule'
2164 ' already exists' in ex.message):
2165 raise ex
2166 else:
2167 self.assertEqual(r_direction, sg_rule.direction)
2168 rules.append(sg_rule)
2169
2170 return rules
2171
Yair Fried5f670ab2013-12-09 09:26:51 +02002172 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +00002173 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +02002174 return self.get_remote_client(server,
2175 username=ssh_login,
2176 private_key=private_key)
2177
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00002178 def _show_quota_network(self, tenant_id):
2179 quota = self.network_client.show_quota(tenant_id)
2180 return quota['quota']['network']
2181
2182 def _show_quota_subnet(self, tenant_id):
2183 quota = self.network_client.show_quota(tenant_id)
2184 return quota['quota']['subnet']
2185
2186 def _show_quota_port(self, tenant_id):
2187 quota = self.network_client.show_quota(tenant_id)
2188 return quota['quota']['port']
2189
Yair Fried4d7efa62013-11-17 17:12:29 +02002190 def _get_router(self, tenant_id):
2191 """Retrieve a router for the given tenant id.
2192
2193 If a public router has been configured, it will be returned.
2194
2195 If a public router has not been configured, but a public
2196 network has, a tenant router will be created and returned that
2197 routes traffic to the public network.
2198 """
Matthew Treinish6c072292014-01-29 19:15:52 +00002199 router_id = CONF.network.public_router_id
2200 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02002201 if router_id:
2202 result = self.network_client.show_router(router_id)
2203 return net_common.AttributeDict(**result['router'])
2204 elif network_id:
2205 router = self._create_router(tenant_id)
2206 router.add_gateway(network_id)
2207 return router
2208 else:
2209 raise Exception("Neither of 'public_router_id' or "
2210 "'public_network_id' has been defined.")
2211
2212 def _create_router(self, tenant_id, namestart='router-smoke-'):
2213 name = data_utils.rand_name(namestart)
2214 body = dict(
2215 router=dict(
2216 name=name,
2217 admin_state_up=True,
2218 tenant_id=tenant_id,
2219 ),
2220 )
2221 result = self.network_client.create_router(body=body)
2222 router = net_common.DeletableRouter(client=self.network_client,
2223 **result['router'])
2224 self.assertEqual(router.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002225 self.addCleanup(self.delete_wrapper, router)
Yair Fried4d7efa62013-11-17 17:12:29 +02002226 return router
2227
David Shrewsbury9bac3662014-08-07 15:07:01 -04002228 def create_networks(self, tenant_id=None):
Yair Fried4d7efa62013-11-17 17:12:29 +02002229 """Create a network with a subnet connected to a router.
2230
David Shrewsbury9bac3662014-08-07 15:07:01 -04002231 The baremetal driver is a special case since all nodes are
2232 on the same shared network.
2233
Yair Fried4d7efa62013-11-17 17:12:29 +02002234 :returns: network, subnet, router
2235 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04002236 if CONF.baremetal.driver_enabled:
2237 # NOTE(Shrews): This exception is for environments where tenant
2238 # credential isolation is available, but network separation is
2239 # not (the current baremetal case). Likely can be removed when
2240 # test account mgmt is reworked:
2241 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
2242 network = self._get_network_by_name(
2243 CONF.compute.fixed_network_name)
2244 router = None
2245 subnet = None
2246 else:
2247 if tenant_id is None:
2248 tenant_id = self.tenant_id
2249 network = self._create_network(tenant_id)
2250 router = self._get_router(tenant_id)
2251 subnet = self._create_subnet(network)
2252 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02002253 return network, subnet, router
2254
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002255
2256class OrchestrationScenarioTest(OfficialClientTest):
2257 """
2258 Base class for orchestration scenario tests
2259 """
2260
2261 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07002262 def setUpClass(cls):
2263 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00002264 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07002265 raise cls.skipException("Heat support is required")
2266
2267 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002268 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00002269 admin_creds = auth.get_default_credentials('identity_admin')
2270 creds = auth.get_default_credentials('user')
2271 admin_creds.tenant_name = creds.tenant_name
2272 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002273
2274 def _load_template(self, base_file, file_name):
2275 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
2276 file_name)
2277 with open(filepath) as f:
2278 return f.read()
2279
2280 @classmethod
2281 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09002282 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12002283
2284 @classmethod
2285 def _get_default_network(cls):
2286 networks = cls.network_client.list_networks()
2287 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00002288 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12002289 return net
Steve Baker22c16602014-05-05 13:34:19 +12002290
2291 @staticmethod
2292 def _stack_output(stack, output_key):
2293 """Return a stack output value for a given key."""
2294 return next((o['output_value'] for o in stack.outputs
2295 if o['output_key'] == output_key), None)
2296
2297 def _ping_ip_address(self, ip_address, should_succeed=True):
2298 cmd = ['ping', '-c1', '-w1', ip_address]
2299
2300 def ping():
2301 proc = subprocess.Popen(cmd,
2302 stdout=subprocess.PIPE,
2303 stderr=subprocess.PIPE)
2304 proc.wait()
2305 return (proc.returncode == 0) == should_succeed
2306
2307 return tempest.test.call_until_true(
2308 ping, CONF.orchestration.build_timeout, 1)
2309
2310 def _wait_for_resource_status(self, stack_identifier, resource_name,
2311 status, failure_pattern='^.*_FAILED$'):
2312 """Waits for a Resource to reach a given status."""
2313 fail_regexp = re.compile(failure_pattern)
2314 build_timeout = CONF.orchestration.build_timeout
2315 build_interval = CONF.orchestration.build_interval
2316
2317 start = timeutils.utcnow()
2318 while timeutils.delta_seconds(start,
2319 timeutils.utcnow()) < build_timeout:
2320 try:
2321 res = self.client.resources.get(
2322 stack_identifier, resource_name)
2323 except heat_exceptions.HTTPNotFound:
2324 # ignore this, as the resource may not have
2325 # been created yet
2326 pass
2327 else:
2328 if res.resource_status == status:
2329 return
2330 if fail_regexp.search(res.resource_status):
2331 raise exceptions.StackResourceBuildErrorException(
2332 resource_name=res.resource_name,
2333 stack_identifier=stack_identifier,
2334 resource_status=res.resource_status,
2335 resource_status_reason=res.resource_status_reason)
2336 time.sleep(build_interval)
2337
2338 message = ('Resource %s failed to reach %s status within '
2339 'the required time (%s s).' %
2340 (res.resource_name, status, build_timeout))
2341 raise exceptions.TimeoutException(message)
2342
2343 def _wait_for_stack_status(self, stack_identifier, status,
2344 failure_pattern='^.*_FAILED$'):
2345 """
2346 Waits for a Stack to reach a given status.
2347
2348 Note this compares the full $action_$status, e.g
2349 CREATE_COMPLETE, not just COMPLETE which is exposed
2350 via the status property of Stack in heatclient
2351 """
2352 fail_regexp = re.compile(failure_pattern)
2353 build_timeout = CONF.orchestration.build_timeout
2354 build_interval = CONF.orchestration.build_interval
2355
2356 start = timeutils.utcnow()
2357 while timeutils.delta_seconds(start,
2358 timeutils.utcnow()) < build_timeout:
2359 try:
2360 stack = self.client.stacks.get(stack_identifier)
2361 except heat_exceptions.HTTPNotFound:
2362 # ignore this, as the stackource may not have
2363 # been created yet
2364 pass
2365 else:
2366 if stack.stack_status == status:
2367 return
2368 if fail_regexp.search(stack.stack_status):
2369 raise exceptions.StackBuildErrorException(
2370 stack_identifier=stack_identifier,
2371 stack_status=stack.stack_status,
2372 stack_status_reason=stack.stack_status_reason)
2373 time.sleep(build_interval)
2374
2375 message = ('Stack %s failed to reach %s status within '
2376 'the required time (%s s).' %
2377 (stack.stack_name, status, build_timeout))
2378 raise exceptions.TimeoutException(message)
2379
2380 def _stack_delete(self, stack_identifier):
2381 try:
2382 self.client.stacks.delete(stack_identifier)
2383 except heat_exceptions.HTTPNotFound:
2384 pass
Chris Dent0d494112014-08-26 13:48:30 +01002385
2386
2387class SwiftScenarioTest(ScenarioTest):
2388 """
2389 Provide harness to do Swift scenario tests.
2390
2391 Subclasses implement the tests that use the methods provided by this
2392 class.
2393 """
2394
2395 @classmethod
2396 def setUpClass(cls):
2397 cls.set_network_resources()
2398 super(SwiftScenarioTest, cls).setUpClass()
2399 if not CONF.service_available.swift:
2400 skip_msg = ("%s skipped as swift is not available" %
2401 cls.__name__)
2402 raise cls.skipException(skip_msg)
2403 # Clients for Swift
2404 cls.account_client = cls.manager.account_client
2405 cls.container_client = cls.manager.container_client
2406 cls.object_client = cls.manager.object_client
2407
2408 def _get_swift_stat(self):
2409 """get swift status for our user account."""
2410 self.account_client.list_account_containers()
2411 LOG.debug('Swift status information obtained successfully')
2412
2413 def _create_container(self, container_name=None):
2414 name = container_name or data_utils.rand_name(
2415 'swift-scenario-container')
2416 self.container_client.create_container(name)
2417 # look for the container to assure it is created
2418 self._list_and_check_container_objects(name)
2419 LOG.debug('Container %s created' % (name))
2420 return name
2421
2422 def _delete_container(self, container_name):
2423 self.container_client.delete_container(container_name)
2424 LOG.debug('Container %s deleted' % (container_name))
2425
2426 def _upload_object_to_container(self, container_name, obj_name=None):
2427 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
2428 obj_data = data_utils.arbitrary_string()
2429 self.object_client.create_object(container_name, obj_name, obj_data)
2430 return obj_name, obj_data
2431
2432 def _delete_object(self, container_name, filename):
2433 self.object_client.delete_object(container_name, filename)
2434 self._list_and_check_container_objects(container_name,
2435 not_present_obj=[filename])
2436
Ghanshyam2a180b82014-06-16 13:54:22 +09002437 def _list_and_check_container_objects(self, container_name,
2438 present_obj=None,
2439 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01002440 """
2441 List objects for a given container and assert which are present and
2442 which are not.
2443 """
Ghanshyam2a180b82014-06-16 13:54:22 +09002444 if present_obj is None:
2445 present_obj = []
2446 if not_present_obj is None:
2447 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01002448 _, object_list = self.container_client.list_container_contents(
2449 container_name)
2450 if present_obj:
2451 for obj in present_obj:
2452 self.assertIn(obj, object_list)
2453 if not_present_obj:
2454 for obj in not_present_obj:
2455 self.assertNotIn(obj, object_list)
2456
2457 def _change_container_acl(self, container_name, acl):
2458 metadata_param = {'metadata_prefix': 'x-container-',
2459 'metadata': {'read': acl}}
2460 self.container_client.update_container_metadata(container_name,
2461 **metadata_param)
2462 resp, _ = self.container_client.list_container_metadata(container_name)
2463 self.assertEqual(resp['x-container-read'], acl)
2464
2465 def _download_and_verify(self, container_name, obj_name, expected_data):
2466 _, obj = self.object_client.get_object(container_name, obj_name)
2467 self.assertEqual(obj, expected_data)