blob: 9988e45d788dcd640bfe4a6ca82c616b8ade7bc2 [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
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import subprocess
20
21# Default client libs
Masayuki Igawa73d9f3a2013-05-24 10:30:01 +090022import cinderclient.client
Sean Dague6dbc6da2013-05-08 17:49:46 -040023import glanceclient
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120024import heatclient.client
Andrey Kurilin5fd3ae02014-02-04 12:20:46 +020025import keystoneclient.exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040026import keystoneclient.v2_0.client
27import netaddr
Mark McClainf2982e82013-07-06 17:48:03 -040028from neutronclient.common import exceptions as exc
29import neutronclient.v2_0.client
Sean Dague6dbc6da2013-05-08 17:49:46 -040030import novaclient.client
fujioka yuuichi636f8db2013-08-09 12:05:24 +090031from novaclient import exceptions as nova_exceptions
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +000032import swiftclient
Sean Dague6dbc6da2013-05-08 17:49:46 -040033
Sean Dague1937d092013-05-17 16:36:38 -040034from tempest.api.network import common as net_common
Matthew Treinishb86cda92013-07-29 11:22:23 -040035from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090036from tempest.common.utils import data_utils
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +090037from tempest.common.utils.linux.remote_client import RemoteClient
Matthew Treinish6c072292014-01-29 19:15:52 +000038from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020039from tempest import exceptions
Sean Dague6dbc6da2013-05-08 17:49:46 -040040import tempest.manager
Attila Fazekasfb7552a2013-08-27 13:02:26 +020041from tempest.openstack.common import log
Sean Dague6dbc6da2013-05-08 17:49:46 -040042import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040043
Matthew Treinish6c072292014-01-29 19:15:52 +000044CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040045
Attila Fazekasfb7552a2013-08-27 13:02:26 +020046LOG = log.getLogger(__name__)
47
48# NOTE(afazekas): Workaround for the stdout logging
49LOG_nova_client = logging.getLogger('novaclient.client')
50LOG_nova_client.addHandler(log.NullHandler())
51
52LOG_cinder_client = logging.getLogger('cinderclient.client')
53LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040054
55
56class OfficialClientManager(tempest.manager.Manager):
57 """
58 Manager that provides access to the official python clients for
59 calling various OpenStack APIs.
60 """
61
62 NOVACLIENT_VERSION = '2'
Masayuki Igawa73d9f3a2013-05-24 10:30:01 +090063 CINDERCLIENT_VERSION = '1'
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120064 HEATCLIENT_VERSION = '1'
Sean Dague6dbc6da2013-05-08 17:49:46 -040065
Matthew Treinishb86cda92013-07-29 11:22:23 -040066 def __init__(self, username, password, tenant_name):
Sean Dague6dbc6da2013-05-08 17:49:46 -040067 super(OfficialClientManager, self).__init__()
Matthew Treinishb86cda92013-07-29 11:22:23 -040068 self.compute_client = self._get_compute_client(username,
69 password,
70 tenant_name)
71 self.identity_client = self._get_identity_client(username,
72 password,
73 tenant_name)
Sean Dague6dbc6da2013-05-08 17:49:46 -040074 self.image_client = self._get_image_client()
Sean Dague6dbc6da2013-05-08 17:49:46 -040075 self.network_client = self._get_network_client()
Matthew Treinishb86cda92013-07-29 11:22:23 -040076 self.volume_client = self._get_volume_client(username,
77 password,
78 tenant_name)
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +000079 self.object_storage_client = self._get_object_storage_client(
80 username,
81 password,
82 tenant_name)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120083 self.orchestration_client = self._get_orchestration_client(
84 username,
85 password,
86 tenant_name)
Sean Dague6dbc6da2013-05-08 17:49:46 -040087
Matthew Treinishb86cda92013-07-29 11:22:23 -040088 def _get_compute_client(self, username, password, tenant_name):
Sean Dague6dbc6da2013-05-08 17:49:46 -040089 # Novaclient will not execute operations for anyone but the
90 # identified user, so a new client needs to be created for
91 # each user that operations need to be performed for.
Sean Dague43cd9052013-07-19 12:20:04 -040092 self._validate_credentials(username, password, tenant_name)
Sean Dague6dbc6da2013-05-08 17:49:46 -040093
Matthew Treinish6c072292014-01-29 19:15:52 +000094 auth_url = CONF.identity.uri
95 dscv = CONF.identity.disable_ssl_certificate_validation
96 region = CONF.identity.region
Sean Dague6dbc6da2013-05-08 17:49:46 -040097
98 client_args = (username, password, tenant_name, auth_url)
99
100 # Create our default Nova client to use in testing
Matthew Treinish6c072292014-01-29 19:15:52 +0000101 service_type = CONF.compute.catalog_type
Sean Dague6dbc6da2013-05-08 17:49:46 -0400102 return novaclient.client.Client(self.NOVACLIENT_VERSION,
103 *client_args,
104 service_type=service_type,
Russell Sim1fd81ce2013-11-07 17:04:21 +1100105 region_name=region,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400106 no_cache=True,
Attila Fazekasfb7552a2013-08-27 13:02:26 +0200107 insecure=dscv,
108 http_log_debug=True)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400109
110 def _get_image_client(self):
Matthew Treinishb86cda92013-07-29 11:22:23 -0400111 token = self.identity_client.auth_token
Matthew Treinish6c072292014-01-29 19:15:52 +0000112 region = CONF.identity.region
Matthew Treinishb86cda92013-07-29 11:22:23 -0400113 endpoint = self.identity_client.service_catalog.url_for(
Russell Sim1fd81ce2013-11-07 17:04:21 +1100114 attr='region', filter_value=region,
Matt Riedemannd3efe902014-02-10 06:46:38 -0800115 service_type=CONF.image.catalog_type, endpoint_type='publicURL')
Matthew Treinish6c072292014-01-29 19:15:52 +0000116 dscv = CONF.identity.disable_ssl_certificate_validation
Sean Dague6dbc6da2013-05-08 17:49:46 -0400117 return glanceclient.Client('1', endpoint=endpoint, token=token,
118 insecure=dscv)
119
Matthew Treinishb86cda92013-07-29 11:22:23 -0400120 def _get_volume_client(self, username, password, tenant_name):
Matthew Treinish6c072292014-01-29 19:15:52 +0000121 auth_url = CONF.identity.uri
122 region = CONF.identity.region
Masayuki Igawa73d9f3a2013-05-24 10:30:01 +0900123 return cinderclient.client.Client(self.CINDERCLIENT_VERSION,
124 username,
125 password,
126 tenant_name,
Attila Fazekasfb7552a2013-08-27 13:02:26 +0200127 auth_url,
Russell Sim1fd81ce2013-11-07 17:04:21 +1100128 region_name=region,
Attila Fazekasfb7552a2013-08-27 13:02:26 +0200129 http_log_debug=True)
Masayuki Igawa73d9f3a2013-05-24 10:30:01 +0900130
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000131 def _get_object_storage_client(self, username, password, tenant_name):
Matthew Treinish6c072292014-01-29 19:15:52 +0000132 auth_url = CONF.identity.uri
Qiu Hua Qiao5384ef52014-01-10 07:55:19 -0600133 # add current tenant to swift operator role group.
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000134 keystone_admin = self._get_identity_client(
Matthew Treinish6c072292014-01-29 19:15:52 +0000135 CONF.identity.admin_username,
136 CONF.identity.admin_password,
137 CONF.identity.admin_tenant_name)
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000138
Qiu Hua Qiao5384ef52014-01-10 07:55:19 -0600139 # enable test user to operate swift by adding operator role to him.
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000140 roles = keystone_admin.roles.list()
Matthew Treinish6c072292014-01-29 19:15:52 +0000141 operator_role = CONF.object_storage.operator_role
Qiu Hua Qiao5384ef52014-01-10 07:55:19 -0600142 member_role = [role for role in roles if role.name == operator_role][0]
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000143 # NOTE(maurosr): This is surrounded in the try-except block cause
144 # neutron tests doesn't have tenant isolation.
145 try:
146 keystone_admin.roles.add_user_role(self.identity_client.user_id,
147 member_role.id,
148 self.identity_client.tenant_id)
Andrey Kurilin5fd3ae02014-02-04 12:20:46 +0200149 except keystoneclient.exceptions.Conflict:
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000150 pass
151
152 return swiftclient.Connection(auth_url, username, password,
153 tenant_name=tenant_name,
154 auth_version='2')
155
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200156 def _get_orchestration_client(self, username=None, password=None,
157 tenant_name=None):
158 if not username:
Matthew Treinish6c072292014-01-29 19:15:52 +0000159 username = CONF.identity.admin_username
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200160 if not password:
Matthew Treinish6c072292014-01-29 19:15:52 +0000161 password = CONF.identity.admin_password
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200162 if not tenant_name:
Matthew Treinish6c072292014-01-29 19:15:52 +0000163 tenant_name = CONF.identity.tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200164
165 self._validate_credentials(username, password, tenant_name)
166
167 keystone = self._get_identity_client(username, password, tenant_name)
Matthew Treinish6c072292014-01-29 19:15:52 +0000168 region = CONF.identity.region
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200169 token = keystone.auth_token
Matt Riedemann1026e2b2014-01-26 11:34:12 -0800170 service_type = CONF.orchestration.catalog_type
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200171 try:
172 endpoint = keystone.service_catalog.url_for(
Russell Sim1fd81ce2013-11-07 17:04:21 +1100173 attr='region',
174 filter_value=region,
Matt Riedemann1026e2b2014-01-26 11:34:12 -0800175 service_type=service_type,
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200176 endpoint_type='publicURL')
177 except keystoneclient.exceptions.EndpointNotFound:
178 return None
179 else:
180 return heatclient.client.Client(self.HEATCLIENT_VERSION,
181 endpoint,
182 token=token,
183 username=username,
184 password=password)
185
Matthew Treinishb86cda92013-07-29 11:22:23 -0400186 def _get_identity_client(self, username, password, tenant_name):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400187 # This identity client is not intended to check the security
188 # of the identity service, so use admin credentials by default.
Sean Dague43cd9052013-07-19 12:20:04 -0400189 self._validate_credentials(username, password, tenant_name)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400190
Matthew Treinish6c072292014-01-29 19:15:52 +0000191 auth_url = CONF.identity.uri
192 dscv = CONF.identity.disable_ssl_certificate_validation
Sean Dague6dbc6da2013-05-08 17:49:46 -0400193
194 return keystoneclient.v2_0.client.Client(username=username,
195 password=password,
196 tenant_name=tenant_name,
197 auth_url=auth_url,
198 insecure=dscv)
199
200 def _get_network_client(self):
201 # The intended configuration is for the network client to have
202 # admin privileges and indicate for whom resources are being
203 # created via a 'tenant_id' parameter. This will often be
204 # preferable to authenticating as a specific user because
205 # working with certain resources (public routers and networks)
206 # often requires admin privileges anyway.
Matthew Treinish6c072292014-01-29 19:15:52 +0000207 username = CONF.identity.admin_username
208 password = CONF.identity.admin_password
209 tenant_name = CONF.identity.admin_tenant_name
Sean Dague6dbc6da2013-05-08 17:49:46 -0400210
Sean Dague43cd9052013-07-19 12:20:04 -0400211 self._validate_credentials(username, password, tenant_name)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400212
Matthew Treinish6c072292014-01-29 19:15:52 +0000213 auth_url = CONF.identity.uri
214 dscv = CONF.identity.disable_ssl_certificate_validation
Sean Dague6dbc6da2013-05-08 17:49:46 -0400215
Mark McClainf2982e82013-07-06 17:48:03 -0400216 return neutronclient.v2_0.client.Client(username=username,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400217 password=password,
218 tenant_name=tenant_name,
219 auth_url=auth_url,
220 insecure=dscv)
221
222
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400223class OfficialClientTest(tempest.test.BaseTestCase):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400224 """
225 Official Client test base class for scenario testing.
226
227 Official Client tests are tests that have the following characteristics:
228
229 * Test basic operations of an API, typically in an order that
230 a regular user would perform those operations
231 * Test only the correct inputs and action paths -- no fuzz or
232 random input data is sent, only valid inputs.
233 * Use only the default client tool for calling an API
234 """
235
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400236 @classmethod
237 def setUpClass(cls):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200238 super(OfficialClientTest, cls).setUpClass()
Matthew Treinishb86cda92013-07-29 11:22:23 -0400239 cls.isolated_creds = isolated_creds.IsolatedCreds(
Sean Dague6969b902014-01-28 06:48:37 -0500240 cls.__name__, tempest_client=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500241 network_resources=cls.network_resources)
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200242
Yair Fried769bbff2013-12-18 16:33:17 +0200243 username, password, tenant_name = cls.credentials()
Matthew Treinishb86cda92013-07-29 11:22:23 -0400244
245 cls.manager = OfficialClientManager(username, password, tenant_name)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400246 cls.compute_client = cls.manager.compute_client
247 cls.image_client = cls.manager.image_client
248 cls.identity_client = cls.manager.identity_client
249 cls.network_client = cls.manager.network_client
250 cls.volume_client = cls.manager.volume_client
Mauro S. M. Rodriguese86ed042013-12-12 18:56:00 +0000251 cls.object_storage_client = cls.manager.object_storage_client
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200252 cls.orchestration_client = cls.manager.orchestration_client
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400253 cls.resource_keys = {}
254 cls.os_resources = []
Sean Dague6dbc6da2013-05-08 17:49:46 -0400255
256 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +0200257 def _get_credentials(cls, get_creds, prefix):
Matthew Treinish6c072292014-01-29 19:15:52 +0000258 if CONF.compute.allow_tenant_isolation:
Yair Fried769bbff2013-12-18 16:33:17 +0200259 username, tenant_name, password = get_creds()
260 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000261 username = getattr(CONF.identity, prefix + 'username')
262 password = getattr(CONF.identity, prefix + 'password')
263 tenant_name = getattr(CONF.identity, prefix + 'tenant_name')
Yair Fried769bbff2013-12-18 16:33:17 +0200264 return username, password, tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200265
266 @classmethod
Yair Frieda71cc442013-12-18 13:32:36 +0200267 def credentials(cls):
268 return cls._get_credentials(cls.isolated_creds.get_primary_creds, '')
269
270 @classmethod
271 def alt_credentials(cls):
272 return cls._get_credentials(cls.isolated_creds.get_alt_creds, 'alt_')
273
274 @classmethod
275 def admin_credentials(cls):
276 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
277 'admin_')
278
279 @classmethod
Sean Dague6dbc6da2013-05-08 17:49:46 -0400280 def tearDownClass(cls):
281 # NOTE(jaypipes): Because scenario tests are typically run in a
282 # specific order, and because test methods in scenario tests
283 # generally create resources in a particular order, we destroy
284 # resources in the reverse order in which resources are added to
285 # the scenario test class object
286 while cls.os_resources:
287 thing = cls.os_resources.pop()
288 LOG.debug("Deleting %r from shared resources of %s" %
289 (thing, cls.__name__))
290
291 try:
292 # OpenStack resources are assumed to have a delete()
293 # method which destroys the resource...
294 thing.delete()
295 except Exception as e:
296 # If the resource is already missing, mission accomplished.
Yair Fried4d7efa62013-11-17 17:12:29 +0200297 # add status code as workaround for bug 1247568
Attila Fazekas09925972013-12-19 16:16:49 +0100298 if (e.__class__.__name__ == 'NotFound' or
299 hasattr(e, 'status_code') and e.status_code == 404):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400300 continue
301 raise
302
303 def is_deletion_complete():
304 # Deletion testing is only required for objects whose
305 # existence cannot be checked via retrieval.
306 if isinstance(thing, dict):
307 return True
308 try:
309 thing.get()
310 except Exception as e:
311 # Clients are expected to return an exception
312 # called 'NotFound' if retrieval fails.
313 if e.__class__.__name__ == 'NotFound':
314 return True
315 raise
316 return False
317
318 # Block until resource deletion has completed or timed-out
319 tempest.test.call_until_true(is_deletion_complete, 10, 1)
Matthew Treinishb86cda92013-07-29 11:22:23 -0400320 cls.isolated_creds.clear_isolated_creds()
321 super(OfficialClientTest, cls).tearDownClass()
Sean Dague6dbc6da2013-05-08 17:49:46 -0400322
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400323 @classmethod
324 def set_resource(cls, key, thing):
325 LOG.debug("Adding %r to shared resources of %s" %
326 (thing, cls.__name__))
327 cls.resource_keys[key] = thing
328 cls.os_resources.append(thing)
329
330 @classmethod
331 def get_resource(cls, key):
332 return cls.resource_keys[key]
333
334 @classmethod
335 def remove_resource(cls, key):
336 thing = cls.resource_keys[key]
337 cls.os_resources.remove(thing)
338 del cls.resource_keys[key]
339
Steve Bakerefde7612013-09-30 11:29:23 +1300340 def status_timeout(self, things, thing_id, expected_status,
341 error_status='ERROR',
342 not_found_exception=nova_exceptions.NotFound):
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400343 """
344 Given a thing and an expected status, do a loop, sleeping
345 for a configurable amount of time, checking for the
346 expected status to show. At any time, if the returned
347 status of the thing is ERROR, fail out.
348 """
Steve Bakerefde7612013-09-30 11:29:23 +1300349 self._status_timeout(things, thing_id,
350 expected_status=expected_status,
351 error_status=error_status,
352 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900353
Steve Bakerefde7612013-09-30 11:29:23 +1300354 def delete_timeout(self, things, thing_id,
355 error_status='ERROR',
356 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900357 """
358 Given a thing, do a loop, sleeping
359 for a configurable amount of time, checking for the
360 deleted status to show. At any time, if the returned
361 status of the thing is ERROR, fail out.
362 """
363 self._status_timeout(things,
364 thing_id,
Steve Bakerefde7612013-09-30 11:29:23 +1300365 allow_notfound=True,
366 error_status=error_status,
367 not_found_exception=not_found_exception)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900368
369 def _status_timeout(self,
370 things,
371 thing_id,
372 expected_status=None,
Steve Bakerefde7612013-09-30 11:29:23 +1300373 allow_notfound=False,
374 error_status='ERROR',
375 not_found_exception=nova_exceptions.NotFound):
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900376
377 log_status = expected_status if expected_status else ''
378 if allow_notfound:
379 log_status += ' or NotFound' if log_status != '' else 'NotFound'
380
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400381 def check_status():
382 # python-novaclient has resources available to its client
383 # that all implement a get() method taking an identifier
384 # for the singular resource to retrieve.
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900385 try:
386 thing = things.get(thing_id)
Steve Bakerefde7612013-09-30 11:29:23 +1300387 except not_found_exception:
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900388 if allow_notfound:
389 return True
390 else:
391 raise
392
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400393 new_status = thing.status
Brent Eaglesc26d4522013-12-02 13:28:49 -0500394
395 # Some components are reporting error status in lower case
396 # so case sensitive comparisons can really mess things
397 # up.
398 if new_status.lower() == error_status.lower():
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +0900399 message = ("%s failed to get to expected status. "
400 "In %s state.") % (thing, new_status)
Masayuki Igawaa0e786a2014-01-27 15:25:06 +0900401 raise exceptions.BuildErrorException(message,
402 server_id=thing_id)
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900403 elif new_status == expected_status and expected_status is not None:
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400404 return True # All good.
405 LOG.debug("Waiting for %s to get to %s status. "
406 "Currently in %s status",
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900407 thing, log_status, new_status)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400408 if not tempest.test.call_until_true(
409 check_status,
Matthew Treinish6c072292014-01-29 19:15:52 +0000410 CONF.compute.build_timeout,
411 CONF.compute.build_interval):
Ken'ichi Ohmichiab1496f2013-12-12 22:17:57 +0900412 message = ("Timed out waiting for thing %s "
413 "to become %s") % (thing_id, log_status)
Giulio Fidente92f77192013-08-26 17:13:28 +0200414 raise exceptions.TimeoutException(message)
Matthew Treinish0ae79ce2013-08-08 14:31:05 -0400415
Yair Friedeb69f3f2013-10-10 13:18:16 +0300416 def _create_loginable_secgroup_rule_nova(self, client=None,
417 secgroup_id=None):
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900418 if client is None:
419 client = self.compute_client
420 if secgroup_id is None:
421 sgs = client.security_groups.list()
422 for sg in sgs:
423 if sg.name == 'default':
424 secgroup_id = sg.id
425
426 # These rules are intended to permit inbound ssh and icmp
427 # traffic from all sources, so no group_id is provided.
428 # Setting a group_id would only permit traffic from ports
429 # belonging to the same security group.
430 rulesets = [
431 {
432 # ssh
433 'ip_protocol': 'tcp',
434 'from_port': 22,
435 'to_port': 22,
436 'cidr': '0.0.0.0/0',
437 },
438 {
439 # ping
440 'ip_protocol': 'icmp',
441 'from_port': -1,
442 'to_port': -1,
443 'cidr': '0.0.0.0/0',
444 }
445 ]
Yair Friedeb69f3f2013-10-10 13:18:16 +0300446 rules = list()
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900447 for ruleset in rulesets:
448 sg_rule = client.security_group_rules.create(secgroup_id,
449 **ruleset)
450 self.set_resource(sg_rule.id, sg_rule)
Yair Friedeb69f3f2013-10-10 13:18:16 +0300451 rules.append(sg_rule)
452 return rules
Ken'ichi Ohmichi3c1f5192013-08-19 19:02:15 +0900453
Giulio Fidente61cadca2013-09-24 18:33:37 +0200454 def create_server(self, client=None, name=None, image=None, flavor=None,
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900455 create_kwargs={}):
Giulio Fidente61cadca2013-09-24 18:33:37 +0200456 if client is None:
457 client = self.compute_client
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900458 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900459 name = data_utils.rand_name('scenario-server-')
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900460 if image is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000461 image = CONF.compute.image_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900462 if flavor is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000463 flavor = CONF.compute.flavor_ref
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900464 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
465 name, image, flavor)
466 server = client.servers.create(name, image, flavor, **create_kwargs)
Giulio Fidente92f77192013-08-26 17:13:28 +0200467 self.assertEqual(server.name, name)
468 self.set_resource(name, server)
Ken'ichi Ohmichi61f272b2013-08-15 15:58:53 +0900469 self.status_timeout(client.servers, server.id, 'ACTIVE')
470 # The instance retrieved on creation is missing network
471 # details, necessitating retrieval after it becomes active to
472 # ensure correct details.
473 server = client.servers.get(server.id)
474 self.set_resource(name, server)
475 LOG.debug("Created server: %s", server)
476 return server
477
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900478 def create_volume(self, client=None, size=1, name=None,
479 snapshot_id=None, imageRef=None):
480 if client is None:
481 client = self.volume_client
482 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900483 name = data_utils.rand_name('scenario-volume-')
Eric Windisch2d26f1b2013-09-04 17:52:16 -0700484 LOG.debug("Creating a volume (size: %s, name: %s)", size, name)
Ken'ichi Ohmichi70672df2013-08-19 18:35:19 +0900485 volume = client.volumes.create(size=size, display_name=name,
486 snapshot_id=snapshot_id,
487 imageRef=imageRef)
488 self.set_resource(name, volume)
489 self.assertEqual(name, volume.display_name)
490 self.status_timeout(client.volumes, volume.id, 'available')
491 LOG.debug("Created volume: %s", volume)
492 return volume
493
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900494 def create_server_snapshot(self, server, compute_client=None,
495 image_client=None, name=None):
496 if compute_client is None:
497 compute_client = self.compute_client
498 if image_client is None:
499 image_client = self.image_client
500 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900501 name = data_utils.rand_name('scenario-snapshot-')
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900502 LOG.debug("Creating a snapshot image for server: %s", server.name)
503 image_id = compute_client.servers.create_image(server, name)
504 self.addCleanup(image_client.images.delete, image_id)
505 self.status_timeout(image_client.images, image_id, 'active')
506 snapshot_image = image_client.images.get(image_id)
Chang Bo Guofc77e932013-09-16 17:38:26 -0700507 self.assertEqual(name, snapshot_image.name)
Ken'ichi Ohmichia4912232013-08-26 14:03:25 +0900508 LOG.debug("Created snapshot image %s for server %s",
509 snapshot_image.name, server.name)
510 return snapshot_image
511
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900512 def create_keypair(self, client=None, name=None):
513 if client is None:
514 client = self.compute_client
515 if name is None:
Masayuki Igawa259c1132013-10-31 17:48:44 +0900516 name = data_utils.rand_name('scenario-keypair-')
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900517 keypair = client.keypairs.create(name)
518 self.assertEqual(keypair.name, name)
519 self.set_resource(name, keypair)
520 return keypair
521
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900522 def get_remote_client(self, server_or_ip, username=None, private_key=None):
523 if isinstance(server_or_ip, basestring):
524 ip = server_or_ip
525 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000526 network_name_for_ssh = CONF.compute.network_for_ssh
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900527 ip = server_or_ip.networks[network_name_for_ssh][0]
528 if username is None:
Matthew Treinish6c072292014-01-29 19:15:52 +0000529 username = CONF.scenario.ssh_user
Ken'ichi Ohmichib3aa9122013-08-22 23:27:25 +0900530 if private_key is None:
531 private_key = self.keypair.private_key
532 return RemoteClient(ip, username, pkey=private_key)
533
Nachi Ueno95b41282014-01-15 06:54:21 -0800534 def _log_console_output(self, servers=None):
535 if not servers:
536 servers = self.compute_client.servers.list()
537 for server in servers:
538 LOG.debug('Console output for %s', server.id)
539 LOG.debug(server.get_console_output())
540
Sean Dague6dbc6da2013-05-08 17:49:46 -0400541
542class NetworkScenarioTest(OfficialClientTest):
543 """
544 Base class for network scenario tests
545 """
546
547 @classmethod
548 def check_preconditions(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +0000549 if (CONF.service_available.neutron):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400550 cls.enabled = True
Attila Fazekasc3a095b2013-08-17 09:15:44 +0200551 # verify that neutron_available is telling the truth
Sean Dague6dbc6da2013-05-08 17:49:46 -0400552 try:
553 cls.network_client.list_networks()
554 except exc.EndpointNotFound:
555 cls.enabled = False
556 raise
557 else:
558 cls.enabled = False
Mark McClainf2982e82013-07-06 17:48:03 -0400559 msg = 'Neutron not available'
Sean Dague6dbc6da2013-05-08 17:49:46 -0400560 raise cls.skipException(msg)
561
562 @classmethod
563 def setUpClass(cls):
564 super(NetworkScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +0000565 if CONF.compute.allow_tenant_isolation:
Miguel Lavalleb8fabc52013-08-23 11:19:57 -0500566 cls.tenant_id = cls.isolated_creds.get_primary_tenant().id
567 else:
568 cls.tenant_id = cls.manager._get_identity_client(
Matthew Treinish6c072292014-01-29 19:15:52 +0000569 CONF.identity.username,
570 CONF.identity.password,
571 CONF.identity.tenant_name).tenant_id
Sean Dague6dbc6da2013-05-08 17:49:46 -0400572
Sean Dague6dbc6da2013-05-08 17:49:46 -0400573 def _create_network(self, tenant_id, namestart='network-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900574 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400575 body = dict(
576 network=dict(
577 name=name,
578 tenant_id=tenant_id,
579 ),
580 )
581 result = self.network_client.create_network(body=body)
582 network = net_common.DeletableNetwork(client=self.network_client,
583 **result['network'])
584 self.assertEqual(network.name, name)
585 self.set_resource(name, network)
586 return network
587
588 def _list_networks(self):
589 nets = self.network_client.list_networks()
590 return nets['networks']
591
592 def _list_subnets(self):
593 subnets = self.network_client.list_subnets()
594 return subnets['subnets']
595
596 def _list_routers(self):
597 routers = self.network_client.list_routers()
598 return routers['routers']
599
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000600 def _list_ports(self):
601 ports = self.network_client.list_ports()
602 return ports['ports']
603
604 def _get_tenant_own_network_num(self, tenant_id):
605 nets = self._list_networks()
606 ownnets = [value for value in nets if tenant_id == value['tenant_id']]
607 return len(ownnets)
608
609 def _get_tenant_own_subnet_num(self, tenant_id):
610 subnets = self._list_subnets()
611 ownsubnets = ([value for value in subnets
612 if tenant_id == value['tenant_id']])
613 return len(ownsubnets)
614
615 def _get_tenant_own_port_num(self, tenant_id):
616 ports = self._list_ports()
617 ownports = ([value for value in ports
618 if tenant_id == value['tenant_id']])
619 return len(ownports)
620
Sean Dague6dbc6da2013-05-08 17:49:46 -0400621 def _create_subnet(self, network, namestart='subnet-smoke-'):
622 """
623 Create a subnet for the given network within the cidr block
624 configured for tenant networks.
625 """
Matthew Treinish6c072292014-01-29 19:15:52 +0000626 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400627 result = None
628 # Repeatedly attempt subnet creation with sequential cidr
629 # blocks until an unallocated block is found.
Matthew Treinish6c072292014-01-29 19:15:52 +0000630 for subnet_cidr in tenant_cidr.subnet(
631 CONF.network.tenant_network_mask_bits):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400632 body = dict(
633 subnet=dict(
634 ip_version=4,
635 network_id=network.id,
636 tenant_id=network.tenant_id,
637 cidr=str(subnet_cidr),
638 ),
639 )
640 try:
641 result = self.network_client.create_subnet(body=body)
642 break
Mark McClainf2982e82013-07-06 17:48:03 -0400643 except exc.NeutronClientException as e:
Sean Dague6dbc6da2013-05-08 17:49:46 -0400644 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
645 if not is_overlapping_cidr:
646 raise
647 self.assertIsNotNone(result, 'Unable to allocate tenant network')
648 subnet = net_common.DeletableSubnet(client=self.network_client,
649 **result['subnet'])
650 self.assertEqual(subnet.cidr, str(subnet_cidr))
Masayuki Igawa259c1132013-10-31 17:48:44 +0900651 self.set_resource(data_utils.rand_name(namestart), subnet)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400652 return subnet
653
654 def _create_port(self, network, namestart='port-quotatest-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900655 name = data_utils.rand_name(namestart)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400656 body = dict(
657 port=dict(name=name,
658 network_id=network.id,
659 tenant_id=network.tenant_id))
660 result = self.network_client.create_port(body=body)
661 self.assertIsNotNone(result, 'Unable to allocate port')
662 port = net_common.DeletablePort(client=self.network_client,
663 **result['port'])
664 self.set_resource(name, port)
665 return port
666
Yair Fried05db2522013-11-18 11:02:10 +0200667 def _get_server_port_id(self, server):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400668 result = self.network_client.list_ports(device_id=server.id)
669 ports = result.get('ports', [])
670 self.assertEqual(len(ports), 1,
671 "Unable to determine which port to target.")
Yair Fried05db2522013-11-18 11:02:10 +0200672 return ports[0]['id']
673
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400674 def _create_floating_ip(self, thing, external_network_id,
675 port_filters=None):
676 if port_filters is None:
677 port_id = self._get_server_port_id(thing)
678 else:
679 port_id = port_filters
Sean Dague6dbc6da2013-05-08 17:49:46 -0400680 body = dict(
681 floatingip=dict(
682 floating_network_id=external_network_id,
683 port_id=port_id,
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400684 tenant_id=thing.tenant_id,
Sean Dague6dbc6da2013-05-08 17:49:46 -0400685 )
686 )
687 result = self.network_client.create_floatingip(body=body)
688 floating_ip = net_common.DeletableFloatingIp(
689 client=self.network_client,
690 **result['floatingip'])
Masayuki Igawa259c1132013-10-31 17:48:44 +0900691 self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
Sean Dague6dbc6da2013-05-08 17:49:46 -0400692 return floating_ip
693
Yair Fried05db2522013-11-18 11:02:10 +0200694 def _associate_floating_ip(self, floating_ip, server):
695 port_id = self._get_server_port_id(server)
696 floating_ip.update(port_id=port_id)
697 self.assertEqual(port_id, floating_ip.port_id)
698 return floating_ip
699
Yair Fried9a551c42013-12-15 14:59:34 +0200700 def _disassociate_floating_ip(self, floating_ip):
701 """
702 :param floating_ip: type DeletableFloatingIp
703 """
704 floating_ip.update(port_id=None)
llg8212e4cd3922014-02-15 12:14:21 +0800705 self.assertIsNone(floating_ip.port_id)
Yair Fried9a551c42013-12-15 14:59:34 +0200706 return floating_ip
707
708 def _ping_ip_address(self, ip_address, should_succeed=True):
Sean Dague6dbc6da2013-05-08 17:49:46 -0400709 cmd = ['ping', '-c1', '-w1', ip_address]
710
711 def ping():
712 proc = subprocess.Popen(cmd,
713 stdout=subprocess.PIPE,
714 stderr=subprocess.PIPE)
715 proc.wait()
Yair Fried9a551c42013-12-15 14:59:34 +0200716 return (proc.returncode == 0) == should_succeed
Sean Dague6dbc6da2013-05-08 17:49:46 -0400717
Nachi Ueno6d580be2013-07-24 10:58:11 -0700718 return tempest.test.call_until_true(
Matthew Treinish6c072292014-01-29 19:15:52 +0000719 ping, CONF.compute.ping_timeout, 1)
Maru Newbyaf292e82013-05-20 21:32:28 +0000720
Elena Ezhovaa5105e62013-11-26 20:46:52 +0400721 def _create_pool(self, lb_method, protocol, subnet_id):
722 """Wrapper utility that returns a test pool."""
723 name = data_utils.rand_name('pool-')
724 body = {
725 "pool": {
726 "protocol": protocol,
727 "name": name,
728 "subnet_id": subnet_id,
729 "lb_method": lb_method
730 }
731 }
732 resp = self.network_client.create_pool(body=body)
733 pool = net_common.DeletablePool(client=self.network_client,
734 **resp['pool'])
735 self.assertEqual(pool['name'], name)
736 self.set_resource(name, pool)
737 return pool
738
739 def _create_member(self, address, protocol_port, pool_id):
740 """Wrapper utility that returns a test member."""
741 body = {
742 "member": {
743 "protocol_port": protocol_port,
744 "pool_id": pool_id,
745 "address": address
746 }
747 }
748 resp = self.network_client.create_member(body)
749 member = net_common.DeletableMember(client=self.network_client,
750 **resp['member'])
751 self.set_resource(data_utils.rand_name('member-'), member)
752 return member
753
754 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
755 """Wrapper utility that returns a test vip."""
756 name = data_utils.rand_name('vip-')
757 body = {
758 "vip": {
759 "protocol": protocol,
760 "name": name,
761 "subnet_id": subnet_id,
762 "pool_id": pool_id,
763 "protocol_port": protocol_port
764 }
765 }
766 resp = self.network_client.create_vip(body)
767 vip = net_common.DeletableVip(client=self.network_client,
768 **resp['vip'])
769 self.assertEqual(vip['name'], name)
770 self.set_resource(name, vip)
771 return vip
772
Yair Fried9a551c42013-12-15 14:59:34 +0200773 def _check_vm_connectivity(self, ip_address,
774 username=None,
775 private_key=None,
776 should_connect=True):
777 """
778 :param ip_address: server to test against
779 :param username: server's ssh username
780 :param private_key: server's ssh private key to be used
781 :param should_connect: True/False indicates positive/negative test
782 positive - attempt ping and ssh
783 negative - attempt ping and fail if succeed
784
785 :raises: AssertError if the result of the connectivity check does
786 not match the value of the should_connect param
787 """
788 if should_connect:
789 msg = "Timed out waiting for %s to become reachable" % ip_address
790 else:
791 msg = "ip address %s is reachable" % ip_address
792 self.assertTrue(self._ping_ip_address(ip_address,
793 should_succeed=should_connect),
794 msg=msg)
795 if should_connect:
796 # no need to check ssh for negative connectivity
Attila Fazekasad7ef7d2013-11-20 10:12:53 +0100797 linux_client = self.get_remote_client(ip_address, username,
798 private_key)
799 linux_client.validate_authentication()
Steve Bakerdd7c6ce2013-06-24 14:46:47 +1200800
Yair Friedeb69f3f2013-10-10 13:18:16 +0300801 def _create_security_group_nova(self, client=None,
802 namestart='secgroup-smoke-',
803 tenant_id=None):
804 if client is None:
805 client = self.compute_client
806 # Create security group
807 sg_name = data_utils.rand_name(namestart)
808 sg_desc = sg_name + " description"
809 secgroup = client.security_groups.create(sg_name, sg_desc)
810 self.assertEqual(secgroup.name, sg_name)
811 self.assertEqual(secgroup.description, sg_desc)
812 self.set_resource(sg_name, secgroup)
813
814 # Add rules to the security group
815 self._create_loginable_secgroup_rule_nova(client, secgroup.id)
816
817 return secgroup
818
819 def _create_security_group_neutron(self, tenant_id, client=None,
820 namestart='secgroup-smoke-'):
821 if client is None:
822 client = self.network_client
823 secgroup = self._create_empty_security_group(namestart=namestart,
824 client=client,
825 tenant_id=tenant_id)
826
827 # Add rules to the security group
828 rules = self._create_loginable_secgroup_rule_neutron(secgroup=secgroup)
829 for rule in rules:
830 self.assertEqual(tenant_id, rule.tenant_id)
831 self.assertEqual(secgroup.id, rule.security_group_id)
832 return secgroup
833
834 def _create_empty_security_group(self, tenant_id, client=None,
835 namestart='secgroup-smoke-'):
836 """Create a security group without rules.
837
838 Default rules will be created:
839 - IPv4 egress to any
840 - IPv6 egress to any
841
842 :param tenant_id: secgroup will be created in this tenant
843 :returns: DeletableSecurityGroup -- containing the secgroup created
844 """
845 if client is None:
846 client = self.network_client
847 sg_name = data_utils.rand_name(namestart)
848 sg_desc = sg_name + " description"
849 sg_dict = dict(name=sg_name,
850 description=sg_desc)
851 sg_dict['tenant_id'] = tenant_id
852 body = dict(security_group=sg_dict)
853 result = client.create_security_group(body=body)
854 secgroup = net_common.DeletableSecurityGroup(
855 client=client,
856 **result['security_group']
857 )
858 self.assertEqual(secgroup.name, sg_name)
859 self.assertEqual(tenant_id, secgroup.tenant_id)
860 self.assertEqual(secgroup.description, sg_desc)
861 self.set_resource(sg_name, secgroup)
862 return secgroup
863
864 def _default_security_group(self, tenant_id, client=None):
865 """Get default secgroup for given tenant_id.
866
867 :returns: DeletableSecurityGroup -- default secgroup for given tenant
868 """
869 if client is None:
870 client = self.network_client
871 sgs = [
872 sg for sg in client.list_security_groups().values()[0]
873 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
874 ]
875 msg = "No default security group for tenant %s." % (tenant_id)
876 self.assertTrue(len(sgs) > 0, msg)
877 if len(sgs) > 1:
878 msg = "Found %d default security groups" % len(sgs)
879 raise exc.NeutronClientNoUniqueMatch(msg=msg)
880 return net_common.DeletableSecurityGroup(client=client,
881 **sgs[0])
882
883 def _create_security_group_rule(self, client=None, secgroup=None,
884 tenant_id=None, **kwargs):
885 """Create a rule from a dictionary of rule parameters.
886
887 Create a rule in a secgroup. if secgroup not defined will search for
888 default secgroup in tenant_id.
889
890 :param secgroup: type DeletableSecurityGroup.
891 :param secgroup_id: search for secgroup by id
892 default -- choose default secgroup for given tenant_id
893 :param tenant_id: if secgroup not passed -- the tenant in which to
894 search for default secgroup
895 :param kwargs: a dictionary containing rule parameters:
896 for example, to allow incoming ssh:
897 rule = {
898 direction: 'ingress'
899 protocol:'tcp',
900 port_range_min: 22,
901 port_range_max: 22
902 }
903 """
904 if client is None:
905 client = self.network_client
906 if secgroup is None:
907 secgroup = self._default_security_group(tenant_id)
908
909 ruleset = dict(security_group_id=secgroup.id,
910 tenant_id=secgroup.tenant_id,
911 )
912 ruleset.update(kwargs)
913
914 body = dict(security_group_rule=dict(ruleset))
915 sg_rule = client.create_security_group_rule(body=body)
916 sg_rule = net_common.DeletableSecurityGroupRule(
917 client=client,
918 **sg_rule['security_group_rule']
919 )
920 self.set_resource(sg_rule.id, sg_rule)
921 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
922 self.assertEqual(secgroup.id, sg_rule.security_group_id)
923
924 return sg_rule
925
926 def _create_loginable_secgroup_rule_neutron(self, client=None,
927 secgroup=None):
928 """These rules are intended to permit inbound ssh and icmp
929 traffic from all sources, so no group_id is provided.
930 Setting a group_id would only permit traffic from ports
931 belonging to the same security group.
932 """
933
934 if client is None:
935 client = self.network_client
936 rules = []
937 rulesets = [
938 dict(
939 # ssh
940 protocol='tcp',
941 port_range_min=22,
942 port_range_max=22,
943 ),
944 dict(
945 # ping
946 protocol='icmp',
947 )
948 ]
949 for ruleset in rulesets:
950 for r_direction in ['ingress', 'egress']:
951 ruleset['direction'] = r_direction
952 try:
953 sg_rule = self._create_security_group_rule(
954 client=client, secgroup=secgroup, **ruleset)
955 except exc.NeutronClientException as ex:
956 # if rule already exist - skip rule and continue
957 if not (ex.status_code is 409 and 'Security group rule'
958 ' already exists' in ex.message):
959 raise ex
960 else:
961 self.assertEqual(r_direction, sg_rule.direction)
962 rules.append(sg_rule)
963
964 return rules
965
Yair Fried5f670ab2013-12-09 09:26:51 +0200966 def _ssh_to_server(self, server, private_key):
Matthew Treinish6c072292014-01-29 19:15:52 +0000967 ssh_login = CONF.compute.image_ssh_user
Yair Fried5f670ab2013-12-09 09:26:51 +0200968 return self.get_remote_client(server,
969 username=ssh_login,
970 private_key=private_key)
971
Yuiko Takada7f4b1b32013-11-20 08:06:26 +0000972 def _show_quota_network(self, tenant_id):
973 quota = self.network_client.show_quota(tenant_id)
974 return quota['quota']['network']
975
976 def _show_quota_subnet(self, tenant_id):
977 quota = self.network_client.show_quota(tenant_id)
978 return quota['quota']['subnet']
979
980 def _show_quota_port(self, tenant_id):
981 quota = self.network_client.show_quota(tenant_id)
982 return quota['quota']['port']
983
Yair Fried4d7efa62013-11-17 17:12:29 +0200984 def _get_router(self, tenant_id):
985 """Retrieve a router for the given tenant id.
986
987 If a public router has been configured, it will be returned.
988
989 If a public router has not been configured, but a public
990 network has, a tenant router will be created and returned that
991 routes traffic to the public network.
992 """
Matthew Treinish6c072292014-01-29 19:15:52 +0000993 router_id = CONF.network.public_router_id
994 network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +0200995 if router_id:
996 result = self.network_client.show_router(router_id)
997 return net_common.AttributeDict(**result['router'])
998 elif network_id:
999 router = self._create_router(tenant_id)
1000 router.add_gateway(network_id)
1001 return router
1002 else:
1003 raise Exception("Neither of 'public_router_id' or "
1004 "'public_network_id' has been defined.")
1005
1006 def _create_router(self, tenant_id, namestart='router-smoke-'):
1007 name = data_utils.rand_name(namestart)
1008 body = dict(
1009 router=dict(
1010 name=name,
1011 admin_state_up=True,
1012 tenant_id=tenant_id,
1013 ),
1014 )
1015 result = self.network_client.create_router(body=body)
1016 router = net_common.DeletableRouter(client=self.network_client,
1017 **result['router'])
1018 self.assertEqual(router.name, name)
1019 self.set_resource(name, router)
1020 return router
1021
1022 def _create_networks(self, tenant_id=None):
1023 """Create a network with a subnet connected to a router.
1024
1025 :returns: network, subnet, router
1026 """
1027 if tenant_id is None:
1028 tenant_id = self.tenant_id
1029 network = self._create_network(tenant_id)
1030 router = self._get_router(tenant_id)
1031 subnet = self._create_subnet(network)
1032 subnet.add_to_router(router.id)
1033 self.networks.append(network)
1034 self.subnets.append(subnet)
1035 self.routers.append(router)
1036 return network, subnet, router
1037
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001038
1039class OrchestrationScenarioTest(OfficialClientTest):
1040 """
1041 Base class for orchestration scenario tests
1042 """
1043
1044 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001045 def setUpClass(cls):
1046 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001047 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001048 raise cls.skipException("Heat support is required")
1049
1050 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001051 def credentials(cls):
Matthew Treinish6c072292014-01-29 19:15:52 +00001052 username = CONF.identity.admin_username
1053 password = CONF.identity.admin_password
1054 tenant_name = CONF.identity.tenant_name
Ryan Hsuee1017c2013-12-20 12:00:34 -08001055 return username, password, tenant_name
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001056
1057 def _load_template(self, base_file, file_name):
1058 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1059 file_name)
1060 with open(filepath) as f:
1061 return f.read()
1062
1063 @classmethod
1064 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001065 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001066
1067 @classmethod
1068 def _get_default_network(cls):
1069 networks = cls.network_client.list_networks()
1070 for net in networks['networks']:
Matthew Treinish6c072292014-01-29 19:15:52 +00001071 if net['name'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001072 return net