blob: 187dabb313392dc70c931de37aa6a2e2908cab8c [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Attila Fazekasfb7552a2013-08-27 13:02:26 +020017import logging
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120018import os
Steve Baker22c16602014-05-05 13:34:19 +120019import re
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import subprocess
Steve Baker22c16602014-05-05 13:34:19 +120021import time
Sean Dague6dbc6da2013-05-08 17:49:46 -040022
Kaitlin Farr366a51f2014-04-21 12:43:54 -040023from cinderclient import exceptions as cinder_exceptions
Matthew Treinishb7144eb2013-12-13 22:57:35 +000024import glanceclient
Steve Baker22c16602014-05-05 13:34:19 +120025from heatclient import exc as heat_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040026import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040027from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090028from novaclient import exceptions as nova_exceptions
Matthew Treinish96e9e882014-06-09 18:37:19 -040029import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040030
Sean Dague1937d092013-05-17 16:36:38 -040031from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000032from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000033from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070034from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040035from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090036from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090037from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000038from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020039from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020040from tempest.openstack.common import log
Steve Baker22c16602014-05-05 13:34:19 +120041from tempest.openstack.common import timeutils
Yair Fried1fc32a12014-08-04 09:11:30 +030042from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040043import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040044
Matthew Treinish6c072292014-01-29 19:15:52 +000045CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040046
Attila Fazekasfb7552a2013-08-27 13:02:26 +020047LOG = log.getLogger(__name__)
48
49# NOTE(afazekas): Workaround for the stdout logging
50LOG_nova_client = logging.getLogger('novaclient.client')
51LOG_nova_client.addHandler(log.NullHandler())
52
53LOG_cinder_client = logging.getLogger('cinderclient.client')
54LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040055
56
Andrea Frittoli2e733b52014-07-16 14:12:11 +010057class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli247058f2014-07-16 16:09:22 +010058 """Replaces the OfficialClientTest base class.
59
60 Uses tempest own clients as opposed to OfficialClients.
61
62 Common differences:
63 - replace resource.attribute with resource['attribute']
64 - replace resouce.delete with delete_callable(resource['id'])
65 - replace local waiters with common / rest_client waiters
66 """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010067
68 @classmethod
69 def setUpClass(cls):
70 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010071 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010072 cls.isolated_creds = isolated_creds.IsolatedCreds(
73 cls.__name__, tempest_client=True,
74 network_resources=cls.network_resources)
75 cls.manager = clients.Manager(
76 credentials=cls.credentials()
77 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010078 cls.admin_manager = clients.Manager(cls.admin_credentials())
79 # Clients (in alphabetical order)
80 cls.floating_ips_client = cls.manager.floating_ips_client
81 # Glance image client v1
82 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000083 # Compute image client
84 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010085 cls.keypairs_client = cls.manager.keypairs_client
86 cls.networks_client = cls.admin_manager.networks_client
87 # Nova security groups client
88 cls.security_groups_client = cls.manager.security_groups_client
89 cls.servers_client = cls.manager.servers_client
90 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000091 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030092 cls.interface_client = cls.manager.interfaces_client
93 # Neutron network client
94 cls.network_client = cls.manager.network_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010095
96 @classmethod
Salvatorecfe42f62014-09-03 23:19:12 +020097 def tearDownClass(cls):
98 # Isolated creds also manages network resources, which should
99 # be cleaned up at the end of the test case
100 cls.isolated_creds.clear_isolated_creds()
101 super(ScenarioTest, cls).tearDownClass()
102
103 @classmethod
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100104 def _get_credentials(cls, get_creds, ctype):
105 if CONF.compute.allow_tenant_isolation:
106 creds = get_creds()
107 else:
108 creds = auth.get_default_credentials(ctype)
109 return creds
110
111 @classmethod
112 def credentials(cls):
113 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
114 'user')
115
Masayuki Igawaccd66592014-07-17 00:42:42 +0900116 @classmethod
117 def admin_credentials(cls):
118 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
119 'identity_admin')
120
Andrea Frittoli247058f2014-07-16 16:09:22 +0100121 # ## Methods to handle sync and async deletes
122
123 def setUp(self):
124 super(ScenarioTest, self).setUp()
125 self.cleanup_waits = []
126 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
127 # because scenario tests in the same test class should not share
128 # resources. If resources were shared between test cases then it
129 # should be a single scenario test instead of multiples.
130
131 # NOTE(yfried): this list is cleaned at the end of test_methods and
132 # not at the end of the class
133 self.addCleanup(self._wait_for_cleanups)
134
Yair Fried1fc32a12014-08-04 09:11:30 +0300135 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100136 """Ignores NotFound exceptions for delete operations.
137
Yair Fried1fc32a12014-08-04 09:11:30 +0300138 @param delete_thing: delete method of a resource. method will be
139 executed as delete_thing(*args, **kwargs)
140
Andrea Frittoli247058f2014-07-16 16:09:22 +0100141 """
142 try:
143 # Tempest clients return dicts, so there is no common delete
144 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300145 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100146 except exceptions.NotFound:
147 # If the resource is already missing, mission accomplished.
148 pass
149
150 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900151 cleanup_callable, cleanup_args=None,
152 cleanup_kwargs=None, ignore_error=True):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100153 """Adds wait for ansyc resource deletion at the end of cleanups
154
155 @param waiter_callable: callable to wait for the resource to delete
156 @param thing_id: the id of the resource to be cleaned-up
157 @param thing_id_param: the name of the id param in the waiter
158 @param cleanup_callable: method to load pass to self.addCleanup with
159 the following *cleanup_args, **cleanup_kwargs.
160 usually a delete method.
161 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900162 if cleanup_args is None:
163 cleanup_args = []
164 if cleanup_kwargs is None:
165 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100166 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
167 wait_dict = {
168 'waiter_callable': waiter_callable,
169 thing_id_param: thing_id
170 }
171 self.cleanup_waits.append(wait_dict)
172
173 def _wait_for_cleanups(self):
174 """To handle async delete actions, a list of waits is added
175 which will be iterated over as the last step of clearing the
176 cleanup queue. That way all the delete calls are made up front
177 and the tests won't succeed unless the deletes are eventually
178 successful. This is the same basic approach used in the api tests to
179 limit cleanup execution time except here it is multi-resource,
180 because of the nature of the scenario tests.
181 """
182 for wait in self.cleanup_waits:
183 waiter_callable = wait.pop('waiter_callable')
184 waiter_callable(**wait)
185
186 # ## Test functions library
187 #
188 # The create_[resource] functions only return body and discard the
189 # resp part which is not used in scenario tests
190
191 def create_keypair(self):
192 name = data_utils.rand_name(self.__class__.__name__)
193 # We don't need to create a keypair by pubkey in scenario
194 resp, body = self.keypairs_client.create_keypair(name)
195 self.addCleanup(self.keypairs_client.delete_keypair, name)
196 return body
197
198 def create_server(self, name=None, image=None, flavor=None,
199 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900200 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100201 """Creates VM instance.
202
203 @param image: image from which to create the instance
204 @param wait_on_boot: wait for status ACTIVE before continue
205 @param wait_on_delete: force synchronous delete on cleanup
206 @param create_kwargs: additional details for instance creation
207 @return: server dict
208 """
209 if name is None:
210 name = data_utils.rand_name(self.__class__.__name__)
211 if image is None:
212 image = CONF.compute.image_ref
213 if flavor is None:
214 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900215 if create_kwargs is None:
216 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100217
218 fixed_network_name = CONF.compute.fixed_network_name
219 if 'nics' not in create_kwargs and fixed_network_name:
220 _, networks = self.networks_client.list_networks()
221 # If several networks found, set the NetID on which to connect the
222 # server to avoid the following error "Multiple possible networks
223 # found, use a Network ID to be more specific."
224 # See Tempest #1250866
225 if len(networks) > 1:
226 for network in networks:
227 if network['label'] == fixed_network_name:
228 create_kwargs['nics'] = [{'net-id': network['id']}]
229 break
230 # If we didn't find the network we were looking for :
231 else:
232 msg = ("The network on which the NIC of the server must "
233 "be connected can not be found : "
234 "fixed_network_name=%s. Starting instance without "
235 "specifying a network.") % fixed_network_name
236 LOG.info(msg)
237
238 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
239 name, image, flavor)
240 _, server = self.servers_client.create_server(name, image, flavor,
241 **create_kwargs)
242 if wait_on_delete:
243 self.addCleanup(self.servers_client.wait_for_server_termination,
244 server['id'])
245 self.addCleanup_with_wait(
246 waiter_callable=self.servers_client.wait_for_server_termination,
247 thing_id=server['id'], thing_id_param='server_id',
248 cleanup_callable=self.delete_wrapper,
249 cleanup_args=[self.servers_client.delete_server, server['id']])
250 if wait_on_boot:
251 self.servers_client.wait_for_server_status(server_id=server['id'],
252 status='ACTIVE')
253 # The instance retrieved on creation is missing network
254 # details, necessitating retrieval after it becomes active to
255 # ensure correct details.
256 _, server = self.servers_client.get_server(server['id'])
257 self.assertEqual(server['name'], name)
258 return server
259
260 def create_volume(self, size=1, name=None, snapshot_id=None,
261 imageRef=None, volume_type=None, wait_on_delete=True):
262 if name is None:
263 name = data_utils.rand_name(self.__class__.__name__)
264 _, volume = self.volumes_client.create_volume(
265 size=size, display_name=name, snapshot_id=snapshot_id,
266 imageRef=imageRef, volume_type=volume_type)
267 if wait_on_delete:
268 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
269 volume['id'])
270 self.addCleanup_with_wait(
271 waiter_callable=self.volumes_client.wait_for_resource_deletion,
272 thing_id=volume['id'], thing_id_param='id',
273 cleanup_callable=self.delete_wrapper,
274 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
275
276 self.assertEqual(name, volume['display_name'])
277 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
278 # The volume retrieved on creation has a non-up-to-date status.
279 # Retrieval after it becomes active ensures correct details.
280 _, volume = self.volumes_client.get_volume(volume['id'])
281 return volume
282
Yair Fried1fc32a12014-08-04 09:11:30 +0300283 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100284 _client = self.security_groups_client
285 if secgroup_id is None:
286 _, sgs = _client.list_security_groups()
287 for sg in sgs:
288 if sg['name'] == 'default':
289 secgroup_id = sg['id']
290
291 # These rules are intended to permit inbound ssh and icmp
292 # traffic from all sources, so no group_id is provided.
293 # Setting a group_id would only permit traffic from ports
294 # belonging to the same security group.
295 rulesets = [
296 {
297 # ssh
298 'ip_proto': 'tcp',
299 'from_port': 22,
300 'to_port': 22,
301 'cidr': '0.0.0.0/0',
302 },
303 {
304 # ping
305 'ip_proto': 'icmp',
306 'from_port': -1,
307 'to_port': -1,
308 'cidr': '0.0.0.0/0',
309 }
310 ]
311 rules = list()
312 for ruleset in rulesets:
313 _, sg_rule = _client.create_security_group_rule(secgroup_id,
314 **ruleset)
315 self.addCleanup(self.delete_wrapper,
316 _client.delete_security_group_rule,
317 sg_rule['id'])
318 rules.append(sg_rule)
319 return rules
320
Yair Fried1fc32a12014-08-04 09:11:30 +0300321 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100322 # Create security group
323 sg_name = data_utils.rand_name(self.__class__.__name__)
324 sg_desc = sg_name + " description"
325 _, secgroup = self.security_groups_client.create_security_group(
326 sg_name, sg_desc)
327 self.assertEqual(secgroup['name'], sg_name)
328 self.assertEqual(secgroup['description'], sg_desc)
329 self.addCleanup(self.delete_wrapper,
330 self.security_groups_client.delete_security_group,
331 secgroup['id'])
332
333 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300334 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100335
336 return secgroup
337
338 def get_remote_client(self, server_or_ip, username=None, private_key=None):
339 if isinstance(server_or_ip, six.string_types):
340 ip = server_or_ip
341 else:
342 network_name_for_ssh = CONF.compute.network_for_ssh
343 ip = server_or_ip.networks[network_name_for_ssh][0]
344 if username is None:
345 username = CONF.scenario.ssh_user
346 if private_key is None:
347 private_key = self.keypair['private_key']
348 linux_client = remote_client.RemoteClient(ip, username,
349 pkey=private_key)
350 try:
351 linux_client.validate_authentication()
352 except exceptions.SSHTimeout:
353 LOG.exception('ssh connection to %s failed' % ip)
354 debug.log_net_debug()
355 raise
356
357 return linux_client
358
Ghanshyam2a180b82014-06-16 13:54:22 +0900359 def _image_create(self, name, fmt, path, properties=None):
360 if properties is None:
361 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100362 name = data_utils.rand_name('%s-' % name)
363 image_file = open(path, 'rb')
364 self.addCleanup(image_file.close)
365 params = {
366 'name': name,
367 'container_format': fmt,
368 'disk_format': fmt,
369 'is_public': 'False',
370 }
371 params.update(properties)
372 _, image = self.image_client.create_image(**params)
373 self.addCleanup(self.image_client.delete_image, image['id'])
374 self.assertEqual("queued", image['status'])
375 self.image_client.update_image(image['id'], data=image_file)
376 return image['id']
377
378 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300379 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100380 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
381 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
382 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300383 img_container_format = CONF.scenario.img_container_format
384 img_disk_format = CONF.scenario.img_disk_format
385 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
386 "ami: %s, ari: %s, aki: %s" %
387 (img_path, img_container_format, img_disk_format,
388 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100389 try:
390 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300391 img_container_format,
392 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100393 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300394 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100395 except IOError:
396 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
397 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
398 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
399 properties = {
400 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
401 }
402 self.image = self._image_create('scenario-ami', 'ami',
403 path=ami_img_path,
404 properties=properties)
405 LOG.debug("image:%s" % self.image)
406
407 def _log_console_output(self, servers=None):
408 if not servers:
409 _, servers = self.servers_client.list_servers()
410 servers = servers['servers']
411 for server in servers:
412 LOG.debug('Console output for %s', server['id'])
413 LOG.debug(self.servers_client.get_console_output(server['id'],
414 length=None))
415
nithya-ganesan882595e2014-07-29 18:51:07 +0000416 def create_server_snapshot(self, server, name=None):
417 # Glance client
418 _image_client = self.image_client
419 # Compute client
420 _images_client = self.images_client
421 if name is None:
422 name = data_utils.rand_name('scenario-snapshot-')
423 LOG.debug("Creating a snapshot image for server: %s", server['name'])
424 resp, image = _images_client.create_image(server['id'], name)
425 image_id = resp['location'].split('images/')[1]
426 _image_client.wait_for_image_status(image_id, 'active')
427 self.addCleanup_with_wait(
428 waiter_callable=_image_client.wait_for_resource_deletion,
429 thing_id=image_id, thing_id_param='id',
430 cleanup_callable=self.delete_wrapper,
431 cleanup_args=[_image_client.delete_image, image_id])
432 _, snapshot_image = _image_client.get_image_meta(image_id)
433 image_name = snapshot_image['name']
434 self.assertEqual(name, image_name)
435 LOG.debug("Created snapshot image %s for server %s",
436 image_name, server['name'])
437 return snapshot_image
438
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100439
Yair Fried1fc32a12014-08-04 09:11:30 +0300440# TODO(yfried): change this class name to NetworkScenarioTest once client
441# migration is complete
442class NeutronScenarioTest(ScenarioTest):
443 """Base class for network scenario tests.
444 This class provide helpers for network scenario tests, using the neutron
445 API. Helpers from ancestor which use the nova network API are overridden
446 with the neutron API.
447
448 This Class also enforces using Neutron instead of novanetwork.
449 Subclassed tests will be skipped if Neutron is not enabled
450
451 """
452
453 @classmethod
454 def check_preconditions(cls):
455 if CONF.service_available.neutron:
456 cls.enabled = True
457 # verify that neutron_available is telling the truth
458 try:
459 cls.network_client.list_networks()
460 except exc.EndpointNotFound:
461 cls.enabled = False
462 raise
463 else:
464 cls.enabled = False
465 msg = 'Neutron not available'
466 raise cls.skipException(msg)
467
468 @classmethod
469 def setUpClass(cls):
470 super(NeutronScenarioTest, cls).setUpClass()
471 cls.tenant_id = cls.manager.identity_client.tenant_id
472 cls.check_preconditions()
473
474 def _create_network(self, tenant_id, namestart='network-smoke-'):
475 name = data_utils.rand_name(namestart)
476 _, result = self.network_client.create_network(name=name,
477 tenant_id=tenant_id)
478 network = net_resources.DeletableNetwork(client=self.network_client,
479 **result['network'])
480 self.assertEqual(network.name, name)
481 self.addCleanup(self.delete_wrapper, network.delete)
482 return network
483
484 def _list_networks(self, *args, **kwargs):
485 """List networks using admin creds """
486 return self._admin_lister('networks')(*args, **kwargs)
487
488 def _list_subnets(self, *args, **kwargs):
489 """List subnets using admin creds """
490 return self._admin_lister('subnets')(*args, **kwargs)
491
492 def _list_routers(self, *args, **kwargs):
493 """List routers using admin creds """
494 return self._admin_lister('routers')(*args, **kwargs)
495
496 def _list_ports(self, *args, **kwargs):
497 """List ports using admin creds """
498 return self._admin_lister('ports')(*args, **kwargs)
499
500 def _admin_lister(self, resource_type):
501 def temp(*args, **kwargs):
502 temp_method = self.admin_manager.network_client.__getattr__(
503 'list_%s' % resource_type)
504 _, resource_list = temp_method(*args, **kwargs)
505 return resource_list[resource_type]
506 return temp
507
508 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
509 """
510 Create a subnet for the given network within the cidr block
511 configured for tenant networks.
512 """
513
514 def cidr_in_use(cidr, tenant_id):
515 """
516 :return True if subnet with cidr already exist in tenant
517 False else
518 """
519 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
520 return len(cidr_in_use) != 0
521
522 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
523 result = None
524 # Repeatedly attempt subnet creation with sequential cidr
525 # blocks until an unallocated block is found.
526 for subnet_cidr in tenant_cidr.subnet(
527 CONF.network.tenant_network_mask_bits):
528 str_cidr = str(subnet_cidr)
529 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
530 continue
531
532 subnet = dict(
533 name=data_utils.rand_name(namestart),
534 ip_version=4,
535 network_id=network.id,
536 tenant_id=network.tenant_id,
537 cidr=str_cidr,
538 **kwargs
539 )
540 try:
541 _, result = self.network_client.create_subnet(**subnet)
542 break
543 except exc.NeutronClientException as e:
544 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
545 if not is_overlapping_cidr:
546 raise
547 self.assertIsNotNone(result, 'Unable to allocate tenant network')
548 subnet = net_resources.DeletableSubnet(client=self.network_client,
549 **result['subnet'])
550 self.assertEqual(subnet.cidr, str_cidr)
551 self.addCleanup(self.delete_wrapper, subnet.delete)
552 return subnet
553
554 def _create_port(self, network, namestart='port-quotatest'):
555 name = data_utils.rand_name(namestart)
556 _, result = self.network_client.create_port(
557 name=name,
558 network_id=network.id,
559 tenant_id=network.tenant_id)
560 self.assertIsNotNone(result, 'Unable to allocate port')
561 port = net_resources.DeletablePort(client=self.network_client,
562 **result['port'])
563 self.addCleanup(self.delete_wrapper, port.delete)
564 return port
565
566 def _get_server_port_id(self, server, ip_addr=None):
567 ports = self._list_ports(device_id=server['id'],
568 fixed_ip=ip_addr)
569 self.assertEqual(len(ports), 1,
570 "Unable to determine which port to target.")
571 return ports[0]['id']
572
573 def _create_floating_ip(self, thing, external_network_id, port_id=None):
574 if not port_id:
575 port_id = self._get_server_port_id(thing)
576 _, result = self.network_client.create_floatingip(
577 floating_network_id=external_network_id,
578 port_id=port_id,
579 tenant_id=thing['tenant_id']
580 )
581 floating_ip = net_resources.DeletableFloatingIp(
582 client=self.network_client,
583 **result['floatingip'])
584 self.addCleanup(self.delete_wrapper, floating_ip.delete)
585 return floating_ip
586
587 def _associate_floating_ip(self, floating_ip, server):
588 port_id = self._get_server_port_id(server)
589 floating_ip.update(port_id=port_id)
590 self.assertEqual(port_id, floating_ip.port_id)
591 return floating_ip
592
593 def _disassociate_floating_ip(self, floating_ip):
594 """
595 :param floating_ip: type DeletableFloatingIp
596 """
597 floating_ip.update(port_id=None)
598 self.assertIsNone(floating_ip.port_id)
599 return floating_ip
600
601 def _ping_ip_address(self, ip_address, should_succeed=True):
602 cmd = ['ping', '-c1', '-w1', ip_address]
603
604 def ping():
605 proc = subprocess.Popen(cmd,
606 stdout=subprocess.PIPE,
607 stderr=subprocess.PIPE)
608 proc.wait()
609 return (proc.returncode == 0) == should_succeed
610
611 return tempest.test.call_until_true(
612 ping, CONF.compute.ping_timeout, 1)
613
614 def _check_vm_connectivity(self, ip_address,
615 username=None,
616 private_key=None,
617 should_connect=True):
618 """
619 :param ip_address: server to test against
620 :param username: server's ssh username
621 :param private_key: server's ssh private key to be used
622 :param should_connect: True/False indicates positive/negative test
623 positive - attempt ping and ssh
624 negative - attempt ping and fail if succeed
625
626 :raises: AssertError if the result of the connectivity check does
627 not match the value of the should_connect param
628 """
629 if should_connect:
630 msg = "Timed out waiting for %s to become reachable" % ip_address
631 else:
632 msg = "ip address %s is reachable" % ip_address
633 self.assertTrue(self._ping_ip_address(ip_address,
634 should_succeed=should_connect),
635 msg=msg)
636 if should_connect:
637 # no need to check ssh for negative connectivity
638 self.get_remote_client(ip_address, username, private_key)
639
640 def _check_public_network_connectivity(self, ip_address, username,
641 private_key, should_connect=True,
642 msg=None, servers=None):
643 # The target login is assumed to have been configured for
644 # key-based authentication by cloud-init.
645 LOG.debug('checking network connections to IP %s with user: %s' %
646 (ip_address, username))
647 try:
648 self._check_vm_connectivity(ip_address,
649 username,
650 private_key,
651 should_connect=should_connect)
652 except Exception as e:
653 ex_msg = 'Public network connectivity check failed'
654 if msg:
655 ex_msg += ": " + msg
656 LOG.exception(ex_msg)
657 self._log_console_output(servers)
658 # network debug is called as part of ssh init
659 if not isinstance(e, exceptions.SSHTimeout):
660 debug.log_net_debug()
661 raise
662
663 def _check_tenant_network_connectivity(self, server,
664 username,
665 private_key,
666 should_connect=True,
667 servers_for_debug=None):
668 if not CONF.network.tenant_networks_reachable:
669 msg = 'Tenant networks not configured to be reachable.'
670 LOG.info(msg)
671 return
672 # The target login is assumed to have been configured for
673 # key-based authentication by cloud-init.
674 try:
675 for net_name, ip_addresses in server['networks'].iteritems():
676 for ip_address in ip_addresses:
677 self._check_vm_connectivity(ip_address,
678 username,
679 private_key,
680 should_connect=should_connect)
681 except Exception as e:
682 LOG.exception('Tenant network connectivity check failed')
683 self._log_console_output(servers_for_debug)
684 # network debug is called as part of ssh init
685 if not isinstance(e, exceptions.SSHTimeout):
686 debug.log_net_debug()
687 raise
688
689 def _check_remote_connectivity(self, source, dest, should_succeed=True):
690 """
691 check ping server via source ssh connection
692
693 :param source: RemoteClient: an ssh connection from which to ping
694 :param dest: and IP to ping against
695 :param should_succeed: boolean should ping succeed or not
696 :returns: boolean -- should_succeed == ping
697 :returns: ping is false if ping failed
698 """
699 def ping_remote():
700 try:
701 source.ping_host(dest)
702 except exceptions.SSHExecCommandFailed:
703 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
704 % (dest, source.ssh_client.host))
705 return not should_succeed
706 return should_succeed
707
708 return tempest.test.call_until_true(ping_remote,
709 CONF.compute.ping_timeout,
710 1)
711
712 def _create_security_group(self, tenant_id, client=None,
713 namestart='secgroup-smoke'):
714 if client is None:
715 client = self.network_client
716 secgroup = self._create_empty_security_group(namestart=namestart,
717 client=client,
718 tenant_id=tenant_id)
719
720 # Add rules to the security group
721 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
722 for rule in rules:
723 self.assertEqual(tenant_id, rule.tenant_id)
724 self.assertEqual(secgroup.id, rule.security_group_id)
725 return secgroup
726
727 def _create_empty_security_group(self, tenant_id, client=None,
728 namestart='secgroup-smoke'):
729 """Create a security group without rules.
730
731 Default rules will be created:
732 - IPv4 egress to any
733 - IPv6 egress to any
734
735 :param tenant_id: secgroup will be created in this tenant
736 :returns: DeletableSecurityGroup -- containing the secgroup created
737 """
738 if client is None:
739 client = self.network_client
740 sg_name = data_utils.rand_name(namestart)
741 sg_desc = sg_name + " description"
742 sg_dict = dict(name=sg_name,
743 description=sg_desc)
744 sg_dict['tenant_id'] = tenant_id
745 _, result = client.create_security_group(**sg_dict)
746 secgroup = net_resources.DeletableSecurityGroup(
747 client=client,
748 **result['security_group']
749 )
750 self.assertEqual(secgroup.name, sg_name)
751 self.assertEqual(tenant_id, secgroup.tenant_id)
752 self.assertEqual(secgroup.description, sg_desc)
753 self.addCleanup(self.delete_wrapper, secgroup.delete)
754 return secgroup
755
756 def _default_security_group(self, tenant_id, client=None):
757 """Get default secgroup for given tenant_id.
758
759 :returns: DeletableSecurityGroup -- default secgroup for given tenant
760 """
761 if client is None:
762 client = self.network_client
763 sgs = [
764 sg for sg in client.list_security_groups().values()[0]
765 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
766 ]
767 msg = "No default security group for tenant %s." % (tenant_id)
768 self.assertTrue(len(sgs) > 0, msg)
769 if len(sgs) > 1:
770 msg = "Found %d default security groups" % len(sgs)
771 raise exc.NeutronClientNoUniqueMatch(msg=msg)
772 return net_resources.DeletableSecurityGroup(client=client,
773 **sgs[0])
774
775 def _create_security_group_rule(self, client=None, secgroup=None,
776 tenant_id=None, **kwargs):
777 """Create a rule from a dictionary of rule parameters.
778
779 Create a rule in a secgroup. if secgroup not defined will search for
780 default secgroup in tenant_id.
781
782 :param secgroup: type DeletableSecurityGroup.
783 :param secgroup_id: search for secgroup by id
784 default -- choose default secgroup for given tenant_id
785 :param tenant_id: if secgroup not passed -- the tenant in which to
786 search for default secgroup
787 :param kwargs: a dictionary containing rule parameters:
788 for example, to allow incoming ssh:
789 rule = {
790 direction: 'ingress'
791 protocol:'tcp',
792 port_range_min: 22,
793 port_range_max: 22
794 }
795 """
796 if client is None:
797 client = self.network_client
798 if secgroup is None:
799 secgroup = self._default_security_group(tenant_id)
800
801 ruleset = dict(security_group_id=secgroup.id,
802 tenant_id=secgroup.tenant_id)
803 ruleset.update(kwargs)
804
805 _, sg_rule = client.create_security_group_rule(**ruleset)
806 sg_rule = net_resources.DeletableSecurityGroupRule(
807 client=client,
808 **sg_rule['security_group_rule']
809 )
810 self.addCleanup(self.delete_wrapper, sg_rule.delete)
811 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
812 self.assertEqual(secgroup.id, sg_rule.security_group_id)
813
814 return sg_rule
815
816 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
817 """These rules are intended to permit inbound ssh and icmp
818 traffic from all sources, so no group_id is provided.
819 Setting a group_id would only permit traffic from ports
820 belonging to the same security group.
821 """
822
823 if client is None:
824 client = self.network_client
825 rules = []
826 rulesets = [
827 dict(
828 # ssh
829 protocol='tcp',
830 port_range_min=22,
831 port_range_max=22,
832 ),
833 dict(
834 # ping
835 protocol='icmp',
836 )
837 ]
838 for ruleset in rulesets:
839 for r_direction in ['ingress', 'egress']:
840 ruleset['direction'] = r_direction
841 try:
842 sg_rule = self._create_security_group_rule(
843 client=client, secgroup=secgroup, **ruleset)
844 except exceptions.Conflict as ex:
845 # if rule already exist - skip rule and continue
846 msg = 'Security group rule already exists'
847 if msg not in ex._error_string:
848 raise ex
849 else:
850 self.assertEqual(r_direction, sg_rule.direction)
851 rules.append(sg_rule)
852
853 return rules
854
855 def _ssh_to_server(self, server, private_key):
856 ssh_login = CONF.compute.image_ssh_user
857 return self.get_remote_client(server,
858 username=ssh_login,
859 private_key=private_key)
860
861 def _get_router(self, tenant_id):
862 """Retrieve a router for the given tenant id.
863
864 If a public router has been configured, it will be returned.
865
866 If a public router has not been configured, but a public
867 network has, a tenant router will be created and returned that
868 routes traffic to the public network.
869 """
870 router_id = CONF.network.public_router_id
871 network_id = CONF.network.public_network_id
872 if router_id:
873 result = self.network_client.show_router(router_id)
874 return net_resources.AttributeDict(**result['router'])
875 elif network_id:
876 router = self._create_router(tenant_id)
877 router.set_gateway(network_id)
878 return router
879 else:
880 raise Exception("Neither of 'public_router_id' or "
881 "'public_network_id' has been defined.")
882
883 def _create_router(self, tenant_id, namestart='router-smoke-'):
884 name = data_utils.rand_name(namestart)
885 _, result = self.network_client.create_router(name=name,
886 admin_state_up=True,
887 tenant_id=tenant_id, )
888 router = net_resources.DeletableRouter(client=self.network_client,
889 **result['router'])
890 self.assertEqual(router.name, name)
891 self.addCleanup(self.delete_wrapper, router.delete)
892 return router
893
894 def _create_networks(self, tenant_id=None):
895 """Create a network with a subnet connected to a router.
896
897 :returns: network, subnet, router
898 """
899 if tenant_id is None:
900 tenant_id = self.tenant_id
901 network = self._create_network(tenant_id)
902 router = self._get_router(tenant_id)
903 subnet = self._create_subnet(network)
904 subnet.add_to_router(router.id)
905 return network, subnet, router
906
907
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400908class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400909 """
910 Official Client test base class for scenario testing.
911
912 Official Client tests are tests that have the following characteristics:
913
914 * Test basic operations of an API, typically in an order that
915 a regular user would perform those operations
916 * Test only the correct inputs and action paths -- no fuzz or
917 random input data is sent, only valid inputs.
918 * Use only the default client tool for calling an API
919 """
920
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400921 @classmethod
922 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200923 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -0400924 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -0500925 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500926 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200927
Andrea Frittolif9cde7e2014-02-18 09:57:04 +0000928 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000929 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400930 cls.compute_client = cls.manager.compute_client
931 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -0700932 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400933 cls.identity_client = cls.manager.identity_client
934 cls.network_client = cls.manager.network_client
935 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000936 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200937 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +0400938 cls.data_processing_client = cls.manager.data_processing_client
Artur Svechnikovc3bf9252014-05-05 16:37:37 +0400939 cls.ceilometer_client = cls.manager.ceilometer_client
Sean Dague6dbc6da2013-05-08 17:49:46 -0400940
941 @classmethod
Miguel Lavalleda5f7082014-07-16 19:18:22 -0500942 def tearDownClass(cls):
943 cls.isolated_creds.clear_isolated_creds()
944 super(OfficialClientTest, cls).tearDownClass()
945
946 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000947 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +0000948 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000949 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +0200950 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000951 creds = auth.get_default_credentials(ctype)
952 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200953
954 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +0200955 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000956 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
957 'user')
Yair Frieda71cc442013-12-18 13:32:36 +0200958
959 @classmethod
960 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000961 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
962 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +0200963
964 @classmethod
965 def admin_credentials(cls):
966 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000967 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +0200968
Matthew Treinishb7144eb2013-12-13 22:57:35 +0000969 def setUp(self):
970 super(OfficialClientTest, self).setUp()
971 self.cleanup_waits = []
972 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
973 # because scenario tests in the same test class should not share
974 # resources. If resources were shared between test cases then it
975 # should be a single scenario test instead of multiples.
Yair Friedbf2e2c42014-01-28 12:06:38 +0200976
Matthew Treinishb7144eb2013-12-13 22:57:35 +0000977 # NOTE(yfried): this list is cleaned at the end of test_methods and
978 # not at the end of the class
979 self.addCleanup(self._wait_for_cleanups)
980
981 @staticmethod
982 def not_found_exception(exception):
983 """
984 @return: True if exception is of NotFound type
985 """
986 NOT_FOUND_LIST = ['NotFound', 'HTTPNotFound']
987 return (exception.__class__.__name__ in NOT_FOUND_LIST
988 or
989 hasattr(exception, 'status_code') and
990 exception.status_code == 404)
991
992 def delete_wrapper(self, thing):
993 """Ignores NotFound exceptions for delete operations.
994
995 @param thing: object with delete() method.
996 OpenStack resources are assumed to have a delete() method which
997 destroys the resource
998 """
999
Yair Friedbf2e2c42014-01-28 12:06:38 +02001000 try:
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001001 thing.delete()
Yair Friedbf2e2c42014-01-28 12:06:38 +02001002 except Exception as e:
1003 # If the resource is already missing, mission accomplished.
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001004 if not self.not_found_exception(e):
Yair Friedbf2e2c42014-01-28 12:06:38 +02001005 raise
Yair Friedbf2e2c42014-01-28 12:06:38 +02001006
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001007 def _wait_for_cleanups(self):
1008 """To handle async delete actions, a list of waits is added
1009 which will be iterated over as the last step of clearing the
1010 cleanup queue. That way all the delete calls are made up front
1011 and the tests won't succeed unless the deletes are eventually
1012 successful. This is the same basic approach used in the api tests to
1013 limit cleanup execution time except here it is multi-resource,
1014 because of the nature of the scenario tests.
1015 """
1016 for wait in self.cleanup_waits:
1017 self.delete_timeout(**wait)
Yair Friedbf2e2c42014-01-28 12:06:38 +02001018
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001019 def addCleanup_with_wait(self, things, thing_id,
1020 error_status='ERROR',
1021 exc_type=nova_exceptions.NotFound,
Ghanshyam2a180b82014-06-16 13:54:22 +09001022 cleanup_callable=None, cleanup_args=None,
1023 cleanup_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001024 """Adds wait for ansyc resource deletion at the end of cleanups
Sean Dague6dbc6da2013-05-08 17:49:46 -04001025
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001026 @param things: type of the resource to delete
1027 @param thing_id:
1028 @param error_status: see manager.delete_timeout()
1029 @param exc_type: see manager.delete_timeout()
1030 @param cleanup_callable: method to load pass to self.addCleanup with
1031 the following *cleanup_args, **cleanup_kwargs.
1032 usually a delete method. if not used, will try to use:
1033 things.delete(thing_id)
1034 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001035 if cleanup_args is None:
1036 cleanup_args = []
1037 if cleanup_kwargs is None:
1038 cleanup_kwargs = {}
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001039 if cleanup_callable is None:
1040 LOG.debug("no delete method passed. using {rclass}.delete({id}) as"
1041 " default".format(rclass=things, id=thing_id))
1042 self.addCleanup(things.delete, thing_id)
1043 else:
1044 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
1045 wait_dict = {
1046 'things': things,
1047 'thing_id': thing_id,
1048 'error_status': error_status,
1049 'not_found_exception': exc_type,
1050 }
1051 self.cleanup_waits.append(wait_dict)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001052
Steve Bakerefde7612013-09-30 11:29:23 +13001053 def status_timeout(self, things, thing_id, expected_status,
1054 error_status='ERROR',
1055 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001056 """
1057 Given a thing and an expected status, do a loop, sleeping
1058 for a configurable amount of time, checking for the
1059 expected status to show. At any time, if the returned
1060 status of the thing is ERROR, fail out.
1061 """
Steve Bakerefde7612013-09-30 11:29:23 +13001062 self._status_timeout(things, thing_id,
1063 expected_status=expected_status,
1064 error_status=error_status,
1065 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001066
Steve Bakerefde7612013-09-30 11:29:23 +13001067 def delete_timeout(self, things, thing_id,
1068 error_status='ERROR',
1069 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001070 """
1071 Given a thing, do a loop, sleeping
1072 for a configurable amount of time, checking for the
1073 deleted status to show. At any time, if the returned
1074 status of the thing is ERROR, fail out.
1075 """
1076 self._status_timeout(things,
1077 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +13001078 allow_notfound=True,
1079 error_status=error_status,
1080 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001081
1082 def _status_timeout(self,
1083 things,
1084 thing_id,
1085 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +13001086 allow_notfound=False,
1087 error_status='ERROR',
1088 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001089
1090 log_status = expected_status if expected_status else ''
1091 if allow_notfound:
1092 log_status += ' or NotFound' if log_status != '' else 'NotFound'
1093
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001094 def check_status():
1095 # python-novaclient has resources available to its client
1096 # that all implement a get() method taking an identifier
1097 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001098 try:
1099 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +13001100 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001101 if allow_notfound:
1102 return True
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001103 raise
1104 except Exception as e:
1105 if allow_notfound and self.not_found_exception(e):
1106 return True
1107 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001108
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001109 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -05001110
1111 # Some components are reporting error status in lower case
1112 # so case sensitive comparisons can really mess things
1113 # up.
1114 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +09001115 message = ("%s failed to get to expected status (%s). "
1116 "In %s state.") % (thing, expected_status,
1117 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +09001118 raise exceptions.BuildErrorException(message,
1119 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001120 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001121 return True # All good.
1122 LOG.debug("Waiting for %s to get to %s status. "
1123 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001124 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001125 if not tempest.test.call_until_true(
1126 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +00001127 CONF.compute.build_timeout,
1128 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +09001129 message = ("Timed out waiting for thing %s "
1130 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +02001131 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -04001132
Yair Friedeb69f3f2013-10-10 13:18:16 +03001133 def _create_loginable_secgroup_rule_nova(self, client=None,
1134 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001135 if client is None:
1136 client = self.compute_client
1137 if secgroup_id is None:
1138 sgs = client.security_groups.list()
1139 for sg in sgs:
1140 if sg.name == 'default':
1141 secgroup_id = sg.id
1142
1143 # These rules are intended to permit inbound ssh and icmp
1144 # traffic from all sources, so no group_id is provided.
1145 # Setting a group_id would only permit traffic from ports
1146 # belonging to the same security group.
1147 rulesets = [
1148 {
1149 # ssh
1150 'ip_protocol': 'tcp',
1151 'from_port': 22,
1152 'to_port': 22,
1153 'cidr': '0.0.0.0/0',
1154 },
1155 {
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001156 # ssh -6
1157 'ip_protocol': 'tcp',
1158 'from_port': 22,
1159 'to_port': 22,
1160 'cidr': '::/0',
1161 },
1162 {
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001163 # ping
1164 'ip_protocol': 'icmp',
1165 'from_port': -1,
1166 'to_port': -1,
1167 'cidr': '0.0.0.0/0',
Kirill Shileev2f9111d2014-08-21 14:32:57 +04001168 },
1169 {
1170 # ping6
1171 'ip_protocol': 'icmp',
1172 'from_port': -1,
1173 'to_port': -1,
1174 'cidr': '::/0',
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001175 }
1176 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +03001177 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001178 for ruleset in rulesets:
1179 sg_rule = client.security_group_rules.create(secgroup_id,
1180 **ruleset)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001181 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03001182 rules.append(sg_rule)
1183 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +09001184
Grishkin0f1e11c2014-05-04 20:44:52 +04001185 def _create_security_group_nova(self, client=None,
1186 namestart='secgroup-smoke-'):
1187 if client is None:
1188 client = self.compute_client
1189 # Create security group
1190 sg_name = data_utils.rand_name(namestart)
1191 sg_desc = sg_name + " description"
1192 secgroup = client.security_groups.create(sg_name, sg_desc)
1193 self.assertEqual(secgroup.name, sg_name)
1194 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001195 self.addCleanup(self.delete_wrapper, secgroup)
Grishkin0f1e11c2014-05-04 20:44:52 +04001196
1197 # Add rules to the security group
1198 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
1199
1200 return secgroup
1201
David Shrewsbury02719362014-05-20 14:10:03 -04001202 def rebuild_server(self, server, client=None, image=None,
1203 preserve_ephemeral=False, wait=True,
1204 rebuild_kwargs=None):
1205 if client is None:
1206 client = self.compute_client
1207 if image is None:
1208 image = CONF.compute.image_ref
1209 rebuild_kwargs = rebuild_kwargs or {}
1210
1211 LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
1212 server.name, image, preserve_ephemeral)
1213 server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
1214 **rebuild_kwargs)
1215 if wait:
1216 self.status_timeout(client.servers, server.id, 'ACTIVE')
1217
Giulio Fidente61cadca2013-09-24 18:33:37 +02001218 def create_server(self, client=None, name=None, image=None, flavor=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001219 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +09001220 create_kwargs=None):
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001221 """Creates VM instance.
1222
1223 @param client: compute client to create the instance
1224 @param image: image from which to create the instance
1225 @param wait_on_boot: wait for status ACTIVE before continue
1226 @param wait_on_delete: force synchronous delete on cleanup
1227 @param create_kwargs: additional details for instance creation
1228 @return: client.server object
1229 """
Giulio Fidente61cadca2013-09-24 18:33:37 +02001230 if client is None:
1231 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001232 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001233 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001234 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001235 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001236 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001237 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +09001238 if create_kwargs is None:
1239 create_kwargs = {}
JordanP9c052aa2014-01-24 13:05:00 +00001240
1241 fixed_network_name = CONF.compute.fixed_network_name
1242 if 'nics' not in create_kwargs and fixed_network_name:
1243 networks = client.networks.list()
1244 # If several networks found, set the NetID on which to connect the
1245 # server to avoid the following error "Multiple possible networks
1246 # found, use a Network ID to be more specific."
1247 # See Tempest #1250866
1248 if len(networks) > 1:
1249 for network in networks:
1250 if network.label == fixed_network_name:
1251 create_kwargs['nics'] = [{'net-id': network.id}]
1252 break
1253 # If we didn't find the network we were looking for :
1254 else:
1255 msg = ("The network on which the NIC of the server must "
1256 "be connected can not be found : "
1257 "fixed_network_name=%s. Starting instance without "
1258 "specifying a network.") % fixed_network_name
1259 LOG.info(msg)
1260
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001261 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
1262 name, image, flavor)
1263 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +02001264 self.assertEqual(server.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001265 if wait_on_delete:
1266 self.addCleanup(self.delete_timeout,
1267 self.compute_client.servers,
1268 server.id)
1269 self.addCleanup_with_wait(self.compute_client.servers, server.id,
1270 cleanup_callable=self.delete_wrapper,
1271 cleanup_args=[server])
1272 if wait_on_boot:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001273 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001274 # The instance retrieved on creation is missing network
1275 # details, necessitating retrieval after it becomes active to
1276 # ensure correct details.
1277 server = client.servers.get(server.id)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +09001278 LOG.debug("Created server: %s", server)
1279 return server
1280
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001281 def create_volume(self, client=None, size=1, name=None,
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001282 snapshot_id=None, imageRef=None, volume_type=None,
1283 wait_on_delete=True):
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001284 if client is None:
1285 client = self.volume_client
1286 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001287 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -07001288 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001289 volume = client.volumes.create(size=size, display_name=name,
1290 snapshot_id=snapshot_id,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001291 imageRef=imageRef,
1292 volume_type=volume_type)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001293 if wait_on_delete:
1294 self.addCleanup(self.delete_timeout,
1295 self.volume_client.volumes,
1296 volume.id)
1297 self.addCleanup_with_wait(self.volume_client.volumes, volume.id,
1298 exc_type=cinder_exceptions.NotFound)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +09001299 self.assertEqual(name, volume.display_name)
1300 self.status_timeout(client.volumes, volume.id, 'available')
1301 LOG.debug("Created volume: %s", volume)
1302 return volume
1303
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001304 def create_server_snapshot(self, server, compute_client=None,
1305 image_client=None, name=None):
1306 if compute_client is None:
1307 compute_client = self.compute_client
1308 if image_client is None:
1309 image_client = self.image_client
1310 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001311 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001312 LOG.debug("Creating a snapshot image for server: %s", server.name)
1313 image_id = compute_client.servers.create_image(server, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001314 self.addCleanup_with_wait(self.image_client.images, image_id,
1315 exc_type=glanceclient.exc.HTTPNotFound)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001316 self.status_timeout(image_client.images, image_id, 'active')
1317 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -07001318 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +09001319 LOG.debug("Created snapshot image %s for server %s",
1320 snapshot_image.name, server.name)
1321 return snapshot_image
1322
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001323 def create_keypair(self, client=None, name=None):
1324 if client is None:
1325 client = self.compute_client
1326 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +09001327 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001328 keypair = client.keypairs.create(name)
1329 self.assertEqual(keypair.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001330 self.addCleanup(self.delete_wrapper, keypair)
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +09001331 return keypair
1332
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001333 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +08001334 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001335 ip = server_or_ip
1336 else:
Matthew Treinish6c072292014-01-29 19:15:52 +00001337 network_name_for_ssh = CONF.compute.network_for_ssh
Adam Gandelman0d9508e2014-08-22 10:58:09 -07001338 ip = server_or_ip.networks[network_name_for_ssh][0]
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001339 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +00001340 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001341 if private_key is None:
1342 private_key = self.keypair.private_key
Yair Fried3960c4d2014-05-07 15:20:30 +03001343 linux_client = remote_client.RemoteClient(ip, username,
1344 pkey=private_key)
1345 try:
1346 linux_client.validate_authentication()
1347 except exceptions.SSHTimeout:
1348 LOG.exception('ssh connection to %s failed' % ip)
1349 debug.log_net_debug()
1350 raise
1351
1352 return linux_client
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +09001353
Nachi Ueno95b41282014-01-15 06:54:21 -08001354 def _log_console_output(self, servers=None):
Adam Gandelmanc6eefb42014-07-15 16:44:08 -07001355 if not CONF.compute_feature_enabled.console_output:
1356 LOG.debug('Console output not supported, cannot log')
1357 return
Nachi Ueno95b41282014-01-15 06:54:21 -08001358 if not servers:
1359 servers = self.compute_client.servers.list()
1360 for server in servers:
1361 LOG.debug('Console output for %s', server.id)
1362 LOG.debug(server.get_console_output())
1363
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001364 def wait_for_volume_status(self, status):
1365 volume_id = self.volume.id
1366 self.status_timeout(
1367 self.volume_client.volumes, volume_id, status)
1368
Ghanshyam2a180b82014-06-16 13:54:22 +09001369 def _image_create(self, name, fmt, path, properties=None):
1370 if properties is None:
1371 properties = {}
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001372 name = data_utils.rand_name('%s-' % name)
1373 image_file = open(path, 'rb')
1374 self.addCleanup(image_file.close)
1375 params = {
1376 'name': name,
1377 'container_format': fmt,
1378 'disk_format': fmt,
Aaron Rosenc7720622014-05-20 10:38:10 -07001379 'is_public': 'False',
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001380 }
1381 params.update(properties)
1382 image = self.image_client.images.create(**params)
1383 self.addCleanup(self.image_client.images.delete, image)
1384 self.assertEqual("queued", image.status)
1385 image.update(data=image_file)
1386 return image.id
1387
1388 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001389 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001390 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
1391 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
1392 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001393 img_container_format = CONF.scenario.img_container_format
1394 img_disk_format = CONF.scenario.img_disk_format
1395 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
1396 "ami: %s, ari: %s, aki: %s" %
1397 (img_path, img_container_format, img_disk_format,
1398 ami_img_path, ari_img_path, aki_img_path))
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001399 try:
1400 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001401 img_container_format,
1402 img_path,
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001403 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +03001404 img_disk_format})
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001405 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +09001406 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +09001407 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
1408 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
1409 properties = {
1410 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
1411 }
1412 self.image = self._image_create('scenario-ami', 'ami',
1413 path=ami_img_path,
1414 properties=properties)
1415 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +09001416
Sean Dague6dbc6da2013-05-08 17:49:46 -04001417
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001418# power/provision states as of icehouse
1419class BaremetalPowerStates(object):
1420 """Possible power states of an Ironic node."""
1421 POWER_ON = 'power on'
1422 POWER_OFF = 'power off'
1423 REBOOT = 'rebooting'
1424 SUSPEND = 'suspended'
1425
1426
1427class BaremetalProvisionStates(object):
1428 """Possible provision states of an Ironic node."""
1429 NOSTATE = None
1430 INIT = 'initializing'
1431 ACTIVE = 'active'
1432 BUILDING = 'building'
1433 DEPLOYWAIT = 'wait call-back'
1434 DEPLOYING = 'deploying'
1435 DEPLOYFAIL = 'deploy failed'
1436 DEPLOYDONE = 'deploy complete'
1437 DELETING = 'deleting'
1438 DELETED = 'deleted'
1439 ERROR = 'error'
1440
1441
Adam Gandelman4a48a602014-03-20 18:23:18 -07001442class BaremetalScenarioTest(OfficialClientTest):
1443 @classmethod
1444 def setUpClass(cls):
1445 super(BaremetalScenarioTest, cls).setUpClass()
1446
1447 if (not CONF.service_available.ironic or
1448 not CONF.baremetal.driver_enabled):
1449 msg = 'Ironic not available or Ironic compute driver not enabled'
1450 raise cls.skipException(msg)
1451
1452 # use an admin client manager for baremetal client
Adam Gandelmanacc13e62014-05-08 11:12:47 -07001453 admin_creds = cls.admin_credentials()
1454 manager = clients.OfficialClientManager(credentials=admin_creds)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001455 cls.baremetal_client = manager.baremetal_client
1456
1457 # allow any issues obtaining the node list to raise early
1458 cls.baremetal_client.node.list()
1459
1460 def _node_state_timeout(self, node_id, state_attr,
1461 target_states, timeout=10, interval=1):
1462 if not isinstance(target_states, list):
1463 target_states = [target_states]
1464
1465 def check_state():
1466 node = self.get_node(node_id=node_id)
1467 if getattr(node, state_attr) in target_states:
1468 return True
1469 return False
1470
1471 if not tempest.test.call_until_true(
1472 check_state, timeout, interval):
1473 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1474 (node_id, state_attr, target_states))
1475 raise exceptions.TimeoutException(msg)
1476
1477 def wait_provisioning_state(self, node_id, state, timeout):
1478 self._node_state_timeout(
1479 node_id=node_id, state_attr='provision_state',
1480 target_states=state, timeout=timeout)
1481
1482 def wait_power_state(self, node_id, state):
1483 self._node_state_timeout(
1484 node_id=node_id, state_attr='power_state',
1485 target_states=state, timeout=CONF.baremetal.power_timeout)
1486
1487 def wait_node(self, instance_id):
1488 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001489 from ironicclient import exc as ironic_exceptions
1490
Adam Gandelman4a48a602014-03-20 18:23:18 -07001491 def _get_node():
1492 node = None
1493 try:
1494 node = self.get_node(instance_id=instance_id)
1495 except ironic_exceptions.HTTPNotFound:
1496 pass
1497 return node is not None
1498
1499 if not tempest.test.call_until_true(
1500 _get_node, CONF.baremetal.association_timeout, 1):
1501 msg = ('Timed out waiting to get Ironic node by instance id %s'
1502 % instance_id)
1503 raise exceptions.TimeoutException(msg)
1504
1505 def get_node(self, node_id=None, instance_id=None):
1506 if node_id:
1507 return self.baremetal_client.node.get(node_id)
1508 elif instance_id:
1509 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
1510
1511 def get_ports(self, node_id):
1512 ports = []
1513 for port in self.baremetal_client.node.list_ports(node_id):
1514 ports.append(self.baremetal_client.port.get(port.uuid))
1515 return ports
1516
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001517 def add_keypair(self):
1518 self.keypair = self.create_keypair()
1519
1520 def verify_connectivity(self, ip=None):
1521 if ip:
1522 dest = self.get_remote_client(ip)
1523 else:
1524 dest = self.get_remote_client(self.instance)
1525 dest.validate_authentication()
1526
1527 def boot_instance(self):
1528 create_kwargs = {
1529 'key_name': self.keypair.id
1530 }
1531 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001532 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001533
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001534 self.addCleanup_with_wait(self.compute_client.servers,
1535 self.instance.id,
1536 cleanup_callable=self.delete_wrapper,
1537 cleanup_args=[self.instance])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001538
1539 self.wait_node(self.instance.id)
1540 self.node = self.get_node(instance_id=self.instance.id)
1541
1542 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_ON)
1543
1544 self.wait_provisioning_state(
1545 self.node.uuid,
1546 [BaremetalProvisionStates.DEPLOYWAIT,
1547 BaremetalProvisionStates.ACTIVE],
1548 timeout=15)
1549
1550 self.wait_provisioning_state(self.node.uuid,
1551 BaremetalProvisionStates.ACTIVE,
1552 timeout=CONF.baremetal.active_timeout)
1553
1554 self.status_timeout(
1555 self.compute_client.servers, self.instance.id, 'ACTIVE')
1556
1557 self.node = self.get_node(instance_id=self.instance.id)
1558 self.instance = self.compute_client.servers.get(self.instance.id)
1559
1560 def terminate_instance(self):
1561 self.instance.delete()
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001562 self.wait_power_state(self.node.uuid, BaremetalPowerStates.POWER_OFF)
1563 self.wait_provisioning_state(
1564 self.node.uuid,
1565 BaremetalProvisionStates.NOSTATE,
1566 timeout=CONF.baremetal.unprovision_timeout)
1567
Adam Gandelman4a48a602014-03-20 18:23:18 -07001568
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001569class EncryptionScenarioTest(OfficialClientTest):
1570 """
1571 Base class for encryption scenario tests
1572 """
1573
1574 @classmethod
1575 def setUpClass(cls):
1576 super(EncryptionScenarioTest, cls).setUpClass()
1577
1578 # use admin credentials to create encrypted volume types
1579 admin_creds = cls.admin_credentials()
1580 manager = clients.OfficialClientManager(credentials=admin_creds)
1581 cls.admin_volume_client = manager.volume_client
1582
1583 def _wait_for_volume_status(self, status):
1584 self.status_timeout(
1585 self.volume_client.volumes, self.volume.id, status)
1586
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001587 def nova_boot(self):
1588 self.keypair = self.create_keypair()
1589 create_kwargs = {'key_name': self.keypair.name}
1590 self.server = self.create_server(self.compute_client,
1591 image=self.image,
1592 create_kwargs=create_kwargs)
1593
1594 def create_volume_type(self, client=None, name=None):
1595 if not client:
1596 client = self.admin_volume_client
1597 if not name:
1598 name = 'generic'
1599 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1600 LOG.debug("Creating a volume type: %s", randomized_name)
1601 volume_type = client.volume_types.create(randomized_name)
1602 self.addCleanup(client.volume_types.delete, volume_type.id)
1603 return volume_type
1604
1605 def create_encryption_type(self, client=None, type_id=None, provider=None,
1606 key_size=None, cipher=None,
1607 control_location=None):
1608 if not client:
1609 client = self.admin_volume_client
1610 if not type_id:
1611 volume_type = self.create_volume_type()
1612 type_id = volume_type.id
1613 LOG.debug("Creating an encryption type for volume type: %s", type_id)
1614 client.volume_encryption_types.create(type_id,
1615 {'provider': provider,
1616 'key_size': key_size,
1617 'cipher': cipher,
1618 'control_location':
1619 control_location})
1620
1621 def nova_volume_attach(self):
1622 attach_volume_client = self.compute_client.volumes.create_server_volume
1623 volume = attach_volume_client(self.server.id,
1624 self.volume.id,
1625 '/dev/vdb')
1626 self.assertEqual(self.volume.id, volume.id)
1627 self._wait_for_volume_status('in-use')
1628
1629 def nova_volume_detach(self):
1630 detach_volume_client = self.compute_client.volumes.delete_server_volume
1631 detach_volume_client(self.server.id, self.volume.id)
1632 self._wait_for_volume_status('available')
1633
1634 volume = self.volume_client.volumes.get(self.volume.id)
1635 self.assertEqual('available', volume.status)
1636
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001637
Sean Dague6dbc6da2013-05-08 17:49:46 -04001638class NetworkScenarioTest(OfficialClientTest):
1639 """
1640 Base class for network scenario tests
1641 """
1642
1643 @classmethod
1644 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001645 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001646 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +02001647 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -04001648 try:
1649 cls.network_client.list_networks()
1650 except exc.EndpointNotFound:
1651 cls.enabled = False
1652 raise
1653 else:
1654 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -04001655 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -04001656 raise cls.skipException(msg)
1657
1658 @classmethod
1659 def setUpClass(cls):
1660 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001661 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -04001662
Sean Dague6dbc6da2013-05-08 17:49:46 -04001663 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001664 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001665 body = dict(
1666 network=dict(
1667 name=name,
1668 tenant_id=tenant_id,
1669 ),
1670 )
1671 result = self.network_client.create_network(body=body)
1672 network = net_common.DeletableNetwork(client=self.network_client,
1673 **result['network'])
1674 self.assertEqual(network.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001675 self.addCleanup(self.delete_wrapper, network)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001676 return network
1677
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001678 def _list_networks(self, **kwargs):
1679 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001680 return nets['networks']
1681
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001682 def _list_subnets(self, **kwargs):
1683 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001684 return subnets['subnets']
1685
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001686 def _list_routers(self, **kwargs):
1687 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001688 return routers['routers']
1689
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001690 def _list_ports(self, **kwargs):
1691 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001692 return ports['ports']
1693
1694 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001695 nets = self._list_networks(tenant_id=tenant_id)
1696 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001697
1698 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001699 subnets = self._list_subnets(tenant_id=tenant_id)
1700 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001701
1702 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001703 ports = self._list_ports(tenant_id=tenant_id)
1704 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00001705
Yair Fried3097dc12014-01-26 08:46:43 +02001706 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001707 """
1708 Create a subnet for the given network within the cidr block
1709 configured for tenant networks.
1710 """
Attila Fazekase857bd62013-10-21 21:02:44 +02001711
1712 def cidr_in_use(cidr, tenant_id):
1713 """
1714 :return True if subnet with cidr already exist in tenant
1715 False else
1716 """
1717 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
1718 return len(cidr_in_use) != 0
1719
Matthew Treinish6c072292014-01-29 19:15:52 +00001720 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001721 result = None
1722 # Repeatedly attempt subnet creation with sequential cidr
1723 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +00001724 for subnet_cidr in tenant_cidr.subnet(
1725 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +02001726 str_cidr = str(subnet_cidr)
1727 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
1728 continue
1729
Sean Dague6dbc6da2013-05-08 17:49:46 -04001730 body = dict(
1731 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +02001732 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -04001733 ip_version=4,
1734 network_id=network.id,
1735 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +02001736 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001737 ),
1738 )
Yair Fried3097dc12014-01-26 08:46:43 +02001739 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001740 try:
1741 result = self.network_client.create_subnet(body=body)
1742 break
Mark McClainf2982e82013-07-06 17:48:03 -04001743 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -04001744 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
1745 if not is_overlapping_cidr:
1746 raise
1747 self.assertIsNotNone(result, 'Unable to allocate tenant network')
1748 subnet = net_common.DeletableSubnet(client=self.network_client,
1749 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +02001750 self.assertEqual(subnet.cidr, str_cidr)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001751 self.addCleanup(self.delete_wrapper, subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001752 return subnet
1753
1754 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001755 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001756 body = dict(
1757 port=dict(name=name,
1758 network_id=network.id,
1759 tenant_id=network.tenant_id))
1760 result = self.network_client.create_port(body=body)
1761 self.assertIsNotNone(result, 'Unable to allocate port')
1762 port = net_common.DeletablePort(client=self.network_client,
1763 **result['port'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001764 self.addCleanup(self.delete_wrapper, port)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001765 return port
1766
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001767 def _get_server_port_id(self, server, ip_addr=None):
1768 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001769 self.assertEqual(len(ports), 1,
1770 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +02001771 return ports[0]['id']
1772
Yair Frieda2e3b2c2014-02-17 10:56:10 +02001773 def _create_floating_ip(self, thing, external_network_id, port_id=None):
1774 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001775 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001776 body = dict(
1777 floatingip=dict(
1778 floating_network_id=external_network_id,
1779 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001780 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -04001781 )
1782 )
1783 result = self.network_client.create_floatingip(body=body)
1784 floating_ip = net_common.DeletableFloatingIp(
1785 client=self.network_client,
1786 **result['floatingip'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001787 self.addCleanup(self.delete_wrapper, floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -04001788 return floating_ip
1789
Yair Fried05db2522013-11-18 11:02:10 +02001790 def _associate_floating_ip(self, floating_ip, server):
1791 port_id = self._get_server_port_id(server)
1792 floating_ip.update(port_id=port_id)
1793 self.assertEqual(port_id, floating_ip.port_id)
1794 return floating_ip
1795
Yair Fried9a551c42013-12-15 14:59:34 +02001796 def _disassociate_floating_ip(self, floating_ip):
1797 """
1798 :param floating_ip: type DeletableFloatingIp
1799 """
1800 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +08001801 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +02001802 return floating_ip
1803
1804 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -04001805 cmd = ['ping', '-c1', '-w1', ip_address]
1806
1807 def ping():
1808 proc = subprocess.Popen(cmd,
1809 stdout=subprocess.PIPE,
1810 stderr=subprocess.PIPE)
1811 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +02001812 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -04001813
Nachi Ueno6d580be2013-07-24 10:58:11 -07001814 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +00001815 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +00001816
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001817 def _create_pool(self, lb_method, protocol, subnet_id):
1818 """Wrapper utility that returns a test pool."""
1819 name = data_utils.rand_name('pool-')
1820 body = {
1821 "pool": {
1822 "protocol": protocol,
1823 "name": name,
1824 "subnet_id": subnet_id,
1825 "lb_method": lb_method
1826 }
1827 }
1828 resp = self.network_client.create_pool(body=body)
1829 pool = net_common.DeletablePool(client=self.network_client,
1830 **resp['pool'])
1831 self.assertEqual(pool['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001832 self.addCleanup(self.delete_wrapper, pool)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001833 return pool
1834
1835 def _create_member(self, address, protocol_port, pool_id):
1836 """Wrapper utility that returns a test member."""
1837 body = {
1838 "member": {
1839 "protocol_port": protocol_port,
1840 "pool_id": pool_id,
1841 "address": address
1842 }
1843 }
1844 resp = self.network_client.create_member(body)
1845 member = net_common.DeletableMember(client=self.network_client,
1846 **resp['member'])
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001847 self.addCleanup(self.delete_wrapper, member)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001848 return member
1849
1850 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
1851 """Wrapper utility that returns a test vip."""
1852 name = data_utils.rand_name('vip-')
1853 body = {
1854 "vip": {
1855 "protocol": protocol,
1856 "name": name,
1857 "subnet_id": subnet_id,
1858 "pool_id": pool_id,
1859 "protocol_port": protocol_port
1860 }
1861 }
1862 resp = self.network_client.create_vip(body)
1863 vip = net_common.DeletableVip(client=self.network_client,
1864 **resp['vip'])
1865 self.assertEqual(vip['name'], name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001866 self.addCleanup(self.delete_wrapper, vip)
Elena Ezhovaa5105e62013-11-26 20:46:52 +04001867 return vip
1868
Yair Fried9a551c42013-12-15 14:59:34 +02001869 def _check_vm_connectivity(self, ip_address,
1870 username=None,
1871 private_key=None,
1872 should_connect=True):
1873 """
1874 :param ip_address: server to test against
1875 :param username: server's ssh username
1876 :param private_key: server's ssh private key to be used
1877 :param should_connect: True/False indicates positive/negative test
1878 positive - attempt ping and ssh
1879 negative - attempt ping and fail if succeed
1880
1881 :raises: AssertError if the result of the connectivity check does
1882 not match the value of the should_connect param
1883 """
1884 if should_connect:
1885 msg = "Timed out waiting for %s to become reachable" % ip_address
1886 else:
1887 msg = "ip address %s is reachable" % ip_address
1888 self.assertTrue(self._ping_ip_address(ip_address,
1889 should_succeed=should_connect),
1890 msg=msg)
1891 if should_connect:
1892 # no need to check ssh for negative connectivity
Yair Fried3960c4d2014-05-07 15:20:30 +03001893 self.get_remote_client(ip_address, username, private_key)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001894
Matt Riedemann343305f2014-05-27 09:55:03 -07001895 def _check_public_network_connectivity(self, ip_address, username,
1896 private_key, should_connect=True,
1897 msg=None, servers=None):
1898 # The target login is assumed to have been configured for
1899 # key-based authentication by cloud-init.
1900 LOG.debug('checking network connections to IP %s with user: %s' %
1901 (ip_address, username))
1902 try:
1903 self._check_vm_connectivity(ip_address,
1904 username,
1905 private_key,
1906 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001907 except Exception as e:
Matt Riedemann343305f2014-05-27 09:55:03 -07001908 ex_msg = 'Public network connectivity check failed'
1909 if msg:
1910 ex_msg += ": " + msg
1911 LOG.exception(ex_msg)
1912 self._log_console_output(servers)
Yair Fried3960c4d2014-05-07 15:20:30 +03001913 # network debug is called as part of ssh init
1914 if not isinstance(e, exceptions.SSHTimeout):
1915 debug.log_net_debug()
Matt Riedemann343305f2014-05-27 09:55:03 -07001916 raise
1917
Matt Riedemann2d005be2014-05-27 10:52:35 -07001918 def _check_tenant_network_connectivity(self, server,
1919 username,
1920 private_key,
1921 should_connect=True,
1922 servers_for_debug=None):
1923 if not CONF.network.tenant_networks_reachable:
1924 msg = 'Tenant networks not configured to be reachable.'
1925 LOG.info(msg)
1926 return
1927 # The target login is assumed to have been configured for
1928 # key-based authentication by cloud-init.
1929 try:
1930 for net_name, ip_addresses in server.networks.iteritems():
1931 for ip_address in ip_addresses:
1932 self._check_vm_connectivity(ip_address,
1933 username,
1934 private_key,
1935 should_connect=should_connect)
Yair Fried3960c4d2014-05-07 15:20:30 +03001936 except Exception as e:
Matt Riedemann2d005be2014-05-27 10:52:35 -07001937 LOG.exception('Tenant network connectivity check failed')
1938 self._log_console_output(servers_for_debug)
Yair Fried3960c4d2014-05-07 15:20:30 +03001939 # network debug is called as part of ssh init
1940 if not isinstance(e, exceptions.SSHTimeout):
1941 debug.log_net_debug()
Matt Riedemann2d005be2014-05-27 10:52:35 -07001942 raise
1943
Yair Fried3097dc12014-01-26 08:46:43 +02001944 def _check_remote_connectivity(self, source, dest, should_succeed=True):
1945 """
1946 check ping server via source ssh connection
1947
1948 :param source: RemoteClient: an ssh connection from which to ping
1949 :param dest: and IP to ping against
1950 :param should_succeed: boolean should ping succeed or not
1951 :returns: boolean -- should_succeed == ping
1952 :returns: ping is false if ping failed
1953 """
1954 def ping_remote():
1955 try:
1956 source.ping_host(dest)
1957 except exceptions.SSHExecCommandFailed:
Matthew Treinishc4131d82014-05-27 11:59:28 -04001958 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
1959 % (dest, source.ssh_client.host))
Yair Fried3097dc12014-01-26 08:46:43 +02001960 return not should_succeed
1961 return should_succeed
1962
1963 return tempest.test.call_until_true(ping_remote,
1964 CONF.compute.ping_timeout,
1965 1)
1966
Yair Friedeb69f3f2013-10-10 13:18:16 +03001967 def _create_security_group_neutron(self, tenant_id, client=None,
1968 namestart='secgroup-smoke-'):
1969 if client is None:
1970 client = self.network_client
1971 secgroup = self._create_empty_security_group(namestart=namestart,
1972 client=client,
1973 tenant_id=tenant_id)
1974
1975 # Add rules to the security group
1976 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
1977 for rule in rules:
1978 self.assertEqual(tenant_id, rule.tenant_id)
1979 self.assertEqual(secgroup.id, rule.security_group_id)
1980 return secgroup
1981
1982 def _create_empty_security_group(self, tenant_id, client=None,
1983 namestart='secgroup-smoke-'):
1984 """Create a security group without rules.
1985
1986 Default rules will be created:
1987 - IPv4 egress to any
1988 - IPv6 egress to any
1989
1990 :param tenant_id: secgroup will be created in this tenant
1991 :returns: DeletableSecurityGroup -- containing the secgroup created
1992 """
1993 if client is None:
1994 client = self.network_client
1995 sg_name = data_utils.rand_name(namestart)
1996 sg_desc = sg_name + " description"
1997 sg_dict = dict(name=sg_name,
1998 description=sg_desc)
1999 sg_dict['tenant_id'] = tenant_id
2000 body = dict(security_group=sg_dict)
2001 result = client.create_security_group(body=body)
2002 secgroup = net_common.DeletableSecurityGroup(
2003 client=client,
2004 **result['security_group']
2005 )
2006 self.assertEqual(secgroup.name, sg_name)
2007 self.assertEqual(tenant_id, secgroup.tenant_id)
2008 self.assertEqual(secgroup.description, sg_desc)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002009 self.addCleanup(self.delete_wrapper, secgroup)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002010 return secgroup
2011
2012 def _default_security_group(self, tenant_id, client=None):
2013 """Get default secgroup for given tenant_id.
2014
2015 :returns: DeletableSecurityGroup -- default secgroup for given tenant
2016 """
2017 if client is None:
2018 client = self.network_client
2019 sgs = [
2020 sg for sg in client.list_security_groups().values()[0]
2021 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
2022 ]
2023 msg = "No default security group for tenant %s." % (tenant_id)
2024 self.assertTrue(len(sgs) > 0, msg)
2025 if len(sgs) > 1:
2026 msg = "Found %d default security groups" % len(sgs)
2027 raise exc.NeutronClientNoUniqueMatch(msg=msg)
2028 return net_common.DeletableSecurityGroup(client=client,
2029 **sgs[0])
2030
2031 def _create_security_group_rule(self, client=None, secgroup=None,
2032 tenant_id=None, **kwargs):
2033 """Create a rule from a dictionary of rule parameters.
2034
2035 Create a rule in a secgroup. if secgroup not defined will search for
2036 default secgroup in tenant_id.
2037
2038 :param secgroup: type DeletableSecurityGroup.
2039 :param secgroup_id: search for secgroup by id
2040 default -- choose default secgroup for given tenant_id
2041 :param tenant_id: if secgroup not passed -- the tenant in which to
2042 search for default secgroup
2043 :param kwargs: a dictionary containing rule parameters:
2044 for example, to allow incoming ssh:
2045 rule = {
2046 direction: 'ingress'
2047 protocol:'tcp',
2048 port_range_min: 22,
2049 port_range_max: 22
2050 }
2051 """
2052 if client is None:
2053 client = self.network_client
2054 if secgroup is None:
2055 secgroup = self._default_security_group(tenant_id)
2056
2057 ruleset = dict(security_group_id=secgroup.id,
2058 tenant_id=secgroup.tenant_id,
2059 )
2060 ruleset.update(kwargs)
2061
2062 body = dict(security_group_rule=dict(ruleset))
2063 sg_rule = client.create_security_group_rule(body=body)
2064 sg_rule = net_common.DeletableSecurityGroupRule(
2065 client=client,
2066 **sg_rule['security_group_rule']
2067 )
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002068 self.addCleanup(self.delete_wrapper, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +03002069 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
2070 self.assertEqual(secgroup.id, sg_rule.security_group_id)
2071
2072 return sg_rule
2073
2074 def _create_loginable_secgroup_rule_neutron(self, client=None,
2075 secgroup=None):
2076 """These rules are intended to permit inbound ssh and icmp
2077 traffic from all sources, so no group_id is provided.
2078 Setting a group_id would only permit traffic from ports
2079 belonging to the same security group.
2080 """
2081
2082 if client is None:
2083 client = self.network_client
2084 rules = []
2085 rulesets = [
2086 dict(
2087 # ssh
2088 protocol='tcp',
2089 port_range_min=22,
2090 port_range_max=22,
2091 ),
2092 dict(
2093 # ping
2094 protocol='icmp',
2095 )
2096 ]
2097 for ruleset in rulesets:
2098 for r_direction in ['ingress', 'egress']:
2099 ruleset['direction'] = r_direction
2100 try:
2101 sg_rule = self._create_security_group_rule(
2102 client=client, secgroup=secgroup, **ruleset)
2103 except exc.NeutronClientException as ex:
2104 # if rule already exist - skip rule and continue
2105 if not (ex.status_code is 409 and 'Security group rule'
2106 ' already exists' in ex.message):
2107 raise ex
2108 else:
2109 self.assertEqual(r_direction, sg_rule.direction)
2110 rules.append(sg_rule)
2111
2112 return rules
2113
Yair Fried5f670ab2013-12-09 09:26:51 +02002114 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +00002115 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +02002116 return self.get_remote_client(server,
2117 username=ssh_login,
2118 private_key=private_key)
2119
Yuiko Takada7f4b1b32013-11-20 08:06:26 +00002120 def _show_quota_network(self, tenant_id):
2121 quota = self.network_client.show_quota(tenant_id)
2122 return quota['quota']['network']
2123
2124 def _show_quota_subnet(self, tenant_id):
2125 quota = self.network_client.show_quota(tenant_id)
2126 return quota['quota']['subnet']
2127
2128 def _show_quota_port(self, tenant_id):
2129 quota = self.network_client.show_quota(tenant_id)
2130 return quota['quota']['port']
2131
Yair Fried4d7efa62013-11-17 17:12:29 +02002132 def _get_router(self, tenant_id):
2133 """Retrieve a router for the given tenant id.
2134
2135 If a public router has been configured, it will be returned.
2136
2137 If a public router has not been configured, but a public
2138 network has, a tenant router will be created and returned that
2139 routes traffic to the public network.
2140 """
Matthew Treinish6c072292014-01-29 19:15:52 +00002141 router_id = CONF.network.public_router_id
2142 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02002143 if router_id:
2144 result = self.network_client.show_router(router_id)
2145 return net_common.AttributeDict(**result['router'])
2146 elif network_id:
2147 router = self._create_router(tenant_id)
2148 router.add_gateway(network_id)
2149 return router
2150 else:
2151 raise Exception("Neither of 'public_router_id' or "
2152 "'public_network_id' has been defined.")
2153
2154 def _create_router(self, tenant_id, namestart='router-smoke-'):
2155 name = data_utils.rand_name(namestart)
2156 body = dict(
2157 router=dict(
2158 name=name,
2159 admin_state_up=True,
2160 tenant_id=tenant_id,
2161 ),
2162 )
2163 result = self.network_client.create_router(body=body)
2164 router = net_common.DeletableRouter(client=self.network_client,
2165 **result['router'])
2166 self.assertEqual(router.name, name)
Matthew Treinishb7144eb2013-12-13 22:57:35 +00002167 self.addCleanup(self.delete_wrapper, router)
Yair Fried4d7efa62013-11-17 17:12:29 +02002168 return router
2169
2170 def _create_networks(self, tenant_id=None):
2171 """Create a network with a subnet connected to a router.
2172
2173 :returns: network, subnet, router
2174 """
2175 if tenant_id is None:
2176 tenant_id = self.tenant_id
2177 network = self._create_network(tenant_id)
2178 router = self._get_router(tenant_id)
2179 subnet = self._create_subnet(network)
2180 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02002181 return network, subnet, router
2182
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002183
2184class OrchestrationScenarioTest(OfficialClientTest):
2185 """
2186 Base class for orchestration scenario tests
2187 """
2188
2189 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07002190 def setUpClass(cls):
2191 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00002192 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07002193 raise cls.skipException("Heat support is required")
2194
2195 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002196 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00002197 admin_creds = auth.get_default_credentials('identity_admin')
2198 creds = auth.get_default_credentials('user')
2199 admin_creds.tenant_name = creds.tenant_name
2200 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12002201
2202 def _load_template(self, base_file, file_name):
2203 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
2204 file_name)
2205 with open(filepath) as f:
2206 return f.read()
2207
2208 @classmethod
2209 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09002210 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12002211
2212 @classmethod
2213 def _get_default_network(cls):
2214 networks = cls.network_client.list_networks()
2215 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00002216 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12002217 return net
Steve Baker22c16602014-05-05 13:34:19 +12002218
2219 @staticmethod
2220 def _stack_output(stack, output_key):
2221 """Return a stack output value for a given key."""
2222 return next((o['output_value'] for o in stack.outputs
2223 if o['output_key'] == output_key), None)
2224
2225 def _ping_ip_address(self, ip_address, should_succeed=True):
2226 cmd = ['ping', '-c1', '-w1', ip_address]
2227
2228 def ping():
2229 proc = subprocess.Popen(cmd,
2230 stdout=subprocess.PIPE,
2231 stderr=subprocess.PIPE)
2232 proc.wait()
2233 return (proc.returncode == 0) == should_succeed
2234
2235 return tempest.test.call_until_true(
2236 ping, CONF.orchestration.build_timeout, 1)
2237
2238 def _wait_for_resource_status(self, stack_identifier, resource_name,
2239 status, failure_pattern='^.*_FAILED$'):
2240 """Waits for a Resource to reach a given status."""
2241 fail_regexp = re.compile(failure_pattern)
2242 build_timeout = CONF.orchestration.build_timeout
2243 build_interval = CONF.orchestration.build_interval
2244
2245 start = timeutils.utcnow()
2246 while timeutils.delta_seconds(start,
2247 timeutils.utcnow()) < build_timeout:
2248 try:
2249 res = self.client.resources.get(
2250 stack_identifier, resource_name)
2251 except heat_exceptions.HTTPNotFound:
2252 # ignore this, as the resource may not have
2253 # been created yet
2254 pass
2255 else:
2256 if res.resource_status == status:
2257 return
2258 if fail_regexp.search(res.resource_status):
2259 raise exceptions.StackResourceBuildErrorException(
2260 resource_name=res.resource_name,
2261 stack_identifier=stack_identifier,
2262 resource_status=res.resource_status,
2263 resource_status_reason=res.resource_status_reason)
2264 time.sleep(build_interval)
2265
2266 message = ('Resource %s failed to reach %s status within '
2267 'the required time (%s s).' %
2268 (res.resource_name, status, build_timeout))
2269 raise exceptions.TimeoutException(message)
2270
2271 def _wait_for_stack_status(self, stack_identifier, status,
2272 failure_pattern='^.*_FAILED$'):
2273 """
2274 Waits for a Stack to reach a given status.
2275
2276 Note this compares the full $action_$status, e.g
2277 CREATE_COMPLETE, not just COMPLETE which is exposed
2278 via the status property of Stack in heatclient
2279 """
2280 fail_regexp = re.compile(failure_pattern)
2281 build_timeout = CONF.orchestration.build_timeout
2282 build_interval = CONF.orchestration.build_interval
2283
2284 start = timeutils.utcnow()
2285 while timeutils.delta_seconds(start,
2286 timeutils.utcnow()) < build_timeout:
2287 try:
2288 stack = self.client.stacks.get(stack_identifier)
2289 except heat_exceptions.HTTPNotFound:
2290 # ignore this, as the stackource may not have
2291 # been created yet
2292 pass
2293 else:
2294 if stack.stack_status == status:
2295 return
2296 if fail_regexp.search(stack.stack_status):
2297 raise exceptions.StackBuildErrorException(
2298 stack_identifier=stack_identifier,
2299 stack_status=stack.stack_status,
2300 stack_status_reason=stack.stack_status_reason)
2301 time.sleep(build_interval)
2302
2303 message = ('Stack %s failed to reach %s status within '
2304 'the required time (%s s).' %
2305 (stack.stack_name, status, build_timeout))
2306 raise exceptions.TimeoutException(message)
2307
2308 def _stack_delete(self, stack_identifier):
2309 try:
2310 self.client.stacks.delete(stack_identifier)
2311 except heat_exceptions.HTTPNotFound:
2312 pass
Chris Dent0d494112014-08-26 13:48:30 +01002313
2314
2315class SwiftScenarioTest(ScenarioTest):
2316 """
2317 Provide harness to do Swift scenario tests.
2318
2319 Subclasses implement the tests that use the methods provided by this
2320 class.
2321 """
2322
2323 @classmethod
2324 def setUpClass(cls):
2325 cls.set_network_resources()
2326 super(SwiftScenarioTest, cls).setUpClass()
2327 if not CONF.service_available.swift:
2328 skip_msg = ("%s skipped as swift is not available" %
2329 cls.__name__)
2330 raise cls.skipException(skip_msg)
2331 # Clients for Swift
2332 cls.account_client = cls.manager.account_client
2333 cls.container_client = cls.manager.container_client
2334 cls.object_client = cls.manager.object_client
2335
2336 def _get_swift_stat(self):
2337 """get swift status for our user account."""
2338 self.account_client.list_account_containers()
2339 LOG.debug('Swift status information obtained successfully')
2340
2341 def _create_container(self, container_name=None):
2342 name = container_name or data_utils.rand_name(
2343 'swift-scenario-container')
2344 self.container_client.create_container(name)
2345 # look for the container to assure it is created
2346 self._list_and_check_container_objects(name)
2347 LOG.debug('Container %s created' % (name))
2348 return name
2349
2350 def _delete_container(self, container_name):
2351 self.container_client.delete_container(container_name)
2352 LOG.debug('Container %s deleted' % (container_name))
2353
2354 def _upload_object_to_container(self, container_name, obj_name=None):
2355 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
2356 obj_data = data_utils.arbitrary_string()
2357 self.object_client.create_object(container_name, obj_name, obj_data)
2358 return obj_name, obj_data
2359
2360 def _delete_object(self, container_name, filename):
2361 self.object_client.delete_object(container_name, filename)
2362 self._list_and_check_container_objects(container_name,
2363 not_present_obj=[filename])
2364
Ghanshyam2a180b82014-06-16 13:54:22 +09002365 def _list_and_check_container_objects(self, container_name,
2366 present_obj=None,
2367 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01002368 """
2369 List objects for a given container and assert which are present and
2370 which are not.
2371 """
Ghanshyam2a180b82014-06-16 13:54:22 +09002372 if present_obj is None:
2373 present_obj = []
2374 if not_present_obj is None:
2375 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01002376 _, object_list = self.container_client.list_container_contents(
2377 container_name)
2378 if present_obj:
2379 for obj in present_obj:
2380 self.assertIn(obj, object_list)
2381 if not_present_obj:
2382 for obj in not_present_obj:
2383 self.assertNotIn(obj, object_list)
2384
2385 def _change_container_acl(self, container_name, acl):
2386 metadata_param = {'metadata_prefix': 'x-container-',
2387 'metadata': {'read': acl}}
2388 self.container_client.update_container_metadata(container_name,
2389 **metadata_param)
2390 resp, _ = self.container_client.list_container_metadata(container_name)
2391 self.assertEqual(resp['x-container-read'], acl)
2392
2393 def _download_and_verify(self, container_name, obj_name, expected_data):
2394 _, obj = self.object_client.get_object(container_name, obj_name)
2395 self.assertEqual(obj, expected_data)