blob: f7db79dc0489d71ed665d8c79c00efa8aeb982d2 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Attila Fazekasfb7552a2013-08-27 13:02:26 +020017import logging
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120018import os
Steve Baker22c16602014-05-05 13:34:19 +120019import re
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import subprocess
Steve Baker22c16602014-05-05 13:34:19 +120021import time
Sean Dague6dbc6da2013-05-08 17:49:46 -040022
Kaitlin Farr366a51f2014-04-21 12:43:54 -040023from cinderclient import exceptions as cinder_exceptions
Matthew Treinishb7144eb2013-12-13 22:57:35 +000024import glanceclient
Steve Baker22c16602014-05-05 13:34:19 +120025from heatclient import exc as heat_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040026import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040027from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090028from novaclient import exceptions as nova_exceptions
Matthew Treinish96e9e882014-06-09 18:37:19 -040029import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040030
Sean Dague1937d092013-05-17 16:36:38 -040031from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000032from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000033from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070034from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040035from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090036from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090037from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000038from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020039from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020040from tempest.openstack.common import log
Steve Baker22c16602014-05-05 13:34:19 +120041from tempest.openstack.common import timeutils
Yair Fried1fc32a12014-08-04 09:11:30 +030042from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040043import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040044
Matthew Treinish6c072292014-01-29 19:15:52 +000045CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
Attila Fazekasfb7552a2013-08-27 13:02:26 +020047LOG = log.getLogger(__name__)
48
49# NOTE(afazekas): Workaround for the stdout logging
50LOG_nova_client = logging.getLogger('novaclient.client')
51LOG_nova_client.addHandler(log.NullHandler())
52
53LOG_cinder_client = logging.getLogger('cinderclient.client')
54LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040055
56
Andrea Frittoli2e733b52014-07-16 14:12:11 +010057class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli247058f2014-07-16 16:09:22 +010058 """Replaces the OfficialClientTest base class.
59
60 Uses tempest own clients as opposed to OfficialClients.
61
62 Common differences:
63 - replace resource.attribute with resource['attribute']
64 - replace resouce.delete with delete_callable(resource['id'])
65 - replace local waiters with common / rest_client waiters
66 """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010067
68 @classmethod
69 def setUpClass(cls):
70 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010071 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010072 cls.isolated_creds = isolated_creds.IsolatedCreds(
73 cls.__name__, tempest_client=True,
74 network_resources=cls.network_resources)
75 cls.manager = clients.Manager(
76 credentials=cls.credentials()
77 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010078 cls.admin_manager = clients.Manager(cls.admin_credentials())
79 # Clients (in alphabetical order)
80 cls.floating_ips_client = cls.manager.floating_ips_client
81 # Glance image client v1
82 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000083 # Compute image client
84 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010085 cls.keypairs_client = cls.manager.keypairs_client
86 cls.networks_client = cls.admin_manager.networks_client
87 # Nova security groups client
88 cls.security_groups_client = cls.manager.security_groups_client
89 cls.servers_client = cls.manager.servers_client
90 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000091 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030092 cls.interface_client = cls.manager.interfaces_client
93 # Neutron network client
94 cls.network_client = cls.manager.network_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010095
96 @classmethod
97 def _get_credentials(cls, get_creds, ctype):
98 if CONF.compute.allow_tenant_isolation:
99 creds = get_creds()
100 else:
101 creds = auth.get_default_credentials(ctype)
102 return creds
103
104 @classmethod
105 def credentials(cls):
106 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
107 'user')
108
Masayuki Igawaccd66592014-07-17 00:42:42 +0900109 @classmethod
110 def admin_credentials(cls):
111 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
112 'identity_admin')
113
Andrea Frittoli247058f2014-07-16 16:09:22 +0100114 # ## Methods to handle sync and async deletes
115
116 def setUp(self):
117 super(ScenarioTest, self).setUp()
118 self.cleanup_waits = []
119 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
120 # because scenario tests in the same test class should not share
121 # resources. If resources were shared between test cases then it
122 # should be a single scenario test instead of multiples.
123
124 # NOTE(yfried): this list is cleaned at the end of test_methods and
125 # not at the end of the class
126 self.addCleanup(self._wait_for_cleanups)
127
Yair Fried1fc32a12014-08-04 09:11:30 +0300128 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100129 """Ignores NotFound exceptions for delete operations.
130
Yair Fried1fc32a12014-08-04 09:11:30 +0300131 @param delete_thing: delete method of a resource. method will be
132 executed as delete_thing(*args, **kwargs)
133
Andrea Frittoli247058f2014-07-16 16:09:22 +0100134 """
135 try:
136 # Tempest clients return dicts, so there is no common delete
137 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300138 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100139 except exceptions.NotFound:
140 # If the resource is already missing, mission accomplished.
141 pass
142
143 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900144 cleanup_callable, cleanup_args=None,
145 cleanup_kwargs=None, ignore_error=True):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100146 """Adds wait for ansyc resource deletion at the end of cleanups
147
148 @param waiter_callable: callable to wait for the resource to delete
149 @param thing_id: the id of the resource to be cleaned-up
150 @param thing_id_param: the name of the id param in the waiter
151 @param cleanup_callable: method to load pass to self.addCleanup with
152 the following *cleanup_args, **cleanup_kwargs.
153 usually a delete method.
154 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900155 if cleanup_args is None:
156 cleanup_args = []
157 if cleanup_kwargs is None:
158 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100159 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
160 wait_dict = {
161 'waiter_callable': waiter_callable,
162 thing_id_param: thing_id
163 }
164 self.cleanup_waits.append(wait_dict)
165
166 def _wait_for_cleanups(self):
167 """To handle async delete actions, a list of waits is added
168 which will be iterated over as the last step of clearing the
169 cleanup queue. That way all the delete calls are made up front
170 and the tests won't succeed unless the deletes are eventually
171 successful. This is the same basic approach used in the api tests to
172 limit cleanup execution time except here it is multi-resource,
173 because of the nature of the scenario tests.
174 """
175 for wait in self.cleanup_waits:
176 waiter_callable = wait.pop('waiter_callable')
177 waiter_callable(**wait)
178
179 # ## Test functions library
180 #
181 # The create_[resource] functions only return body and discard the
182 # resp part which is not used in scenario tests
183
184 def create_keypair(self):
185 name = data_utils.rand_name(self.__class__.__name__)
186 # We don't need to create a keypair by pubkey in scenario
187 resp, body = self.keypairs_client.create_keypair(name)
188 self.addCleanup(self.keypairs_client.delete_keypair, name)
189 return body
190
191 def create_server(self, name=None, image=None, flavor=None,
192 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900193 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100194 """Creates VM instance.
195
196 @param image: image from which to create the instance
197 @param wait_on_boot: wait for status ACTIVE before continue
198 @param wait_on_delete: force synchronous delete on cleanup
199 @param create_kwargs: additional details for instance creation
200 @return: server dict
201 """
202 if name is None:
203 name = data_utils.rand_name(self.__class__.__name__)
204 if image is None:
205 image = CONF.compute.image_ref
206 if flavor is None:
207 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900208 if create_kwargs is None:
209 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100210
211 fixed_network_name = CONF.compute.fixed_network_name
212 if 'nics' not in create_kwargs and fixed_network_name:
213 _, networks = self.networks_client.list_networks()
214 # If several networks found, set the NetID on which to connect the
215 # server to avoid the following error "Multiple possible networks
216 # found, use a Network ID to be more specific."
217 # See Tempest #1250866
218 if len(networks) > 1:
219 for network in networks:
220 if network['label'] == fixed_network_name:
221 create_kwargs['nics'] = [{'net-id': network['id']}]
222 break
223 # If we didn't find the network we were looking for :
224 else:
225 msg = ("The network on which the NIC of the server must "
226 "be connected can not be found : "
227 "fixed_network_name=%s. Starting instance without "
228 "specifying a network.") % fixed_network_name
229 LOG.info(msg)
230
231 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
232 name, image, flavor)
233 _, server = self.servers_client.create_server(name, image, flavor,
234 **create_kwargs)
235 if wait_on_delete:
236 self.addCleanup(self.servers_client.wait_for_server_termination,
237 server['id'])
238 self.addCleanup_with_wait(
239 waiter_callable=self.servers_client.wait_for_server_termination,
240 thing_id=server['id'], thing_id_param='server_id',
241 cleanup_callable=self.delete_wrapper,
242 cleanup_args=[self.servers_client.delete_server, server['id']])
243 if wait_on_boot:
244 self.servers_client.wait_for_server_status(server_id=server['id'],
245 status='ACTIVE')
246 # The instance retrieved on creation is missing network
247 # details, necessitating retrieval after it becomes active to
248 # ensure correct details.
249 _, server = self.servers_client.get_server(server['id'])
250 self.assertEqual(server['name'], name)
251 return server
252
253 def create_volume(self, size=1, name=None, snapshot_id=None,
254 imageRef=None, volume_type=None, wait_on_delete=True):
255 if name is None:
256 name = data_utils.rand_name(self.__class__.__name__)
257 _, volume = self.volumes_client.create_volume(
258 size=size, display_name=name, snapshot_id=snapshot_id,
259 imageRef=imageRef, volume_type=volume_type)
260 if wait_on_delete:
261 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
262 volume['id'])
263 self.addCleanup_with_wait(
264 waiter_callable=self.volumes_client.wait_for_resource_deletion,
265 thing_id=volume['id'], thing_id_param='id',
266 cleanup_callable=self.delete_wrapper,
267 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
268
269 self.assertEqual(name, volume['display_name'])
270 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
271 # The volume retrieved on creation has a non-up-to-date status.
272 # Retrieval after it becomes active ensures correct details.
273 _, volume = self.volumes_client.get_volume(volume['id'])
274 return volume
275
Yair Fried1fc32a12014-08-04 09:11:30 +0300276 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100277 _client = self.security_groups_client
278 if secgroup_id is None:
279 _, sgs = _client.list_security_groups()
280 for sg in sgs:
281 if sg['name'] == 'default':
282 secgroup_id = sg['id']
283
284 # These rules are intended to permit inbound ssh and icmp
285 # traffic from all sources, so no group_id is provided.
286 # Setting a group_id would only permit traffic from ports
287 # belonging to the same security group.
288 rulesets = [
289 {
290 # ssh
291 'ip_proto': 'tcp',
292 'from_port': 22,
293 'to_port': 22,
294 'cidr': '0.0.0.0/0',
295 },
296 {
297 # ping
298 'ip_proto': 'icmp',
299 'from_port': -1,
300 'to_port': -1,
301 'cidr': '0.0.0.0/0',
302 }
303 ]
304 rules = list()
305 for ruleset in rulesets:
306 _, sg_rule = _client.create_security_group_rule(secgroup_id,
307 **ruleset)
308 self.addCleanup(self.delete_wrapper,
309 _client.delete_security_group_rule,
310 sg_rule['id'])
311 rules.append(sg_rule)
312 return rules
313
Yair Fried1fc32a12014-08-04 09:11:30 +0300314 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100315 # Create security group
316 sg_name = data_utils.rand_name(self.__class__.__name__)
317 sg_desc = sg_name + " description"
318 _, secgroup = self.security_groups_client.create_security_group(
319 sg_name, sg_desc)
320 self.assertEqual(secgroup['name'], sg_name)
321 self.assertEqual(secgroup['description'], sg_desc)
322 self.addCleanup(self.delete_wrapper,
323 self.security_groups_client.delete_security_group,
324 secgroup['id'])
325
326 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300327 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100328
329 return secgroup
330
331 def get_remote_client(self, server_or_ip, username=None, private_key=None):
332 if isinstance(server_or_ip, six.string_types):
333 ip = server_or_ip
334 else:
335 network_name_for_ssh = CONF.compute.network_for_ssh
336 ip = server_or_ip.networks[network_name_for_ssh][0]
337 if username is None:
338 username = CONF.scenario.ssh_user
339 if private_key is None:
340 private_key = self.keypair['private_key']
341 linux_client = remote_client.RemoteClient(ip, username,
342 pkey=private_key)
343 try:
344 linux_client.validate_authentication()
345 except exceptions.SSHTimeout:
346 LOG.exception('ssh connection to %s failed' % ip)
347 debug.log_net_debug()
348 raise
349
350 return linux_client
351
Ghanshyam2a180b82014-06-16 13:54:22 +0900352 def _image_create(self, name, fmt, path, properties=None):
353 if properties is None:
354 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100355 name = data_utils.rand_name('%s-' % name)
356 image_file = open(path, 'rb')
357 self.addCleanup(image_file.close)
358 params = {
359 'name': name,
360 'container_format': fmt,
361 'disk_format': fmt,
362 'is_public': 'False',
363 }
364 params.update(properties)
365 _, image = self.image_client.create_image(**params)
366 self.addCleanup(self.image_client.delete_image, image['id'])
367 self.assertEqual("queued", image['status'])
368 self.image_client.update_image(image['id'], data=image_file)
369 return image['id']
370
371 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300372 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100373 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
374 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
375 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300376 img_container_format = CONF.scenario.img_container_format
377 img_disk_format = CONF.scenario.img_disk_format
378 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
379 "ami: %s, ari: %s, aki: %s" %
380 (img_path, img_container_format, img_disk_format,
381 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100382 try:
383 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300384 img_container_format,
385 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100386 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300387 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100388 except IOError:
389 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
390 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
391 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
392 properties = {
393 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
394 }
395 self.image = self._image_create('scenario-ami', 'ami',
396 path=ami_img_path,
397 properties=properties)
398 LOG.debug("image:%s" % self.image)
399
400 def _log_console_output(self, servers=None):
401 if not servers:
402 _, servers = self.servers_client.list_servers()
403 servers = servers['servers']
404 for server in servers:
405 LOG.debug('Console output for %s', server['id'])
406 LOG.debug(self.servers_client.get_console_output(server['id'],
407 length=None))
408
nithya-ganesan882595e2014-07-29 18:51:07 +0000409 def create_server_snapshot(self, server, name=None):
410 # Glance client
411 _image_client = self.image_client
412 # Compute client
413 _images_client = self.images_client
414 if name is None:
415 name = data_utils.rand_name('scenario-snapshot-')
416 LOG.debug("Creating a snapshot image for server: %s", server['name'])
417 resp, image = _images_client.create_image(server['id'], name)
418 image_id = resp['location'].split('images/')[1]
419 _image_client.wait_for_image_status(image_id, 'active')
420 self.addCleanup_with_wait(
421 waiter_callable=_image_client.wait_for_resource_deletion,
422 thing_id=image_id, thing_id_param='id',
423 cleanup_callable=self.delete_wrapper,
424 cleanup_args=[_image_client.delete_image, image_id])
425 _, snapshot_image = _image_client.get_image_meta(image_id)
426 image_name = snapshot_image['name']
427 self.assertEqual(name, image_name)
428 LOG.debug("Created snapshot image %s for server %s",
429 image_name, server['name'])
430 return snapshot_image
431
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100432
Yair Fried1fc32a12014-08-04 09:11:30 +0300433# TODO(yfried): change this class name to NetworkScenarioTest once client
434# migration is complete
435class NeutronScenarioTest(ScenarioTest):
436 """Base class for network scenario tests.
437 This class provide helpers for network scenario tests, using the neutron
438 API. Helpers from ancestor which use the nova network API are overridden
439 with the neutron API.
440
441 This Class also enforces using Neutron instead of novanetwork.
442 Subclassed tests will be skipped if Neutron is not enabled
443
444 """
445
446 @classmethod
447 def check_preconditions(cls):
448 if CONF.service_available.neutron:
449 cls.enabled = True
450 # verify that neutron_available is telling the truth
451 try:
452 cls.network_client.list_networks()
453 except exc.EndpointNotFound:
454 cls.enabled = False
455 raise
456 else:
457 cls.enabled = False
458 msg = 'Neutron not available'
459 raise cls.skipException(msg)
460
461 @classmethod
462 def setUpClass(cls):
463 super(NeutronScenarioTest, cls).setUpClass()
464 cls.tenant_id = cls.manager.identity_client.tenant_id
465 cls.check_preconditions()
466
467 def _create_network(self, tenant_id, namestart='network-smoke-'):
468 name = data_utils.rand_name(namestart)
469 _, result = self.network_client.create_network(name=name,
470 tenant_id=tenant_id)
471 network = net_resources.DeletableNetwork(client=self.network_client,
472 **result['network'])
473 self.assertEqual(network.name, name)
474 self.addCleanup(self.delete_wrapper, network.delete)
475 return network
476
477 def _list_networks(self, *args, **kwargs):
478 """List networks using admin creds """
479 return self._admin_lister('networks')(*args, **kwargs)
480
481 def _list_subnets(self, *args, **kwargs):
482 """List subnets using admin creds """
483 return self._admin_lister('subnets')(*args, **kwargs)
484
485 def _list_routers(self, *args, **kwargs):
486 """List routers using admin creds """
487 return self._admin_lister('routers')(*args, **kwargs)
488
489 def _list_ports(self, *args, **kwargs):
490 """List ports using admin creds """
491 return self._admin_lister('ports')(*args, **kwargs)
492
493 def _admin_lister(self, resource_type):
494 def temp(*args, **kwargs):
495 temp_method = self.admin_manager.network_client.__getattr__(
496 'list_%s' % resource_type)
497 _, resource_list = temp_method(*args, **kwargs)
498 return resource_list[resource_type]
499 return temp
500
501 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
502 """
503 Create a subnet for the given network within the cidr block
504 configured for tenant networks.
505 """
506
507 def cidr_in_use(cidr, tenant_id):
508 """
509 :return True if subnet with cidr already exist in tenant
510 False else
511 """
512 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
513 return len(cidr_in_use) != 0
514
515 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
516 result = None
517 # Repeatedly attempt subnet creation with sequential cidr
518 # blocks until an unallocated block is found.
519 for subnet_cidr in tenant_cidr.subnet(
520 CONF.network.tenant_network_mask_bits):
521 str_cidr = str(subnet_cidr)
522 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
523 continue
524
525 subnet = dict(
526 name=data_utils.rand_name(namestart),
527 ip_version=4,
528 network_id=network.id,
529 tenant_id=network.tenant_id,
530 cidr=str_cidr,
531 **kwargs
532 )
533 try:
534 _, result = self.network_client.create_subnet(**subnet)
535 break
536 except exc.NeutronClientException as e:
537 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
538 if not is_overlapping_cidr:
539 raise
540 self.assertIsNotNone(result, 'Unable to allocate tenant network')
541 subnet = net_resources.DeletableSubnet(client=self.network_client,
542 **result['subnet'])
543 self.assertEqual(subnet.cidr, str_cidr)
544 self.addCleanup(self.delete_wrapper, subnet.delete)
545 return subnet
546
547 def _create_port(self, network, namestart='port-quotatest'):
548 name = data_utils.rand_name(namestart)
549 _, result = self.network_client.create_port(
550 name=name,
551 network_id=network.id,
552 tenant_id=network.tenant_id)
553 self.assertIsNotNone(result, 'Unable to allocate port')
554 port = net_resources.DeletablePort(client=self.network_client,
555 **result['port'])
556 self.addCleanup(self.delete_wrapper, port.delete)
557 return port
558
559 def _get_server_port_id(self, server, ip_addr=None):
560 ports = self._list_ports(device_id=server['id'],
561 fixed_ip=ip_addr)
562 self.assertEqual(len(ports), 1,
563 "Unable to determine which port to target.")
564 return ports[0]['id']
565
566 def _create_floating_ip(self, thing, external_network_id, port_id=None):
567 if not port_id:
568 port_id = self._get_server_port_id(thing)
569 _, result = self.network_client.create_floatingip(
570 floating_network_id=external_network_id,
571 port_id=port_id,
572 tenant_id=thing['tenant_id']
573 )
574 floating_ip = net_resources.DeletableFloatingIp(
575 client=self.network_client,
576 **result['floatingip'])
577 self.addCleanup(self.delete_wrapper, floating_ip.delete)
578 return floating_ip
579
580 def _associate_floating_ip(self, floating_ip, server):
581 port_id = self._get_server_port_id(server)
582 floating_ip.update(port_id=port_id)
583 self.assertEqual(port_id, floating_ip.port_id)
584 return floating_ip
585
586 def _disassociate_floating_ip(self, floating_ip):
587 """
588 :param floating_ip: type DeletableFloatingIp
589 """
590 floating_ip.update(port_id=None)
591 self.assertIsNone(floating_ip.port_id)
592 return floating_ip
593
594 def _ping_ip_address(self, ip_address, should_succeed=True):
595 cmd = ['ping', '-c1', '-w1', ip_address]
596
597 def ping():
598 proc = subprocess.Popen(cmd,
599 stdout=subprocess.PIPE,
600 stderr=subprocess.PIPE)
601 proc.wait()
602 return (proc.returncode == 0) == should_succeed
603
604 return tempest.test.call_until_true(
605 ping, CONF.compute.ping_timeout, 1)
606
607 def _check_vm_connectivity(self, ip_address,
608 username=None,
609 private_key=None,
610 should_connect=True):
611 """
612 :param ip_address: server to test against
613 :param username: server's ssh username
614 :param private_key: server's ssh private key to be used
615 :param should_connect: True/False indicates positive/negative test
616 positive - attempt ping and ssh
617 negative - attempt ping and fail if succeed
618
619 :raises: AssertError if the result of the connectivity check does
620 not match the value of the should_connect param
621 """
622 if should_connect:
623 msg = "Timed out waiting for %s to become reachable" % ip_address
624 else:
625 msg = "ip address %s is reachable" % ip_address
626 self.assertTrue(self._ping_ip_address(ip_address,
627 should_succeed=should_connect),
628 msg=msg)
629 if should_connect:
630 # no need to check ssh for negative connectivity
631 self.get_remote_client(ip_address, username, private_key)
632
633 def _check_public_network_connectivity(self, ip_address, username,
634 private_key, should_connect=True,
635 msg=None, servers=None):
636 # The target login is assumed to have been configured for
637 # key-based authentication by cloud-init.
638 LOG.debug('checking network connections to IP %s with user: %s' %
639 (ip_address, username))
640 try:
641 self._check_vm_connectivity(ip_address,
642 username,
643 private_key,
644 should_connect=should_connect)
645 except Exception as e:
646 ex_msg = 'Public network connectivity check failed'
647 if msg:
648 ex_msg += ": " + msg
649 LOG.exception(ex_msg)
650 self._log_console_output(servers)
651 # network debug is called as part of ssh init
652 if not isinstance(e, exceptions.SSHTimeout):
653 debug.log_net_debug()
654 raise
655
656 def _check_tenant_network_connectivity(self, server,
657 username,
658 private_key,
659 should_connect=True,
660 servers_for_debug=None):
661 if not CONF.network.tenant_networks_reachable:
662 msg = 'Tenant networks not configured to be reachable.'
663 LOG.info(msg)
664 return
665 # The target login is assumed to have been configured for
666 # key-based authentication by cloud-init.
667 try:
668 for net_name, ip_addresses in server['networks'].iteritems():
669 for ip_address in ip_addresses:
670 self._check_vm_connectivity(ip_address,
671 username,
672 private_key,
673 should_connect=should_connect)
674 except Exception as e:
675 LOG.exception('Tenant network connectivity check failed')
676 self._log_console_output(servers_for_debug)
677 # network debug is called as part of ssh init
678 if not isinstance(e, exceptions.SSHTimeout):
679 debug.log_net_debug()
680 raise
681
682 def _check_remote_connectivity(self, source, dest, should_succeed=True):
683 """
684 check ping server via source ssh connection
685
686 :param source: RemoteClient: an ssh connection from which to ping
687 :param dest: and IP to ping against
688 :param should_succeed: boolean should ping succeed or not
689 :returns: boolean -- should_succeed == ping
690 :returns: ping is false if ping failed
691 """
692 def ping_remote():
693 try:
694 source.ping_host(dest)
695 except exceptions.SSHExecCommandFailed:
696 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
697 % (dest, source.ssh_client.host))
698 return not should_succeed
699 return should_succeed
700
701 return tempest.test.call_until_true(ping_remote,
702 CONF.compute.ping_timeout,
703 1)
704
705 def _create_security_group(self, tenant_id, client=None,
706 namestart='secgroup-smoke'):
707 if client is None:
708 client = self.network_client
709 secgroup = self._create_empty_security_group(namestart=namestart,
710 client=client,
711 tenant_id=tenant_id)
712
713 # Add rules to the security group
714 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
715 for rule in rules:
716 self.assertEqual(tenant_id, rule.tenant_id)
717 self.assertEqual(secgroup.id, rule.security_group_id)
718 return secgroup
719
720 def _create_empty_security_group(self, tenant_id, client=None,
721 namestart='secgroup-smoke'):
722 """Create a security group without rules.
723
724 Default rules will be created:
725 - IPv4 egress to any
726 - IPv6 egress to any
727
728 :param tenant_id: secgroup will be created in this tenant
729 :returns: DeletableSecurityGroup -- containing the secgroup created
730 """
731 if client is None:
732 client = self.network_client
733 sg_name = data_utils.rand_name(namestart)
734 sg_desc = sg_name + " description"
735 sg_dict = dict(name=sg_name,
736 description=sg_desc)
737 sg_dict['tenant_id'] = tenant_id
738 _, result = client.create_security_group(**sg_dict)
739 secgroup = net_resources.DeletableSecurityGroup(
740 client=client,
741 **result['security_group']
742 )
743 self.assertEqual(secgroup.name, sg_name)
744 self.assertEqual(tenant_id, secgroup.tenant_id)
745 self.assertEqual(secgroup.description, sg_desc)
746 self.addCleanup(self.delete_wrapper, secgroup.delete)
747 return secgroup
748
749 def _default_security_group(self, tenant_id, client=None):
750 """Get default secgroup for given tenant_id.
751
752 :returns: DeletableSecurityGroup -- default secgroup for given tenant
753 """
754 if client is None:
755 client = self.network_client
756 sgs = [
757 sg for sg in client.list_security_groups().values()[0]
758 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
759 ]
760 msg = "No default security group for tenant %s." % (tenant_id)
761 self.assertTrue(len(sgs) > 0, msg)
762 if len(sgs) > 1:
763 msg = "Found %d default security groups" % len(sgs)
764 raise exc.NeutronClientNoUniqueMatch(msg=msg)
765 return net_resources.DeletableSecurityGroup(client=client,
766 **sgs[0])
767
768 def _create_security_group_rule(self, client=None, secgroup=None,
769 tenant_id=None, **kwargs):
770 """Create a rule from a dictionary of rule parameters.
771
772 Create a rule in a secgroup. if secgroup not defined will search for
773 default secgroup in tenant_id.
774
775 :param secgroup: type DeletableSecurityGroup.
776 :param secgroup_id: search for secgroup by id
777 default -- choose default secgroup for given tenant_id
778 :param tenant_id: if secgroup not passed -- the tenant in which to
779 search for default secgroup
780 :param kwargs: a dictionary containing rule parameters:
781 for example, to allow incoming ssh:
782 rule = {
783 direction: 'ingress'
784 protocol:'tcp',
785 port_range_min: 22,
786 port_range_max: 22
787 }
788 """
789 if client is None:
790 client = self.network_client
791 if secgroup is None:
792 secgroup = self._default_security_group(tenant_id)
793
794 ruleset = dict(security_group_id=secgroup.id,
795 tenant_id=secgroup.tenant_id)
796 ruleset.update(kwargs)
797
798 _, sg_rule = client.create_security_group_rule(**ruleset)
799 sg_rule = net_resources.DeletableSecurityGroupRule(
800 client=client,
801 **sg_rule['security_group_rule']
802 )
803 self.addCleanup(self.delete_wrapper, sg_rule.delete)
804 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
805 self.assertEqual(secgroup.id, sg_rule.security_group_id)
806
807 return sg_rule
808
809 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
810 """These rules are intended to permit inbound ssh and icmp
811 traffic from all sources, so no group_id is provided.
812 Setting a group_id would only permit traffic from ports
813 belonging to the same security group.
814 """
815
816 if client is None:
817 client = self.network_client
818 rules = []
819 rulesets = [
820 dict(
821 # ssh
822 protocol='tcp',
823 port_range_min=22,
824 port_range_max=22,
825 ),
826 dict(
827 # ping
828 protocol='icmp',
829 )
830 ]
831 for ruleset in rulesets:
832 for r_direction in ['ingress', 'egress']:
833 ruleset['direction'] = r_direction
834 try:
835 sg_rule = self._create_security_group_rule(
836 client=client, secgroup=secgroup, **ruleset)
837 except exceptions.Conflict as ex:
838 # if rule already exist - skip rule and continue
839 msg = 'Security group rule already exists'
840 if msg not in ex._error_string:
841 raise ex
842 else:
843 self.assertEqual(r_direction, sg_rule.direction)
844 rules.append(sg_rule)
845
846 return rules
847
848 def _ssh_to_server(self, server, private_key):
849 ssh_login = CONF.compute.image_ssh_user
850 return self.get_remote_client(server,
851 username=ssh_login,
852 private_key=private_key)
853
854 def _get_router(self, tenant_id):
855 """Retrieve a router for the given tenant id.
856
857 If a public router has been configured, it will be returned.
858
859 If a public router has not been configured, but a public
860 network has, a tenant router will be created and returned that
861 routes traffic to the public network.
862 """
863 router_id = CONF.network.public_router_id
864 network_id = CONF.network.public_network_id
865 if router_id:
866 result = self.network_client.show_router(router_id)
867 return net_resources.AttributeDict(**result['router'])
868 elif network_id:
869 router = self._create_router(tenant_id)
870 router.set_gateway(network_id)
871 return router
872 else:
873 raise Exception("Neither of 'public_router_id' or "
874 "'public_network_id' has been defined.")
875
876 def _create_router(self, tenant_id, namestart='router-smoke-'):
877 name = data_utils.rand_name(namestart)
878 _, result = self.network_client.create_router(name=name,
879 admin_state_up=True,
880 tenant_id=tenant_id, )
881 router = net_resources.DeletableRouter(client=self.network_client,
882 **result['router'])
883 self.assertEqual(router.name, name)
884 self.addCleanup(self.delete_wrapper, router.delete)
885 return router
886
887 def _create_networks(self, tenant_id=None):
888 """Create a network with a subnet connected to a router.
889
890 :returns: network, subnet, router
891 """
892 if tenant_id is None:
893 tenant_id = self.tenant_id
894 network = self._create_network(tenant_id)
895 router = self._get_router(tenant_id)
896 subnet = self._create_subnet(network)
897 subnet.add_to_router(router.id)
898 return network, subnet, router
899
900
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400901class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400902 """
903 Official Client test base class for scenario testing.
904
905 Official Client tests are tests that have the following characteristics:
906
907 * Test basic operations of an API, typically in an order that
908 a regular user would perform those operations
909 * Test only the correct inputs and action paths -- no fuzz or
910 random input data is sent, only valid inputs.
911 * Use only the default client tool for calling an API
912 """
913
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400914 @classmethod
915 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200916 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -0400917 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -0500918 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500919 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200920
Andrea Frittolif9cde7e2014-02-18 09:57:04 +0000921 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000922 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400923 cls.compute_client = cls.manager.compute_client
924 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -0700925 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400926 cls.identity_client = cls.manager.identity_client
927 cls.network_client = cls.manager.network_client
928 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000929 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200930 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +0400931 cls.data_processing_client = cls.manager.data_processing_client
Artur Svechnikovc3bf9252014-05-05 16:37:37 +0400932 cls.ceilometer_client = cls.manager.ceilometer_client
Sean Dague6dbc6da2013-05-08 17:49:46 -0400933
934 @classmethod
Miguel Lavalleda5f7082014-07-16 19:18:22 -0500935 def tearDownClass(cls):
936 cls.isolated_creds.clear_isolated_creds()
937 super(OfficialClientTest, cls).tearDownClass()
938
939 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000940 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +0000941 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000942 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +0200943 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000944 creds = auth.get_default_credentials(ctype)
945 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200946
947 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +0200948 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000949 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
950 'user')
Yair Frieda71cc442013-12-18 13:32:36 +0200951
952 @classmethod
953 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000954 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
955 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +0200956
957 @classmethod
958 def admin_credentials(cls):
959 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000960 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +0200961
Matthew Treinishb7144eb2013-12-13 22:57:35 +0000962 def setUp(self):
963 super(OfficialClientTest, self).setUp()
964 self.cleanup_waits = []
965 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
966 # because scenario tests in the same test class should not share
967 # resources. If resources were shared between test cases then it
968 # should be a single scenario test instead of multiples.
Yair Friedbf2e2c42014-01-28 12:06:38 +0200969
Matthew Treinishb7144eb2013-12-13 22:57:35 +0000970 # NOTE(yfried): this list is cleaned at the end of test_methods and
971 # not at the end of the class
972 self.addCleanup(self._wait_for_cleanups)
973
974 @staticmethod
975 def not_found_exception(exception):
976 """
977 @return: True if exception is of NotFound type
978 """
979 NOT_FOUND_LIST = ['NotFound', 'HTTPNotFound']
980 return (exception.__class__.__name__ in NOT_FOUND_LIST
981 or
982 hasattr(exception, 'status_code') and
983 exception.status_code == 404)
984
985 def delete_wrapper(self, thing):
986 """Ignores NotFound exceptions for delete operations.
987
988 @param thing: object with delete() method.
989 OpenStack resources are assumed to have a delete() method which
990 destroys the resource
991 """
992
Yair Friedbf2e2c42014-01-28 12:06:38 +0200993 try:
Matthew Treinishb7144eb2013-12-13 22:57:35 +0000994 thing.delete()
Yair Friedbf2e2c42014-01-28 12:06:38 +0200995 except Exception as e:
996 # If the resource is already missing, mission accomplished.
Matthew Treinishb7144eb2013-12-13 22:57:35 +0000997 if not self.not_found_exception(e):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200998 raise
Yair Friedbf2e2c42014-01-28 12:06:38 +0200999
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001000 def _wait_for_cleanups(self):
1001 """To handle async delete actions, a list of waits is added
1002 which will be iterated over as the last step of clearing the
1003 cleanup queue. That way all the delete calls are made up front
1004 and the tests won't succeed unless the deletes are eventually
1005 successful. This is the same basic approach used in the api tests to
1006 limit cleanup execution time except here it is multi-resource,
1007 because of the nature of the scenario tests.
1008 """
1009 for wait in self.cleanup_waits:
1010 self.delete_timeout(**wait)
Yair Friedbf2e2c42014-01-28 12:06:38 +02001011
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001012 def addCleanup_with_wait(self, things, thing_id,
1013 error_status='ERROR',
1014 exc_type=nova_exceptions.NotFound,
Ghanshyam2a180b82014-06-16 13:54:22 +09001015 cleanup_callable=None, cleanup_args=None,
1016 cleanup_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001017 """Adds wait for ansyc resource deletion at the end of cleanups
Sean Dague6dbc6da2013-05-08 17:49:46 -04001018
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001019 @param things: type of the resource to delete
1020 @param thing_id:
1021 @param error_status: see manager.delete_timeout()
1022 @param exc_type: see manager.delete_timeout()
1023 @param cleanup_callable: method to load pass to self.addCleanup with
1024 the following *cleanup_args, **cleanup_kwargs.
1025 usually a delete method. if not used, will try to use:
1026 things.delete(thing_id)
1027 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001028 if cleanup_args is None:
1029 cleanup_args = []
1030 if cleanup_kwargs is None:
1031 cleanup_kwargs = {}
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001032 if cleanup_callable is None:
1033 LOG.debug("no delete method passed. using {rclass}.delete({id}) as"
1034 " default".format(rclass=things, id=thing_id))
1035 self.addCleanup(things.delete, thing_id)
1036 else:
1037 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
1038 wait_dict = {
1039 'things': things,
1040 'thing_id': thing_id,
1041 'error_status': error_status,
1042 'not_found_exception': exc_type,
1043 }
1044 self.cleanup_waits.append(wait_dict)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001045
Steve Bakerefde7612013-09-30 11:29:23 +13001046 def status_timeout(self, things, thing_id, expected_status,
1047 error_status='ERROR',
1048 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001049 """
1050 Given a thing and an expected status, do a loop, sleeping
1051 for a configurable amount of time, checking for the
1052 expected status to show. At any time, if the returned
1053 status of the thing is ERROR, fail out.
1054 """
Steve Bakerefde7612013-09-30 11:29:23 +13001055 self._status_timeout(things, thing_id,
1056 expected_status=expected_status,
1057 error_status=error_status,
1058 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001059
Steve Bakerefde7612013-09-30 11:29:23 +13001060 def delete_timeout(self, things, thing_id,
1061 error_status='ERROR',
1062 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001063 """
1064 Given a thing, do a loop, sleeping
1065 for a configurable amount of time, checking for the
1066 deleted status to show. At any time, if the returned
1067 status of the thing is ERROR, fail out.
1068 """
1069 self._status_timeout(things,
1070 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +13001071 allow_notfound=True,
1072 error_status=error_status,
1073 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001074
1075 def _status_timeout(self,
1076 things,
1077 thing_id,
1078 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +13001079 allow_notfound=False,
1080 error_status='ERROR',
1081 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001082
1083 log_status = expected_status if expected_status else ''
1084 if allow_notfound:
1085 log_status += ' or NotFound' if log_status != '' else 'NotFound'
1086
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001087 def check_status():
1088 # python-novaclient has resources available to its client
1089 # that all implement a get() method taking an identifier
1090 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001091 try:
1092 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +13001093 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001094 if allow_notfound:
1095 return True
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001096 raise
1097 except Exception as e:
1098 if allow_notfound and self.not_found_exception(e):
1099 return True
1100 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001101
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001102 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -05001103
1104 # Some components are reporting error status in lower case
1105 # so case sensitive comparisons can really mess things
1106 # up.
1107 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +09001108 message = ("%s failed to get to expected status (%s). "
1109 "In %s state.") % (thing, expected_status,
1110 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +09001111 raise exceptions.BuildErrorException(message,
1112 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001113 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001114 return True # All good.
1115 LOG.debug("Waiting for %s to get to %s status. "
1116 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001117 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001118 if not tempest.test.call_until_true(
1119 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +00001120 CONF.compute.build_timeout,
1121 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +09001122 message = ("Timed out waiting for thing %s "
1123 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +02001124 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001125
Yair Friedeb69f3f2013-10-10 13:18:16 +03001126 def _create_loginable_secgroup_rule_nova(self, client=None,
1127 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001128 if client is None:
1129 client = self.compute_client
1130 if secgroup_id is None:
1131 sgs = client.security_groups.list()
1132 for sg in sgs:
1133 if sg.name == 'default':
1134 secgroup_id = sg.id
1135
1136 # These rules are intended to permit inbound ssh and icmp
1137 # traffic from all sources, so no group_id is provided.
1138 # Setting a group_id would only permit traffic from ports
1139 # belonging to the same security group.
1140 rulesets = [
1141 {
1142 # ssh
1143 'ip_protocol': 'tcp',
1144 'from_port': 22,
1145 'to_port': 22,
1146 'cidr': '0.0.0.0/0',
1147 },
1148 {
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001149 # ssh -6
1150 'ip_protocol': 'tcp',
1151 'from_port': 22,
1152 'to_port': 22,
1153 'cidr': '::/0',
1154 },
1155 {
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001156 # ping
1157 'ip_protocol': 'icmp',
1158 'from_port': -1,
1159 'to_port': -1,
1160 'cidr': '0.0.0.0/0',
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001161 },
1162 {
1163 # ping6
1164 'ip_protocol': 'icmp',
1165 'from_port': -1,
1166 'to_port': -1,
1167 'cidr': '::/0',
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001168 }
1169 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +03001170 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001171 for ruleset in rulesets:
1172 sg_rule = client.security_group_rules.create(secgroup_id,
1173 **ruleset)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001174 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03001175 rules.append(sg_rule)
1176 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001177
Grishkin0f1e11c2014-05-04 20:44:52 +04001178 def _create_security_group_nova(self, client=None,
1179 namestart='secgroup-smoke-'):
1180 if client is None:
1181 client = self.compute_client
1182 # Create security group
1183 sg_name = data_utils.rand_name(namestart)
1184 sg_desc = sg_name + " description"
1185 secgroup = client.security_groups.create(sg_name, sg_desc)
1186 self.assertEqual(secgroup.name, sg_name)
1187 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001188 self.addCleanup(self.delete_wrapper, secgroup)
Grishkin0f1e11c2014-05-04 20:44:52 +04001189
1190 # Add rules to the security group
1191 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
1192
1193 return secgroup
1194
David Shrewsbury02719362014-05-20 14:10:03 -04001195 def rebuild_server(self, server, client=None, image=None,
1196 preserve_ephemeral=False, wait=True,
1197 rebuild_kwargs=None):
1198 if client is None:
1199 client = self.compute_client
1200 if image is None:
1201 image = CONF.compute.image_ref
1202 rebuild_kwargs = rebuild_kwargs or {}
1203
1204 LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
1205 server.name, image, preserve_ephemeral)
1206 server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
1207 **rebuild_kwargs)
1208 if wait:
1209 self.status_timeout(client.servers, server.id, 'ACTIVE')
1210
Giulio Fidente61cadca2013-09-24 18:33:37 +02001211 def create_server(self, client=None, name=None, image=None, flavor=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001212 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +09001213 create_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001214 """Creates VM instance.
1215
1216 @param client: compute client to create the instance
1217 @param image: image from which to create the instance
1218 @param wait_on_boot: wait for status ACTIVE before continue
1219 @param wait_on_delete: force synchronous delete on cleanup
1220 @param create_kwargs: additional details for instance creation
1221 @return: client.server object
1222 """
Giulio Fidente61cadca2013-09-24 18:33:37 +02001223 if client is None:
1224 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001225 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001226 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001227 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001228 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001229 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001230 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +09001231 if create_kwargs is None:
1232 create_kwargs = {}
JordanP9c052aa2014-01-24 13:05:00 +00001233
1234 fixed_network_name = CONF.compute.fixed_network_name
1235 if 'nics' not in create_kwargs and fixed_network_name:
1236 networks = client.networks.list()
1237 # If several networks found, set the NetID on which to connect the
1238 # server to avoid the following error "Multiple possible networks
1239 # found, use a Network ID to be more specific."
1240 # See Tempest #1250866
1241 if len(networks) > 1:
1242 for network in networks:
1243 if network.label == fixed_network_name:
1244 create_kwargs['nics'] = [{'net-id': network.id}]
1245 break
1246 # If we didn't find the network we were looking for :
1247 else:
1248 msg = ("The network on which the NIC of the server must "
1249 "be connected can not be found : "
1250 "fixed_network_name=%s. Starting instance without "
1251 "specifying a network.") % fixed_network_name
1252 LOG.info(msg)
1253
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001254 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
1255 name, image, flavor)
1256 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +02001257 self.assertEqual(server.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001258 if wait_on_delete:
1259 self.addCleanup(self.delete_timeout,
1260 self.compute_client.servers,
1261 server.id)
1262 self.addCleanup_with_wait(self.compute_client.servers, server.id,
1263 cleanup_callable=self.delete_wrapper,
1264 cleanup_args=[server])
1265 if wait_on_boot:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001266 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001267 # The instance retrieved on creation is missing network
1268 # details, necessitating retrieval after it becomes active to
1269 # ensure correct details.
1270 server = client.servers.get(server.id)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001271 LOG.debug("Created server: %s", server)
1272 return server
1273
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001274 def create_volume(self, client=None, size=1, name=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001275 snapshot_id=None, imageRef=None, volume_type=None,
1276 wait_on_delete=True):
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001277 if client is None:
1278 client = self.volume_client
1279 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001280 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -07001281 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001282 volume = client.volumes.create(size=size, display_name=name,
1283 snapshot_id=snapshot_id,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001284 imageRef=imageRef,
1285 volume_type=volume_type)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001286 if wait_on_delete:
1287 self.addCleanup(self.delete_timeout,
1288 self.volume_client.volumes,
1289 volume.id)
1290 self.addCleanup_with_wait(self.volume_client.volumes, volume.id,
1291 exc_type=cinder_exceptions.NotFound)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001292 self.assertEqual(name, volume.display_name)
1293 self.status_timeout(client.volumes, volume.id, 'available')
1294 LOG.debug("Created volume: %s", volume)
1295 return volume
1296
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001297 def create_server_snapshot(self, server, compute_client=None,
1298 image_client=None, name=None):
1299 if compute_client is None:
1300 compute_client = self.compute_client
1301 if image_client is None:
1302 image_client = self.image_client
1303 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001304 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001305 LOG.debug("Creating a snapshot image for server: %s", server.name)
1306 image_id = compute_client.servers.create_image(server, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001307 self.addCleanup_with_wait(self.image_client.images, image_id,
1308 exc_type=glanceclient.exc.HTTPNotFound)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001309 self.status_timeout(image_client.images, image_id, 'active')
1310 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -07001311 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001312 LOG.debug("Created snapshot image %s for server %s",
1313 snapshot_image.name, server.name)
1314 return snapshot_image
1315
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001316 def create_keypair(self, client=None, name=None):
1317 if client is None:
1318 client = self.compute_client
1319 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001320 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001321 keypair = client.keypairs.create(name)
1322 self.assertEqual(keypair.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001323 self.addCleanup(self.delete_wrapper, keypair)
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001324 return keypair
1325
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001326 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +08001327 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001328 ip = server_or_ip
1329 else:
Matthew Treinish6c072292014-01-29 19:15:52 +00001330 network_name_for_ssh = CONF.compute.network_for_ssh
Adam Gandelman0d9508e2014-08-22 10:58:09 -07001331 ip = server_or_ip.networks[network_name_for_ssh][0]
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001332 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001333 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001334 if private_key is None:
1335 private_key = self.keypair.private_key
Yair Fried3960c4d2014-05-07 15:20:30 +03001336 linux_client = remote_client.RemoteClient(ip, username,
1337 pkey=private_key)
1338 try:
1339 linux_client.validate_authentication()
1340 except exceptions.SSHTimeout:
1341 LOG.exception('ssh connection to %s failed' % ip)
1342 debug.log_net_debug()
1343 raise
1344
1345 return linux_client
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001346
Nachi Ueno95b41282014-01-15 06:54:21 -08001347 def _log_console_output(self, servers=None):
Adam Gandelmanc6eefb42014-07-15 16:44:08 -07001348 if not CONF.compute_feature_enabled.console_output:
1349 LOG.debug('Console output not supported, cannot log')
1350 return
Nachi Ueno95b41282014-01-15 06:54:21 -08001351 if not servers:
1352 servers = self.compute_client.servers.list()
1353 for server in servers:
1354 LOG.debug('Console output for %s', server.id)
1355 LOG.debug(server.get_console_output())
1356
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001357 def wait_for_volume_status(self, status):
1358 volume_id = self.volume.id
1359 self.status_timeout(
1360 self.volume_client.volumes, volume_id, status)
1361
Ghanshyam2a180b82014-06-16 13:54:22 +09001362 def _image_create(self, name, fmt, path, properties=None):
1363 if properties is None:
1364 properties = {}
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001365 name = data_utils.rand_name('%s-' % name)
1366 image_file = open(path, 'rb')
1367 self.addCleanup(image_file.close)
1368 params = {
1369 'name': name,
1370 'container_format': fmt,
1371 'disk_format': fmt,
Aaron Rosenc7720622014-05-20 10:38:10 -07001372 'is_public': 'False',
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001373 }
1374 params.update(properties)
1375 image = self.image_client.images.create(**params)
1376 self.addCleanup(self.image_client.images.delete, image)
1377 self.assertEqual("queued", image.status)
1378 image.update(data=image_file)
1379 return image.id
1380
1381 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001382 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001383 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
1384 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
1385 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001386 img_container_format = CONF.scenario.img_container_format
1387 img_disk_format = CONF.scenario.img_disk_format
1388 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
1389 "ami: %s, ari: %s, aki: %s" %
1390 (img_path, img_container_format, img_disk_format,
1391 ami_img_path, ari_img_path, aki_img_path))
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001392 try:
1393 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001394 img_container_format,
1395 img_path,
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001396 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001397 img_disk_format})
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001398 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +09001399 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001400 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
1401 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
1402 properties = {
1403 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
1404 }
1405 self.image = self._image_create('scenario-ami', 'ami',
1406 path=ami_img_path,
1407 properties=properties)
1408 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001409
Sean Dague6dbc6da2013-05-08 17:49:46 -04001410
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001411# power/provision states as of icehouse
1412class BaremetalPowerStates(object):
1413 """Possible power states of an Ironic node."""
1414 POWER_ON = 'power on'
1415 POWER_OFF = 'power off'
1416 REBOOT = 'rebooting'
1417 SUSPEND = 'suspended'
1418
1419
1420class BaremetalProvisionStates(object):
1421 """Possible provision states of an Ironic node."""
1422 NOSTATE = None
1423 INIT = 'initializing'
1424 ACTIVE = 'active'
1425 BUILDING = 'building'
1426 DEPLOYWAIT = 'wait call-back'
1427 DEPLOYING = 'deploying'
1428 DEPLOYFAIL = 'deploy failed'
1429 DEPLOYDONE = 'deploy complete'
1430 DELETING = 'deleting'
1431 DELETED = 'deleted'
1432 ERROR = 'error'
1433
1434
Adam Gandelman4a48a602014-03-20 18:23:18 -07001435class BaremetalScenarioTest(OfficialClientTest):
1436 @classmethod
1437 def setUpClass(cls):
1438 super(BaremetalScenarioTest, cls).setUpClass()
1439
1440 if (not CONF.service_available.ironic or
1441 not CONF.baremetal.driver_enabled):
1442 msg = 'Ironic not available or Ironic compute driver not enabled'
1443 raise cls.skipException(msg)
1444
1445 # use an admin client manager for baremetal client
Adam Gandelmanacc13e62014-05-08 11:12:47 -07001446 admin_creds = cls.admin_credentials()
1447 manager = clients.OfficialClientManager(credentials=admin_creds)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001448 cls.baremetal_client = manager.baremetal_client
1449
1450 # allow any issues obtaining the node list to raise early
1451 cls.baremetal_client.node.list()
1452
1453 def _node_state_timeout(self, node_id, state_attr,
1454 target_states, timeout=10, interval=1):
1455 if not isinstance(target_states, list):
1456 target_states = [target_states]
1457
1458 def check_state():
1459 node = self.get_node(node_id=node_id)
1460 if getattr(node, state_attr) in target_states:
1461 return True
1462 return False
1463
1464 if not tempest.test.call_until_true(
1465 check_state, timeout, interval):
1466 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1467 (node_id, state_attr, target_states))
1468 raise exceptions.TimeoutException(msg)
1469
1470 def wait_provisioning_state(self, node_id, state, timeout):
1471 self._node_state_timeout(
1472 node_id=node_id, state_attr='provision_state',
1473 target_states=state, timeout=timeout)
1474
1475 def wait_power_state(self, node_id, state):
1476 self._node_state_timeout(
1477 node_id=node_id, state_attr='power_state',
1478 target_states=state, timeout=CONF.baremetal.power_timeout)
1479
1480 def wait_node(self, instance_id):
1481 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001482 from ironicclient import exc as ironic_exceptions
1483
Adam Gandelman4a48a602014-03-20 18:23:18 -07001484 def _get_node():
1485 node = None
1486 try:
1487 node = self.get_node(instance_id=instance_id)
1488 except ironic_exceptions.HTTPNotFound:
1489 pass
1490 return node is not None
1491
1492 if not tempest.test.call_until_true(
1493 _get_node, CONF.baremetal.association_timeout, 1):
1494 msg = ('Timed out waiting to get Ironic node by instance id %s'
1495 % instance_id)
1496 raise exceptions.TimeoutException(msg)
1497
1498 def get_node(self, node_id=None, instance_id=None):
1499 if node_id:
1500 return self.baremetal_client.node.get(node_id)
1501 elif instance_id:
1502 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
1503
1504 def get_ports(self, node_id):
1505 ports = []
1506 for port in self.baremetal_client.node.list_ports(node_id):
1507 ports.append(self.baremetal_client.port.get(port.uuid))
1508 return ports
1509
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001510 def add_keypair(self):
1511 self.keypair = self.create_keypair()
1512
1513 def verify_connectivity(self, ip=None):
1514 if ip:
1515 dest = self.get_remote_client(ip)
1516 else:
1517 dest = self.get_remote_client(self.instance)
1518 dest.validate_authentication()
1519
1520 def boot_instance(self):
1521 create_kwargs = {
1522 'key_name': self.keypair.id
1523 }
1524 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001525 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001526
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001527 self.addCleanup_with_wait(self.compute_client.servers,
1528 self.instance.id,
1529 cleanup_callable=self.delete_wrapper,
1530 cleanup_args=[self.instance])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001531
1532 self.wait_node(self.instance.id)
1533 self.node = self.get_node(instance_id=self.instance.id)
1534
1535 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_ON)
1536
1537 self.wait_provisioning_state(
1538 self.node.uuid,
1539 [BaremetalProvisionStates.DEPLOYWAIT,
1540 BaremetalProvisionStates.ACTIVE],
1541 timeout=15)
1542
1543 self.wait_provisioning_state(self.node.uuid,
1544 BaremetalProvisionStates.ACTIVE,
1545 timeout=CONF.baremetal.active_timeout)
1546
1547 self.status_timeout(
1548 self.compute_client.servers, self.instance.id, 'ACTIVE')
1549
1550 self.node = self.get_node(instance_id=self.instance.id)
1551 self.instance = self.compute_client.servers.get(self.instance.id)
1552
1553 def terminate_instance(self):
1554 self.instance.delete()
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001555 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_OFF)
1556 self.wait_provisioning_state(
1557 self.node.uuid,
1558 BaremetalProvisionStates.NOSTATE,
1559 timeout=CONF.baremetal.unprovision_timeout)
1560
Adam Gandelman4a48a602014-03-20 18:23:18 -07001561
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001562class EncryptionScenarioTest(OfficialClientTest):
1563 """
1564 Base class for encryption scenario tests
1565 """
1566
1567 @classmethod
1568 def setUpClass(cls):
1569 super(EncryptionScenarioTest, cls).setUpClass()
1570
1571 # use admin credentials to create encrypted volume types
1572 admin_creds = cls.admin_credentials()
1573 manager = clients.OfficialClientManager(credentials=admin_creds)
1574 cls.admin_volume_client = manager.volume_client
1575
1576 def _wait_for_volume_status(self, status):
1577 self.status_timeout(
1578 self.volume_client.volumes, self.volume.id, status)
1579
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001580 def nova_boot(self):
1581 self.keypair = self.create_keypair()
1582 create_kwargs = {'key_name': self.keypair.name}
1583 self.server = self.create_server(self.compute_client,
1584 image=self.image,
1585 create_kwargs=create_kwargs)
1586
1587 def create_volume_type(self, client=None, name=None):
1588 if not client:
1589 client = self.admin_volume_client
1590 if not name:
1591 name = 'generic'
1592 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1593 LOG.debug("Creating a volume type: %s", randomized_name)
1594 volume_type = client.volume_types.create(randomized_name)
1595 self.addCleanup(client.volume_types.delete, volume_type.id)
1596 return volume_type
1597
1598 def create_encryption_type(self, client=None, type_id=None, provider=None,
1599 key_size=None, cipher=None,
1600 control_location=None):
1601 if not client:
1602 client = self.admin_volume_client
1603 if not type_id:
1604 volume_type = self.create_volume_type()
1605 type_id = volume_type.id
1606 LOG.debug("Creating an encryption type for volume type: %s", type_id)
1607 client.volume_encryption_types.create(type_id,
1608 {'provider': provider,
1609 'key_size': key_size,
1610 'cipher': cipher,
1611 'control_location':
1612 control_location})
1613
1614 def nova_volume_attach(self):
1615 attach_volume_client = self.compute_client.volumes.create_server_volume
1616 volume = attach_volume_client(self.server.id,
1617 self.volume.id,
1618 '/dev/vdb')
1619 self.assertEqual(self.volume.id, volume.id)
1620 self._wait_for_volume_status('in-use')
1621
1622 def nova_volume_detach(self):
1623 detach_volume_client = self.compute_client.volumes.delete_server_volume
1624 detach_volume_client(self.server.id, self.volume.id)
1625 self._wait_for_volume_status('available')
1626
1627 volume = self.volume_client.volumes.get(self.volume.id)
1628 self.assertEqual('available', volume.status)
1629
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001630
Sean Dague6dbc6da2013-05-08 17:49:46 -04001631class NetworkScenarioTest(OfficialClientTest):
1632 """
1633 Base class for network scenario tests
1634 """
1635
1636 @classmethod
1637 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001638 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001639 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +02001640 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -04001641 try:
1642 cls.network_client.list_networks()
1643 except exc.EndpointNotFound:
1644 cls.enabled = False
1645 raise
1646 else:
1647 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -04001648 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -04001649 raise cls.skipException(msg)
1650
1651 @classmethod
1652 def setUpClass(cls):
1653 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001654 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -04001655
Sean Dague6dbc6da2013-05-08 17:49:46 -04001656 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001657 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001658 body = dict(
1659 network=dict(
1660 name=name,
1661 tenant_id=tenant_id,
1662 ),
1663 )
1664 result = self.network_client.create_network(body=body)
1665 network = net_common.DeletableNetwork(client=self.network_client,
1666 **result['network'])
1667 self.assertEqual(network.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001668 self.addCleanup(self.delete_wrapper, network)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001669 return network
1670
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001671 def _list_networks(self, **kwargs):
1672 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001673 return nets['networks']
1674
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001675 def _list_subnets(self, **kwargs):
1676 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001677 return subnets['subnets']
1678
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001679 def _list_routers(self, **kwargs):
1680 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001681 return routers['routers']
1682
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001683 def _list_ports(self, **kwargs):
1684 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001685 return ports['ports']
1686
1687 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001688 nets = self._list_networks(tenant_id=tenant_id)
1689 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001690
1691 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001692 subnets = self._list_subnets(tenant_id=tenant_id)
1693 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001694
1695 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001696 ports = self._list_ports(tenant_id=tenant_id)
1697 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001698
Yair Fried3097dc12014-01-26 08:46:43 +02001699 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001700 """
1701 Create a subnet for the given network within the cidr block
1702 configured for tenant networks.
1703 """
Attila Fazekase857bd62013-10-21 21:02:44 +02001704
1705 def cidr_in_use(cidr, tenant_id):
1706 """
1707 :return True if subnet with cidr already exist in tenant
1708 False else
1709 """
1710 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
1711 return len(cidr_in_use) != 0
1712
Matthew Treinish6c072292014-01-29 19:15:52 +00001713 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001714 result = None
1715 # Repeatedly attempt subnet creation with sequential cidr
1716 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +00001717 for subnet_cidr in tenant_cidr.subnet(
1718 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +02001719 str_cidr = str(subnet_cidr)
1720 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
1721 continue
1722
Sean Dague6dbc6da2013-05-08 17:49:46 -04001723 body = dict(
1724 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +02001725 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -04001726 ip_version=4,
1727 network_id=network.id,
1728 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +02001729 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001730 ),
1731 )
Yair Fried3097dc12014-01-26 08:46:43 +02001732 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001733 try:
1734 result = self.network_client.create_subnet(body=body)
1735 break
Mark McClainf2982e82013-07-06 17:48:03 -04001736 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -04001737 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1738 if not is_overlapping_cidr:
1739 raise
1740 self.assertIsNotNone(result, 'Unable to allocate tenant network')
1741 subnet = net_common.DeletableSubnet(client=self.network_client,
1742 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +02001743 self.assertEqual(subnet.cidr, str_cidr)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001744 self.addCleanup(self.delete_wrapper, subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001745 return subnet
1746
1747 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001748 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001749 body = dict(
1750 port=dict(name=name,
1751 network_id=network.id,
1752 tenant_id=network.tenant_id))
1753 result = self.network_client.create_port(body=body)
1754 self.assertIsNotNone(result, 'Unable to allocate port')
1755 port = net_common.DeletablePort(client=self.network_client,
1756 **result['port'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001757 self.addCleanup(self.delete_wrapper, port)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001758 return port
1759
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001760 def _get_server_port_id(self, server, ip_addr=None):
1761 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001762 self.assertEqual(len(ports), 1,
1763 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +02001764 return ports[0]['id']
1765
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001766 def _create_floating_ip(self, thing, external_network_id, port_id=None):
1767 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001768 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001769 body = dict(
1770 floatingip=dict(
1771 floating_network_id=external_network_id,
1772 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001773 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001774 )
1775 )
1776 result = self.network_client.create_floatingip(body=body)
1777 floating_ip = net_common.DeletableFloatingIp(
1778 client=self.network_client,
1779 **result['floatingip'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001780 self.addCleanup(self.delete_wrapper, floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001781 return floating_ip
1782
Yair Fried05db2522013-11-18 11:02:10 +02001783 def _associate_floating_ip(self, floating_ip, server):
1784 port_id = self._get_server_port_id(server)
1785 floating_ip.update(port_id=port_id)
1786 self.assertEqual(port_id, floating_ip.port_id)
1787 return floating_ip
1788
Yair Fried9a551c42013-12-15 14:59:34 +02001789 def _disassociate_floating_ip(self, floating_ip):
1790 """
1791 :param floating_ip: type DeletableFloatingIp
1792 """
1793 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +08001794 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +02001795 return floating_ip
1796
1797 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001798 cmd = ['ping', '-c1', '-w1', ip_address]
1799
1800 def ping():
1801 proc = subprocess.Popen(cmd,
1802 stdout=subprocess.PIPE,
1803 stderr=subprocess.PIPE)
1804 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +02001805 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -04001806
Nachi Ueno6d580be2013-07-24 10:58:11 -07001807 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +00001808 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +00001809
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001810 def _create_pool(self, lb_method, protocol, subnet_id):
1811 """Wrapper utility that returns a test pool."""
1812 name = data_utils.rand_name('pool-')
1813 body = {
1814 "pool": {
1815 "protocol": protocol,
1816 "name": name,
1817 "subnet_id": subnet_id,
1818 "lb_method": lb_method
1819 }
1820 }
1821 resp = self.network_client.create_pool(body=body)
1822 pool = net_common.DeletablePool(client=self.network_client,
1823 **resp['pool'])
1824 self.assertEqual(pool['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001825 self.addCleanup(self.delete_wrapper, pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001826 return pool
1827
1828 def _create_member(self, address, protocol_port, pool_id):
1829 """Wrapper utility that returns a test member."""
1830 body = {
1831 "member": {
1832 "protocol_port": protocol_port,
1833 "pool_id": pool_id,
1834 "address": address
1835 }
1836 }
1837 resp = self.network_client.create_member(body)
1838 member = net_common.DeletableMember(client=self.network_client,
1839 **resp['member'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001840 self.addCleanup(self.delete_wrapper, member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001841 return member
1842
1843 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
1844 """Wrapper utility that returns a test vip."""
1845 name = data_utils.rand_name('vip-')
1846 body = {
1847 "vip": {
1848 "protocol": protocol,
1849 "name": name,
1850 "subnet_id": subnet_id,
1851 "pool_id": pool_id,
1852 "protocol_port": protocol_port
1853 }
1854 }
1855 resp = self.network_client.create_vip(body)
1856 vip = net_common.DeletableVip(client=self.network_client,
1857 **resp['vip'])
1858 self.assertEqual(vip['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001859 self.addCleanup(self.delete_wrapper, vip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001860 return vip
1861
Yair Fried9a551c42013-12-15 14:59:34 +02001862 def _check_vm_connectivity(self, ip_address,
1863 username=None,
1864 private_key=None,
1865 should_connect=True):
1866 """
1867 :param ip_address: server to test against
1868 :param username: server's ssh username
1869 :param private_key: server's ssh private key to be used
1870 :param should_connect: True/False indicates positive/negative test
1871 positive - attempt ping and ssh
1872 negative - attempt ping and fail if succeed
1873
1874 :raises: AssertError if the result of the connectivity check does
1875 not match the value of the should_connect param
1876 """
1877 if should_connect:
1878 msg = "Timed out waiting for %s to become reachable" % ip_address
1879 else:
1880 msg = "ip address %s is reachable" % ip_address
1881 self.assertTrue(self._ping_ip_address(ip_address,
1882 should_succeed=should_connect),
1883 msg=msg)
1884 if should_connect:
1885 # no need to check ssh for negative connectivity
Yair Fried3960c4d2014-05-07 15:20:30 +03001886 self.get_remote_client(ip_address, username, private_key)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001887
Matt Riedemann343305f2014-05-27 09:55:03 -07001888 def _check_public_network_connectivity(self, ip_address, username,
1889 private_key, should_connect=True,
1890 msg=None, servers=None):
1891 # The target login is assumed to have been configured for
1892 # key-based authentication by cloud-init.
1893 LOG.debug('checking network connections to IP %s with user: %s' %
1894 (ip_address, username))
1895 try:
1896 self._check_vm_connectivity(ip_address,
1897 username,
1898 private_key,
1899 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001900 except Exception as e:
Matt Riedemann343305f2014-05-27 09:55:03 -07001901 ex_msg = 'Public network connectivity check failed'
1902 if msg:
1903 ex_msg += ": " + msg
1904 LOG.exception(ex_msg)
1905 self._log_console_output(servers)
Yair Fried3960c4d2014-05-07 15:20:30 +03001906 # network debug is called as part of ssh init
1907 if not isinstance(e, exceptions.SSHTimeout):
1908 debug.log_net_debug()
Matt Riedemann343305f2014-05-27 09:55:03 -07001909 raise
1910
Matt Riedemann2d005be2014-05-27 10:52:35 -07001911 def _check_tenant_network_connectivity(self, server,
1912 username,
1913 private_key,
1914 should_connect=True,
1915 servers_for_debug=None):
1916 if not CONF.network.tenant_networks_reachable:
1917 msg = 'Tenant networks not configured to be reachable.'
1918 LOG.info(msg)
1919 return
1920 # The target login is assumed to have been configured for
1921 # key-based authentication by cloud-init.
1922 try:
1923 for net_name, ip_addresses in server.networks.iteritems():
1924 for ip_address in ip_addresses:
1925 self._check_vm_connectivity(ip_address,
1926 username,
1927 private_key,
1928 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001929 except Exception as e:
Matt Riedemann2d005be2014-05-27 10:52:35 -07001930 LOG.exception('Tenant network connectivity check failed')
1931 self._log_console_output(servers_for_debug)
Yair Fried3960c4d2014-05-07 15:20:30 +03001932 # network debug is called as part of ssh init
1933 if not isinstance(e, exceptions.SSHTimeout):
1934 debug.log_net_debug()
Matt Riedemann2d005be2014-05-27 10:52:35 -07001935 raise
1936
Yair Fried3097dc12014-01-26 08:46:43 +02001937 def _check_remote_connectivity(self, source, dest, should_succeed=True):
1938 """
1939 check ping server via source ssh connection
1940
1941 :param source: RemoteClient: an ssh connection from which to ping
1942 :param dest: and IP to ping against
1943 :param should_succeed: boolean should ping succeed or not
1944 :returns: boolean -- should_succeed == ping
1945 :returns: ping is false if ping failed
1946 """
1947 def ping_remote():
1948 try:
1949 source.ping_host(dest)
1950 except exceptions.SSHExecCommandFailed:
Matthew Treinishc4131d82014-05-27 11:59:28 -04001951 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
1952 % (dest, source.ssh_client.host))
Yair Fried3097dc12014-01-26 08:46:43 +02001953 return not should_succeed
1954 return should_succeed
1955
1956 return tempest.test.call_until_true(ping_remote,
1957 CONF.compute.ping_timeout,
1958 1)
1959
Yair Friedeb69f3f2013-10-10 13:18:16 +03001960 def _create_security_group_neutron(self, tenant_id, client=None,
1961 namestart='secgroup-smoke-'):
1962 if client is None:
1963 client = self.network_client
1964 secgroup = self._create_empty_security_group(namestart=namestart,
1965 client=client,
1966 tenant_id=tenant_id)
1967
1968 # Add rules to the security group
1969 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
1970 for rule in rules:
1971 self.assertEqual(tenant_id, rule.tenant_id)
1972 self.assertEqual(secgroup.id, rule.security_group_id)
1973 return secgroup
1974
1975 def _create_empty_security_group(self, tenant_id, client=None,
1976 namestart='secgroup-smoke-'):
1977 """Create a security group without rules.
1978
1979 Default rules will be created:
1980 - IPv4 egress to any
1981 - IPv6 egress to any
1982
1983 :param tenant_id: secgroup will be created in this tenant
1984 :returns: DeletableSecurityGroup -- containing the secgroup created
1985 """
1986 if client is None:
1987 client = self.network_client
1988 sg_name = data_utils.rand_name(namestart)
1989 sg_desc = sg_name + " description"
1990 sg_dict = dict(name=sg_name,
1991 description=sg_desc)
1992 sg_dict['tenant_id'] = tenant_id
1993 body = dict(security_group=sg_dict)
1994 result = client.create_security_group(body=body)
1995 secgroup = net_common.DeletableSecurityGroup(
1996 client=client,
1997 **result['security_group']
1998 )
1999 self.assertEqual(secgroup.name, sg_name)
2000 self.assertEqual(tenant_id, secgroup.tenant_id)
2001 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002002 self.addCleanup(self.delete_wrapper, secgroup)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002003 return secgroup
2004
2005 def _default_security_group(self, tenant_id, client=None):
2006 """Get default secgroup for given tenant_id.
2007
2008 :returns: DeletableSecurityGroup -- default secgroup for given tenant
2009 """
2010 if client is None:
2011 client = self.network_client
2012 sgs = [
2013 sg for sg in client.list_security_groups().values()[0]
2014 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
2015 ]
2016 msg = "No default security group for tenant %s." % (tenant_id)
2017 self.assertTrue(len(sgs) > 0, msg)
2018 if len(sgs) > 1:
2019 msg = "Found %d default security groups" % len(sgs)
2020 raise exc.NeutronClientNoUniqueMatch(msg=msg)
2021 return net_common.DeletableSecurityGroup(client=client,
2022 **sgs[0])
2023
2024 def _create_security_group_rule(self, client=None, secgroup=None,
2025 tenant_id=None, **kwargs):
2026 """Create a rule from a dictionary of rule parameters.
2027
2028 Create a rule in a secgroup. if secgroup not defined will search for
2029 default secgroup in tenant_id.
2030
2031 :param secgroup: type DeletableSecurityGroup.
2032 :param secgroup_id: search for secgroup by id
2033 default -- choose default secgroup for given tenant_id
2034 :param tenant_id: if secgroup not passed -- the tenant in which to
2035 search for default secgroup
2036 :param kwargs: a dictionary containing rule parameters:
2037 for example, to allow incoming ssh:
2038 rule = {
2039 direction: 'ingress'
2040 protocol:'tcp',
2041 port_range_min: 22,
2042 port_range_max: 22
2043 }
2044 """
2045 if client is None:
2046 client = self.network_client
2047 if secgroup is None:
2048 secgroup = self._default_security_group(tenant_id)
2049
2050 ruleset = dict(security_group_id=secgroup.id,
2051 tenant_id=secgroup.tenant_id,
2052 )
2053 ruleset.update(kwargs)
2054
2055 body = dict(security_group_rule=dict(ruleset))
2056 sg_rule = client.create_security_group_rule(body=body)
2057 sg_rule = net_common.DeletableSecurityGroupRule(
2058 client=client,
2059 **sg_rule['security_group_rule']
2060 )
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002061 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002062 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
2063 self.assertEqual(secgroup.id, sg_rule.security_group_id)
2064
2065 return sg_rule
2066
2067 def _create_loginable_secgroup_rule_neutron(self, client=None,
2068 secgroup=None):
2069 """These rules are intended to permit inbound ssh and icmp
2070 traffic from all sources, so no group_id is provided.
2071 Setting a group_id would only permit traffic from ports
2072 belonging to the same security group.
2073 """
2074
2075 if client is None:
2076 client = self.network_client
2077 rules = []
2078 rulesets = [
2079 dict(
2080 # ssh
2081 protocol='tcp',
2082 port_range_min=22,
2083 port_range_max=22,
2084 ),
2085 dict(
2086 # ping
2087 protocol='icmp',
2088 )
2089 ]
2090 for ruleset in rulesets:
2091 for r_direction in ['ingress', 'egress']:
2092 ruleset['direction'] = r_direction
2093 try:
2094 sg_rule = self._create_security_group_rule(
2095 client=client, secgroup=secgroup, **ruleset)
2096 except exc.NeutronClientException as ex:
2097 # if rule already exist - skip rule and continue
2098 if not (ex.status_code is 409 and 'Security group rule'
2099 ' already exists' in ex.message):
2100 raise ex
2101 else:
2102 self.assertEqual(r_direction, sg_rule.direction)
2103 rules.append(sg_rule)
2104
2105 return rules
2106
Yair Fried5f670ab2013-12-09 09:26:51 +02002107 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +00002108 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +02002109 return self.get_remote_client(server,
2110 username=ssh_login,
2111 private_key=private_key)
2112
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00002113 def _show_quota_network(self, tenant_id):
2114 quota = self.network_client.show_quota(tenant_id)
2115 return quota['quota']['network']
2116
2117 def _show_quota_subnet(self, tenant_id):
2118 quota = self.network_client.show_quota(tenant_id)
2119 return quota['quota']['subnet']
2120
2121 def _show_quota_port(self, tenant_id):
2122 quota = self.network_client.show_quota(tenant_id)
2123 return quota['quota']['port']
2124
Yair Fried4d7efa62013-11-17 17:12:29 +02002125 def _get_router(self, tenant_id):
2126 """Retrieve a router for the given tenant id.
2127
2128 If a public router has been configured, it will be returned.
2129
2130 If a public router has not been configured, but a public
2131 network has, a tenant router will be created and returned that
2132 routes traffic to the public network.
2133 """
Matthew Treinish6c072292014-01-29 19:15:52 +00002134 router_id = CONF.network.public_router_id
2135 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02002136 if router_id:
2137 result = self.network_client.show_router(router_id)
2138 return net_common.AttributeDict(**result['router'])
2139 elif network_id:
2140 router = self._create_router(tenant_id)
2141 router.add_gateway(network_id)
2142 return router
2143 else:
2144 raise Exception("Neither of 'public_router_id' or "
2145 "'public_network_id' has been defined.")
2146
2147 def _create_router(self, tenant_id, namestart='router-smoke-'):
2148 name = data_utils.rand_name(namestart)
2149 body = dict(
2150 router=dict(
2151 name=name,
2152 admin_state_up=True,
2153 tenant_id=tenant_id,
2154 ),
2155 )
2156 result = self.network_client.create_router(body=body)
2157 router = net_common.DeletableRouter(client=self.network_client,
2158 **result['router'])
2159 self.assertEqual(router.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002160 self.addCleanup(self.delete_wrapper, router)
Yair Fried4d7efa62013-11-17 17:12:29 +02002161 return router
2162
2163 def _create_networks(self, tenant_id=None):
2164 """Create a network with a subnet connected to a router.
2165
2166 :returns: network, subnet, router
2167 """
2168 if tenant_id is None:
2169 tenant_id = self.tenant_id
2170 network = self._create_network(tenant_id)
2171 router = self._get_router(tenant_id)
2172 subnet = self._create_subnet(network)
2173 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02002174 return network, subnet, router
2175
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002176
2177class OrchestrationScenarioTest(OfficialClientTest):
2178 """
2179 Base class for orchestration scenario tests
2180 """
2181
2182 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07002183 def setUpClass(cls):
2184 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00002185 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07002186 raise cls.skipException("Heat support is required")
2187
2188 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002189 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00002190 admin_creds = auth.get_default_credentials('identity_admin')
2191 creds = auth.get_default_credentials('user')
2192 admin_creds.tenant_name = creds.tenant_name
2193 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002194
2195 def _load_template(self, base_file, file_name):
2196 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
2197 file_name)
2198 with open(filepath) as f:
2199 return f.read()
2200
2201 @classmethod
2202 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09002203 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12002204
2205 @classmethod
2206 def _get_default_network(cls):
2207 networks = cls.network_client.list_networks()
2208 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00002209 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12002210 return net
Steve Baker22c16602014-05-05 13:34:19 +12002211
2212 @staticmethod
2213 def _stack_output(stack, output_key):
2214 """Return a stack output value for a given key."""
2215 return next((o['output_value'] for o in stack.outputs
2216 if o['output_key'] == output_key), None)
2217
2218 def _ping_ip_address(self, ip_address, should_succeed=True):
2219 cmd = ['ping', '-c1', '-w1', ip_address]
2220
2221 def ping():
2222 proc = subprocess.Popen(cmd,
2223 stdout=subprocess.PIPE,
2224 stderr=subprocess.PIPE)
2225 proc.wait()
2226 return (proc.returncode == 0) == should_succeed
2227
2228 return tempest.test.call_until_true(
2229 ping, CONF.orchestration.build_timeout, 1)
2230
2231 def _wait_for_resource_status(self, stack_identifier, resource_name,
2232 status, failure_pattern='^.*_FAILED$'):
2233 """Waits for a Resource to reach a given status."""
2234 fail_regexp = re.compile(failure_pattern)
2235 build_timeout = CONF.orchestration.build_timeout
2236 build_interval = CONF.orchestration.build_interval
2237
2238 start = timeutils.utcnow()
2239 while timeutils.delta_seconds(start,
2240 timeutils.utcnow()) < build_timeout:
2241 try:
2242 res = self.client.resources.get(
2243 stack_identifier, resource_name)
2244 except heat_exceptions.HTTPNotFound:
2245 # ignore this, as the resource may not have
2246 # been created yet
2247 pass
2248 else:
2249 if res.resource_status == status:
2250 return
2251 if fail_regexp.search(res.resource_status):
2252 raise exceptions.StackResourceBuildErrorException(
2253 resource_name=res.resource_name,
2254 stack_identifier=stack_identifier,
2255 resource_status=res.resource_status,
2256 resource_status_reason=res.resource_status_reason)
2257 time.sleep(build_interval)
2258
2259 message = ('Resource %s failed to reach %s status within '
2260 'the required time (%s s).' %
2261 (res.resource_name, status, build_timeout))
2262 raise exceptions.TimeoutException(message)
2263
2264 def _wait_for_stack_status(self, stack_identifier, status,
2265 failure_pattern='^.*_FAILED$'):
2266 """
2267 Waits for a Stack to reach a given status.
2268
2269 Note this compares the full $action_$status, e.g
2270 CREATE_COMPLETE, not just COMPLETE which is exposed
2271 via the status property of Stack in heatclient
2272 """
2273 fail_regexp = re.compile(failure_pattern)
2274 build_timeout = CONF.orchestration.build_timeout
2275 build_interval = CONF.orchestration.build_interval
2276
2277 start = timeutils.utcnow()
2278 while timeutils.delta_seconds(start,
2279 timeutils.utcnow()) < build_timeout:
2280 try:
2281 stack = self.client.stacks.get(stack_identifier)
2282 except heat_exceptions.HTTPNotFound:
2283 # ignore this, as the stackource may not have
2284 # been created yet
2285 pass
2286 else:
2287 if stack.stack_status == status:
2288 return
2289 if fail_regexp.search(stack.stack_status):
2290 raise exceptions.StackBuildErrorException(
2291 stack_identifier=stack_identifier,
2292 stack_status=stack.stack_status,
2293 stack_status_reason=stack.stack_status_reason)
2294 time.sleep(build_interval)
2295
2296 message = ('Stack %s failed to reach %s status within '
2297 'the required time (%s s).' %
2298 (stack.stack_name, status, build_timeout))
2299 raise exceptions.TimeoutException(message)
2300
2301 def _stack_delete(self, stack_identifier):
2302 try:
2303 self.client.stacks.delete(stack_identifier)
2304 except heat_exceptions.HTTPNotFound:
2305 pass
Chris Dent0d494112014-08-26 13:48:30 +01002306
2307
2308class SwiftScenarioTest(ScenarioTest):
2309 """
2310 Provide harness to do Swift scenario tests.
2311
2312 Subclasses implement the tests that use the methods provided by this
2313 class.
2314 """
2315
2316 @classmethod
2317 def setUpClass(cls):
2318 cls.set_network_resources()
2319 super(SwiftScenarioTest, cls).setUpClass()
2320 if not CONF.service_available.swift:
2321 skip_msg = ("%s skipped as swift is not available" %
2322 cls.__name__)
2323 raise cls.skipException(skip_msg)
2324 # Clients for Swift
2325 cls.account_client = cls.manager.account_client
2326 cls.container_client = cls.manager.container_client
2327 cls.object_client = cls.manager.object_client
2328
2329 def _get_swift_stat(self):
2330 """get swift status for our user account."""
2331 self.account_client.list_account_containers()
2332 LOG.debug('Swift status information obtained successfully')
2333
2334 def _create_container(self, container_name=None):
2335 name = container_name or data_utils.rand_name(
2336 'swift-scenario-container')
2337 self.container_client.create_container(name)
2338 # look for the container to assure it is created
2339 self._list_and_check_container_objects(name)
2340 LOG.debug('Container %s created' % (name))
2341 return name
2342
2343 def _delete_container(self, container_name):
2344 self.container_client.delete_container(container_name)
2345 LOG.debug('Container %s deleted' % (container_name))
2346
2347 def _upload_object_to_container(self, container_name, obj_name=None):
2348 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
2349 obj_data = data_utils.arbitrary_string()
2350 self.object_client.create_object(container_name, obj_name, obj_data)
2351 return obj_name, obj_data
2352
2353 def _delete_object(self, container_name, filename):
2354 self.object_client.delete_object(container_name, filename)
2355 self._list_and_check_container_objects(container_name,
2356 not_present_obj=[filename])
2357
Ghanshyam2a180b82014-06-16 13:54:22 +09002358 def _list_and_check_container_objects(self, container_name,
2359 present_obj=None,
2360 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01002361 """
2362 List objects for a given container and assert which are present and
2363 which are not.
2364 """
Ghanshyam2a180b82014-06-16 13:54:22 +09002365 if present_obj is None:
2366 present_obj = []
2367 if not_present_obj is None:
2368 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01002369 _, object_list = self.container_client.list_container_contents(
2370 container_name)
2371 if present_obj:
2372 for obj in present_obj:
2373 self.assertIn(obj, object_list)
2374 if not_present_obj:
2375 for obj in not_present_obj:
2376 self.assertNotIn(obj, object_list)
2377
2378 def _change_container_acl(self, container_name, acl):
2379 metadata_param = {'metadata_prefix': 'x-container-',
2380 'metadata': {'read': acl}}
2381 self.container_client.update_container_metadata(container_name,
2382 **metadata_param)
2383 resp, _ = self.container_client.list_container_metadata(container_name)
2384 self.assertEqual(resp['x-container-read'], acl)
2385
2386 def _download_and_verify(self, container_name, obj_name, expected_data):
2387 _, obj = self.object_client.get_object(container_name, obj_name)
2388 self.assertEqual(obj, expected_data)