blob: 5b8d05be7f416a42e1d147e2a79546c98d405400 [file] [log] [blame]
Tal Kammerc6b97882013-08-20 18:06:18 +03001# Copyright 2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
Tal Kammerf30b4ef2013-11-12 14:11:23 +020015#
tanlin4956a642014-02-13 16:52:11 +080016# This script aims to configure an initial OpenStack environment with all the
17# necessary configurations for tempest's run using nothing but OpenStack's
Tal Kammerf30b4ef2013-11-12 14:11:23 +020018# native API.
19# That includes, creating users, tenants, registering images (cirros),
20# configuring neutron and so on.
21#
22# ASSUMPTION: this script is run by an admin user as it is meant to configure
tanlin4956a642014-02-13 16:52:11 +080023# the OpenStack environment prior to actual use.
Tal Kammerc6b97882013-08-20 18:06:18 +030024
25# Config
26import ConfigParser
27import os
Tal Kammerf30b4ef2013-11-12 14:11:23 +020028import tarfile
29import urllib2
Tal Kammerc6b97882013-08-20 18:06:18 +030030
31# Default client libs
Tal Kammerf30b4ef2013-11-12 14:11:23 +020032import glanceclient as glance_client
Tal Kammerc6b97882013-08-20 18:06:18 +030033import keystoneclient.v2_0.client as keystone_client
34
tanlin4956a642014-02-13 16:52:11 +080035# Import OpenStack exceptions
Tal Kammerf30b4ef2013-11-12 14:11:23 +020036import glanceclient.exc as glance_exception
Tal Kammerc6b97882013-08-20 18:06:18 +030037import keystoneclient.exceptions as keystone_exception
38
39
Tal Kammerf30b4ef2013-11-12 14:11:23 +020040TEMPEST_TEMP_DIR = os.getenv("TEMPEST_TEMP_DIR", "/tmp").rstrip('/')
41TEMPEST_ROOT_DIR = os.getenv("TEMPEST_ROOT_DIR", os.getenv("HOME")).rstrip('/')
Tal Kammerc6b97882013-08-20 18:06:18 +030042
43# Environment variables override defaults
Tal Kammerf30b4ef2013-11-12 14:11:23 +020044TEMPEST_CONFIG_DIR = os.getenv("TEMPEST_CONFIG_DIR",
45 "%s%s" % (TEMPEST_ROOT_DIR, "/etc")).rstrip('/')
46TEMPEST_CONFIG_FILE = os.getenv("TEMPEST_CONFIG_FILE",
47 "%s%s" % (TEMPEST_CONFIG_DIR, "/tempest.conf"))
48TEMPEST_CONFIG_SAMPLE = os.getenv("TEMPEST_CONFIG_SAMPLE",
49 "%s%s" % (TEMPEST_CONFIG_DIR,
50 "/tempest.conf.sample"))
Tal Kammerc6b97882013-08-20 18:06:18 +030051# Image references
Tal Kammerf30b4ef2013-11-12 14:11:23 +020052IMAGE_DOWNLOAD_CHUNK_SIZE = 8 * 1024
53IMAGE_UEC_SOURCE_URL = os.getenv("IMAGE_UEC_SOURCE_URL",
54 "http://download.cirros-cloud.net/0.3.1/"
55 "cirros-0.3.1-x86_64-uec.tar.gz")
56TEMPEST_IMAGE_ID = os.getenv('IMAGE_ID')
57TEMPEST_IMAGE_ID_ALT = os.getenv('IMAGE_ID_ALT')
58IMAGE_STATUS_ACTIVE = 'active'
Tal Kammerc6b97882013-08-20 18:06:18 +030059
60
61class ClientManager(object):
62 """
63 Manager that provides access to the official python clients for
64 calling various OpenStack APIs.
65 """
66 def __init__(self):
67 self.identity_client = None
68 self.image_client = None
69 self.network_client = None
70 self.compute_client = None
71 self.volume_client = None
72
73 def get_identity_client(self, **kwargs):
74 """
75 Returns the openstack identity python client
76 :param username: a string representing the username
77 :param password: a string representing the user's password
78 :param tenant_name: a string representing the tenant name of the user
79 :param auth_url: a string representing the auth url of the identity
80 :param insecure: True if we wish to disable ssl certificate validation,
81 False otherwise
82 :returns an instance of openstack identity python client
83 """
84 if not self.identity_client:
85 self.identity_client = keystone_client.Client(**kwargs)
86
87 return self.identity_client
88
Tal Kammerf30b4ef2013-11-12 14:11:23 +020089 def get_image_client(self, version="1", *args, **kwargs):
90 """
tanlin4956a642014-02-13 16:52:11 +080091 This method returns OpenStack glance python client
Tal Kammerf30b4ef2013-11-12 14:11:23 +020092 :param version: a string representing the version of the glance client
93 to use.
94 :param string endpoint: A user-supplied endpoint URL for the glance
95 service.
96 :param string token: Token for authentication.
97 :param integer timeout: Allows customization of the timeout for client
98 http requests. (optional)
99 :return: a Client object representing the glance client
100 """
101 if not self.image_client:
102 self.image_client = glance_client.Client(version, *args, **kwargs)
Tal Kammerc6b97882013-08-20 18:06:18 +0300103
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200104 return self.image_client
105
106
107def get_tempest_config(path_to_config):
Tal Kammerc6b97882013-08-20 18:06:18 +0300108 """
109 Gets the tempest configuration file as a ConfigParser object
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200110 :param path_to_config: path to the config file
111 :return: a ConfigParser object representing the tempest configuration file
Tal Kammerc6b97882013-08-20 18:06:18 +0300112 """
113 # get the sample config file from the sample
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200114 config = ConfigParser.ConfigParser()
115 config.readfp(open(path_to_config))
Tal Kammerc6b97882013-08-20 18:06:18 +0300116
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200117 return config
Tal Kammerc6b97882013-08-20 18:06:18 +0300118
119
120def update_config_admin_credentials(config, config_section):
121 """
122 Updates the tempest config with the admin credentials
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200123 :param config: a ConfigParser object representing the tempest config file
Tal Kammerc6b97882013-08-20 18:06:18 +0300124 :param config_section: the section name where the admin credentials are
125 """
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200126 # Check if credentials are present, default uses the config credentials
127 OS_USERNAME = os.getenv('OS_USERNAME',
128 config.get(config_section, "admin_username"))
129 OS_PASSWORD = os.getenv('OS_PASSWORD',
130 config.get(config_section, "admin_password"))
131 OS_TENANT_NAME = os.getenv('OS_TENANT_NAME',
132 config.get(config_section, "admin_tenant_name"))
133 OS_AUTH_URL = os.getenv('OS_AUTH_URL', config.get(config_section, "uri"))
134
Tal Kammerc6b97882013-08-20 18:06:18 +0300135 if not (OS_AUTH_URL and
136 OS_USERNAME and
137 OS_PASSWORD and
138 OS_TENANT_NAME):
139 raise Exception("Admin environment variables not found.")
140
141 # TODO(tkammer): Add support for uri_v3
142 config_identity_params = {'uri': OS_AUTH_URL,
143 'admin_username': OS_USERNAME,
144 'admin_password': OS_PASSWORD,
145 'admin_tenant_name': OS_TENANT_NAME}
146
147 update_config_section_with_params(config,
148 config_section,
149 config_identity_params)
150
151
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200152def update_config_section_with_params(config, config_section, params):
Tal Kammerc6b97882013-08-20 18:06:18 +0300153 """
154 Updates a given config object with given params
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200155 :param config: a ConfigParser object representing the tempest config file
156 :param config_section: the section we would like to update
Tal Kammerc6b97882013-08-20 18:06:18 +0300157 :param params: the parameters we wish to update for that section
158 """
159 for option, value in params.items():
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200160 config.set(config_section, option, value)
Tal Kammerc6b97882013-08-20 18:06:18 +0300161
162
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200163def get_identity_client_kwargs(config, config_section):
Tal Kammerc6b97882013-08-20 18:06:18 +0300164 """
165 Get the required arguments for the identity python client
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200166 :param config: a ConfigParser object representing the tempest config file
167 :param config_section: the section name in the configuration where the
Tal Kammerc6b97882013-08-20 18:06:18 +0300168 arguments can be found
169 :return: a dictionary representing the needed arguments for the identity
170 client
171 """
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200172 username = config.get(config_section, 'admin_username')
173 password = config.get(config_section, 'admin_password')
174 tenant_name = config.get(config_section, 'admin_tenant_name')
175 auth_url = config.get(config_section, 'uri')
176 dscv = config.get(config_section, 'disable_ssl_certificate_validation')
Tal Kammerc6b97882013-08-20 18:06:18 +0300177 kwargs = {'username': username,
178 'password': password,
179 'tenant_name': tenant_name,
180 'auth_url': auth_url,
181 'insecure': dscv}
182
183 return kwargs
184
185
186def create_user_with_tenant(identity_client, username, password, tenant_name):
187 """
188 Creates a user using a given identity client
189 :param identity_client: openstack identity python client
190 :param username: a string representing the username
191 :param password: a string representing the user's password
192 :param tenant_name: a string representing the tenant name of the user
193 """
194 # Try to create the necessary tenant
195 tenant_id = None
196 try:
197 tenant_description = "Tenant for Tempest %s user" % username
198 tenant = identity_client.tenants.create(tenant_name,
199 tenant_description)
200 tenant_id = tenant.id
201 except keystone_exception.Conflict:
202
203 # if already exist, use existing tenant
204 tenant_list = identity_client.tenants.list()
205 for tenant in tenant_list:
206 if tenant.name == tenant_name:
207 tenant_id = tenant.id
208
209 # Try to create the user
210 try:
211 email = "%s@test.com" % username
212 identity_client.users.create(name=username,
213 password=password,
214 email=email,
215 tenant_id=tenant_id)
216 except keystone_exception.Conflict:
217
218 # if already exist, use existing user
219 pass
220
221
222def create_users_and_tenants(identity_client,
223 config,
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200224 config_section):
Tal Kammerc6b97882013-08-20 18:06:18 +0300225 """
226 Creates the two non admin users and tenants for tempest
227 :param identity_client: openstack identity python client
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200228 :param config: a ConfigParser object representing the tempest config file
229 :param config_section: the section name of identity in the config
Tal Kammerc6b97882013-08-20 18:06:18 +0300230 """
231 # Get the necessary params from the config file
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200232 tenant_name = config.get(config_section, 'tenant_name')
233 username = config.get(config_section, 'username')
234 password = config.get(config_section, 'password')
Tal Kammerc6b97882013-08-20 18:06:18 +0300235
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200236 alt_tenant_name = config.get(config_section, 'alt_tenant_name')
237 alt_username = config.get(config_section, 'alt_username')
238 alt_password = config.get(config_section, 'alt_password')
Tal Kammerc6b97882013-08-20 18:06:18 +0300239
240 # Create the necessary users for the test runs
241 create_user_with_tenant(identity_client, username, password, tenant_name)
242 create_user_with_tenant(identity_client, alt_username, alt_password,
243 alt_tenant_name)
244
245
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200246def get_image_client_kwargs(identity_client, config, config_section):
247 """
248 Get the required arguments for the image python client
249 :param identity_client: openstack identity python client
250 :param config: a ConfigParser object representing the tempest config file
251 :param config_section: the section name of identity in the config
252 :return: a dictionary representing the needed arguments for the image
253 client
254 """
255
256 token = identity_client.auth_token
257 endpoint = identity_client.\
258 service_catalog.url_for(service_type='image', endpoint_type='publicURL'
259 )
260 dscv = config.get(config_section, 'disable_ssl_certificate_validation')
261 kwargs = {'endpoint': endpoint,
262 'token': token,
263 'insecure': dscv}
264
265 return kwargs
266
267
268def images_exist(image_client):
269 """
270 Checks whether the images ID's located in the environment variable are
271 indeed registered
272 :param image_client: the openstack python client representing the image
273 client
274 """
275 exist = True
276 if not TEMPEST_IMAGE_ID or not TEMPEST_IMAGE_ID_ALT:
277 exist = False
278 else:
279 try:
280 image_client.images.get(TEMPEST_IMAGE_ID)
281 image_client.images.get(TEMPEST_IMAGE_ID_ALT)
282 except glance_exception.HTTPNotFound:
283 exist = False
284
285 return exist
286
287
288def download_and_register_uec_images(image_client, download_url,
289 download_folder):
290 """
291 Downloads and registered the UEC AKI/AMI/ARI images
292 :param image_client:
293 :param download_url: the url of the uec tar file
294 :param download_folder: the destination folder we wish to save the file to
295 """
296 basename = os.path.basename(download_url)
297 path = os.path.join(download_folder, basename)
298
299 request = urllib2.urlopen(download_url)
300
301 # First, download the file
302 with open(path, "wb") as fp:
303 while True:
304 chunk = request.read(IMAGE_DOWNLOAD_CHUNK_SIZE)
305 if not chunk:
306 break
307
308 fp.write(chunk)
309
310 # Then extract and register images
311 tar = tarfile.open(path, "r")
312 for name in tar.getnames():
313 file_obj = tar.extractfile(name)
314 format = "aki"
315
316 if file_obj.name.endswith(".img"):
317 format = "ami"
318
319 if file_obj.name.endswith("initrd"):
320 format = "ari"
321
322 # Register images in image client
323 image_client.images.create(name=file_obj.name, disk_format=format,
324 container_format=format, data=file_obj,
325 is_public="true")
326
327 tar.close()
328
329
330def create_images(image_client, config, config_section,
331 download_url=IMAGE_UEC_SOURCE_URL,
332 download_folder=TEMPEST_TEMP_DIR):
333 """
334 Creates images for tempest's use and registers the environment variables
335 IMAGE_ID and IMAGE_ID_ALT with registered images
tanlin4956a642014-02-13 16:52:11 +0800336 :param image_client: OpenStack python image client
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200337 :param config: a ConfigParser object representing the tempest config file
338 :param config_section: the section name where the IMAGE ids are set
339 :param download_url: the URL from which we should download the UEC tar
340 :param download_folder: the place where we want to save the download file
341 """
342 if not images_exist(image_client):
343 # Falls down to the default uec images
344 download_and_register_uec_images(image_client, download_url,
345 download_folder)
346 image_ids = []
347 for image in image_client.images.list():
348 image_ids.append(image.id)
349
350 os.environ["IMAGE_ID"] = image_ids[0]
351 os.environ["IMAGE_ID_ALT"] = image_ids[1]
352
353 params = {'image_ref': os.getenv("IMAGE_ID"),
354 'image_ref_alt': os.getenv("IMAGE_ID_ALT")}
355
356 update_config_section_with_params(config, config_section, params)
357
358
Tal Kammerc6b97882013-08-20 18:06:18 +0300359def main():
360 """
361 Main module to control the script
362 """
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200363 # Check if config file exists or fall to the default sample otherwise
364 path_to_config = TEMPEST_CONFIG_SAMPLE
365
366 if os.path.isfile(TEMPEST_CONFIG_FILE):
367 path_to_config = TEMPEST_CONFIG_FILE
368
369 config = get_tempest_config(path_to_config)
370 update_config_admin_credentials(config, 'identity')
Tal Kammerc6b97882013-08-20 18:06:18 +0300371
372 client_manager = ClientManager()
373
374 # Set the identity related info for tempest
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200375 identity_client_kwargs = get_identity_client_kwargs(config,
Tal Kammerc6b97882013-08-20 18:06:18 +0300376 'identity')
377 identity_client = client_manager.get_identity_client(
378 **identity_client_kwargs)
379
380 # Create the necessary users and tenants for tempest run
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200381 create_users_and_tenants(identity_client, config, 'identity')
Tal Kammerc6b97882013-08-20 18:06:18 +0300382
Tal Kammerf30b4ef2013-11-12 14:11:23 +0200383 # Set the image related info for tempest
384 image_client_kwargs = get_image_client_kwargs(identity_client,
385 config,
386 'identity')
387 image_client = client_manager.get_image_client(**image_client_kwargs)
388
389 # Create the necessary users and tenants for tempest run
390 create_images(image_client, config, 'compute')
391
392 # TODO(tkammer): add network implementation
Tal Kammerc6b97882013-08-20 18:06:18 +0300393
394if __name__ == "__main__":
395 main()