blob: f83a4b84bab03c172f8d4bc84b69621f38fe335d [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
llg821243b20502014-02-22 10:32:49 +080020import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040021import subprocess
Steve Baker22c16602014-05-05 13:34:19 +120022import time
Sean Dague6dbc6da2013-05-08 17:49:46 -040023
Steve Baker22c16602014-05-05 13:34:19 +120024from heatclient import exc as heat_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040025import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040026from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090027from novaclient import exceptions as nova_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040028
Sean Dague1937d092013-05-17 16:36:38 -040029from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000030from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000031from tempest import clients
Matthew Treinishb86cda92013-07-29 11:22:23 -040032from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090033from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090034from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000035from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020036from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020037from tempest.openstack.common import log
Steve Baker22c16602014-05-05 13:34:19 +120038from tempest.openstack.common import timeutils
Sean Dague6dbc6da2013-05-08 17:49:46 -040039import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040040
Matthew Treinish6c072292014-01-29 19:15:52 +000041CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040042
Attila Fazekasfb7552a2013-08-27 13:02:26 +020043LOG = log.getLogger(__name__)
44
45# NOTE(afazekas): Workaround for the stdout logging
46LOG_nova_client = logging.getLogger('novaclient.client')
47LOG_nova_client.addHandler(log.NullHandler())
48
49LOG_cinder_client = logging.getLogger('cinderclient.client')
50LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040051
52
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040053class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -040054 """
55 Official Client test base class for scenario testing.
56
57 Official Client tests are tests that have the following characteristics:
58
59 * Test basic operations of an API, typically in an order that
60 a regular user would perform those operations
61 * Test only the correct inputs and action paths -- no fuzz or
62 random input data is sent, only valid inputs.
63 * Use only the default client tool for calling an API
64 """
65
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040066 @classmethod
67 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +020068 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -040069 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -050070 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -050071 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120072
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000073 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000074 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040075 cls.compute_client = cls.manager.compute_client
76 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -070077 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040078 cls.identity_client = cls.manager.identity_client
79 cls.network_client = cls.manager.network_client
80 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +000081 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120082 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +040083 cls.data_processing_client = cls.manager.data_processing_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040084 cls.resource_keys = {}
85 cls.os_resources = []
Sean Dague6dbc6da2013-05-08 17:49:46 -040086
87 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000088 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +000089 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000090 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +020091 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000092 creds = auth.get_default_credentials(ctype)
93 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120094
95 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +020096 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000097 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
98 'user')
Yair Frieda71cc442013-12-18 13:32:36 +020099
100 @classmethod
101 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000102 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
103 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +0200104
105 @classmethod
106 def admin_credentials(cls):
107 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000108 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +0200109
Yair Friedbf2e2c42014-01-28 12:06:38 +0200110 @staticmethod
111 def cleanup_resource(resource, test_name):
112
113 LOG.debug("Deleting %r from shared resources of %s" %
114 (resource, test_name))
115 try:
116 # OpenStack resources are assumed to have a delete()
117 # method which destroys the resource...
118 resource.delete()
119 except Exception as e:
120 # If the resource is already missing, mission accomplished.
Steven Hardyef1c8962014-05-07 10:05:45 +0100121 # - Status code tolerated as a workaround for bug 1247568
122 # - HTTPNotFound tolerated as this is currently raised when
123 # attempting to delete an already-deleted heat stack.
124 if (e.__class__.__name__ in ('NotFound', 'HTTPNotFound') or
Yair Friedbf2e2c42014-01-28 12:06:38 +0200125 (hasattr(e, 'status_code') and e.status_code == 404)):
126 return
127 raise
128
129 def is_deletion_complete():
130 # Deletion testing is only required for objects whose
131 # existence cannot be checked via retrieval.
132 if isinstance(resource, dict):
133 return True
134 try:
135 resource.get()
136 except Exception as e:
137 # Clients are expected to return an exception
138 # called 'NotFound' if retrieval fails.
139 if e.__class__.__name__ == 'NotFound':
140 return True
141 raise
142 return False
143
144 # Block until resource deletion has completed or timed-out
145 tempest.test.call_until_true(is_deletion_complete, 10, 1)
146
Yair Frieda71cc442013-12-18 13:32:36 +0200147 @classmethod
Sean Dague6dbc6da2013-05-08 17:49:46 -0400148 def tearDownClass(cls):
149 # NOTE(jaypipes): Because scenario tests are typically run in a
150 # specific order, and because test methods in scenario tests
151 # generally create resources in a particular order, we destroy
152 # resources in the reverse order in which resources are added to
153 # the scenario test class object
154 while cls.os_resources:
155 thing = cls.os_resources.pop()
Yair Friedbf2e2c42014-01-28 12:06:38 +0200156 cls.cleanup_resource(thing, cls.__name__)
Matthew Treinishb86cda92013-07-29 11:22:23 -0400157 cls.isolated_creds.clear_isolated_creds()
158 super(OfficialClientTest, cls).tearDownClass()
Sean Dague6dbc6da2013-05-08 17:49:46 -0400159
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400160 @classmethod
161 def set_resource(cls, key, thing):
162 LOG.debug("Adding %r to shared resources of %s" %
163 (thing, cls.__name__))
164 cls.resource_keys[key] = thing
165 cls.os_resources.append(thing)
166
167 @classmethod
168 def get_resource(cls, key):
169 return cls.resource_keys[key]
170
171 @classmethod
172 def remove_resource(cls, key):
173 thing = cls.resource_keys[key]
174 cls.os_resources.remove(thing)
175 del cls.resource_keys[key]
176
Steve Bakerefde7612013-09-30 11:29:23 +1300177 def status_timeout(self, things, thing_id, expected_status,
178 error_status='ERROR',
179 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400180 """
181 Given a thing and an expected status, do a loop, sleeping
182 for a configurable amount of time, checking for the
183 expected status to show. At any time, if the returned
184 status of the thing is ERROR, fail out.
185 """
Steve Bakerefde7612013-09-30 11:29:23 +1300186 self._status_timeout(things, thing_id,
187 expected_status=expected_status,
188 error_status=error_status,
189 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900190
Steve Bakerefde7612013-09-30 11:29:23 +1300191 def delete_timeout(self, things, thing_id,
192 error_status='ERROR',
193 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900194 """
195 Given a thing, do a loop, sleeping
196 for a configurable amount of time, checking for the
197 deleted status to show. At any time, if the returned
198 status of the thing is ERROR, fail out.
199 """
200 self._status_timeout(things,
201 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +1300202 allow_notfound=True,
203 error_status=error_status,
204 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900205
206 def _status_timeout(self,
207 things,
208 thing_id,
209 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +1300210 allow_notfound=False,
211 error_status='ERROR',
212 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900213
214 log_status = expected_status if expected_status else ''
215 if allow_notfound:
216 log_status += ' or NotFound' if log_status != '' else 'NotFound'
217
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400218 def check_status():
219 # python-novaclient has resources available to its client
220 # that all implement a get() method taking an identifier
221 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900222 try:
223 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +1300224 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900225 if allow_notfound:
226 return True
227 else:
228 raise
229
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400230 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -0500231
232 # Some components are reporting error status in lower case
233 # so case sensitive comparisons can really mess things
234 # up.
235 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +0900236 message = ("%s failed to get to expected status (%s). "
237 "In %s state.") % (thing, expected_status,
238 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +0900239 raise exceptions.BuildErrorException(message,
240 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900241 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400242 return True # All good.
243 LOG.debug("Waiting for %s to get to %s status. "
244 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900245 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400246 if not tempest.test.call_until_true(
247 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +0000248 CONF.compute.build_timeout,
249 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +0900250 message = ("Timed out waiting for thing %s "
251 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +0200252 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400253
Yair Friedeb69f3f2013-10-10 13:18:16 +0300254 def _create_loginable_secgroup_rule_nova(self, client=None,
255 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900256 if client is None:
257 client = self.compute_client
258 if secgroup_id is None:
259 sgs = client.security_groups.list()
260 for sg in sgs:
261 if sg.name == 'default':
262 secgroup_id = sg.id
263
264 # These rules are intended to permit inbound ssh and icmp
265 # traffic from all sources, so no group_id is provided.
266 # Setting a group_id would only permit traffic from ports
267 # belonging to the same security group.
268 rulesets = [
269 {
270 # ssh
271 'ip_protocol': 'tcp',
272 'from_port': 22,
273 'to_port': 22,
274 'cidr': '0.0.0.0/0',
275 },
276 {
277 # ping
278 'ip_protocol': 'icmp',
279 'from_port': -1,
280 'to_port': -1,
281 'cidr': '0.0.0.0/0',
282 }
283 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +0300284 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900285 for ruleset in rulesets:
286 sg_rule = client.security_group_rules.create(secgroup_id,
287 **ruleset)
288 self.set_resource(sg_rule.id, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +0300289 rules.append(sg_rule)
290 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900291
Giulio Fidente61cadca2013-09-24 18:33:37 +0200292 def create_server(self, client=None, name=None, image=None, flavor=None,
Adam Gandelman4a48a602014-03-20 18:23:18 -0700293 wait=True, create_kwargs={}):
Giulio Fidente61cadca2013-09-24 18:33:37 +0200294 if client is None:
295 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900296 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900297 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900298 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000299 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900300 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000301 flavor = CONF.compute.flavor_ref
JordanP9c052aa2014-01-24 13:05:00 +0000302
303 fixed_network_name = CONF.compute.fixed_network_name
304 if 'nics' not in create_kwargs and fixed_network_name:
305 networks = client.networks.list()
306 # If several networks found, set the NetID on which to connect the
307 # server to avoid the following error "Multiple possible networks
308 # found, use a Network ID to be more specific."
309 # See Tempest #1250866
310 if len(networks) > 1:
311 for network in networks:
312 if network.label == fixed_network_name:
313 create_kwargs['nics'] = [{'net-id': network.id}]
314 break
315 # If we didn't find the network we were looking for :
316 else:
317 msg = ("The network on which the NIC of the server must "
318 "be connected can not be found : "
319 "fixed_network_name=%s. Starting instance without "
320 "specifying a network.") % fixed_network_name
321 LOG.info(msg)
322
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900323 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
324 name, image, flavor)
325 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +0200326 self.assertEqual(server.name, name)
327 self.set_resource(name, server)
Adam Gandelman4a48a602014-03-20 18:23:18 -0700328 if wait:
329 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900330 # The instance retrieved on creation is missing network
331 # details, necessitating retrieval after it becomes active to
332 # ensure correct details.
333 server = client.servers.get(server.id)
334 self.set_resource(name, server)
335 LOG.debug("Created server: %s", server)
336 return server
337
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900338 def create_volume(self, client=None, size=1, name=None,
339 snapshot_id=None, imageRef=None):
340 if client is None:
341 client = self.volume_client
342 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900343 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -0700344 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900345 volume = client.volumes.create(size=size, display_name=name,
346 snapshot_id=snapshot_id,
347 imageRef=imageRef)
348 self.set_resource(name, volume)
349 self.assertEqual(name, volume.display_name)
350 self.status_timeout(client.volumes, volume.id, 'available')
351 LOG.debug("Created volume: %s", volume)
352 return volume
353
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900354 def create_server_snapshot(self, server, compute_client=None,
355 image_client=None, name=None):
356 if compute_client is None:
357 compute_client = self.compute_client
358 if image_client is None:
359 image_client = self.image_client
360 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900361 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900362 LOG.debug("Creating a snapshot image for server: %s", server.name)
363 image_id = compute_client.servers.create_image(server, name)
364 self.addCleanup(image_client.images.delete, image_id)
365 self.status_timeout(image_client.images, image_id, 'active')
366 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -0700367 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900368 LOG.debug("Created snapshot image %s for server %s",
369 snapshot_image.name, server.name)
370 return snapshot_image
371
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900372 def create_keypair(self, client=None, name=None):
373 if client is None:
374 client = self.compute_client
375 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900376 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900377 keypair = client.keypairs.create(name)
378 self.assertEqual(keypair.name, name)
379 self.set_resource(name, keypair)
380 return keypair
381
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900382 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +0800383 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900384 ip = server_or_ip
385 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000386 network_name_for_ssh = CONF.compute.network_for_ssh
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900387 ip = server_or_ip.networks[network_name_for_ssh][0]
388 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000389 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900390 if private_key is None:
391 private_key = self.keypair.private_key
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900392 return remote_client.RemoteClient(ip, username, pkey=private_key)
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900393
Nachi Ueno95b41282014-01-15 06:54:21 -0800394 def _log_console_output(self, servers=None):
395 if not servers:
396 servers = self.compute_client.servers.list()
397 for server in servers:
398 LOG.debug('Console output for %s', server.id)
399 LOG.debug(server.get_console_output())
400
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900401 def wait_for_volume_status(self, status):
402 volume_id = self.volume.id
403 self.status_timeout(
404 self.volume_client.volumes, volume_id, status)
405
406 def _image_create(self, name, fmt, path, properties={}):
407 name = data_utils.rand_name('%s-' % name)
408 image_file = open(path, 'rb')
409 self.addCleanup(image_file.close)
410 params = {
411 'name': name,
412 'container_format': fmt,
413 'disk_format': fmt,
414 'is_public': 'True',
415 }
416 params.update(properties)
417 image = self.image_client.images.create(**params)
418 self.addCleanup(self.image_client.images.delete, image)
419 self.assertEqual("queued", image.status)
420 image.update(data=image_file)
421 return image.id
422
423 def glance_image_create(self):
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900424 qcow2_img_path = (CONF.scenario.img_dir + "/" +
425 CONF.scenario.qcow2_img_file)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900426 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
427 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
428 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900429 LOG.debug("paths: img: %s, ami: %s, ari: %s, aki: %s"
430 % (qcow2_img_path, ami_img_path, ari_img_path, aki_img_path))
431 try:
432 self.image = self._image_create('scenario-img',
433 'bare',
434 qcow2_img_path,
435 properties={'disk_format':
436 'qcow2'})
437 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +0900438 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900439 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
440 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
441 properties = {
442 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
443 }
444 self.image = self._image_create('scenario-ami', 'ami',
445 path=ami_img_path,
446 properties=properties)
447 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900448
Sean Dague6dbc6da2013-05-08 17:49:46 -0400449
Adam Gandelman4a48a602014-03-20 18:23:18 -0700450class BaremetalScenarioTest(OfficialClientTest):
451 @classmethod
452 def setUpClass(cls):
453 super(BaremetalScenarioTest, cls).setUpClass()
454
455 if (not CONF.service_available.ironic or
456 not CONF.baremetal.driver_enabled):
457 msg = 'Ironic not available or Ironic compute driver not enabled'
458 raise cls.skipException(msg)
459
460 # use an admin client manager for baremetal client
Adam Gandelmanacc13e62014-05-08 11:12:47 -0700461 admin_creds = cls.admin_credentials()
462 manager = clients.OfficialClientManager(credentials=admin_creds)
Adam Gandelman4a48a602014-03-20 18:23:18 -0700463 cls.baremetal_client = manager.baremetal_client
464
465 # allow any issues obtaining the node list to raise early
466 cls.baremetal_client.node.list()
467
468 def _node_state_timeout(self, node_id, state_attr,
469 target_states, timeout=10, interval=1):
470 if not isinstance(target_states, list):
471 target_states = [target_states]
472
473 def check_state():
474 node = self.get_node(node_id=node_id)
475 if getattr(node, state_attr) in target_states:
476 return True
477 return False
478
479 if not tempest.test.call_until_true(
480 check_state, timeout, interval):
481 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
482 (node_id, state_attr, target_states))
483 raise exceptions.TimeoutException(msg)
484
485 def wait_provisioning_state(self, node_id, state, timeout):
486 self._node_state_timeout(
487 node_id=node_id, state_attr='provision_state',
488 target_states=state, timeout=timeout)
489
490 def wait_power_state(self, node_id, state):
491 self._node_state_timeout(
492 node_id=node_id, state_attr='power_state',
493 target_states=state, timeout=CONF.baremetal.power_timeout)
494
495 def wait_node(self, instance_id):
496 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -0500497 from ironicclient import exc as ironic_exceptions
498
Adam Gandelman4a48a602014-03-20 18:23:18 -0700499 def _get_node():
500 node = None
501 try:
502 node = self.get_node(instance_id=instance_id)
503 except ironic_exceptions.HTTPNotFound:
504 pass
505 return node is not None
506
507 if not tempest.test.call_until_true(
508 _get_node, CONF.baremetal.association_timeout, 1):
509 msg = ('Timed out waiting to get Ironic node by instance id %s'
510 % instance_id)
511 raise exceptions.TimeoutException(msg)
512
513 def get_node(self, node_id=None, instance_id=None):
514 if node_id:
515 return self.baremetal_client.node.get(node_id)
516 elif instance_id:
517 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
518
519 def get_ports(self, node_id):
520 ports = []
521 for port in self.baremetal_client.node.list_ports(node_id):
522 ports.append(self.baremetal_client.port.get(port.uuid))
523 return ports
524
525
Sean Dague6dbc6da2013-05-08 17:49:46 -0400526class NetworkScenarioTest(OfficialClientTest):
527 """
528 Base class for network scenario tests
529 """
530
531 @classmethod
532 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +0000533 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400534 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +0200535 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -0400536 try:
537 cls.network_client.list_networks()
538 except exc.EndpointNotFound:
539 cls.enabled = False
540 raise
541 else:
542 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -0400543 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -0400544 raise cls.skipException(msg)
545
546 @classmethod
547 def setUpClass(cls):
548 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000549 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -0400550
Sean Dague6dbc6da2013-05-08 17:49:46 -0400551 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900552 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400553 body = dict(
554 network=dict(
555 name=name,
556 tenant_id=tenant_id,
557 ),
558 )
559 result = self.network_client.create_network(body=body)
560 network = net_common.DeletableNetwork(client=self.network_client,
561 **result['network'])
562 self.assertEqual(network.name, name)
563 self.set_resource(name, network)
564 return network
565
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200566 def _list_networks(self, **kwargs):
567 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400568 return nets['networks']
569
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200570 def _list_subnets(self, **kwargs):
571 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400572 return subnets['subnets']
573
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200574 def _list_routers(self, **kwargs):
575 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400576 return routers['routers']
577
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200578 def _list_ports(self, **kwargs):
579 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000580 return ports['ports']
581
582 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200583 nets = self._list_networks(tenant_id=tenant_id)
584 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000585
586 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200587 subnets = self._list_subnets(tenant_id=tenant_id)
588 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000589
590 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200591 ports = self._list_ports(tenant_id=tenant_id)
592 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000593
Yair Fried3097dc12014-01-26 08:46:43 +0200594 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400595 """
596 Create a subnet for the given network within the cidr block
597 configured for tenant networks.
598 """
Attila Fazekase857bd62013-10-21 21:02:44 +0200599
600 def cidr_in_use(cidr, tenant_id):
601 """
602 :return True if subnet with cidr already exist in tenant
603 False else
604 """
605 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
606 return len(cidr_in_use) != 0
607
Matthew Treinish6c072292014-01-29 19:15:52 +0000608 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400609 result = None
610 # Repeatedly attempt subnet creation with sequential cidr
611 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +0000612 for subnet_cidr in tenant_cidr.subnet(
613 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +0200614 str_cidr = str(subnet_cidr)
615 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
616 continue
617
Sean Dague6dbc6da2013-05-08 17:49:46 -0400618 body = dict(
619 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +0200620 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -0400621 ip_version=4,
622 network_id=network.id,
623 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +0200624 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400625 ),
626 )
Yair Fried3097dc12014-01-26 08:46:43 +0200627 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400628 try:
629 result = self.network_client.create_subnet(body=body)
630 break
Mark McClainf2982e82013-07-06 17:48:03 -0400631 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -0400632 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
633 if not is_overlapping_cidr:
634 raise
635 self.assertIsNotNone(result, 'Unable to allocate tenant network')
636 subnet = net_common.DeletableSubnet(client=self.network_client,
637 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +0200638 self.assertEqual(subnet.cidr, str_cidr)
Masayuki Igawa259c1132013-10-31 17:48:44 +0900639 self.set_resource(data_utils.rand_name(namestart), subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400640 return subnet
641
642 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900643 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400644 body = dict(
645 port=dict(name=name,
646 network_id=network.id,
647 tenant_id=network.tenant_id))
648 result = self.network_client.create_port(body=body)
649 self.assertIsNotNone(result, 'Unable to allocate port')
650 port = net_common.DeletablePort(client=self.network_client,
651 **result['port'])
652 self.set_resource(name, port)
653 return port
654
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200655 def _get_server_port_id(self, server, ip_addr=None):
656 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400657 self.assertEqual(len(ports), 1,
658 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +0200659 return ports[0]['id']
660
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200661 def _create_floating_ip(self, thing, external_network_id, port_id=None):
662 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400663 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400664 body = dict(
665 floatingip=dict(
666 floating_network_id=external_network_id,
667 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400668 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400669 )
670 )
671 result = self.network_client.create_floatingip(body=body)
672 floating_ip = net_common.DeletableFloatingIp(
673 client=self.network_client,
674 **result['floatingip'])
Masayuki Igawa259c1132013-10-31 17:48:44 +0900675 self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400676 return floating_ip
677
Yair Fried05db2522013-11-18 11:02:10 +0200678 def _associate_floating_ip(self, floating_ip, server):
679 port_id = self._get_server_port_id(server)
680 floating_ip.update(port_id=port_id)
681 self.assertEqual(port_id, floating_ip.port_id)
682 return floating_ip
683
Yair Fried9a551c42013-12-15 14:59:34 +0200684 def _disassociate_floating_ip(self, floating_ip):
685 """
686 :param floating_ip: type DeletableFloatingIp
687 """
688 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +0800689 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +0200690 return floating_ip
691
692 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400693 cmd = ['ping', '-c1', '-w1', ip_address]
694
695 def ping():
696 proc = subprocess.Popen(cmd,
697 stdout=subprocess.PIPE,
698 stderr=subprocess.PIPE)
699 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +0200700 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -0400701
Nachi Ueno6d580be2013-07-24 10:58:11 -0700702 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +0000703 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +0000704
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400705 def _create_pool(self, lb_method, protocol, subnet_id):
706 """Wrapper utility that returns a test pool."""
707 name = data_utils.rand_name('pool-')
708 body = {
709 "pool": {
710 "protocol": protocol,
711 "name": name,
712 "subnet_id": subnet_id,
713 "lb_method": lb_method
714 }
715 }
716 resp = self.network_client.create_pool(body=body)
717 pool = net_common.DeletablePool(client=self.network_client,
718 **resp['pool'])
719 self.assertEqual(pool['name'], name)
720 self.set_resource(name, pool)
721 return pool
722
723 def _create_member(self, address, protocol_port, pool_id):
724 """Wrapper utility that returns a test member."""
725 body = {
726 "member": {
727 "protocol_port": protocol_port,
728 "pool_id": pool_id,
729 "address": address
730 }
731 }
732 resp = self.network_client.create_member(body)
733 member = net_common.DeletableMember(client=self.network_client,
734 **resp['member'])
735 self.set_resource(data_utils.rand_name('member-'), member)
736 return member
737
738 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
739 """Wrapper utility that returns a test vip."""
740 name = data_utils.rand_name('vip-')
741 body = {
742 "vip": {
743 "protocol": protocol,
744 "name": name,
745 "subnet_id": subnet_id,
746 "pool_id": pool_id,
747 "protocol_port": protocol_port
748 }
749 }
750 resp = self.network_client.create_vip(body)
751 vip = net_common.DeletableVip(client=self.network_client,
752 **resp['vip'])
753 self.assertEqual(vip['name'], name)
754 self.set_resource(name, vip)
755 return vip
756
Yair Fried9a551c42013-12-15 14:59:34 +0200757 def _check_vm_connectivity(self, ip_address,
758 username=None,
759 private_key=None,
760 should_connect=True):
761 """
762 :param ip_address: server to test against
763 :param username: server's ssh username
764 :param private_key: server's ssh private key to be used
765 :param should_connect: True/False indicates positive/negative test
766 positive - attempt ping and ssh
767 negative - attempt ping and fail if succeed
768
769 :raises: AssertError if the result of the connectivity check does
770 not match the value of the should_connect param
771 """
772 if should_connect:
773 msg = "Timed out waiting for %s to become reachable" % ip_address
774 else:
775 msg = "ip address %s is reachable" % ip_address
776 self.assertTrue(self._ping_ip_address(ip_address,
777 should_succeed=should_connect),
778 msg=msg)
779 if should_connect:
780 # no need to check ssh for negative connectivity
Attila Fazekasad7ef7d2013-11-20 10:12:53 +0100781 linux_client = self.get_remote_client(ip_address, username,
782 private_key)
783 linux_client.validate_authentication()
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200784
Yair Fried3097dc12014-01-26 08:46:43 +0200785 def _check_remote_connectivity(self, source, dest, should_succeed=True):
786 """
787 check ping server via source ssh connection
788
789 :param source: RemoteClient: an ssh connection from which to ping
790 :param dest: and IP to ping against
791 :param should_succeed: boolean should ping succeed or not
792 :returns: boolean -- should_succeed == ping
793 :returns: ping is false if ping failed
794 """
795 def ping_remote():
796 try:
797 source.ping_host(dest)
798 except exceptions.SSHExecCommandFailed:
799 LOG.exception('Failed to ping host via ssh connection')
800 return not should_succeed
801 return should_succeed
802
803 return tempest.test.call_until_true(ping_remote,
804 CONF.compute.ping_timeout,
805 1)
806
Yair Friedeb69f3f2013-10-10 13:18:16 +0300807 def _create_security_group_nova(self, client=None,
808 namestart='secgroup-smoke-',
809 tenant_id=None):
810 if client is None:
811 client = self.compute_client
812 # Create security group
813 sg_name = data_utils.rand_name(namestart)
814 sg_desc = sg_name + " description"
815 secgroup = client.security_groups.create(sg_name, sg_desc)
816 self.assertEqual(secgroup.name, sg_name)
817 self.assertEqual(secgroup.description, sg_desc)
818 self.set_resource(sg_name, secgroup)
819
820 # Add rules to the security group
821 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
822
823 return secgroup
824
825 def _create_security_group_neutron(self, tenant_id, client=None,
826 namestart='secgroup-smoke-'):
827 if client is None:
828 client = self.network_client
829 secgroup = self._create_empty_security_group(namestart=namestart,
830 client=client,
831 tenant_id=tenant_id)
832
833 # Add rules to the security group
834 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
835 for rule in rules:
836 self.assertEqual(tenant_id, rule.tenant_id)
837 self.assertEqual(secgroup.id, rule.security_group_id)
838 return secgroup
839
840 def _create_empty_security_group(self, tenant_id, client=None,
841 namestart='secgroup-smoke-'):
842 """Create a security group without rules.
843
844 Default rules will be created:
845 - IPv4 egress to any
846 - IPv6 egress to any
847
848 :param tenant_id: secgroup will be created in this tenant
849 :returns: DeletableSecurityGroup -- containing the secgroup created
850 """
851 if client is None:
852 client = self.network_client
853 sg_name = data_utils.rand_name(namestart)
854 sg_desc = sg_name + " description"
855 sg_dict = dict(name=sg_name,
856 description=sg_desc)
857 sg_dict['tenant_id'] = tenant_id
858 body = dict(security_group=sg_dict)
859 result = client.create_security_group(body=body)
860 secgroup = net_common.DeletableSecurityGroup(
861 client=client,
862 **result['security_group']
863 )
864 self.assertEqual(secgroup.name, sg_name)
865 self.assertEqual(tenant_id, secgroup.tenant_id)
866 self.assertEqual(secgroup.description, sg_desc)
867 self.set_resource(sg_name, secgroup)
868 return secgroup
869
870 def _default_security_group(self, tenant_id, client=None):
871 """Get default secgroup for given tenant_id.
872
873 :returns: DeletableSecurityGroup -- default secgroup for given tenant
874 """
875 if client is None:
876 client = self.network_client
877 sgs = [
878 sg for sg in client.list_security_groups().values()[0]
879 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
880 ]
881 msg = "No default security group for tenant %s." % (tenant_id)
882 self.assertTrue(len(sgs) > 0, msg)
883 if len(sgs) > 1:
884 msg = "Found %d default security groups" % len(sgs)
885 raise exc.NeutronClientNoUniqueMatch(msg=msg)
886 return net_common.DeletableSecurityGroup(client=client,
887 **sgs[0])
888
889 def _create_security_group_rule(self, client=None, secgroup=None,
890 tenant_id=None, **kwargs):
891 """Create a rule from a dictionary of rule parameters.
892
893 Create a rule in a secgroup. if secgroup not defined will search for
894 default secgroup in tenant_id.
895
896 :param secgroup: type DeletableSecurityGroup.
897 :param secgroup_id: search for secgroup by id
898 default -- choose default secgroup for given tenant_id
899 :param tenant_id: if secgroup not passed -- the tenant in which to
900 search for default secgroup
901 :param kwargs: a dictionary containing rule parameters:
902 for example, to allow incoming ssh:
903 rule = {
904 direction: 'ingress'
905 protocol:'tcp',
906 port_range_min: 22,
907 port_range_max: 22
908 }
909 """
910 if client is None:
911 client = self.network_client
912 if secgroup is None:
913 secgroup = self._default_security_group(tenant_id)
914
915 ruleset = dict(security_group_id=secgroup.id,
916 tenant_id=secgroup.tenant_id,
917 )
918 ruleset.update(kwargs)
919
920 body = dict(security_group_rule=dict(ruleset))
921 sg_rule = client.create_security_group_rule(body=body)
922 sg_rule = net_common.DeletableSecurityGroupRule(
923 client=client,
924 **sg_rule['security_group_rule']
925 )
926 self.set_resource(sg_rule.id, sg_rule)
927 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
928 self.assertEqual(secgroup.id, sg_rule.security_group_id)
929
930 return sg_rule
931
932 def _create_loginable_secgroup_rule_neutron(self, client=None,
933 secgroup=None):
934 """These rules are intended to permit inbound ssh and icmp
935 traffic from all sources, so no group_id is provided.
936 Setting a group_id would only permit traffic from ports
937 belonging to the same security group.
938 """
939
940 if client is None:
941 client = self.network_client
942 rules = []
943 rulesets = [
944 dict(
945 # ssh
946 protocol='tcp',
947 port_range_min=22,
948 port_range_max=22,
949 ),
950 dict(
951 # ping
952 protocol='icmp',
953 )
954 ]
955 for ruleset in rulesets:
956 for r_direction in ['ingress', 'egress']:
957 ruleset['direction'] = r_direction
958 try:
959 sg_rule = self._create_security_group_rule(
960 client=client, secgroup=secgroup, **ruleset)
961 except exc.NeutronClientException as ex:
962 # if rule already exist - skip rule and continue
963 if not (ex.status_code is 409 and 'Security group rule'
964 ' already exists' in ex.message):
965 raise ex
966 else:
967 self.assertEqual(r_direction, sg_rule.direction)
968 rules.append(sg_rule)
969
970 return rules
971
Yair Fried5f670ab2013-12-09 09:26:51 +0200972 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +0000973 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +0200974 return self.get_remote_client(server,
975 username=ssh_login,
976 private_key=private_key)
977
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000978 def _show_quota_network(self, tenant_id):
979 quota = self.network_client.show_quota(tenant_id)
980 return quota['quota']['network']
981
982 def _show_quota_subnet(self, tenant_id):
983 quota = self.network_client.show_quota(tenant_id)
984 return quota['quota']['subnet']
985
986 def _show_quota_port(self, tenant_id):
987 quota = self.network_client.show_quota(tenant_id)
988 return quota['quota']['port']
989
Yair Fried4d7efa62013-11-17 17:12:29 +0200990 def _get_router(self, tenant_id):
991 """Retrieve a router for the given tenant id.
992
993 If a public router has been configured, it will be returned.
994
995 If a public router has not been configured, but a public
996 network has, a tenant router will be created and returned that
997 routes traffic to the public network.
998 """
Matthew Treinish6c072292014-01-29 19:15:52 +0000999 router_id = CONF.network.public_router_id
1000 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02001001 if router_id:
1002 result = self.network_client.show_router(router_id)
1003 return net_common.AttributeDict(**result['router'])
1004 elif network_id:
1005 router = self._create_router(tenant_id)
1006 router.add_gateway(network_id)
1007 return router
1008 else:
1009 raise Exception("Neither of 'public_router_id' or "
1010 "'public_network_id' has been defined.")
1011
1012 def _create_router(self, tenant_id, namestart='router-smoke-'):
1013 name = data_utils.rand_name(namestart)
1014 body = dict(
1015 router=dict(
1016 name=name,
1017 admin_state_up=True,
1018 tenant_id=tenant_id,
1019 ),
1020 )
1021 result = self.network_client.create_router(body=body)
1022 router = net_common.DeletableRouter(client=self.network_client,
1023 **result['router'])
1024 self.assertEqual(router.name, name)
1025 self.set_resource(name, router)
1026 return router
1027
1028 def _create_networks(self, tenant_id=None):
1029 """Create a network with a subnet connected to a router.
1030
1031 :returns: network, subnet, router
1032 """
1033 if tenant_id is None:
1034 tenant_id = self.tenant_id
1035 network = self._create_network(tenant_id)
1036 router = self._get_router(tenant_id)
1037 subnet = self._create_subnet(network)
1038 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02001039 return network, subnet, router
1040
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001041
1042class OrchestrationScenarioTest(OfficialClientTest):
1043 """
1044 Base class for orchestration scenario tests
1045 """
1046
1047 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001048 def setUpClass(cls):
1049 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001050 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001051 raise cls.skipException("Heat support is required")
1052
1053 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001054 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001055 admin_creds = auth.get_default_credentials('identity_admin')
1056 creds = auth.get_default_credentials('user')
1057 admin_creds.tenant_name = creds.tenant_name
1058 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001059
1060 def _load_template(self, base_file, file_name):
1061 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1062 file_name)
1063 with open(filepath) as f:
1064 return f.read()
1065
1066 @classmethod
1067 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001068 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001069
1070 @classmethod
1071 def _get_default_network(cls):
1072 networks = cls.network_client.list_networks()
1073 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00001074 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001075 return net
Steve Baker22c16602014-05-05 13:34:19 +12001076
1077 @staticmethod
1078 def _stack_output(stack, output_key):
1079 """Return a stack output value for a given key."""
1080 return next((o['output_value'] for o in stack.outputs
1081 if o['output_key'] == output_key), None)
1082
1083 def _ping_ip_address(self, ip_address, should_succeed=True):
1084 cmd = ['ping', '-c1', '-w1', ip_address]
1085
1086 def ping():
1087 proc = subprocess.Popen(cmd,
1088 stdout=subprocess.PIPE,
1089 stderr=subprocess.PIPE)
1090 proc.wait()
1091 return (proc.returncode == 0) == should_succeed
1092
1093 return tempest.test.call_until_true(
1094 ping, CONF.orchestration.build_timeout, 1)
1095
1096 def _wait_for_resource_status(self, stack_identifier, resource_name,
1097 status, failure_pattern='^.*_FAILED$'):
1098 """Waits for a Resource to reach a given status."""
1099 fail_regexp = re.compile(failure_pattern)
1100 build_timeout = CONF.orchestration.build_timeout
1101 build_interval = CONF.orchestration.build_interval
1102
1103 start = timeutils.utcnow()
1104 while timeutils.delta_seconds(start,
1105 timeutils.utcnow()) < build_timeout:
1106 try:
1107 res = self.client.resources.get(
1108 stack_identifier, resource_name)
1109 except heat_exceptions.HTTPNotFound:
1110 # ignore this, as the resource may not have
1111 # been created yet
1112 pass
1113 else:
1114 if res.resource_status == status:
1115 return
1116 if fail_regexp.search(res.resource_status):
1117 raise exceptions.StackResourceBuildErrorException(
1118 resource_name=res.resource_name,
1119 stack_identifier=stack_identifier,
1120 resource_status=res.resource_status,
1121 resource_status_reason=res.resource_status_reason)
1122 time.sleep(build_interval)
1123
1124 message = ('Resource %s failed to reach %s status within '
1125 'the required time (%s s).' %
1126 (res.resource_name, status, build_timeout))
1127 raise exceptions.TimeoutException(message)
1128
1129 def _wait_for_stack_status(self, stack_identifier, status,
1130 failure_pattern='^.*_FAILED$'):
1131 """
1132 Waits for a Stack to reach a given status.
1133
1134 Note this compares the full $action_$status, e.g
1135 CREATE_COMPLETE, not just COMPLETE which is exposed
1136 via the status property of Stack in heatclient
1137 """
1138 fail_regexp = re.compile(failure_pattern)
1139 build_timeout = CONF.orchestration.build_timeout
1140 build_interval = CONF.orchestration.build_interval
1141
1142 start = timeutils.utcnow()
1143 while timeutils.delta_seconds(start,
1144 timeutils.utcnow()) < build_timeout:
1145 try:
1146 stack = self.client.stacks.get(stack_identifier)
1147 except heat_exceptions.HTTPNotFound:
1148 # ignore this, as the stackource may not have
1149 # been created yet
1150 pass
1151 else:
1152 if stack.stack_status == status:
1153 return
1154 if fail_regexp.search(stack.stack_status):
1155 raise exceptions.StackBuildErrorException(
1156 stack_identifier=stack_identifier,
1157 stack_status=stack.stack_status,
1158 stack_status_reason=stack.stack_status_reason)
1159 time.sleep(build_interval)
1160
1161 message = ('Stack %s failed to reach %s status within '
1162 'the required time (%s s).' %
1163 (stack.stack_name, status, build_timeout))
1164 raise exceptions.TimeoutException(message)
1165
1166 def _stack_delete(self, stack_identifier):
1167 try:
1168 self.client.stacks.delete(stack_identifier)
1169 except heat_exceptions.HTTPNotFound:
1170 pass