blob: 7d18974d8424770c1ae196317f50393dbc75c80b [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
Jay Pipes3f981df2012-03-27 18:59:44 -0400128 def username(self):
129 """Username to use for Nova API requests."""
130 return self.get("username", "demo")
131
132 @property
133 def tenant_name(self):
134 """Tenant name to use for Nova API requests."""
135 return self.get("tenant_name", "demo")
136
137 @property
138 def password(self):
139 """API key to use when authenticating."""
140 return self.get("password", "pass")
141
142 @property
143 def alt_username(self):
144 """Username of alternate user to use for Nova API requests."""
David Kranz7490e952012-04-02 13:28:27 -0400145 return self.get("alt_username")
Jay Pipes3f981df2012-03-27 18:59:44 -0400146
147 @property
148 def alt_tenant_name(self):
149 """Alternate user's Tenant name to use for Nova API requests."""
David Kranz7490e952012-04-02 13:28:27 -0400150 return self.get("alt_tenant_name")
Jay Pipes3f981df2012-03-27 18:59:44 -0400151
152 @property
153 def alt_password(self):
154 """API key to use when authenticating as alternate user."""
David Kranz7490e952012-04-02 13:28:27 -0400155 return self.get("alt_password")
Daryl Walleck587385b2012-03-03 13:00:26 -0600156
157 @property
158 def image_ref(self):
159 """Valid primary image to use in tests."""
Jay Pipes3f981df2012-03-27 18:59:44 -0400160 return self.get("image_ref", "{$IMAGE_ID}")
Daryl Walleck587385b2012-03-03 13:00:26 -0600161
162 @property
163 def image_ref_alt(self):
164 """Valid secondary image reference to be used in tests."""
Jay Pipes3f981df2012-03-27 18:59:44 -0400165 return self.get("image_ref_alt", "{$IMAGE_ID_ALT}")
Daryl Walleck587385b2012-03-03 13:00:26 -0600166
167 @property
168 def flavor_ref(self):
169 """Valid primary flavor to use in tests."""
170 return self.get("flavor_ref", 1)
171
172 @property
173 def flavor_ref_alt(self):
174 """Valid secondary flavor to be used in tests."""
175 return self.get("flavor_ref_alt", 2)
176
177 @property
178 def resize_available(self):
179 """Does the test environment support resizing?"""
Dan Prince95195922012-03-12 15:14:51 -0400180 return self.get("resize_available", 'false').lower() != 'false'
Daryl Walleck587385b2012-03-03 13:00:26 -0600181
182 @property
David Kranzf97d5fd2012-07-30 13:46:45 -0400183 def change_password_available(self):
184 """Does the test environment support changing the admin password?"""
185 return self.get("change_password_available", 'false').lower() != \
186 'false'
187
188 @property
Daryl Walleck587385b2012-03-03 13:00:26 -0600189 def create_image_enabled(self):
190 """Does the test environment support snapshots?"""
Dan Prince95195922012-03-12 15:14:51 -0400191 return self.get("create_image_enabled", 'false').lower() != 'false'
Daryl Walleck587385b2012-03-03 13:00:26 -0600192
193 @property
Daryl Walleck1465d612011-11-02 02:22:15 -0500194 def build_interval(self):
195 """Time in seconds between build status checks."""
196 return float(self.get("build_interval", 10))
197
198 @property
Daryl Walleck1465d612011-11-02 02:22:15 -0500199 def build_timeout(self):
200 """Timeout in seconds to wait for an entity to build."""
201 return float(self.get("build_timeout", 300))
202
Daryl Walleck4aa82a92012-02-14 15:45:46 -0600203 @property
Daryl Walleck6b9b2882012-04-08 21:43:39 -0500204 def run_ssh(self):
205 """Does the test environment support snapshots?"""
206 return self.get("run_ssh", 'false').lower() != 'false'
207
208 @property
209 def ssh_user(self):
210 """User name used to authenticate to an instance."""
211 return self.get("ssh_user", "root")
212
213 @property
214 def ssh_timeout(self):
215 """Timeout in seconds to wait for authentcation to succeed."""
216 return float(self.get("ssh_timeout", 300))
217
218 @property
219 def network_for_ssh(self):
220 """Network used for SSH connections."""
221 return self.get("network_for_ssh", "public")
222
223 @property
224 def ip_version_for_ssh(self):
225 """IP version used for SSH connections."""
226 return int(self.get("ip_version_for_ssh", 4))
227
228 @property
Daryl Walleckb90a1a62012-02-27 11:23:10 -0600229 def catalog_type(self):
Daryl Walleck587385b2012-03-03 13:00:26 -0600230 """Catalog type of the Compute service."""
Daryl Walleckb90a1a62012-02-27 11:23:10 -0600231 return self.get("catalog_type", 'compute')
Daryl Walleck4aa82a92012-02-14 15:45:46 -0600232
David Kranz180fed12012-03-27 14:31:29 -0400233 @property
234 def log_level(self):
235 """Level for logging compute API calls."""
236 return self.get("log_level", 'ERROR')
237
Daryl Walleck1465d612011-11-02 02:22:15 -0500238
Jay Pipesff10d552012-04-06 14:18:50 -0400239class ComputeAdminConfig(BaseConfig):
240
241 SECTION_NAME = "compute-admin"
242
243 @property
244 def username(self):
245 """Administrative Username to use for Nova API requests."""
246 return self.get("username", "admin")
247
248 @property
249 def tenant_name(self):
250 """Administrative Tenant name to use for Nova API requests."""
251 return self.get("tenant_name", "admin")
252
253 @property
254 def password(self):
255 """API key to use when authenticating as admin."""
256 return self.get("password", "pass")
257
258
Jay Pipes3f981df2012-03-27 18:59:44 -0400259class ImagesConfig(BaseConfig):
260
Jay Pipes50677282012-01-06 15:39:20 -0500261 """
262 Provides configuration information for connecting to an
263 OpenStack Images service.
264 """
265
Jay Pipes3f981df2012-03-27 18:59:44 -0400266 SECTION_NAME = "image"
Jay Pipes50677282012-01-06 15:39:20 -0500267
268 @property
269 def host(self):
270 """Host IP for making Images API requests. Defaults to '127.0.0.1'."""
271 return self.get("host", "127.0.0.1")
272
273 @property
274 def port(self):
275 """Listen port of the Images service."""
276 return int(self.get("port", "9292"))
277
278 @property
279 def api_version(self):
280 """Version of the API"""
281 return self.get("api_version", "1")
282
283 @property
284 def username(self):
Jay Pipes3f981df2012-03-27 18:59:44 -0400285 """Username to use for Images API requests. Defaults to 'demo'."""
Julien Danjou75a677e2012-04-11 15:49:15 +0200286 return self.get("username", "demo")
Jay Pipes50677282012-01-06 15:39:20 -0500287
288 @property
289 def password(self):
290 """Password for user"""
Jay Pipes3f981df2012-03-27 18:59:44 -0400291 return self.get("password", "pass")
Jay Pipes50677282012-01-06 15:39:20 -0500292
293 @property
Jay Pipes3f981df2012-03-27 18:59:44 -0400294 def tenant_name(self):
295 """Tenant to use for Images API requests. Defaults to 'demo'."""
296 return self.get("tenant_name", "demo")
Jay Pipes50677282012-01-06 15:39:20 -0500297
298
Unmesh Gurjar44986832012-05-08 19:57:10 +0530299class NetworkConfig(BaseConfig):
300 """Provides configuration information for connecting to an OpenStack
301 Network Service.
302 """
303
304 SECTION_NAME = "network"
305
306 @property
307 def catalog_type(self):
308 """Catalog type of the Quantum service."""
309 return self.get("catalog_type", 'network')
310
311 @property
312 def api_version(self):
313 """Version of Quantum API"""
314 return self.get("api_version", "v1.1")
315
316
Jay Pipes3f981df2012-03-27 18:59:44 -0400317# TODO(jaypipes): Move this to a common utils (not data_utils...)
318def singleton(cls):
319 """Simple wrapper for classes that should only have a single instance"""
320 instances = {}
321
322 def getinstance():
323 if cls not in instances:
324 instances[cls] = cls()
325 return instances[cls]
326 return getinstance
327
328
329@singleton
330class TempestConfig:
Daryl Walleck1465d612011-11-02 02:22:15 -0500331 """Provides OpenStack configuration information."""
332
Brian Waldon738cd632011-12-12 18:45:09 -0500333 DEFAULT_CONFIG_DIR = os.path.join(
334 os.path.abspath(
335 os.path.dirname(
336 os.path.dirname(__file__))),
337 "etc")
Daryl Walleck1465d612011-11-02 02:22:15 -0500338
Brian Waldon738cd632011-12-12 18:45:09 -0500339 DEFAULT_CONFIG_FILE = "tempest.conf"
340
341 def __init__(self):
342 """Initialize a configuration from a conf directory and conf file."""
343
344 # Environment variables override defaults...
345 conf_dir = os.environ.get('TEMPEST_CONFIG_DIR',
346 self.DEFAULT_CONFIG_DIR)
347 conf_file = os.environ.get('TEMPEST_CONFIG',
348 self.DEFAULT_CONFIG_FILE)
349
Jay Pipes7f757632011-12-02 15:53:32 -0500350 path = os.path.join(conf_dir, conf_file)
351
Jay Pipes3f981df2012-03-27 18:59:44 -0400352 LOG.info("Using tempest config file %s" % path)
353
Jay Pipes7f757632011-12-02 15:53:32 -0500354 if not os.path.exists(path):
355 msg = "Config file %(path)s not found" % locals()
356 raise RuntimeError(msg)
357
358 self._conf = self.load_config(path)
Daryl Walleck587385b2012-03-03 13:00:26 -0600359 self.compute = ComputeConfig(self._conf)
Jay Pipesff10d552012-04-06 14:18:50 -0400360 self.compute_admin = ComputeAdminConfig(self._conf)
Daryl Walleck587385b2012-03-03 13:00:26 -0600361 self.identity = IdentityConfig(self._conf)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400362 self.identity_admin = IdentityAdminConfig(self._conf)
Jay Pipes50677282012-01-06 15:39:20 -0500363 self.images = ImagesConfig(self._conf)
Unmesh Gurjar44986832012-05-08 19:57:10 +0530364 self.network = NetworkConfig(self._conf)
Daryl Walleck1465d612011-11-02 02:22:15 -0500365
Jay Pipes7f757632011-12-02 15:53:32 -0500366 def load_config(self, path):
Daryl Walleck1465d612011-11-02 02:22:15 -0500367 """Read configuration from given path and return a config object."""
368 config = ConfigParser.SafeConfigParser()
369 config.read(path)
370 return config