blob: 84465ef2982151a841acf9e642030a43565a135a [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
llg821243b20502014-02-22 10:32:49 +080019import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040020import subprocess
21
Sean Dague6dbc6da2013-05-08 17:49:46 -040022import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040023from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090024from novaclient import exceptions as nova_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
Sean Dague1937d092013-05-17 16:36:38 -040026from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000027from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000028from tempest import clients
Matthew Treinishb86cda92013-07-29 11:22:23 -040029from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090030from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090031from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000032from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020033from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020034from tempest.openstack.common import log
Sean Dague6dbc6da2013-05-08 17:49:46 -040035import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040036
Matthew Treinish6c072292014-01-29 19:15:52 +000037CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Attila Fazekasfb7552a2013-08-27 13:02:26 +020039LOG = log.getLogger(__name__)
40
41# NOTE(afazekas): Workaround for the stdout logging
42LOG_nova_client = logging.getLogger('novaclient.client')
43LOG_nova_client.addHandler(log.NullHandler())
44
45LOG_cinder_client = logging.getLogger('cinderclient.client')
46LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040047
48
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040049class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -040050 """
51 Official Client test base class for scenario testing.
52
53 Official Client tests are tests that have the following characteristics:
54
55 * Test basic operations of an API, typically in an order that
56 a regular user would perform those operations
57 * Test only the correct inputs and action paths -- no fuzz or
58 random input data is sent, only valid inputs.
59 * Use only the default client tool for calling an API
60 """
61
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040062 @classmethod
63 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +020064 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -040065 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -050066 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -050067 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120068
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000069 cls.manager = clients.OfficialClientManager(
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000070 credentials=cls.credentials())
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040071 cls.compute_client = cls.manager.compute_client
72 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -070073 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040074 cls.identity_client = cls.manager.identity_client
75 cls.network_client = cls.manager.network_client
76 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +000077 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120078 cls.orchestration_client = cls.manager.orchestration_client
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +040079 cls.data_processing_client = cls.manager.data_processing_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040080 cls.resource_keys = {}
81 cls.os_resources = []
Sean Dague6dbc6da2013-05-08 17:49:46 -040082
83 @classmethod
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000084 def _get_credentials(cls, get_creds, ctype):
Matthew Treinish6c072292014-01-29 19:15:52 +000085 if CONF.compute.allow_tenant_isolation:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000086 creds = get_creds()
Yair Fried769bbff2013-12-18 16:33:17 +020087 else:
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000088 creds = auth.get_default_credentials(ctype)
89 return creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120090
91 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +020092 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000093 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
94 'user')
Yair Frieda71cc442013-12-18 13:32:36 +020095
96 @classmethod
97 def alt_credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000098 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
99 'alt_user')
Yair Frieda71cc442013-12-18 13:32:36 +0200100
101 @classmethod
102 def admin_credentials(cls):
103 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000104 'identity_admin')
Yair Frieda71cc442013-12-18 13:32:36 +0200105
Yair Friedbf2e2c42014-01-28 12:06:38 +0200106 @staticmethod
107 def cleanup_resource(resource, test_name):
108
109 LOG.debug("Deleting %r from shared resources of %s" %
110 (resource, test_name))
111 try:
112 # OpenStack resources are assumed to have a delete()
113 # method which destroys the resource...
114 resource.delete()
115 except Exception as e:
116 # If the resource is already missing, mission accomplished.
117 # add status code as workaround for bug 1247568
118 if (e.__class__.__name__ == 'NotFound' or
119 (hasattr(e, 'status_code') and e.status_code == 404)):
120 return
121 raise
122
123 def is_deletion_complete():
124 # Deletion testing is only required for objects whose
125 # existence cannot be checked via retrieval.
126 if isinstance(resource, dict):
127 return True
128 try:
129 resource.get()
130 except Exception as e:
131 # Clients are expected to return an exception
132 # called 'NotFound' if retrieval fails.
133 if e.__class__.__name__ == 'NotFound':
134 return True
135 raise
136 return False
137
138 # Block until resource deletion has completed or timed-out
139 tempest.test.call_until_true(is_deletion_complete, 10, 1)
140
Yair Frieda71cc442013-12-18 13:32:36 +0200141 @classmethod
Sean Dague6dbc6da2013-05-08 17:49:46 -0400142 def tearDownClass(cls):
143 # NOTE(jaypipes): Because scenario tests are typically run in a
144 # specific order, and because test methods in scenario tests
145 # generally create resources in a particular order, we destroy
146 # resources in the reverse order in which resources are added to
147 # the scenario test class object
148 while cls.os_resources:
149 thing = cls.os_resources.pop()
Yair Friedbf2e2c42014-01-28 12:06:38 +0200150 cls.cleanup_resource(thing, cls.__name__)
Matthew Treinishb86cda92013-07-29 11:22:23 -0400151 cls.isolated_creds.clear_isolated_creds()
152 super(OfficialClientTest, cls).tearDownClass()
Sean Dague6dbc6da2013-05-08 17:49:46 -0400153
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400154 @classmethod
155 def set_resource(cls, key, thing):
156 LOG.debug("Adding %r to shared resources of %s" %
157 (thing, cls.__name__))
158 cls.resource_keys[key] = thing
159 cls.os_resources.append(thing)
160
161 @classmethod
162 def get_resource(cls, key):
163 return cls.resource_keys[key]
164
165 @classmethod
166 def remove_resource(cls, key):
167 thing = cls.resource_keys[key]
168 cls.os_resources.remove(thing)
169 del cls.resource_keys[key]
170
Steve Bakerefde7612013-09-30 11:29:23 +1300171 def status_timeout(self, things, thing_id, expected_status,
172 error_status='ERROR',
173 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400174 """
175 Given a thing and an expected status, do a loop, sleeping
176 for a configurable amount of time, checking for the
177 expected status to show. At any time, if the returned
178 status of the thing is ERROR, fail out.
179 """
Steve Bakerefde7612013-09-30 11:29:23 +1300180 self._status_timeout(things, thing_id,
181 expected_status=expected_status,
182 error_status=error_status,
183 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900184
Steve Bakerefde7612013-09-30 11:29:23 +1300185 def delete_timeout(self, things, thing_id,
186 error_status='ERROR',
187 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900188 """
189 Given a thing, do a loop, sleeping
190 for a configurable amount of time, checking for the
191 deleted status to show. At any time, if the returned
192 status of the thing is ERROR, fail out.
193 """
194 self._status_timeout(things,
195 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +1300196 allow_notfound=True,
197 error_status=error_status,
198 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900199
200 def _status_timeout(self,
201 things,
202 thing_id,
203 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +1300204 allow_notfound=False,
205 error_status='ERROR',
206 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900207
208 log_status = expected_status if expected_status else ''
209 if allow_notfound:
210 log_status += ' or NotFound' if log_status != '' else 'NotFound'
211
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400212 def check_status():
213 # python-novaclient has resources available to its client
214 # that all implement a get() method taking an identifier
215 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900216 try:
217 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +1300218 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900219 if allow_notfound:
220 return True
221 else:
222 raise
223
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400224 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -0500225
226 # Some components are reporting error status in lower case
227 # so case sensitive comparisons can really mess things
228 # up.
229 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +0900230 message = ("%s failed to get to expected status (%s). "
231 "In %s state.") % (thing, expected_status,
232 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +0900233 raise exceptions.BuildErrorException(message,
234 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900235 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400236 return True # All good.
237 LOG.debug("Waiting for %s to get to %s status. "
238 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900239 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400240 if not tempest.test.call_until_true(
241 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +0000242 CONF.compute.build_timeout,
243 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +0900244 message = ("Timed out waiting for thing %s "
245 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +0200246 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400247
Yair Friedeb69f3f2013-10-10 13:18:16 +0300248 def _create_loginable_secgroup_rule_nova(self, client=None,
249 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900250 if client is None:
251 client = self.compute_client
252 if secgroup_id is None:
253 sgs = client.security_groups.list()
254 for sg in sgs:
255 if sg.name == 'default':
256 secgroup_id = sg.id
257
258 # These rules are intended to permit inbound ssh and icmp
259 # traffic from all sources, so no group_id is provided.
260 # Setting a group_id would only permit traffic from ports
261 # belonging to the same security group.
262 rulesets = [
263 {
264 # ssh
265 'ip_protocol': 'tcp',
266 'from_port': 22,
267 'to_port': 22,
268 'cidr': '0.0.0.0/0',
269 },
270 {
271 # ping
272 'ip_protocol': 'icmp',
273 'from_port': -1,
274 'to_port': -1,
275 'cidr': '0.0.0.0/0',
276 }
277 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +0300278 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900279 for ruleset in rulesets:
280 sg_rule = client.security_group_rules.create(secgroup_id,
281 **ruleset)
282 self.set_resource(sg_rule.id, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +0300283 rules.append(sg_rule)
284 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900285
Grishkin0f1e11c2014-05-04 20:44:52 +0400286 def _create_security_group_nova(self, client=None,
287 namestart='secgroup-smoke-'):
288 if client is None:
289 client = self.compute_client
290 # Create security group
291 sg_name = data_utils.rand_name(namestart)
292 sg_desc = sg_name + " description"
293 secgroup = client.security_groups.create(sg_name, sg_desc)
294 self.assertEqual(secgroup.name, sg_name)
295 self.assertEqual(secgroup.description, sg_desc)
296 self.set_resource(sg_name, secgroup)
297
298 # Add rules to the security group
299 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
300
301 return secgroup
302
Giulio Fidente61cadca2013-09-24 18:33:37 +0200303 def create_server(self, client=None, name=None, image=None, flavor=None,
Adam Gandelman4a48a602014-03-20 18:23:18 -0700304 wait=True, create_kwargs={}):
Giulio Fidente61cadca2013-09-24 18:33:37 +0200305 if client is None:
306 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900307 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900308 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900309 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000310 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900311 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000312 flavor = CONF.compute.flavor_ref
JordanP9c052aa2014-01-24 13:05:00 +0000313
314 fixed_network_name = CONF.compute.fixed_network_name
315 if 'nics' not in create_kwargs and fixed_network_name:
316 networks = client.networks.list()
317 # If several networks found, set the NetID on which to connect the
318 # server to avoid the following error "Multiple possible networks
319 # found, use a Network ID to be more specific."
320 # See Tempest #1250866
321 if len(networks) > 1:
322 for network in networks:
323 if network.label == fixed_network_name:
324 create_kwargs['nics'] = [{'net-id': network.id}]
325 break
326 # If we didn't find the network we were looking for :
327 else:
328 msg = ("The network on which the NIC of the server must "
329 "be connected can not be found : "
330 "fixed_network_name=%s. Starting instance without "
331 "specifying a network.") % fixed_network_name
332 LOG.info(msg)
333
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900334 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
335 name, image, flavor)
336 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +0200337 self.assertEqual(server.name, name)
338 self.set_resource(name, server)
Adam Gandelman4a48a602014-03-20 18:23:18 -0700339 if wait:
340 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900341 # The instance retrieved on creation is missing network
342 # details, necessitating retrieval after it becomes active to
343 # ensure correct details.
344 server = client.servers.get(server.id)
345 self.set_resource(name, server)
346 LOG.debug("Created server: %s", server)
347 return server
348
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900349 def create_volume(self, client=None, size=1, name=None,
350 snapshot_id=None, imageRef=None):
351 if client is None:
352 client = self.volume_client
353 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900354 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -0700355 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900356 volume = client.volumes.create(size=size, display_name=name,
357 snapshot_id=snapshot_id,
358 imageRef=imageRef)
359 self.set_resource(name, volume)
360 self.assertEqual(name, volume.display_name)
361 self.status_timeout(client.volumes, volume.id, 'available')
362 LOG.debug("Created volume: %s", volume)
363 return volume
364
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900365 def create_server_snapshot(self, server, compute_client=None,
366 image_client=None, name=None):
367 if compute_client is None:
368 compute_client = self.compute_client
369 if image_client is None:
370 image_client = self.image_client
371 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900372 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900373 LOG.debug("Creating a snapshot image for server: %s", server.name)
374 image_id = compute_client.servers.create_image(server, name)
375 self.addCleanup(image_client.images.delete, image_id)
376 self.status_timeout(image_client.images, image_id, 'active')
377 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -0700378 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900379 LOG.debug("Created snapshot image %s for server %s",
380 snapshot_image.name, server.name)
381 return snapshot_image
382
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900383 def create_keypair(self, client=None, name=None):
384 if client is None:
385 client = self.compute_client
386 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900387 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900388 keypair = client.keypairs.create(name)
389 self.assertEqual(keypair.name, name)
390 self.set_resource(name, keypair)
391 return keypair
392
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900393 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +0800394 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900395 ip = server_or_ip
396 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000397 network_name_for_ssh = CONF.compute.network_for_ssh
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900398 ip = server_or_ip.networks[network_name_for_ssh][0]
399 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000400 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900401 if private_key is None:
402 private_key = self.keypair.private_key
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900403 return remote_client.RemoteClient(ip, username, pkey=private_key)
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900404
Nachi Ueno95b41282014-01-15 06:54:21 -0800405 def _log_console_output(self, servers=None):
406 if not servers:
407 servers = self.compute_client.servers.list()
408 for server in servers:
409 LOG.debug('Console output for %s', server.id)
410 LOG.debug(server.get_console_output())
411
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900412 def wait_for_volume_status(self, status):
413 volume_id = self.volume.id
414 self.status_timeout(
415 self.volume_client.volumes, volume_id, status)
416
417 def _image_create(self, name, fmt, path, properties={}):
418 name = data_utils.rand_name('%s-' % name)
419 image_file = open(path, 'rb')
420 self.addCleanup(image_file.close)
421 params = {
422 'name': name,
423 'container_format': fmt,
424 'disk_format': fmt,
425 'is_public': 'True',
426 }
427 params.update(properties)
428 image = self.image_client.images.create(**params)
429 self.addCleanup(self.image_client.images.delete, image)
430 self.assertEqual("queued", image.status)
431 image.update(data=image_file)
432 return image.id
433
434 def glance_image_create(self):
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900435 qcow2_img_path = (CONF.scenario.img_dir + "/" +
436 CONF.scenario.qcow2_img_file)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900437 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
438 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
439 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900440 LOG.debug("paths: img: %s, ami: %s, ari: %s, aki: %s"
441 % (qcow2_img_path, ami_img_path, ari_img_path, aki_img_path))
442 try:
443 self.image = self._image_create('scenario-img',
444 'bare',
445 qcow2_img_path,
446 properties={'disk_format':
447 'qcow2'})
448 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +0900449 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900450 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
451 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
452 properties = {
453 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
454 }
455 self.image = self._image_create('scenario-ami', 'ami',
456 path=ami_img_path,
457 properties=properties)
458 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900459
Sean Dague6dbc6da2013-05-08 17:49:46 -0400460
Adam Gandelman4a48a602014-03-20 18:23:18 -0700461class BaremetalScenarioTest(OfficialClientTest):
462 @classmethod
463 def setUpClass(cls):
464 super(BaremetalScenarioTest, cls).setUpClass()
465
466 if (not CONF.service_available.ironic or
467 not CONF.baremetal.driver_enabled):
468 msg = 'Ironic not available or Ironic compute driver not enabled'
469 raise cls.skipException(msg)
470
471 # use an admin client manager for baremetal client
Adam Gandelmanacc13e62014-05-08 11:12:47 -0700472 admin_creds = cls.admin_credentials()
473 manager = clients.OfficialClientManager(credentials=admin_creds)
Adam Gandelman4a48a602014-03-20 18:23:18 -0700474 cls.baremetal_client = manager.baremetal_client
475
476 # allow any issues obtaining the node list to raise early
477 cls.baremetal_client.node.list()
478
479 def _node_state_timeout(self, node_id, state_attr,
480 target_states, timeout=10, interval=1):
481 if not isinstance(target_states, list):
482 target_states = [target_states]
483
484 def check_state():
485 node = self.get_node(node_id=node_id)
486 if getattr(node, state_attr) in target_states:
487 return True
488 return False
489
490 if not tempest.test.call_until_true(
491 check_state, timeout, interval):
492 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
493 (node_id, state_attr, target_states))
494 raise exceptions.TimeoutException(msg)
495
496 def wait_provisioning_state(self, node_id, state, timeout):
497 self._node_state_timeout(
498 node_id=node_id, state_attr='provision_state',
499 target_states=state, timeout=timeout)
500
501 def wait_power_state(self, node_id, state):
502 self._node_state_timeout(
503 node_id=node_id, state_attr='power_state',
504 target_states=state, timeout=CONF.baremetal.power_timeout)
505
506 def wait_node(self, instance_id):
507 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -0500508 from ironicclient import exc as ironic_exceptions
509
Adam Gandelman4a48a602014-03-20 18:23:18 -0700510 def _get_node():
511 node = None
512 try:
513 node = self.get_node(instance_id=instance_id)
514 except ironic_exceptions.HTTPNotFound:
515 pass
516 return node is not None
517
518 if not tempest.test.call_until_true(
519 _get_node, CONF.baremetal.association_timeout, 1):
520 msg = ('Timed out waiting to get Ironic node by instance id %s'
521 % instance_id)
522 raise exceptions.TimeoutException(msg)
523
524 def get_node(self, node_id=None, instance_id=None):
525 if node_id:
526 return self.baremetal_client.node.get(node_id)
527 elif instance_id:
528 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
529
530 def get_ports(self, node_id):
531 ports = []
532 for port in self.baremetal_client.node.list_ports(node_id):
533 ports.append(self.baremetal_client.port.get(port.uuid))
534 return ports
535
536
Sean Dague6dbc6da2013-05-08 17:49:46 -0400537class NetworkScenarioTest(OfficialClientTest):
538 """
539 Base class for network scenario tests
540 """
541
542 @classmethod
543 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +0000544 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400545 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +0200546 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -0400547 try:
548 cls.network_client.list_networks()
549 except exc.EndpointNotFound:
550 cls.enabled = False
551 raise
552 else:
553 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -0400554 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -0400555 raise cls.skipException(msg)
556
557 @classmethod
558 def setUpClass(cls):
559 super(NetworkScenarioTest, cls).setUpClass()
Andrea Frittoli422fbdf2014-03-20 10:05:18 +0000560 cls.tenant_id = cls.manager.identity_client.tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -0400561
Sean Dague6dbc6da2013-05-08 17:49:46 -0400562 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900563 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400564 body = dict(
565 network=dict(
566 name=name,
567 tenant_id=tenant_id,
568 ),
569 )
570 result = self.network_client.create_network(body=body)
571 network = net_common.DeletableNetwork(client=self.network_client,
572 **result['network'])
573 self.assertEqual(network.name, name)
574 self.set_resource(name, network)
575 return network
576
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200577 def _list_networks(self, **kwargs):
578 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400579 return nets['networks']
580
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200581 def _list_subnets(self, **kwargs):
582 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400583 return subnets['subnets']
584
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200585 def _list_routers(self, **kwargs):
586 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400587 return routers['routers']
588
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200589 def _list_ports(self, **kwargs):
590 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000591 return ports['ports']
592
593 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200594 nets = self._list_networks(tenant_id=tenant_id)
595 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000596
597 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200598 subnets = self._list_subnets(tenant_id=tenant_id)
599 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000600
601 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200602 ports = self._list_ports(tenant_id=tenant_id)
603 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000604
Yair Fried3097dc12014-01-26 08:46:43 +0200605 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400606 """
607 Create a subnet for the given network within the cidr block
608 configured for tenant networks.
609 """
Attila Fazekase857bd62013-10-21 21:02:44 +0200610
611 def cidr_in_use(cidr, tenant_id):
612 """
613 :return True if subnet with cidr already exist in tenant
614 False else
615 """
616 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
617 return len(cidr_in_use) != 0
618
Matthew Treinish6c072292014-01-29 19:15:52 +0000619 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400620 result = None
621 # Repeatedly attempt subnet creation with sequential cidr
622 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +0000623 for subnet_cidr in tenant_cidr.subnet(
624 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +0200625 str_cidr = str(subnet_cidr)
626 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
627 continue
628
Sean Dague6dbc6da2013-05-08 17:49:46 -0400629 body = dict(
630 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +0200631 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -0400632 ip_version=4,
633 network_id=network.id,
634 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +0200635 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400636 ),
637 )
Yair Fried3097dc12014-01-26 08:46:43 +0200638 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400639 try:
640 result = self.network_client.create_subnet(body=body)
641 break
Mark McClainf2982e82013-07-06 17:48:03 -0400642 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -0400643 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
644 if not is_overlapping_cidr:
645 raise
646 self.assertIsNotNone(result, 'Unable to allocate tenant network')
647 subnet = net_common.DeletableSubnet(client=self.network_client,
648 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +0200649 self.assertEqual(subnet.cidr, str_cidr)
Masayuki Igawa259c1132013-10-31 17:48:44 +0900650 self.set_resource(data_utils.rand_name(namestart), subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400651 return subnet
652
653 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900654 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400655 body = dict(
656 port=dict(name=name,
657 network_id=network.id,
658 tenant_id=network.tenant_id))
659 result = self.network_client.create_port(body=body)
660 self.assertIsNotNone(result, 'Unable to allocate port')
661 port = net_common.DeletablePort(client=self.network_client,
662 **result['port'])
663 self.set_resource(name, port)
664 return port
665
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200666 def _get_server_port_id(self, server, ip_addr=None):
667 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400668 self.assertEqual(len(ports), 1,
669 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +0200670 return ports[0]['id']
671
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200672 def _create_floating_ip(self, thing, external_network_id, port_id=None):
673 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400674 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400675 body = dict(
676 floatingip=dict(
677 floating_network_id=external_network_id,
678 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400679 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400680 )
681 )
682 result = self.network_client.create_floatingip(body=body)
683 floating_ip = net_common.DeletableFloatingIp(
684 client=self.network_client,
685 **result['floatingip'])
Masayuki Igawa259c1132013-10-31 17:48:44 +0900686 self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400687 return floating_ip
688
Yair Fried05db2522013-11-18 11:02:10 +0200689 def _associate_floating_ip(self, floating_ip, server):
690 port_id = self._get_server_port_id(server)
691 floating_ip.update(port_id=port_id)
692 self.assertEqual(port_id, floating_ip.port_id)
693 return floating_ip
694
Yair Fried9a551c42013-12-15 14:59:34 +0200695 def _disassociate_floating_ip(self, floating_ip):
696 """
697 :param floating_ip: type DeletableFloatingIp
698 """
699 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +0800700 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +0200701 return floating_ip
702
703 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400704 cmd = ['ping', '-c1', '-w1', ip_address]
705
706 def ping():
707 proc = subprocess.Popen(cmd,
708 stdout=subprocess.PIPE,
709 stderr=subprocess.PIPE)
710 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +0200711 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -0400712
Nachi Ueno6d580be2013-07-24 10:58:11 -0700713 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +0000714 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +0000715
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400716 def _create_pool(self, lb_method, protocol, subnet_id):
717 """Wrapper utility that returns a test pool."""
718 name = data_utils.rand_name('pool-')
719 body = {
720 "pool": {
721 "protocol": protocol,
722 "name": name,
723 "subnet_id": subnet_id,
724 "lb_method": lb_method
725 }
726 }
727 resp = self.network_client.create_pool(body=body)
728 pool = net_common.DeletablePool(client=self.network_client,
729 **resp['pool'])
730 self.assertEqual(pool['name'], name)
731 self.set_resource(name, pool)
732 return pool
733
734 def _create_member(self, address, protocol_port, pool_id):
735 """Wrapper utility that returns a test member."""
736 body = {
737 "member": {
738 "protocol_port": protocol_port,
739 "pool_id": pool_id,
740 "address": address
741 }
742 }
743 resp = self.network_client.create_member(body)
744 member = net_common.DeletableMember(client=self.network_client,
745 **resp['member'])
746 self.set_resource(data_utils.rand_name('member-'), member)
747 return member
748
749 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
750 """Wrapper utility that returns a test vip."""
751 name = data_utils.rand_name('vip-')
752 body = {
753 "vip": {
754 "protocol": protocol,
755 "name": name,
756 "subnet_id": subnet_id,
757 "pool_id": pool_id,
758 "protocol_port": protocol_port
759 }
760 }
761 resp = self.network_client.create_vip(body)
762 vip = net_common.DeletableVip(client=self.network_client,
763 **resp['vip'])
764 self.assertEqual(vip['name'], name)
765 self.set_resource(name, vip)
766 return vip
767
Yair Fried9a551c42013-12-15 14:59:34 +0200768 def _check_vm_connectivity(self, ip_address,
769 username=None,
770 private_key=None,
771 should_connect=True):
772 """
773 :param ip_address: server to test against
774 :param username: server's ssh username
775 :param private_key: server's ssh private key to be used
776 :param should_connect: True/False indicates positive/negative test
777 positive - attempt ping and ssh
778 negative - attempt ping and fail if succeed
779
780 :raises: AssertError if the result of the connectivity check does
781 not match the value of the should_connect param
782 """
783 if should_connect:
784 msg = "Timed out waiting for %s to become reachable" % ip_address
785 else:
786 msg = "ip address %s is reachable" % ip_address
787 self.assertTrue(self._ping_ip_address(ip_address,
788 should_succeed=should_connect),
789 msg=msg)
790 if should_connect:
791 # no need to check ssh for negative connectivity
Attila Fazekasad7ef7d2013-11-20 10:12:53 +0100792 linux_client = self.get_remote_client(ip_address, username,
793 private_key)
794 linux_client.validate_authentication()
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200795
Yair Fried3097dc12014-01-26 08:46:43 +0200796 def _check_remote_connectivity(self, source, dest, should_succeed=True):
797 """
798 check ping server via source ssh connection
799
800 :param source: RemoteClient: an ssh connection from which to ping
801 :param dest: and IP to ping against
802 :param should_succeed: boolean should ping succeed or not
803 :returns: boolean -- should_succeed == ping
804 :returns: ping is false if ping failed
805 """
806 def ping_remote():
807 try:
808 source.ping_host(dest)
809 except exceptions.SSHExecCommandFailed:
810 LOG.exception('Failed to ping host via ssh connection')
811 return not should_succeed
812 return should_succeed
813
814 return tempest.test.call_until_true(ping_remote,
815 CONF.compute.ping_timeout,
816 1)
817
Yair Friedeb69f3f2013-10-10 13:18:16 +0300818 def _create_security_group_neutron(self, tenant_id, client=None,
819 namestart='secgroup-smoke-'):
820 if client is None:
821 client = self.network_client
822 secgroup = self._create_empty_security_group(namestart=namestart,
823 client=client,
824 tenant_id=tenant_id)
825
826 # Add rules to the security group
827 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
828 for rule in rules:
829 self.assertEqual(tenant_id, rule.tenant_id)
830 self.assertEqual(secgroup.id, rule.security_group_id)
831 return secgroup
832
833 def _create_empty_security_group(self, tenant_id, client=None,
834 namestart='secgroup-smoke-'):
835 """Create a security group without rules.
836
837 Default rules will be created:
838 - IPv4 egress to any
839 - IPv6 egress to any
840
841 :param tenant_id: secgroup will be created in this tenant
842 :returns: DeletableSecurityGroup -- containing the secgroup created
843 """
844 if client is None:
845 client = self.network_client
846 sg_name = data_utils.rand_name(namestart)
847 sg_desc = sg_name + " description"
848 sg_dict = dict(name=sg_name,
849 description=sg_desc)
850 sg_dict['tenant_id'] = tenant_id
851 body = dict(security_group=sg_dict)
852 result = client.create_security_group(body=body)
853 secgroup = net_common.DeletableSecurityGroup(
854 client=client,
855 **result['security_group']
856 )
857 self.assertEqual(secgroup.name, sg_name)
858 self.assertEqual(tenant_id, secgroup.tenant_id)
859 self.assertEqual(secgroup.description, sg_desc)
860 self.set_resource(sg_name, secgroup)
861 return secgroup
862
863 def _default_security_group(self, tenant_id, client=None):
864 """Get default secgroup for given tenant_id.
865
866 :returns: DeletableSecurityGroup -- default secgroup for given tenant
867 """
868 if client is None:
869 client = self.network_client
870 sgs = [
871 sg for sg in client.list_security_groups().values()[0]
872 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
873 ]
874 msg = "No default security group for tenant %s." % (tenant_id)
875 self.assertTrue(len(sgs) > 0, msg)
876 if len(sgs) > 1:
877 msg = "Found %d default security groups" % len(sgs)
878 raise exc.NeutronClientNoUniqueMatch(msg=msg)
879 return net_common.DeletableSecurityGroup(client=client,
880 **sgs[0])
881
882 def _create_security_group_rule(self, client=None, secgroup=None,
883 tenant_id=None, **kwargs):
884 """Create a rule from a dictionary of rule parameters.
885
886 Create a rule in a secgroup. if secgroup not defined will search for
887 default secgroup in tenant_id.
888
889 :param secgroup: type DeletableSecurityGroup.
890 :param secgroup_id: search for secgroup by id
891 default -- choose default secgroup for given tenant_id
892 :param tenant_id: if secgroup not passed -- the tenant in which to
893 search for default secgroup
894 :param kwargs: a dictionary containing rule parameters:
895 for example, to allow incoming ssh:
896 rule = {
897 direction: 'ingress'
898 protocol:'tcp',
899 port_range_min: 22,
900 port_range_max: 22
901 }
902 """
903 if client is None:
904 client = self.network_client
905 if secgroup is None:
906 secgroup = self._default_security_group(tenant_id)
907
908 ruleset = dict(security_group_id=secgroup.id,
909 tenant_id=secgroup.tenant_id,
910 )
911 ruleset.update(kwargs)
912
913 body = dict(security_group_rule=dict(ruleset))
914 sg_rule = client.create_security_group_rule(body=body)
915 sg_rule = net_common.DeletableSecurityGroupRule(
916 client=client,
917 **sg_rule['security_group_rule']
918 )
919 self.set_resource(sg_rule.id, sg_rule)
920 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
921 self.assertEqual(secgroup.id, sg_rule.security_group_id)
922
923 return sg_rule
924
925 def _create_loginable_secgroup_rule_neutron(self, client=None,
926 secgroup=None):
927 """These rules are intended to permit inbound ssh and icmp
928 traffic from all sources, so no group_id is provided.
929 Setting a group_id would only permit traffic from ports
930 belonging to the same security group.
931 """
932
933 if client is None:
934 client = self.network_client
935 rules = []
936 rulesets = [
937 dict(
938 # ssh
939 protocol='tcp',
940 port_range_min=22,
941 port_range_max=22,
942 ),
943 dict(
944 # ping
945 protocol='icmp',
946 )
947 ]
948 for ruleset in rulesets:
949 for r_direction in ['ingress', 'egress']:
950 ruleset['direction'] = r_direction
951 try:
952 sg_rule = self._create_security_group_rule(
953 client=client, secgroup=secgroup, **ruleset)
954 except exc.NeutronClientException as ex:
955 # if rule already exist - skip rule and continue
956 if not (ex.status_code is 409 and 'Security group rule'
957 ' already exists' in ex.message):
958 raise ex
959 else:
960 self.assertEqual(r_direction, sg_rule.direction)
961 rules.append(sg_rule)
962
963 return rules
964
Yair Fried5f670ab2013-12-09 09:26:51 +0200965 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +0000966 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +0200967 return self.get_remote_client(server,
968 username=ssh_login,
969 private_key=private_key)
970
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000971 def _show_quota_network(self, tenant_id):
972 quota = self.network_client.show_quota(tenant_id)
973 return quota['quota']['network']
974
975 def _show_quota_subnet(self, tenant_id):
976 quota = self.network_client.show_quota(tenant_id)
977 return quota['quota']['subnet']
978
979 def _show_quota_port(self, tenant_id):
980 quota = self.network_client.show_quota(tenant_id)
981 return quota['quota']['port']
982
Yair Fried4d7efa62013-11-17 17:12:29 +0200983 def _get_router(self, tenant_id):
984 """Retrieve a router for the given tenant id.
985
986 If a public router has been configured, it will be returned.
987
988 If a public router has not been configured, but a public
989 network has, a tenant router will be created and returned that
990 routes traffic to the public network.
991 """
Matthew Treinish6c072292014-01-29 19:15:52 +0000992 router_id = CONF.network.public_router_id
993 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +0200994 if router_id:
995 result = self.network_client.show_router(router_id)
996 return net_common.AttributeDict(**result['router'])
997 elif network_id:
998 router = self._create_router(tenant_id)
999 router.add_gateway(network_id)
1000 return router
1001 else:
1002 raise Exception("Neither of 'public_router_id' or "
1003 "'public_network_id' has been defined.")
1004
1005 def _create_router(self, tenant_id, namestart='router-smoke-'):
1006 name = data_utils.rand_name(namestart)
1007 body = dict(
1008 router=dict(
1009 name=name,
1010 admin_state_up=True,
1011 tenant_id=tenant_id,
1012 ),
1013 )
1014 result = self.network_client.create_router(body=body)
1015 router = net_common.DeletableRouter(client=self.network_client,
1016 **result['router'])
1017 self.assertEqual(router.name, name)
1018 self.set_resource(name, router)
1019 return router
1020
1021 def _create_networks(self, tenant_id=None):
1022 """Create a network with a subnet connected to a router.
1023
1024 :returns: network, subnet, router
1025 """
1026 if tenant_id is None:
1027 tenant_id = self.tenant_id
1028 network = self._create_network(tenant_id)
1029 router = self._get_router(tenant_id)
1030 subnet = self._create_subnet(network)
1031 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02001032 return network, subnet, router
1033
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001034
1035class OrchestrationScenarioTest(OfficialClientTest):
1036 """
1037 Base class for orchestration scenario tests
1038 """
1039
1040 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001041 def setUpClass(cls):
1042 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001043 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001044 raise cls.skipException("Heat support is required")
1045
1046 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001047 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001048 admin_creds = auth.get_default_credentials('identity_admin')
1049 creds = auth.get_default_credentials('user')
1050 admin_creds.tenant_name = creds.tenant_name
1051 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001052
1053 def _load_template(self, base_file, file_name):
1054 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1055 file_name)
1056 with open(filepath) as f:
1057 return f.read()
1058
1059 @classmethod
1060 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001061 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001062
1063 @classmethod
1064 def _get_default_network(cls):
1065 networks = cls.network_client.list_networks()
1066 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00001067 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001068 return net