blob: 5895c37bae21137db530e6cc39ea0b46bb5f1f43 [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
Sergey Lukjanov7409e2e2014-03-27 12:55:50 +040081 cls.data_processing_client = cls.manager.data_processing_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -040082 cls.resource_keys = {}
83 cls.os_resources = []
Sean Dague6dbc6da2013-05-08 17:49:46 -040084
85 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +020086 def _get_credentials(cls, get_creds, prefix):
Matthew Treinish6c072292014-01-29 19:15:52 +000087 if CONF.compute.allow_tenant_isolation:
Yair Fried769bbff2013-12-18 16:33:17 +020088 username, tenant_name, password = get_creds()
89 else:
Matthew Treinish6c072292014-01-29 19:15:52 +000090 username = getattr(CONF.identity, prefix + 'username')
91 password = getattr(CONF.identity, prefix + 'password')
92 tenant_name = getattr(CONF.identity, prefix + 'tenant_name')
Yair Fried769bbff2013-12-18 16:33:17 +020093 return username, password, tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120094
95 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +020096 def credentials(cls):
97 return cls._get_credentials(cls.isolated_creds.get_primary_creds, '')
98
99 @classmethod
100 def alt_credentials(cls):
101 return cls._get_credentials(cls.isolated_creds.get_alt_creds, 'alt_')
102
103 @classmethod
104 def admin_credentials(cls):
105 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
106 'admin_')
107
Yair Friedbf2e2c42014-01-28 12:06:38 +0200108 @staticmethod
109 def cleanup_resource(resource, test_name):
110
111 LOG.debug("Deleting %r from shared resources of %s" %
112 (resource, test_name))
113 try:
114 # OpenStack resources are assumed to have a delete()
115 # method which destroys the resource...
116 resource.delete()
117 except Exception as e:
118 # If the resource is already missing, mission accomplished.
119 # add status code as workaround for bug 1247568
120 if (e.__class__.__name__ == 'NotFound' or
121 (hasattr(e, 'status_code') and e.status_code == 404)):
122 return
123 raise
124
125 def is_deletion_complete():
126 # Deletion testing is only required for objects whose
127 # existence cannot be checked via retrieval.
128 if isinstance(resource, dict):
129 return True
130 try:
131 resource.get()
132 except Exception as e:
133 # Clients are expected to return an exception
134 # called 'NotFound' if retrieval fails.
135 if e.__class__.__name__ == 'NotFound':
136 return True
137 raise
138 return False
139
140 # Block until resource deletion has completed or timed-out
141 tempest.test.call_until_true(is_deletion_complete, 10, 1)
142
Yair Frieda71cc442013-12-18 13:32:36 +0200143 @classmethod
Sean Dague6dbc6da2013-05-08 17:49:46 -0400144 def tearDownClass(cls):
145 # NOTE(jaypipes): Because scenario tests are typically run in a
146 # specific order, and because test methods in scenario tests
147 # generally create resources in a particular order, we destroy
148 # resources in the reverse order in which resources are added to
149 # the scenario test class object
150 while cls.os_resources:
151 thing = cls.os_resources.pop()
Yair Friedbf2e2c42014-01-28 12:06:38 +0200152 cls.cleanup_resource(thing, cls.__name__)
Matthew Treinishb86cda92013-07-29 11:22:23 -0400153 cls.isolated_creds.clear_isolated_creds()
154 super(OfficialClientTest, cls).tearDownClass()
Sean Dague6dbc6da2013-05-08 17:49:46 -0400155
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400156 @classmethod
157 def set_resource(cls, key, thing):
158 LOG.debug("Adding %r to shared resources of %s" %
159 (thing, cls.__name__))
160 cls.resource_keys[key] = thing
161 cls.os_resources.append(thing)
162
163 @classmethod
164 def get_resource(cls, key):
165 return cls.resource_keys[key]
166
167 @classmethod
168 def remove_resource(cls, key):
169 thing = cls.resource_keys[key]
170 cls.os_resources.remove(thing)
171 del cls.resource_keys[key]
172
Steve Bakerefde7612013-09-30 11:29:23 +1300173 def status_timeout(self, things, thing_id, expected_status,
174 error_status='ERROR',
175 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400176 """
177 Given a thing and an expected status, do a loop, sleeping
178 for a configurable amount of time, checking for the
179 expected status to show. At any time, if the returned
180 status of the thing is ERROR, fail out.
181 """
Steve Bakerefde7612013-09-30 11:29:23 +1300182 self._status_timeout(things, thing_id,
183 expected_status=expected_status,
184 error_status=error_status,
185 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900186
Steve Bakerefde7612013-09-30 11:29:23 +1300187 def delete_timeout(self, things, thing_id,
188 error_status='ERROR',
189 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900190 """
191 Given a thing, do a loop, sleeping
192 for a configurable amount of time, checking for the
193 deleted status to show. At any time, if the returned
194 status of the thing is ERROR, fail out.
195 """
196 self._status_timeout(things,
197 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +1300198 allow_notfound=True,
199 error_status=error_status,
200 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900201
202 def _status_timeout(self,
203 things,
204 thing_id,
205 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +1300206 allow_notfound=False,
207 error_status='ERROR',
208 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900209
210 log_status = expected_status if expected_status else ''
211 if allow_notfound:
212 log_status += ' or NotFound' if log_status != '' else 'NotFound'
213
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400214 def check_status():
215 # python-novaclient has resources available to its client
216 # that all implement a get() method taking an identifier
217 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900218 try:
219 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +1300220 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900221 if allow_notfound:
222 return True
223 else:
224 raise
225
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400226 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -0500227
228 # Some components are reporting error status in lower case
229 # so case sensitive comparisons can really mess things
230 # up.
231 if new_status.lower() == error_status.lower():
Masayuki Igawa2a8a8122014-02-07 11:24:49 +0900232 message = ("%s failed to get to expected status (%s). "
233 "In %s state.") % (thing, expected_status,
234 new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +0900235 raise exceptions.BuildErrorException(message,
236 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900237 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400238 return True # All good.
239 LOG.debug("Waiting for %s to get to %s status. "
240 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900241 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400242 if not tempest.test.call_until_true(
243 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +0000244 CONF.compute.build_timeout,
245 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +0900246 message = ("Timed out waiting for thing %s "
247 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +0200248 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400249
Yair Friedeb69f3f2013-10-10 13:18:16 +0300250 def _create_loginable_secgroup_rule_nova(self, client=None,
251 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900252 if client is None:
253 client = self.compute_client
254 if secgroup_id is None:
255 sgs = client.security_groups.list()
256 for sg in sgs:
257 if sg.name == 'default':
258 secgroup_id = sg.id
259
260 # These rules are intended to permit inbound ssh and icmp
261 # traffic from all sources, so no group_id is provided.
262 # Setting a group_id would only permit traffic from ports
263 # belonging to the same security group.
264 rulesets = [
265 {
266 # ssh
267 'ip_protocol': 'tcp',
268 'from_port': 22,
269 'to_port': 22,
270 'cidr': '0.0.0.0/0',
271 },
272 {
273 # ping
274 'ip_protocol': 'icmp',
275 'from_port': -1,
276 'to_port': -1,
277 'cidr': '0.0.0.0/0',
278 }
279 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +0300280 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900281 for ruleset in rulesets:
282 sg_rule = client.security_group_rules.create(secgroup_id,
283 **ruleset)
284 self.set_resource(sg_rule.id, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +0300285 rules.append(sg_rule)
286 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900287
Giulio Fidente61cadca2013-09-24 18:33:37 +0200288 def create_server(self, client=None, name=None, image=None, flavor=None,
Adam Gandelman4a48a602014-03-20 18:23:18 -0700289 wait=True, create_kwargs={}):
Giulio Fidente61cadca2013-09-24 18:33:37 +0200290 if client is None:
291 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900292 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900293 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900294 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000295 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900296 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000297 flavor = CONF.compute.flavor_ref
JordanP9c052aa2014-01-24 13:05:00 +0000298
299 fixed_network_name = CONF.compute.fixed_network_name
300 if 'nics' not in create_kwargs and fixed_network_name:
301 networks = client.networks.list()
302 # If several networks found, set the NetID on which to connect the
303 # server to avoid the following error "Multiple possible networks
304 # found, use a Network ID to be more specific."
305 # See Tempest #1250866
306 if len(networks) > 1:
307 for network in networks:
308 if network.label == fixed_network_name:
309 create_kwargs['nics'] = [{'net-id': network.id}]
310 break
311 # If we didn't find the network we were looking for :
312 else:
313 msg = ("The network on which the NIC of the server must "
314 "be connected can not be found : "
315 "fixed_network_name=%s. Starting instance without "
316 "specifying a network.") % fixed_network_name
317 LOG.info(msg)
318
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900319 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
320 name, image, flavor)
321 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +0200322 self.assertEqual(server.name, name)
323 self.set_resource(name, server)
Adam Gandelman4a48a602014-03-20 18:23:18 -0700324 if wait:
325 self.status_timeout(client.servers, server.id, 'ACTIVE')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900326 # The instance retrieved on creation is missing network
327 # details, necessitating retrieval after it becomes active to
328 # ensure correct details.
329 server = client.servers.get(server.id)
330 self.set_resource(name, server)
331 LOG.debug("Created server: %s", server)
332 return server
333
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900334 def create_volume(self, client=None, size=1, name=None,
335 snapshot_id=None, imageRef=None):
336 if client is None:
337 client = self.volume_client
338 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900339 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -0700340 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900341 volume = client.volumes.create(size=size, display_name=name,
342 snapshot_id=snapshot_id,
343 imageRef=imageRef)
344 self.set_resource(name, volume)
345 self.assertEqual(name, volume.display_name)
346 self.status_timeout(client.volumes, volume.id, 'available')
347 LOG.debug("Created volume: %s", volume)
348 return volume
349
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900350 def create_server_snapshot(self, server, compute_client=None,
351 image_client=None, name=None):
352 if compute_client is None:
353 compute_client = self.compute_client
354 if image_client is None:
355 image_client = self.image_client
356 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900357 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900358 LOG.debug("Creating a snapshot image for server: %s", server.name)
359 image_id = compute_client.servers.create_image(server, name)
360 self.addCleanup(image_client.images.delete, image_id)
361 self.status_timeout(image_client.images, image_id, 'active')
362 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -0700363 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900364 LOG.debug("Created snapshot image %s for server %s",
365 snapshot_image.name, server.name)
366 return snapshot_image
367
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900368 def create_keypair(self, client=None, name=None):
369 if client is None:
370 client = self.compute_client
371 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900372 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900373 keypair = client.keypairs.create(name)
374 self.assertEqual(keypair.name, name)
375 self.set_resource(name, keypair)
376 return keypair
377
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900378 def get_remote_client(self, server_or_ip, username=None, private_key=None):
llg821243b20502014-02-22 10:32:49 +0800379 if isinstance(server_or_ip, six.string_types):
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900380 ip = server_or_ip
381 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000382 network_name_for_ssh = CONF.compute.network_for_ssh
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900383 ip = server_or_ip.networks[network_name_for_ssh][0]
384 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000385 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900386 if private_key is None:
387 private_key = self.keypair.private_key
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900388 return remote_client.RemoteClient(ip, username, pkey=private_key)
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900389
Nachi Ueno95b41282014-01-15 06:54:21 -0800390 def _log_console_output(self, servers=None):
391 if not servers:
392 servers = self.compute_client.servers.list()
393 for server in servers:
394 LOG.debug('Console output for %s', server.id)
395 LOG.debug(server.get_console_output())
396
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900397 def wait_for_volume_status(self, status):
398 volume_id = self.volume.id
399 self.status_timeout(
400 self.volume_client.volumes, volume_id, status)
401
402 def _image_create(self, name, fmt, path, properties={}):
403 name = data_utils.rand_name('%s-' % name)
404 image_file = open(path, 'rb')
405 self.addCleanup(image_file.close)
406 params = {
407 'name': name,
408 'container_format': fmt,
409 'disk_format': fmt,
410 'is_public': 'True',
411 }
412 params.update(properties)
413 image = self.image_client.images.create(**params)
414 self.addCleanup(self.image_client.images.delete, image)
415 self.assertEqual("queued", image.status)
416 image.update(data=image_file)
417 return image.id
418
419 def glance_image_create(self):
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900420 qcow2_img_path = (CONF.scenario.img_dir + "/" +
421 CONF.scenario.qcow2_img_file)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900422 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
423 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
424 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900425 LOG.debug("paths: img: %s, ami: %s, ari: %s, aki: %s"
426 % (qcow2_img_path, ami_img_path, ari_img_path, aki_img_path))
427 try:
428 self.image = self._image_create('scenario-img',
429 'bare',
430 qcow2_img_path,
431 properties={'disk_format':
432 'qcow2'})
433 except IOError:
Masayuki Igawa188fc002014-02-23 06:42:44 +0900434 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
Masayuki Igawa4f71bf02014-02-21 14:02:29 +0900435 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
436 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
437 properties = {
438 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
439 }
440 self.image = self._image_create('scenario-ami', 'ami',
441 path=ami_img_path,
442 properties=properties)
443 LOG.debug("image:%s" % self.image)
Masayuki Igawa5cf31902014-02-21 17:30:25 +0900444
Sean Dague6dbc6da2013-05-08 17:49:46 -0400445
Adam Gandelman4a48a602014-03-20 18:23:18 -0700446class BaremetalScenarioTest(OfficialClientTest):
447 @classmethod
448 def setUpClass(cls):
449 super(BaremetalScenarioTest, cls).setUpClass()
450
451 if (not CONF.service_available.ironic or
452 not CONF.baremetal.driver_enabled):
453 msg = 'Ironic not available or Ironic compute driver not enabled'
454 raise cls.skipException(msg)
455
456 # use an admin client manager for baremetal client
457 username, password, tenant = cls.admin_credentials()
458 manager = clients.OfficialClientManager(username, password, tenant)
459 cls.baremetal_client = manager.baremetal_client
460
461 # allow any issues obtaining the node list to raise early
462 cls.baremetal_client.node.list()
463
464 def _node_state_timeout(self, node_id, state_attr,
465 target_states, timeout=10, interval=1):
466 if not isinstance(target_states, list):
467 target_states = [target_states]
468
469 def check_state():
470 node = self.get_node(node_id=node_id)
471 if getattr(node, state_attr) in target_states:
472 return True
473 return False
474
475 if not tempest.test.call_until_true(
476 check_state, timeout, interval):
477 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
478 (node_id, state_attr, target_states))
479 raise exceptions.TimeoutException(msg)
480
481 def wait_provisioning_state(self, node_id, state, timeout):
482 self._node_state_timeout(
483 node_id=node_id, state_attr='provision_state',
484 target_states=state, timeout=timeout)
485
486 def wait_power_state(self, node_id, state):
487 self._node_state_timeout(
488 node_id=node_id, state_attr='power_state',
489 target_states=state, timeout=CONF.baremetal.power_timeout)
490
491 def wait_node(self, instance_id):
492 """Waits for a node to be associated with instance_id."""
493 def _get_node():
494 node = None
495 try:
496 node = self.get_node(instance_id=instance_id)
497 except ironic_exceptions.HTTPNotFound:
498 pass
499 return node is not None
500
501 if not tempest.test.call_until_true(
502 _get_node, CONF.baremetal.association_timeout, 1):
503 msg = ('Timed out waiting to get Ironic node by instance id %s'
504 % instance_id)
505 raise exceptions.TimeoutException(msg)
506
507 def get_node(self, node_id=None, instance_id=None):
508 if node_id:
509 return self.baremetal_client.node.get(node_id)
510 elif instance_id:
511 return self.baremetal_client.node.get_by_instance_uuid(instance_id)
512
513 def get_ports(self, node_id):
514 ports = []
515 for port in self.baremetal_client.node.list_ports(node_id):
516 ports.append(self.baremetal_client.port.get(port.uuid))
517 return ports
518
519
Sean Dague6dbc6da2013-05-08 17:49:46 -0400520class NetworkScenarioTest(OfficialClientTest):
521 """
522 Base class for network scenario tests
523 """
524
525 @classmethod
526 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +0000527 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400528 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +0200529 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -0400530 try:
531 cls.network_client.list_networks()
532 except exc.EndpointNotFound:
533 cls.enabled = False
534 raise
535 else:
536 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -0400537 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -0400538 raise cls.skipException(msg)
539
540 @classmethod
541 def setUpClass(cls):
542 super(NetworkScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +0000543 if CONF.compute.allow_tenant_isolation:
Miguel Lavalleb8fabc52013-08-23 11:19:57 -0500544 cls.tenant_id = cls.isolated_creds.get_primary_tenant().id
545 else:
546 cls.tenant_id = cls.manager._get_identity_client(
Matthew Treinish6c072292014-01-29 19:15:52 +0000547 CONF.identity.username,
548 CONF.identity.password,
549 CONF.identity.tenant_name).tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -0400550
Sean Dague6dbc6da2013-05-08 17:49:46 -0400551 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900552 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400553 body = dict(
554 network=dict(
555 name=name,
556 tenant_id=tenant_id,
557 ),
558 )
559 result = self.network_client.create_network(body=body)
560 network = net_common.DeletableNetwork(client=self.network_client,
561 **result['network'])
562 self.assertEqual(network.name, name)
563 self.set_resource(name, network)
564 return network
565
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200566 def _list_networks(self, **kwargs):
567 nets = self.network_client.list_networks(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400568 return nets['networks']
569
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200570 def _list_subnets(self, **kwargs):
571 subnets = self.network_client.list_subnets(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400572 return subnets['subnets']
573
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200574 def _list_routers(self, **kwargs):
575 routers = self.network_client.list_routers(**kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400576 return routers['routers']
577
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200578 def _list_ports(self, **kwargs):
579 ports = self.network_client.list_ports(**kwargs)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000580 return ports['ports']
581
582 def _get_tenant_own_network_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200583 nets = self._list_networks(tenant_id=tenant_id)
584 return len(nets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000585
586 def _get_tenant_own_subnet_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200587 subnets = self._list_subnets(tenant_id=tenant_id)
588 return len(subnets)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000589
590 def _get_tenant_own_port_num(self, tenant_id):
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200591 ports = self._list_ports(tenant_id=tenant_id)
592 return len(ports)
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000593
Yair Fried3097dc12014-01-26 08:46:43 +0200594 def _create_subnet(self, network, namestart='subnet-smoke-', **kwargs):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400595 """
596 Create a subnet for the given network within the cidr block
597 configured for tenant networks.
598 """
Attila Fazekase857bd62013-10-21 21:02:44 +0200599
600 def cidr_in_use(cidr, tenant_id):
601 """
602 :return True if subnet with cidr already exist in tenant
603 False else
604 """
605 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
606 return len(cidr_in_use) != 0
607
Matthew Treinish6c072292014-01-29 19:15:52 +0000608 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400609 result = None
610 # Repeatedly attempt subnet creation with sequential cidr
611 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +0000612 for subnet_cidr in tenant_cidr.subnet(
613 CONF.network.tenant_network_mask_bits):
Attila Fazekase857bd62013-10-21 21:02:44 +0200614 str_cidr = str(subnet_cidr)
615 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
616 continue
617
Sean Dague6dbc6da2013-05-08 17:49:46 -0400618 body = dict(
619 subnet=dict(
Attila Fazekase857bd62013-10-21 21:02:44 +0200620 name=data_utils.rand_name(namestart),
Sean Dague6dbc6da2013-05-08 17:49:46 -0400621 ip_version=4,
622 network_id=network.id,
623 tenant_id=network.tenant_id,
Attila Fazekase857bd62013-10-21 21:02:44 +0200624 cidr=str_cidr,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400625 ),
626 )
Yair Fried3097dc12014-01-26 08:46:43 +0200627 body['subnet'].update(kwargs)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400628 try:
629 result = self.network_client.create_subnet(body=body)
630 break
Mark McClainf2982e82013-07-06 17:48:03 -0400631 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -0400632 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
633 if not is_overlapping_cidr:
634 raise
635 self.assertIsNotNone(result, 'Unable to allocate tenant network')
636 subnet = net_common.DeletableSubnet(client=self.network_client,
637 **result['subnet'])
Attila Fazekase857bd62013-10-21 21:02:44 +0200638 self.assertEqual(subnet.cidr, str_cidr)
Masayuki Igawa259c1132013-10-31 17:48:44 +0900639 self.set_resource(data_utils.rand_name(namestart), subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400640 return subnet
641
642 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900643 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400644 body = dict(
645 port=dict(name=name,
646 network_id=network.id,
647 tenant_id=network.tenant_id))
648 result = self.network_client.create_port(body=body)
649 self.assertIsNotNone(result, 'Unable to allocate port')
650 port = net_common.DeletablePort(client=self.network_client,
651 **result['port'])
652 self.set_resource(name, port)
653 return port
654
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200655 def _get_server_port_id(self, server, ip_addr=None):
656 ports = self._list_ports(device_id=server.id, fixed_ip=ip_addr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400657 self.assertEqual(len(ports), 1,
658 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +0200659 return ports[0]['id']
660
Yair Frieda2e3b2c2014-02-17 10:56:10 +0200661 def _create_floating_ip(self, thing, external_network_id, port_id=None):
662 if not port_id:
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400663 port_id = self._get_server_port_id(thing)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400664 body = dict(
665 floatingip=dict(
666 floating_network_id=external_network_id,
667 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400668 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400669 )
670 )
671 result = self.network_client.create_floatingip(body=body)
672 floating_ip = net_common.DeletableFloatingIp(
673 client=self.network_client,
674 **result['floatingip'])
Masayuki Igawa259c1132013-10-31 17:48:44 +0900675 self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400676 return floating_ip
677
Yair Fried05db2522013-11-18 11:02:10 +0200678 def _associate_floating_ip(self, floating_ip, server):
679 port_id = self._get_server_port_id(server)
680 floating_ip.update(port_id=port_id)
681 self.assertEqual(port_id, floating_ip.port_id)
682 return floating_ip
683
Yair Fried9a551c42013-12-15 14:59:34 +0200684 def _disassociate_floating_ip(self, floating_ip):
685 """
686 :param floating_ip: type DeletableFloatingIp
687 """
688 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +0800689 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +0200690 return floating_ip
691
692 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400693 cmd = ['ping', '-c1', '-w1', ip_address]
694
695 def ping():
696 proc = subprocess.Popen(cmd,
697 stdout=subprocess.PIPE,
698 stderr=subprocess.PIPE)
699 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +0200700 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -0400701
Nachi Ueno6d580be2013-07-24 10:58:11 -0700702 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +0000703 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +0000704
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400705 def _create_pool(self, lb_method, protocol, subnet_id):
706 """Wrapper utility that returns a test pool."""
707 name = data_utils.rand_name('pool-')
708 body = {
709 "pool": {
710 "protocol": protocol,
711 "name": name,
712 "subnet_id": subnet_id,
713 "lb_method": lb_method
714 }
715 }
716 resp = self.network_client.create_pool(body=body)
717 pool = net_common.DeletablePool(client=self.network_client,
718 **resp['pool'])
719 self.assertEqual(pool['name'], name)
720 self.set_resource(name, pool)
721 return pool
722
723 def _create_member(self, address, protocol_port, pool_id):
724 """Wrapper utility that returns a test member."""
725 body = {
726 "member": {
727 "protocol_port": protocol_port,
728 "pool_id": pool_id,
729 "address": address
730 }
731 }
732 resp = self.network_client.create_member(body)
733 member = net_common.DeletableMember(client=self.network_client,
734 **resp['member'])
735 self.set_resource(data_utils.rand_name('member-'), member)
736 return member
737
738 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
739 """Wrapper utility that returns a test vip."""
740 name = data_utils.rand_name('vip-')
741 body = {
742 "vip": {
743 "protocol": protocol,
744 "name": name,
745 "subnet_id": subnet_id,
746 "pool_id": pool_id,
747 "protocol_port": protocol_port
748 }
749 }
750 resp = self.network_client.create_vip(body)
751 vip = net_common.DeletableVip(client=self.network_client,
752 **resp['vip'])
753 self.assertEqual(vip['name'], name)
754 self.set_resource(name, vip)
755 return vip
756
Yair Fried9a551c42013-12-15 14:59:34 +0200757 def _check_vm_connectivity(self, ip_address,
758 username=None,
759 private_key=None,
760 should_connect=True):
761 """
762 :param ip_address: server to test against
763 :param username: server's ssh username
764 :param private_key: server's ssh private key to be used
765 :param should_connect: True/False indicates positive/negative test
766 positive - attempt ping and ssh
767 negative - attempt ping and fail if succeed
768
769 :raises: AssertError if the result of the connectivity check does
770 not match the value of the should_connect param
771 """
772 if should_connect:
773 msg = "Timed out waiting for %s to become reachable" % ip_address
774 else:
775 msg = "ip address %s is reachable" % ip_address
776 self.assertTrue(self._ping_ip_address(ip_address,
777 should_succeed=should_connect),
778 msg=msg)
779 if should_connect:
780 # no need to check ssh for negative connectivity
Attila Fazekasad7ef7d2013-11-20 10:12:53 +0100781 linux_client = self.get_remote_client(ip_address, username,
782 private_key)
783 linux_client.validate_authentication()
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200784
Yair Fried3097dc12014-01-26 08:46:43 +0200785 def _check_remote_connectivity(self, source, dest, should_succeed=True):
786 """
787 check ping server via source ssh connection
788
789 :param source: RemoteClient: an ssh connection from which to ping
790 :param dest: and IP to ping against
791 :param should_succeed: boolean should ping succeed or not
792 :returns: boolean -- should_succeed == ping
793 :returns: ping is false if ping failed
794 """
795 def ping_remote():
796 try:
797 source.ping_host(dest)
798 except exceptions.SSHExecCommandFailed:
799 LOG.exception('Failed to ping host via ssh connection')
800 return not should_succeed
801 return should_succeed
802
803 return tempest.test.call_until_true(ping_remote,
804 CONF.compute.ping_timeout,
805 1)
806
Yair Friedeb69f3f2013-10-10 13:18:16 +0300807 def _create_security_group_nova(self, client=None,
808 namestart='secgroup-smoke-',
809 tenant_id=None):
810 if client is None:
811 client = self.compute_client
812 # Create security group
813 sg_name = data_utils.rand_name(namestart)
814 sg_desc = sg_name + " description"
815 secgroup = client.security_groups.create(sg_name, sg_desc)
816 self.assertEqual(secgroup.name, sg_name)
817 self.assertEqual(secgroup.description, sg_desc)
818 self.set_resource(sg_name, secgroup)
819
820 # Add rules to the security group
821 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
822
823 return secgroup
824
825 def _create_security_group_neutron(self, tenant_id, client=None,
826 namestart='secgroup-smoke-'):
827 if client is None:
828 client = self.network_client
829 secgroup = self._create_empty_security_group(namestart=namestart,
830 client=client,
831 tenant_id=tenant_id)
832
833 # Add rules to the security group
834 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
835 for rule in rules:
836 self.assertEqual(tenant_id, rule.tenant_id)
837 self.assertEqual(secgroup.id, rule.security_group_id)
838 return secgroup
839
840 def _create_empty_security_group(self, tenant_id, client=None,
841 namestart='secgroup-smoke-'):
842 """Create a security group without rules.
843
844 Default rules will be created:
845 - IPv4 egress to any
846 - IPv6 egress to any
847
848 :param tenant_id: secgroup will be created in this tenant
849 :returns: DeletableSecurityGroup -- containing the secgroup created
850 """
851 if client is None:
852 client = self.network_client
853 sg_name = data_utils.rand_name(namestart)
854 sg_desc = sg_name + " description"
855 sg_dict = dict(name=sg_name,
856 description=sg_desc)
857 sg_dict['tenant_id'] = tenant_id
858 body = dict(security_group=sg_dict)
859 result = client.create_security_group(body=body)
860 secgroup = net_common.DeletableSecurityGroup(
861 client=client,
862 **result['security_group']
863 )
864 self.assertEqual(secgroup.name, sg_name)
865 self.assertEqual(tenant_id, secgroup.tenant_id)
866 self.assertEqual(secgroup.description, sg_desc)
867 self.set_resource(sg_name, secgroup)
868 return secgroup
869
870 def _default_security_group(self, tenant_id, client=None):
871 """Get default secgroup for given tenant_id.
872
873 :returns: DeletableSecurityGroup -- default secgroup for given tenant
874 """
875 if client is None:
876 client = self.network_client
877 sgs = [
878 sg for sg in client.list_security_groups().values()[0]
879 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
880 ]
881 msg = "No default security group for tenant %s." % (tenant_id)
882 self.assertTrue(len(sgs) > 0, msg)
883 if len(sgs) > 1:
884 msg = "Found %d default security groups" % len(sgs)
885 raise exc.NeutronClientNoUniqueMatch(msg=msg)
886 return net_common.DeletableSecurityGroup(client=client,
887 **sgs[0])
888
889 def _create_security_group_rule(self, client=None, secgroup=None,
890 tenant_id=None, **kwargs):
891 """Create a rule from a dictionary of rule parameters.
892
893 Create a rule in a secgroup. if secgroup not defined will search for
894 default secgroup in tenant_id.
895
896 :param secgroup: type DeletableSecurityGroup.
897 :param secgroup_id: search for secgroup by id
898 default -- choose default secgroup for given tenant_id
899 :param tenant_id: if secgroup not passed -- the tenant in which to
900 search for default secgroup
901 :param kwargs: a dictionary containing rule parameters:
902 for example, to allow incoming ssh:
903 rule = {
904 direction: 'ingress'
905 protocol:'tcp',
906 port_range_min: 22,
907 port_range_max: 22
908 }
909 """
910 if client is None:
911 client = self.network_client
912 if secgroup is None:
913 secgroup = self._default_security_group(tenant_id)
914
915 ruleset = dict(security_group_id=secgroup.id,
916 tenant_id=secgroup.tenant_id,
917 )
918 ruleset.update(kwargs)
919
920 body = dict(security_group_rule=dict(ruleset))
921 sg_rule = client.create_security_group_rule(body=body)
922 sg_rule = net_common.DeletableSecurityGroupRule(
923 client=client,
924 **sg_rule['security_group_rule']
925 )
926 self.set_resource(sg_rule.id, sg_rule)
927 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
928 self.assertEqual(secgroup.id, sg_rule.security_group_id)
929
930 return sg_rule
931
932 def _create_loginable_secgroup_rule_neutron(self, client=None,
933 secgroup=None):
934 """These rules are intended to permit inbound ssh and icmp
935 traffic from all sources, so no group_id is provided.
936 Setting a group_id would only permit traffic from ports
937 belonging to the same security group.
938 """
939
940 if client is None:
941 client = self.network_client
942 rules = []
943 rulesets = [
944 dict(
945 # ssh
946 protocol='tcp',
947 port_range_min=22,
948 port_range_max=22,
949 ),
950 dict(
951 # ping
952 protocol='icmp',
953 )
954 ]
955 for ruleset in rulesets:
956 for r_direction in ['ingress', 'egress']:
957 ruleset['direction'] = r_direction
958 try:
959 sg_rule = self._create_security_group_rule(
960 client=client, secgroup=secgroup, **ruleset)
961 except exc.NeutronClientException as ex:
962 # if rule already exist - skip rule and continue
963 if not (ex.status_code is 409 and 'Security group rule'
964 ' already exists' in ex.message):
965 raise ex
966 else:
967 self.assertEqual(r_direction, sg_rule.direction)
968 rules.append(sg_rule)
969
970 return rules
971
Yair Fried5f670ab2013-12-09 09:26:51 +0200972 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +0000973 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +0200974 return self.get_remote_client(server,
975 username=ssh_login,
976 private_key=private_key)
977
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000978 def _show_quota_network(self, tenant_id):
979 quota = self.network_client.show_quota(tenant_id)
980 return quota['quota']['network']
981
982 def _show_quota_subnet(self, tenant_id):
983 quota = self.network_client.show_quota(tenant_id)
984 return quota['quota']['subnet']
985
986 def _show_quota_port(self, tenant_id):
987 quota = self.network_client.show_quota(tenant_id)
988 return quota['quota']['port']
989
Yair Fried4d7efa62013-11-17 17:12:29 +0200990 def _get_router(self, tenant_id):
991 """Retrieve a router for the given tenant id.
992
993 If a public router has been configured, it will be returned.
994
995 If a public router has not been configured, but a public
996 network has, a tenant router will be created and returned that
997 routes traffic to the public network.
998 """
Matthew Treinish6c072292014-01-29 19:15:52 +0000999 router_id = CONF.network.public_router_id
1000 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +02001001 if router_id:
1002 result = self.network_client.show_router(router_id)
1003 return net_common.AttributeDict(**result['router'])
1004 elif network_id:
1005 router = self._create_router(tenant_id)
1006 router.add_gateway(network_id)
1007 return router
1008 else:
1009 raise Exception("Neither of 'public_router_id' or "
1010 "'public_network_id' has been defined.")
1011
1012 def _create_router(self, tenant_id, namestart='router-smoke-'):
1013 name = data_utils.rand_name(namestart)
1014 body = dict(
1015 router=dict(
1016 name=name,
1017 admin_state_up=True,
1018 tenant_id=tenant_id,
1019 ),
1020 )
1021 result = self.network_client.create_router(body=body)
1022 router = net_common.DeletableRouter(client=self.network_client,
1023 **result['router'])
1024 self.assertEqual(router.name, name)
1025 self.set_resource(name, router)
1026 return router
1027
1028 def _create_networks(self, tenant_id=None):
1029 """Create a network with a subnet connected to a router.
1030
1031 :returns: network, subnet, router
1032 """
1033 if tenant_id is None:
1034 tenant_id = self.tenant_id
1035 network = self._create_network(tenant_id)
1036 router = self._get_router(tenant_id)
1037 subnet = self._create_subnet(network)
1038 subnet.add_to_router(router.id)
Yair Fried4d7efa62013-11-17 17:12:29 +02001039 return network, subnet, router
1040
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001041
1042class OrchestrationScenarioTest(OfficialClientTest):
1043 """
1044 Base class for orchestration scenario tests
1045 """
1046
1047 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001048 def setUpClass(cls):
1049 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001050 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001051 raise cls.skipException("Heat support is required")
1052
1053 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001054 def credentials(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001055 username = CONF.identity.admin_username
1056 password = CONF.identity.admin_password
1057 tenant_name = CONF.identity.tenant_name
Ryan Hsuee1017c2013-12-20 12:00:34 -08001058 return username, password, tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001059
1060 def _load_template(self, base_file, file_name):
1061 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1062 file_name)
1063 with open(filepath) as f:
1064 return f.read()
1065
1066 @classmethod
1067 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001068 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001069
1070 @classmethod
1071 def _get_default_network(cls):
1072 networks = cls.network_client.list_networks()
1073 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00001074 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001075 return net