blob: 52a4aadb95cd4c8d1983472dd40409525f621669 [file] [log] [blame]
Jay Pipes3f981df2012-03-27 18:59:44 -04001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2012 OpenStack, LLC
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
Daryl Walleck1465d612011-11-02 02:22:15 -050018import ConfigParser
Jay Pipes7f757632011-12-02 15:53:32 -050019import logging
20import os
Jay Pipes3f981df2012-03-27 18:59:44 -040021
kavan-patil4ea2efb2011-12-09 08:58:50 +000022from tempest.common.utils import data_utils
Jay Pipes7f757632011-12-02 15:53:32 -050023
24LOG = logging.getLogger(__name__)
Daryl Walleck1465d612011-11-02 02:22:15 -050025
26
Jay Pipes3f981df2012-03-27 18:59:44 -040027class BaseConfig(object):
28
29 SECTION_NAME = None
Daryl Walleck1465d612011-11-02 02:22:15 -050030
31 def __init__(self, conf):
Daryl Walleck1465d612011-11-02 02:22:15 -050032 self.conf = conf
33
donald-ngo7fb1efa2011-12-13 17:17:36 -080034 def get(self, item_name, default_value=None):
Daryl Walleck1465d612011-11-02 02:22:15 -050035 try:
Rohit Karajgicfc0c022012-07-17 23:11:38 -070036 return self.conf.get(self.SECTION_NAME, item_name, raw=True)
Daryl Walleck1465d612011-11-02 02:22:15 -050037 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
38 return default_value
39
Jay Pipes3f981df2012-03-27 18:59:44 -040040
41class IdentityConfig(BaseConfig):
42
43 """Provides configuration information for authenticating with Keystone."""
44
45 SECTION_NAME = "identity"
46
Daryl Walleck1465d612011-11-02 02:22:15 -050047 @property
chris fattarsi8ed39ac2012-04-30 14:11:27 -070048 def catalog_type(self):
49 """Catalog type of the Identity service."""
50 return self.get("catalog_type", 'identity')
51
52 @property
Rohit Karajgie1b050d2011-12-02 16:13:18 -080053 def host(self):
Daryl Walleck587385b2012-03-03 13:00:26 -060054 """Host IP for making Identity API requests."""
donald-ngo7fb1efa2011-12-13 17:17:36 -080055 return self.get("host", "127.0.0.1")
Rohit Karajgie1b050d2011-12-02 16:13:18 -080056
57 @property
58 def port(self):
Daryl Walleck587385b2012-03-03 13:00:26 -060059 """Port for the Identity service."""
Rohit Karajgie1b050d2011-12-02 16:13:18 -080060 return self.get("port", "8773")
61
62 @property
Daryl Walleck587385b2012-03-03 13:00:26 -060063 def api_version(self):
64 """Version of the Identity API"""
65 return self.get("api_version", "v1.1")
Rohit Karajgie1b050d2011-12-02 16:13:18 -080066
67 @property
68 def path(self):
69 """Path of API request"""
70 return self.get("path", "/")
71
kavan-patil4ea2efb2011-12-09 08:58:50 +000072 @property
73 def auth_url(self):
Daryl Walleck587385b2012-03-03 13:00:26 -060074 """The Identity URL (derived)"""
kavan-patil4ea2efb2011-12-09 08:58:50 +000075 auth_url = data_utils.build_url(self.host,
76 self.port,
Daryl Walleck587385b2012-03-03 13:00:26 -060077 self.api_version,
donald-ngo7fb1efa2011-12-13 17:17:36 -080078 self.path,
79 use_ssl=self.use_ssl)
kavan-patil4ea2efb2011-12-09 08:58:50 +000080 return auth_url
81
Daryl Walleck1465d612011-11-02 02:22:15 -050082 @property
donald-ngo7fb1efa2011-12-13 17:17:36 -080083 def use_ssl(self):
84 """Specifies if we are using https."""
Dan Prince95195922012-03-12 15:14:51 -040085 return self.get("use_ssl", 'false').lower() != 'false'
donald-ngo7fb1efa2011-12-13 17:17:36 -080086
87 @property
Daryl Walleck587385b2012-03-03 13:00:26 -060088 def strategy(self):
89 """Which auth method does the environment use? (basic|keystone)"""
90 return self.get("strategy", 'keystone')
91
92
Jay Pipesf38eaac2012-06-21 13:37:35 -040093class IdentityAdminConfig(BaseConfig):
94
95 SECTION_NAME = "identity-admin"
96
97 @property
98 def username(self):
99 """Username to use for Identity Admin API requests"""
100 return self.get("username", "admin")
101
102 @property
103 def tenant_name(self):
104 """Tenant name to use for Identity Admin API requests"""
105 return self.get("tenant_name", "admin")
106
107 @property
108 def password(self):
109 """API key to use for Identity Admin API requests"""
110 return self.get("password", "pass")
111
112
Jay Pipes3f981df2012-03-27 18:59:44 -0400113class ComputeConfig(BaseConfig):
Daryl Walleck587385b2012-03-03 13:00:26 -0600114
Jay Pipes3f981df2012-03-27 18:59:44 -0400115 SECTION_NAME = "compute"
116
117 @property
Jay Pipesf38eaac2012-06-21 13:37:35 -0400118 def allow_tenant_isolation(self):
119 """
120 Allows test cases to create/destroy tenants and users. This option
121 enables isolated test cases and better parallel execution,
122 but also requires that OpenStack Identity API admin credentials
123 are known.
124 """
125 return self.get("allow_tenant_isolation", 'false').lower() != 'false'
126
127 @property
Dan Smithd6ff6b72012-08-23 10:29:41 -0700128 def allow_tenant_reuse(self):
129 """
130 If allow_tenant_isolation is True and a tenant that would be created
131 for a given test already exists (such as from a previously-failed run),
132 re-use that tenant instead of failing because of the conflict. Note
133 that this would result in the tenant being deleted at the end of a
134 subsequent successful run.
135 """
136 return self.get("allow_tenant_reuse", 'true').lower() != 'false'
137
138 @property
Jay Pipes3f981df2012-03-27 18:59:44 -0400139 def username(self):
140 """Username to use for Nova API requests."""
141 return self.get("username", "demo")
142
143 @property
144 def tenant_name(self):
145 """Tenant name to use for Nova API requests."""
146 return self.get("tenant_name", "demo")
147
148 @property
149 def password(self):
150 """API key to use when authenticating."""
151 return self.get("password", "pass")
152
153 @property
154 def alt_username(self):
155 """Username of alternate user to use for Nova API requests."""
David Kranz7490e952012-04-02 13:28:27 -0400156 return self.get("alt_username")
Jay Pipes3f981df2012-03-27 18:59:44 -0400157
158 @property
159 def alt_tenant_name(self):
160 """Alternate user's Tenant name to use for Nova API requests."""
David Kranz7490e952012-04-02 13:28:27 -0400161 return self.get("alt_tenant_name")
Jay Pipes3f981df2012-03-27 18:59:44 -0400162
163 @property
164 def alt_password(self):
165 """API key to use when authenticating as alternate user."""
David Kranz7490e952012-04-02 13:28:27 -0400166 return self.get("alt_password")
Daryl Walleck587385b2012-03-03 13:00:26 -0600167
168 @property
169 def image_ref(self):
170 """Valid primary image to use in tests."""
Jay Pipes3f981df2012-03-27 18:59:44 -0400171 return self.get("image_ref", "{$IMAGE_ID}")
Daryl Walleck587385b2012-03-03 13:00:26 -0600172
173 @property
174 def image_ref_alt(self):
175 """Valid secondary image reference to be used in tests."""
Jay Pipes3f981df2012-03-27 18:59:44 -0400176 return self.get("image_ref_alt", "{$IMAGE_ID_ALT}")
Daryl Walleck587385b2012-03-03 13:00:26 -0600177
178 @property
179 def flavor_ref(self):
180 """Valid primary flavor to use in tests."""
181 return self.get("flavor_ref", 1)
182
183 @property
184 def flavor_ref_alt(self):
185 """Valid secondary flavor to be used in tests."""
186 return self.get("flavor_ref_alt", 2)
187
188 @property
189 def resize_available(self):
190 """Does the test environment support resizing?"""
Dan Prince95195922012-03-12 15:14:51 -0400191 return self.get("resize_available", 'false').lower() != 'false'
Daryl Walleck587385b2012-03-03 13:00:26 -0600192
193 @property
David Kranzf97d5fd2012-07-30 13:46:45 -0400194 def change_password_available(self):
195 """Does the test environment support changing the admin password?"""
196 return self.get("change_password_available", 'false').lower() != \
197 'false'
198
199 @property
Daryl Walleck587385b2012-03-03 13:00:26 -0600200 def create_image_enabled(self):
201 """Does the test environment support snapshots?"""
Dan Prince95195922012-03-12 15:14:51 -0400202 return self.get("create_image_enabled", 'false').lower() != 'false'
Daryl Walleck587385b2012-03-03 13:00:26 -0600203
204 @property
Daryl Walleck1465d612011-11-02 02:22:15 -0500205 def build_interval(self):
206 """Time in seconds between build status checks."""
207 return float(self.get("build_interval", 10))
208
209 @property
Daryl Walleck1465d612011-11-02 02:22:15 -0500210 def build_timeout(self):
Rohit Karajgidd47d7e2012-07-31 04:11:01 -0700211 """Timeout in seconds to wait for an instance to build."""
Daryl Walleck1465d612011-11-02 02:22:15 -0500212 return float(self.get("build_timeout", 300))
213
Daryl Walleck4aa82a92012-02-14 15:45:46 -0600214 @property
Daryl Walleck6b9b2882012-04-08 21:43:39 -0500215 def run_ssh(self):
216 """Does the test environment support snapshots?"""
217 return self.get("run_ssh", 'false').lower() != 'false'
218
219 @property
220 def ssh_user(self):
221 """User name used to authenticate to an instance."""
222 return self.get("ssh_user", "root")
223
224 @property
225 def ssh_timeout(self):
226 """Timeout in seconds to wait for authentcation to succeed."""
227 return float(self.get("ssh_timeout", 300))
228
229 @property
230 def network_for_ssh(self):
231 """Network used for SSH connections."""
232 return self.get("network_for_ssh", "public")
233
234 @property
235 def ip_version_for_ssh(self):
236 """IP version used for SSH connections."""
237 return int(self.get("ip_version_for_ssh", 4))
238
239 @property
Daryl Walleckb90a1a62012-02-27 11:23:10 -0600240 def catalog_type(self):
Daryl Walleck587385b2012-03-03 13:00:26 -0600241 """Catalog type of the Compute service."""
Daryl Walleckb90a1a62012-02-27 11:23:10 -0600242 return self.get("catalog_type", 'compute')
Daryl Walleck4aa82a92012-02-14 15:45:46 -0600243
David Kranz180fed12012-03-27 14:31:29 -0400244 @property
245 def log_level(self):
246 """Level for logging compute API calls."""
247 return self.get("log_level", 'ERROR')
248
Jay Pipes051075a2012-04-28 17:39:37 -0400249 @property
250 def whitebox_enabled(self):
251 """Does the test environment support whitebox tests for Compute?"""
252 return self.get("whitebox_enabled", 'false').lower() != 'false'
253
254 @property
255 def db_uri(self):
256 """Connection string to the database of Compute service"""
257 return self.get("db_uri", None)
258
259 @property
260 def source_dir(self):
261 """Path of nova source directory"""
262 return self.get("source_dir", "/opt/stack/nova")
263
264 @property
265 def config_path(self):
266 """Path of nova configuration file"""
267 return self.get("config_path", "/etc/nova/nova.conf")
268
269 @property
270 def bin_dir(self):
271 """Directory containing nova binaries such as nova-manage"""
272 return self.get("bin_dir", "/usr/local/bin/")
273
274 @property
275 def path_to_private_key(self):
276 """Path to a private key file for SSH access to remote hosts"""
277 return self.get("path_to_private_key")
278
Daryl Walleck1465d612011-11-02 02:22:15 -0500279
Jay Pipesff10d552012-04-06 14:18:50 -0400280class ComputeAdminConfig(BaseConfig):
281
282 SECTION_NAME = "compute-admin"
283
284 @property
285 def username(self):
286 """Administrative Username to use for Nova API requests."""
287 return self.get("username", "admin")
288
289 @property
290 def tenant_name(self):
291 """Administrative Tenant name to use for Nova API requests."""
292 return self.get("tenant_name", "admin")
293
294 @property
295 def password(self):
296 """API key to use when authenticating as admin."""
297 return self.get("password", "pass")
298
299
Jay Pipes3f981df2012-03-27 18:59:44 -0400300class ImagesConfig(BaseConfig):
301
Jay Pipes50677282012-01-06 15:39:20 -0500302 """
303 Provides configuration information for connecting to an
304 OpenStack Images service.
305 """
306
Jay Pipes3f981df2012-03-27 18:59:44 -0400307 SECTION_NAME = "image"
Jay Pipes50677282012-01-06 15:39:20 -0500308
309 @property
310 def host(self):
311 """Host IP for making Images API requests. Defaults to '127.0.0.1'."""
312 return self.get("host", "127.0.0.1")
313
314 @property
315 def port(self):
316 """Listen port of the Images service."""
317 return int(self.get("port", "9292"))
318
319 @property
320 def api_version(self):
321 """Version of the API"""
322 return self.get("api_version", "1")
323
324 @property
325 def username(self):
Jay Pipes3f981df2012-03-27 18:59:44 -0400326 """Username to use for Images API requests. Defaults to 'demo'."""
Julien Danjou75a677e2012-04-11 15:49:15 +0200327 return self.get("username", "demo")
Jay Pipes50677282012-01-06 15:39:20 -0500328
329 @property
330 def password(self):
331 """Password for user"""
Jay Pipes3f981df2012-03-27 18:59:44 -0400332 return self.get("password", "pass")
Jay Pipes50677282012-01-06 15:39:20 -0500333
334 @property
Jay Pipes3f981df2012-03-27 18:59:44 -0400335 def tenant_name(self):
336 """Tenant to use for Images API requests. Defaults to 'demo'."""
337 return self.get("tenant_name", "demo")
Jay Pipes50677282012-01-06 15:39:20 -0500338
339
Unmesh Gurjar44986832012-05-08 19:57:10 +0530340class NetworkConfig(BaseConfig):
341 """Provides configuration information for connecting to an OpenStack
342 Network Service.
343 """
344
345 SECTION_NAME = "network"
346
347 @property
348 def catalog_type(self):
349 """Catalog type of the Quantum service."""
350 return self.get("catalog_type", 'network')
351
352 @property
353 def api_version(self):
354 """Version of Quantum API"""
355 return self.get("api_version", "v1.1")
356
357
Rohit Karajgidd47d7e2012-07-31 04:11:01 -0700358class VolumeConfig(BaseConfig):
359 """Provides configuration information for connecting to an OpenStack Block
360 Storage Service.
361 """
362
363 SECTION_NAME = "volume"
364
365 @property
366 def build_interval(self):
367 """Time in seconds between volume availability checks."""
368 return float(self.get("build_interval", 10))
369
370 @property
371 def build_timeout(self):
372 """Timeout in seconds to wait for a volume to become available."""
373 return float(self.get("build_timeout", 300))
374
375 @property
376 def catalog_type(self):
377 """Catalog type of the Volume Service"""
378 return self.get("catalog_type", 'volume')
379
380
381# TODO(jaypipes): Move this to a common utils (not data_utils...)
Jay Pipes3f981df2012-03-27 18:59:44 -0400382def singleton(cls):
383 """Simple wrapper for classes that should only have a single instance"""
384 instances = {}
385
386 def getinstance():
387 if cls not in instances:
388 instances[cls] = cls()
389 return instances[cls]
390 return getinstance
391
392
393@singleton
394class TempestConfig:
Daryl Walleck1465d612011-11-02 02:22:15 -0500395 """Provides OpenStack configuration information."""
396
Brian Waldon738cd632011-12-12 18:45:09 -0500397 DEFAULT_CONFIG_DIR = os.path.join(
398 os.path.abspath(
399 os.path.dirname(
400 os.path.dirname(__file__))),
401 "etc")
Daryl Walleck1465d612011-11-02 02:22:15 -0500402
Brian Waldon738cd632011-12-12 18:45:09 -0500403 DEFAULT_CONFIG_FILE = "tempest.conf"
404
405 def __init__(self):
406 """Initialize a configuration from a conf directory and conf file."""
407
408 # Environment variables override defaults...
409 conf_dir = os.environ.get('TEMPEST_CONFIG_DIR',
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800410 self.DEFAULT_CONFIG_DIR)
411 conf_file = os.environ.get('TEMPEST_CONFIG', self.DEFAULT_CONFIG_FILE)
Brian Waldon738cd632011-12-12 18:45:09 -0500412
Jay Pipes7f757632011-12-02 15:53:32 -0500413 path = os.path.join(conf_dir, conf_file)
414
Jay Pipes3f981df2012-03-27 18:59:44 -0400415 LOG.info("Using tempest config file %s" % path)
416
Jay Pipes7f757632011-12-02 15:53:32 -0500417 if not os.path.exists(path):
418 msg = "Config file %(path)s not found" % locals()
419 raise RuntimeError(msg)
420
421 self._conf = self.load_config(path)
Daryl Walleck587385b2012-03-03 13:00:26 -0600422 self.compute = ComputeConfig(self._conf)
Jay Pipesff10d552012-04-06 14:18:50 -0400423 self.compute_admin = ComputeAdminConfig(self._conf)
Daryl Walleck587385b2012-03-03 13:00:26 -0600424 self.identity = IdentityConfig(self._conf)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400425 self.identity_admin = IdentityAdminConfig(self._conf)
Jay Pipes50677282012-01-06 15:39:20 -0500426 self.images = ImagesConfig(self._conf)
Unmesh Gurjar44986832012-05-08 19:57:10 +0530427 self.network = NetworkConfig(self._conf)
Rohit Karajgidd47d7e2012-07-31 04:11:01 -0700428 self.volume = VolumeConfig(self._conf)
Daryl Walleck1465d612011-11-02 02:22:15 -0500429
Jay Pipes7f757632011-12-02 15:53:32 -0500430 def load_config(self, path):
Daryl Walleck1465d612011-11-02 02:22:15 -0500431 """Read configuration from given path and return a config object."""
432 config = ConfigParser.SafeConfigParser()
433 config.read(path)
434 return config