blob: d7be5342697ba891c0b58b40d02231028df08741 [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
Adam Gandelman4a48a602014-03-20 18:23:18 -070022from ironicclient import exc as ironic_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040023import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040024from neutronclient.common import exceptions as exc
fujioka yuuichi636f8db2013-08-09 12:05:24 +090025from novaclient import exceptions as nova_exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040026
Sean Dague1937d092013-05-17 16:36:38 -040027from tempest.api.network import common as net_common
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
Yair Fried769bbff2013-12-18 16:33:17 +020069 username, password, tenant_name = cls.credentials()
Matthew Treinishb86cda92013-07-29 11:22:23 -040070
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000071 cls.manager = clients.OfficialClientManager(
72 username, password, tenant_name)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040073 cls.compute_client = cls.manager.compute_client
74 cls.image_client = cls.manager.image_client
Adam Gandelman4a48a602014-03-20 18:23:18 -070075 cls.baremetal_client = cls.manager.baremetal_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040076 cls.identity_client = cls.manager.identity_client
77 cls.network_client = cls.manager.network_client
78 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +000079 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120080 cls.orchestration_client = cls.manager.orchestration_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040081 cls.resource_keys = {}
82 cls.os_resources = []
Sean Dague6dbc6da2013-05-08 17:49:46 -040083
84 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +020085 def _get_credentials(cls, get_creds, prefix):
Matthew Treinish6c072292014-01-29 19:15:52 +000086 if CONF.compute.allow_tenant_isolation:
Yair Fried769bbff2013-12-18 16:33:17 +020087 username, tenant_name, password = get_creds()
88 else:
Matthew Treinish6c072292014-01-29 19:15:52 +000089 username = getattr(CONF.identity, prefix + 'username')
90 password = getattr(CONF.identity, prefix + 'password')
91 tenant_name = getattr(CONF.identity, prefix + 'tenant_name')
Yair Fried769bbff2013-12-18 16:33:17 +020092 return username, password, tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120093
94 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +020095 def credentials(cls):
96 return cls._get_credentials(cls.isolated_creds.get_primary_creds, '')
97
98 @classmethod
99 def alt_credentials(cls):
100 return cls._get_credentials(cls.isolated_creds.get_alt_creds, 'alt_')
101
102 @classmethod
103 def admin_credentials(cls):
104 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
105 'admin_')
106
Yair Friedbf2e2c42014-01-28 12:06:38 +0200107 @staticmethod
108 def cleanup_resource(resource, test_name):
109
110 LOG.debug("Deleting %r from shared resources of %s" %
111 (resource, test_name))
112 try:
113 # OpenStack resources are assumed to have a delete()
114 # method which destroys the resource...
115 resource.delete()
116 except Exception as e:
117 # If the resource is already missing, mission accomplished.
118 # add status code as workaround for bug 1247568
119 if (e.__class__.__name__ == 'NotFound' or
120 (hasattr(e, 'status_code') and e.status_code == 404)):
121 return
122 raise
123
124 def is_deletion_complete():
125 # Deletion testing is only required for objects whose
126 # existence cannot be checked via retrieval.
127 if isinstance(resource, dict):
128 return True
129 try:
130 resource.get()
131 except Exception as e:
132 # Clients are expected to return an exception
133 # called 'NotFound' if retrieval fails.
134 if e.__class__.__name__ == 'NotFound':
135 return True
136 raise
137 return False
138
139 # Block until resource deletion has completed or timed-out
140 tempest.test.call_until_true(is_deletion_complete, 10, 1)
141
Yair Frieda71cc442013-12-18 13:32:36 +0200142 @classmethod
Sean Dague6dbc6da2013-05-08 17:49:46 -0400143 def tearDownClass(cls):
144 # NOTE(jaypipes): Because scenario tests are typically run in a
145 # specific order, and because test methods in scenario tests
146 # generally create resources in a particular order, we destroy
147 # resources in the reverse order in which resources are added to
148 # the scenario test class object
149 while cls.os_resources:
150 thing = cls.os_resources.pop()
Yair Friedbf2e2c42014-01-28 12:06:38 +0200151 cls.cleanup_resource(thing, cls.__name__)
Matthew Treinishb86cda92013-07-29 11:22:23 -0400152 cls.isolated_creds.clear_isolated_creds()
153 super(OfficialClientTest, cls).tearDownClass()
Sean Dague6dbc6da2013-05-08 17:49:46 -0400154
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400155 @classmethod
156 def set_resource(cls, key, thing):
157 LOG.debug("Adding %r to shared resources of %s" %
158 (thing, cls.__name__))
159 cls.resource_keys[key] = thing
160 cls.os_resources.append(thing)
161
162 @classmethod
163 def get_resource(cls, key):
164 return cls.resource_keys[key]
165
166 @classmethod
167 def remove_resource(cls, key):
168 thing = cls.resource_keys[key]
169 cls.os_resources.remove(thing)
170 del cls.resource_keys[key]
171
Steve Bakerefde7612013-09-30 11:29:23 +1300172 def status_timeout(self, things, thing_id, expected_status,
173 error_status='ERROR',
174 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400175 """
176 Given a thing and an expected status, do a loop, sleeping
177 for a configurable amount of time, checking for the
178 expected status to show. At any time, if the returned
179 status of the thing is ERROR, fail out.
180 """
Steve Bakerefde7612013-09-30 11:29:23 +1300181 self._status_timeout(things, thing_id,
182 expected_status=expected_status,
183 error_status=error_status,
184 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900185
Steve Bakerefde7612013-09-30 11:29:23 +1300186 def delete_timeout(self, things, thing_id,
187 error_status='ERROR',
188 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900189 """
190 Given a thing, do a loop, sleeping
191 for a configurable amount of time, checking for the
192 deleted status to show. At any time, if the returned
193 status of the thing is ERROR, fail out.
194 """
195 self._status_timeout(things,
196 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +1300197 allow_notfound=True,
198 error_status=error_status,
199 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900200
201 def _status_timeout(self,
202 things,
203 thing_id,
204 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +1300205 allow_notfound=False,
206 error_status='ERROR',
207 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900208
209 log_status = expected_status if expected_status else ''
210 if allow_notfound:
211 log_status += ' or NotFound' if log_status != '' else 'NotFound'
212
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400213 def check_status():
214 # python-novaclient has resources available to its client
215 # that all implement a get() method taking an identifier
216 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900217 try:
218 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +1300219 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900220 if allow_notfound:
221 return True
222 else:
223 raise
224
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400225 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -0500226
227 # Some components are reporting error status in lower case
228 # so case sensitive comparisons can really mess things
229 # up.
230 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +0900231 message = ("%s failed to get to expected status (%s). "
232 "In %s state.") % (thing, expected_status,
233 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +0900234 raise exceptions.BuildErrorException(message,
235 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900236 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400237 return True # All good.
238 LOG.debug("Waiting for %s to get to %s status. "
239 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900240 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400241 if not tempest.test.call_until_true(
242 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +0000243 CONF.compute.build_timeout,
244 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +0900245 message = ("Timed out waiting for thing %s "
246 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +0200247 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400248
Yair Friedeb69f3f2013-10-10 13:18:16 +0300249 def _create_loginable_secgroup_rule_nova(self, client=None,
250 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900251 if client is None:
252 client = self.compute_client
253 if secgroup_id is None:
254 sgs = client.security_groups.list()
255 for sg in sgs:
256 if sg.name == 'default':
257 secgroup_id = sg.id
258
259 # These rules are intended to permit inbound ssh and icmp
260 # traffic from all sources, so no group_id is provided.
261 # Setting a group_id would only permit traffic from ports
262 # belonging to the same security group.
263 rulesets = [
264 {
265 # ssh
266 'ip_protocol': 'tcp',
267 'from_port': 22,
268 'to_port': 22,
269 'cidr': '0.0.0.0/0',
270 },
271 {
272 # ping
273 'ip_protocol': 'icmp',
274 'from_port': -1,
275 'to_port': -1,
276 'cidr': '0.0.0.0/0',
277 }
278 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +0300279 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900280 for ruleset in rulesets:
281 sg_rule = client.security_group_rules.create(secgroup_id,
282 **ruleset)
283 self.set_resource(sg_rule.id, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +0300284 rules.append(sg_rule)
285 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900286
Giulio Fidente61cadca2013-09-24 18:33:37 +0200287 def create_server(self, client=None, name=None, image=None, flavor=None,
Adam Gandelman4a48a602014-03-20 18:23:18 -0700288 wait=True, create_kwargs={}):
Giulio Fidente61cadca2013-09-24 18:33:37 +0200289 if client is None:
290 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900291 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900292 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900293 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000294 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900295 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000296 flavor = CONF.compute.flavor_ref
JordanP9c052aa2014-01-24 13:05:00 +0000297
298 fixed_network_name = CONF.compute.fixed_network_name
299 if 'nics' not in create_kwargs and fixed_network_name:
300 networks = client.networks.list()
301 # If several networks found, set the NetID on which to connect the
302 # server to avoid the following error "Multiple possible networks
303 # found, use a Network ID to be more specific."
304 # See Tempest #1250866
305 if len(networks) > 1:
306 for network in networks:
307 if network.label == fixed_network_name:
308 create_kwargs['nics'] = [{'net-id': network.id}]
309 break
310 # If we didn't find the network we were looking for :
311 else:
312 msg = ("The network on which the NIC of the server must "
313 "be connected can not be found : "
314 "fixed_network_name=%s. Starting instance without "
315 "specifying a network.") % fixed_network_name
316 LOG.info(msg)
317
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900318 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
319 name, image, flavor)
320 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +0200321 self.assertEqual(server.name, name)
322 self.set_resource(name, server)
Adam Gandelman4a48a602014-03-20 18:23:18 -0700323 if wait:
324 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900325 # The instance retrieved on creation is missing network
326 # details, necessitating retrieval after it becomes active to
327 # ensure correct details.
328 server = client.servers.get(server.id)
329 self.set_resource(name, server)
330 LOG.debug("Created server: %s", server)
331 return server
332
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900333 def create_volume(self, client=None, size=1, name=None,
334 snapshot_id=None, imageRef=None):
335 if client is None:
336 client = self.volume_client
337 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900338 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -0700339 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900340 volume = client.volumes.create(size=size, display_name=name,
341 snapshot_id=snapshot_id,
342 imageRef=imageRef)
343 self.set_resource(name, volume)
344 self.assertEqual(name, volume.display_name)
345 self.status_timeout(client.volumes, volume.id, 'available')
346 LOG.debug("Created volume: %s", volume)
347 return volume
348
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900349 def create_server_snapshot(self, server, compute_client=None,
350 image_client=None, name=None):
351 if compute_client is None:
352 compute_client = self.compute_client
353 if image_client is None:
354 image_client = self.image_client
355 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900356 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900357 LOG.debug("Creating a snapshot image for server: %s", server.name)
358 image_id = compute_client.servers.create_image(server, name)
359 self.addCleanup(image_client.images.delete, image_id)
360 self.status_timeout(image_client.images, image_id, 'active')
361 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -0700362 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900363 LOG.debug("Created snapshot image %s for server %s",
364 snapshot_image.name, server.name)
365 return snapshot_image
366
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900367 def create_keypair(self, client=None, name=None):
368 if client is None:
369 client = self.compute_client
370 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900371 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900372 keypair = client.keypairs.create(name)
373 self.assertEqual(keypair.name, name)
374 self.set_resource(name, keypair)
375 return keypair
376
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900377 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +0800378 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900379 ip = server_or_ip
380 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000381 network_name_for_ssh = CONF.compute.network_for_ssh
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900382 ip = server_or_ip.networks[network_name_for_ssh][0]
383 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000384 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900385 if private_key is None:
386 private_key = self.keypair.private_key
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900387 return remote_client.RemoteClient(ip, username, pkey=private_key)
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900388
Nachi Ueno95b41282014-01-15 06:54:21 -0800389 def _log_console_output(self, servers=None):
390 if not servers:
391 servers = self.compute_client.servers.list()
392 for server in servers:
393 LOG.debug('Console output for %s', server.id)
394 LOG.debug(server.get_console_output())
395
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900396 def wait_for_volume_status(self, status):
397 volume_id = self.volume.id
398 self.status_timeout(
399 self.volume_client.volumes, volume_id, status)
400
401 def _image_create(self, name, fmt, path, properties={}):
402 name = data_utils.rand_name('%s-' % name)
403 image_file = open(path, 'rb')
404 self.addCleanup(image_file.close)
405 params = {
406 'name': name,
407 'container_format': fmt,
408 'disk_format': fmt,
409 'is_public': 'True',
410 }
411 params.update(properties)
412 image = self.image_client.images.create(**params)
413 self.addCleanup(self.image_client.images.delete, image)
414 self.assertEqual("queued", image.status)
415 image.update(data=image_file)
416 return image.id
417
418 def glance_image_create(self):
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900419 qcow2_img_path = (CONF.scenario.img_dir + "/" +
420 CONF.scenario.qcow2_img_file)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900421 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
422 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
423 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900424 LOG.debug("paths: img: %s, ami: %s, ari: %s, aki: %s"
425 % (qcow2_img_path, ami_img_path, ari_img_path, aki_img_path))
426 try:
427 self.image = self._image_create('scenario-img',
428 'bare',
429 qcow2_img_path,
430 properties={'disk_format':
431 'qcow2'})
432 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +0900433 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900434 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
435 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
436 properties = {
437 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
438 }
439 self.image = self._image_create('scenario-ami', 'ami',
440 path=ami_img_path,
441 properties=properties)
442 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900443
Sean Dague6dbc6da2013-05-08 17:49:46 -0400444
Adam Gandelman4a48a602014-03-20 18:23:18 -0700445class BaremetalScenarioTest(OfficialClientTest):
446 @classmethod
447 def setUpClass(cls):
448 super(BaremetalScenarioTest, cls).setUpClass()
449
450 if (not CONF.service_available.ironic or
451 not CONF.baremetal.driver_enabled):
452 msg = 'Ironic not available or Ironic compute driver not enabled'
453 raise cls.skipException(msg)
454
455 # use an admin client manager for baremetal client
456 username, password, tenant = cls.admin_credentials()
457 manager = clients.OfficialClientManager(username, password, tenant)
458 cls.baremetal_client = manager.baremetal_client
459
460 # allow any issues obtaining the node list to raise early
461 cls.baremetal_client.node.list()
462
463 def _node_state_timeout(self, node_id, state_attr,
464 target_states, timeout=10, interval=1):
465 if not isinstance(target_states, list):
466 target_states = [target_states]
467
468 def check_state():
469 node = self.get_node(node_id=node_id)
470 if getattr(node, state_attr) in target_states:
471 return True
472 return False
473
474 if not tempest.test.call_until_true(
475 check_state, timeout, interval):
476 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
477 (node_id, state_attr, target_states))
478 raise exceptions.TimeoutException(msg)
479
480 def wait_provisioning_state(self, node_id, state, timeout):
481 self._node_state_timeout(
482 node_id=node_id, state_attr='provision_state',
483 target_states=state, timeout=timeout)
484
485 def wait_power_state(self, node_id, state):
486 self._node_state_timeout(
487 node_id=node_id, state_attr='power_state',
488 target_states=state, timeout=CONF.baremetal.power_timeout)
489
490 def wait_node(self, instance_id):
491 """Waits for a node to be associated with instance_id."""
492 def _get_node():
493 node = None
494 try:
495 node = self.get_node(instance_id=instance_id)
496 except ironic_exceptions.HTTPNotFound:
497 pass
498 return node is not None
499
500 if not tempest.test.call_until_true(
501 _get_node, CONF.baremetal.association_timeout, 1):
502 msg = ('Timed out waiting to get Ironic node by instance id %s'
503 % instance_id)
504 raise exceptions.TimeoutException(msg)
505
506 def get_node(self, node_id=None, instance_id=None):
507 if node_id:
508 return self.baremetal_client.node.get(node_id)
509 elif instance_id:
510 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
511
512 def get_ports(self, node_id):
513 ports = []
514 for port in self.baremetal_client.node.list_ports(node_id):
515 ports.append(self.baremetal_client.port.get(port.uuid))
516 return ports
517
518
Sean Dague6dbc6da2013-05-08 17:49:46 -0400519class NetworkScenarioTest(OfficialClientTest):
520 """
521 Base class for network scenario tests
522 """
523
524 @classmethod
525 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +0000526 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400527 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +0200528 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -0400529 try:
530 cls.network_client.list_networks()
531 except exc.EndpointNotFound:
532 cls.enabled = False
533 raise
534 else:
535 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -0400536 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -0400537 raise cls.skipException(msg)
538
539 @classmethod
540 def setUpClass(cls):
541 super(NetworkScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +0000542 if CONF.compute.allow_tenant_isolation:
Miguel Lavalleb8fabc52013-08-23 11:19:57 -0500543 cls.tenant_id = cls.isolated_creds.get_primary_tenant().id
544 else:
545 cls.tenant_id = cls.manager._get_identity_client(
Matthew Treinish6c072292014-01-29 19:15:52 +0000546 CONF.identity.username,
547 CONF.identity.password,
548 CONF.identity.tenant_name).tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -0400549
Sean Dague6dbc6da2013-05-08 17:49:46 -0400550 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900551 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400552 body = dict(
553 network=dict(
554 name=name,
555 tenant_id=tenant_id,
556 ),
557 )
558 result = self.network_client.create_network(body=body)
559 network = net_common.DeletableNetwork(client=self.network_client,
560 **result['network'])
561 self.assertEqual(network.name, name)
562 self.set_resource(name, network)
563 return network
564
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200565 def _list_networks(self, **kwargs):
566 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400567 return nets['networks']
568
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200569 def _list_subnets(self, **kwargs):
570 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400571 return subnets['subnets']
572
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200573 def _list_routers(self, **kwargs):
574 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400575 return routers['routers']
576
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200577 def _list_ports(self, **kwargs):
578 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000579 return ports['ports']
580
581 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200582 nets = self._list_networks(tenant_id=tenant_id)
583 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000584
585 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200586 subnets = self._list_subnets(tenant_id=tenant_id)
587 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000588
589 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200590 ports = self._list_ports(tenant_id=tenant_id)
591 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000592
Yair Fried3097dc12014-01-26 08:46:43 +0200593 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400594 """
595 Create a subnet for the given network within the cidr block
596 configured for tenant networks.
597 """
Attila Fazekase857bd62013-10-21 21:02:44 +0200598
599 def cidr_in_use(cidr, tenant_id):
600 """
601 :return True if subnet with cidr already exist in tenant
602 False else
603 """
604 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
605 return len(cidr_in_use) != 0
606
Matthew Treinish6c072292014-01-29 19:15:52 +0000607 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400608 result = None
609 # Repeatedly attempt subnet creation with sequential cidr
610 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +0000611 for subnet_cidr in tenant_cidr.subnet(
612 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +0200613 str_cidr = str(subnet_cidr)
614 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
615 continue
616
Sean Dague6dbc6da2013-05-08 17:49:46 -0400617 body = dict(
618 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +0200619 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -0400620 ip_version=4,
621 network_id=network.id,
622 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +0200623 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400624 ),
625 )
Yair Fried3097dc12014-01-26 08:46:43 +0200626 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400627 try:
628 result = self.network_client.create_subnet(body=body)
629 break
Mark McClainf2982e82013-07-06 17:48:03 -0400630 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -0400631 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
632 if not is_overlapping_cidr:
633 raise
634 self.assertIsNotNone(result, 'Unable to allocate tenant network')
635 subnet = net_common.DeletableSubnet(client=self.network_client,
636 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +0200637 self.assertEqual(subnet.cidr, str_cidr)
Masayuki Igawa259c1132013-10-31 17:48:44 +0900638 self.set_resource(data_utils.rand_name(namestart), subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400639 return subnet
640
641 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900642 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400643 body = dict(
644 port=dict(name=name,
645 network_id=network.id,
646 tenant_id=network.tenant_id))
647 result = self.network_client.create_port(body=body)
648 self.assertIsNotNone(result, 'Unable to allocate port')
649 port = net_common.DeletablePort(client=self.network_client,
650 **result['port'])
651 self.set_resource(name, port)
652 return port
653
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200654 def _get_server_port_id(self, server, ip_addr=None):
655 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400656 self.assertEqual(len(ports), 1,
657 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +0200658 return ports[0]['id']
659
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200660 def _create_floating_ip(self, thing, external_network_id, port_id=None):
661 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400662 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400663 body = dict(
664 floatingip=dict(
665 floating_network_id=external_network_id,
666 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400667 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400668 )
669 )
670 result = self.network_client.create_floatingip(body=body)
671 floating_ip = net_common.DeletableFloatingIp(
672 client=self.network_client,
673 **result['floatingip'])
Masayuki Igawa259c1132013-10-31 17:48:44 +0900674 self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400675 return floating_ip
676
Yair Fried05db2522013-11-18 11:02:10 +0200677 def _associate_floating_ip(self, floating_ip, server):
678 port_id = self._get_server_port_id(server)
679 floating_ip.update(port_id=port_id)
680 self.assertEqual(port_id, floating_ip.port_id)
681 return floating_ip
682
Yair Fried9a551c42013-12-15 14:59:34 +0200683 def _disassociate_floating_ip(self, floating_ip):
684 """
685 :param floating_ip: type DeletableFloatingIp
686 """
687 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +0800688 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +0200689 return floating_ip
690
691 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400692 cmd = ['ping', '-c1', '-w1', ip_address]
693
694 def ping():
695 proc = subprocess.Popen(cmd,
696 stdout=subprocess.PIPE,
697 stderr=subprocess.PIPE)
698 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +0200699 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -0400700
Nachi Ueno6d580be2013-07-24 10:58:11 -0700701 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +0000702 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +0000703
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400704 def _create_pool(self, lb_method, protocol, subnet_id):
705 """Wrapper utility that returns a test pool."""
706 name = data_utils.rand_name('pool-')
707 body = {
708 "pool": {
709 "protocol": protocol,
710 "name": name,
711 "subnet_id": subnet_id,
712 "lb_method": lb_method
713 }
714 }
715 resp = self.network_client.create_pool(body=body)
716 pool = net_common.DeletablePool(client=self.network_client,
717 **resp['pool'])
718 self.assertEqual(pool['name'], name)
719 self.set_resource(name, pool)
720 return pool
721
722 def _create_member(self, address, protocol_port, pool_id):
723 """Wrapper utility that returns a test member."""
724 body = {
725 "member": {
726 "protocol_port": protocol_port,
727 "pool_id": pool_id,
728 "address": address
729 }
730 }
731 resp = self.network_client.create_member(body)
732 member = net_common.DeletableMember(client=self.network_client,
733 **resp['member'])
734 self.set_resource(data_utils.rand_name('member-'), member)
735 return member
736
737 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
738 """Wrapper utility that returns a test vip."""
739 name = data_utils.rand_name('vip-')
740 body = {
741 "vip": {
742 "protocol": protocol,
743 "name": name,
744 "subnet_id": subnet_id,
745 "pool_id": pool_id,
746 "protocol_port": protocol_port
747 }
748 }
749 resp = self.network_client.create_vip(body)
750 vip = net_common.DeletableVip(client=self.network_client,
751 **resp['vip'])
752 self.assertEqual(vip['name'], name)
753 self.set_resource(name, vip)
754 return vip
755
Yair Fried9a551c42013-12-15 14:59:34 +0200756 def _check_vm_connectivity(self, ip_address,
757 username=None,
758 private_key=None,
759 should_connect=True):
760 """
761 :param ip_address: server to test against
762 :param username: server's ssh username
763 :param private_key: server's ssh private key to be used
764 :param should_connect: True/False indicates positive/negative test
765 positive - attempt ping and ssh
766 negative - attempt ping and fail if succeed
767
768 :raises: AssertError if the result of the connectivity check does
769 not match the value of the should_connect param
770 """
771 if should_connect:
772 msg = "Timed out waiting for %s to become reachable" % ip_address
773 else:
774 msg = "ip address %s is reachable" % ip_address
775 self.assertTrue(self._ping_ip_address(ip_address,
776 should_succeed=should_connect),
777 msg=msg)
778 if should_connect:
779 # no need to check ssh for negative connectivity
Attila Fazekasad7ef7d2013-11-20 10:12:53 +0100780 linux_client = self.get_remote_client(ip_address, username,
781 private_key)
782 linux_client.validate_authentication()
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200783
Yair Fried3097dc12014-01-26 08:46:43 +0200784 def _check_remote_connectivity(self, source, dest, should_succeed=True):
785 """
786 check ping server via source ssh connection
787
788 :param source: RemoteClient: an ssh connection from which to ping
789 :param dest: and IP to ping against
790 :param should_succeed: boolean should ping succeed or not
791 :returns: boolean -- should_succeed == ping
792 :returns: ping is false if ping failed
793 """
794 def ping_remote():
795 try:
796 source.ping_host(dest)
797 except exceptions.SSHExecCommandFailed:
798 LOG.exception('Failed to ping host via ssh connection')
799 return not should_succeed
800 return should_succeed
801
802 return tempest.test.call_until_true(ping_remote,
803 CONF.compute.ping_timeout,
804 1)
805
Yair Friedeb69f3f2013-10-10 13:18:16 +0300806 def _create_security_group_nova(self, client=None,
807 namestart='secgroup-smoke-',
808 tenant_id=None):
809 if client is None:
810 client = self.compute_client
811 # Create security group
812 sg_name = data_utils.rand_name(namestart)
813 sg_desc = sg_name + " description"
814 secgroup = client.security_groups.create(sg_name, sg_desc)
815 self.assertEqual(secgroup.name, sg_name)
816 self.assertEqual(secgroup.description, sg_desc)
817 self.set_resource(sg_name, secgroup)
818
819 # Add rules to the security group
820 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
821
822 return secgroup
823
824 def _create_security_group_neutron(self, tenant_id, client=None,
825 namestart='secgroup-smoke-'):
826 if client is None:
827 client = self.network_client
828 secgroup = self._create_empty_security_group(namestart=namestart,
829 client=client,
830 tenant_id=tenant_id)
831
832 # Add rules to the security group
833 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
834 for rule in rules:
835 self.assertEqual(tenant_id, rule.tenant_id)
836 self.assertEqual(secgroup.id, rule.security_group_id)
837 return secgroup
838
839 def _create_empty_security_group(self, tenant_id, client=None,
840 namestart='secgroup-smoke-'):
841 """Create a security group without rules.
842
843 Default rules will be created:
844 - IPv4 egress to any
845 - IPv6 egress to any
846
847 :param tenant_id: secgroup will be created in this tenant
848 :returns: DeletableSecurityGroup -- containing the secgroup created
849 """
850 if client is None:
851 client = self.network_client
852 sg_name = data_utils.rand_name(namestart)
853 sg_desc = sg_name + " description"
854 sg_dict = dict(name=sg_name,
855 description=sg_desc)
856 sg_dict['tenant_id'] = tenant_id
857 body = dict(security_group=sg_dict)
858 result = client.create_security_group(body=body)
859 secgroup = net_common.DeletableSecurityGroup(
860 client=client,
861 **result['security_group']
862 )
863 self.assertEqual(secgroup.name, sg_name)
864 self.assertEqual(tenant_id, secgroup.tenant_id)
865 self.assertEqual(secgroup.description, sg_desc)
866 self.set_resource(sg_name, secgroup)
867 return secgroup
868
869 def _default_security_group(self, tenant_id, client=None):
870 """Get default secgroup for given tenant_id.
871
872 :returns: DeletableSecurityGroup -- default secgroup for given tenant
873 """
874 if client is None:
875 client = self.network_client
876 sgs = [
877 sg for sg in client.list_security_groups().values()[0]
878 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
879 ]
880 msg = "No default security group for tenant %s." % (tenant_id)
881 self.assertTrue(len(sgs) > 0, msg)
882 if len(sgs) > 1:
883 msg = "Found %d default security groups" % len(sgs)
884 raise exc.NeutronClientNoUniqueMatch(msg=msg)
885 return net_common.DeletableSecurityGroup(client=client,
886 **sgs[0])
887
888 def _create_security_group_rule(self, client=None, secgroup=None,
889 tenant_id=None, **kwargs):
890 """Create a rule from a dictionary of rule parameters.
891
892 Create a rule in a secgroup. if secgroup not defined will search for
893 default secgroup in tenant_id.
894
895 :param secgroup: type DeletableSecurityGroup.
896 :param secgroup_id: search for secgroup by id
897 default -- choose default secgroup for given tenant_id
898 :param tenant_id: if secgroup not passed -- the tenant in which to
899 search for default secgroup
900 :param kwargs: a dictionary containing rule parameters:
901 for example, to allow incoming ssh:
902 rule = {
903 direction: 'ingress'
904 protocol:'tcp',
905 port_range_min: 22,
906 port_range_max: 22
907 }
908 """
909 if client is None:
910 client = self.network_client
911 if secgroup is None:
912 secgroup = self._default_security_group(tenant_id)
913
914 ruleset = dict(security_group_id=secgroup.id,
915 tenant_id=secgroup.tenant_id,
916 )
917 ruleset.update(kwargs)
918
919 body = dict(security_group_rule=dict(ruleset))
920 sg_rule = client.create_security_group_rule(body=body)
921 sg_rule = net_common.DeletableSecurityGroupRule(
922 client=client,
923 **sg_rule['security_group_rule']
924 )
925 self.set_resource(sg_rule.id, sg_rule)
926 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
927 self.assertEqual(secgroup.id, sg_rule.security_group_id)
928
929 return sg_rule
930
931 def _create_loginable_secgroup_rule_neutron(self, client=None,
932 secgroup=None):
933 """These rules are intended to permit inbound ssh and icmp
934 traffic from all sources, so no group_id is provided.
935 Setting a group_id would only permit traffic from ports
936 belonging to the same security group.
937 """
938
939 if client is None:
940 client = self.network_client
941 rules = []
942 rulesets = [
943 dict(
944 # ssh
945 protocol='tcp',
946 port_range_min=22,
947 port_range_max=22,
948 ),
949 dict(
950 # ping
951 protocol='icmp',
952 )
953 ]
954 for ruleset in rulesets:
955 for r_direction in ['ingress', 'egress']:
956 ruleset['direction'] = r_direction
957 try:
958 sg_rule = self._create_security_group_rule(
959 client=client, secgroup=secgroup, **ruleset)
960 except exc.NeutronClientException as ex:
961 # if rule already exist - skip rule and continue
962 if not (ex.status_code is 409 and 'Security group rule'
963 ' already exists' in ex.message):
964 raise ex
965 else:
966 self.assertEqual(r_direction, sg_rule.direction)
967 rules.append(sg_rule)
968
969 return rules
970
Yair Fried5f670ab2013-12-09 09:26:51 +0200971 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +0000972 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +0200973 return self.get_remote_client(server,
974 username=ssh_login,
975 private_key=private_key)
976
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000977 def _show_quota_network(self, tenant_id):
978 quota = self.network_client.show_quota(tenant_id)
979 return quota['quota']['network']
980
981 def _show_quota_subnet(self, tenant_id):
982 quota = self.network_client.show_quota(tenant_id)
983 return quota['quota']['subnet']
984
985 def _show_quota_port(self, tenant_id):
986 quota = self.network_client.show_quota(tenant_id)
987 return quota['quota']['port']
988
Yair Fried4d7efa62013-11-17 17:12:29 +0200989 def _get_router(self, tenant_id):
990 """Retrieve a router for the given tenant id.
991
992 If a public router has been configured, it will be returned.
993
994 If a public router has not been configured, but a public
995 network has, a tenant router will be created and returned that
996 routes traffic to the public network.
997 """
Matthew Treinish6c072292014-01-29 19:15:52 +0000998 router_id = CONF.network.public_router_id
999 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02001000 if router_id:
1001 result = self.network_client.show_router(router_id)
1002 return net_common.AttributeDict(**result['router'])
1003 elif network_id:
1004 router = self._create_router(tenant_id)
1005 router.add_gateway(network_id)
1006 return router
1007 else:
1008 raise Exception("Neither of 'public_router_id' or "
1009 "'public_network_id' has been defined.")
1010
1011 def _create_router(self, tenant_id, namestart='router-smoke-'):
1012 name = data_utils.rand_name(namestart)
1013 body = dict(
1014 router=dict(
1015 name=name,
1016 admin_state_up=True,
1017 tenant_id=tenant_id,
1018 ),
1019 )
1020 result = self.network_client.create_router(body=body)
1021 router = net_common.DeletableRouter(client=self.network_client,
1022 **result['router'])
1023 self.assertEqual(router.name, name)
1024 self.set_resource(name, router)
1025 return router
1026
1027 def _create_networks(self, tenant_id=None):
1028 """Create a network with a subnet connected to a router.
1029
1030 :returns: network, subnet, router
1031 """
1032 if tenant_id is None:
1033 tenant_id = self.tenant_id
1034 network = self._create_network(tenant_id)
1035 router = self._get_router(tenant_id)
1036 subnet = self._create_subnet(network)
1037 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02001038 return network, subnet, router
1039
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001040
1041class OrchestrationScenarioTest(OfficialClientTest):
1042 """
1043 Base class for orchestration scenario tests
1044 """
1045
1046 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001047 def setUpClass(cls):
1048 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001049 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001050 raise cls.skipException("Heat support is required")
1051
1052 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001053 def credentials(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001054 username = CONF.identity.admin_username
1055 password = CONF.identity.admin_password
1056 tenant_name = CONF.identity.tenant_name
Ryan Hsuee1017c2013-12-20 12:00:34 -08001057 return username, password, tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001058
1059 def _load_template(self, base_file, file_name):
1060 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1061 file_name)
1062 with open(filepath) as f:
1063 return f.read()
1064
1065 @classmethod
1066 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001067 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001068
1069 @classmethod
1070 def _get_default_network(cls):
1071 networks = cls.network_client.list_networks()
1072 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00001073 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001074 return net