Merge "Added images support and existing config support"
diff --git a/tools/tempest_auto_config.py b/tools/tempest_auto_config.py
index aef6a1f..fe9f5af 100644
--- a/tools/tempest_auto_config.py
+++ b/tools/tempest_auto_config.py
@@ -14,38 +14,50 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
+#
+# This script aims to configure an initial Openstack environment with all the
+# necessary configurations for tempest's run using nothing but Openstack's
+# native API.
+# That includes, creating users, tenants, registering images (cirros),
+# configuring neutron and so on.
+#
+# ASSUMPTION: this script is run by an admin user as it is meant to configure
+# the Openstack environment prior to actual use.
# Config
import ConfigParser
import os
+import tarfile
+import urllib2
# Default client libs
+import glanceclient as glance_client
import keystoneclient.v2_0.client as keystone_client
# Import Openstack exceptions
+import glanceclient.exc as glance_exception
import keystoneclient.exceptions as keystone_exception
-DEFAULT_CONFIG_DIR = "%s/etc" % os.path.abspath(os.path.pardir)
-DEFAULT_CONFIG_FILE = "tempest.conf"
-DEFAULT_CONFIG_SAMPLE = "tempest.conf.sample"
+TEMPEST_TEMP_DIR = os.getenv("TEMPEST_TEMP_DIR", "/tmp").rstrip('/')
+TEMPEST_ROOT_DIR = os.getenv("TEMPEST_ROOT_DIR", os.getenv("HOME")).rstrip('/')
# Environment variables override defaults
-TEMPEST_CONFIG_DIR = os.environ.get('TEMPEST_CONFIG_DIR') or DEFAULT_CONFIG_DIR
-TEMPEST_CONFIG = os.environ.get('TEMPEST_CONFIG') or "%s/%s" % \
- (TEMPEST_CONFIG_DIR, DEFAULT_CONFIG_FILE)
-TEMPEST_CONFIG_SAMPLE = os.environ.get('TEMPEST_CONFIG_SAMPLE') or "%s/%s" % \
- (TEMPEST_CONFIG_DIR, DEFAULT_CONFIG_SAMPLE)
-
-# Admin credentials
-OS_USERNAME = os.environ.get('OS_USERNAME')
-OS_PASSWORD = os.environ.get('OS_PASSWORD')
-OS_TENANT_NAME = os.environ.get('OS_TENANT_NAME')
-OS_AUTH_URL = os.environ.get('OS_AUTH_URL')
-
+TEMPEST_CONFIG_DIR = os.getenv("TEMPEST_CONFIG_DIR",
+ "%s%s" % (TEMPEST_ROOT_DIR, "/etc")).rstrip('/')
+TEMPEST_CONFIG_FILE = os.getenv("TEMPEST_CONFIG_FILE",
+ "%s%s" % (TEMPEST_CONFIG_DIR, "/tempest.conf"))
+TEMPEST_CONFIG_SAMPLE = os.getenv("TEMPEST_CONFIG_SAMPLE",
+ "%s%s" % (TEMPEST_CONFIG_DIR,
+ "/tempest.conf.sample"))
# Image references
-IMAGE_ID = os.environ.get('IMAGE_ID')
-IMAGE_ID_ALT = os.environ.get('IMAGE_ID_ALT')
+IMAGE_DOWNLOAD_CHUNK_SIZE = 8 * 1024
+IMAGE_UEC_SOURCE_URL = os.getenv("IMAGE_UEC_SOURCE_URL",
+ "http://download.cirros-cloud.net/0.3.1/"
+ "cirros-0.3.1-x86_64-uec.tar.gz")
+TEMPEST_IMAGE_ID = os.getenv('IMAGE_ID')
+TEMPEST_IMAGE_ID_ALT = os.getenv('IMAGE_ID_ALT')
+IMAGE_STATUS_ACTIVE = 'active'
class ClientManager(object):
@@ -76,26 +88,52 @@
return self.identity_client
+ def get_image_client(self, version="1", *args, **kwargs):
+ """
+ This method returns Openstack glance python client
+ :param version: a string representing the version of the glance client
+ to use.
+ :param string endpoint: A user-supplied endpoint URL for the glance
+ service.
+ :param string token: Token for authentication.
+ :param integer timeout: Allows customization of the timeout for client
+ http requests. (optional)
+ :return: a Client object representing the glance client
+ """
+ if not self.image_client:
+ self.image_client = glance_client.Client(version, *args, **kwargs)
-def getTempestConfigSample():
+ return self.image_client
+
+
+def get_tempest_config(path_to_config):
"""
Gets the tempest configuration file as a ConfigParser object
- :return: the tempest configuration file
+ :param path_to_config: path to the config file
+ :return: a ConfigParser object representing the tempest configuration file
"""
# get the sample config file from the sample
- config_sample = ConfigParser.ConfigParser()
- config_sample.readfp(open(TEMPEST_CONFIG_SAMPLE))
+ config = ConfigParser.ConfigParser()
+ config.readfp(open(path_to_config))
- return config_sample
+ return config
def update_config_admin_credentials(config, config_section):
"""
Updates the tempest config with the admin credentials
- :param config: an object representing the tempest config file
+ :param config: a ConfigParser object representing the tempest config file
:param config_section: the section name where the admin credentials are
"""
- # Check if credentials are present
+ # Check if credentials are present, default uses the config credentials
+ OS_USERNAME = os.getenv('OS_USERNAME',
+ config.get(config_section, "admin_username"))
+ OS_PASSWORD = os.getenv('OS_PASSWORD',
+ config.get(config_section, "admin_password"))
+ OS_TENANT_NAME = os.getenv('OS_TENANT_NAME',
+ config.get(config_section, "admin_tenant_name"))
+ OS_AUTH_URL = os.getenv('OS_AUTH_URL', config.get(config_section, "uri"))
+
if not (OS_AUTH_URL and
OS_USERNAME and
OS_PASSWORD and
@@ -113,31 +151,31 @@
config_identity_params)
-def update_config_section_with_params(config, section, params):
+def update_config_section_with_params(config, config_section, params):
"""
Updates a given config object with given params
- :param config: the object representing the config file of tempest
- :param section: the section we would like to update
+ :param config: a ConfigParser object representing the tempest config file
+ :param config_section: the section we would like to update
:param params: the parameters we wish to update for that section
"""
for option, value in params.items():
- config.set(section, option, value)
+ config.set(config_section, option, value)
-def get_identity_client_kwargs(config, section_name):
+def get_identity_client_kwargs(config, config_section):
"""
Get the required arguments for the identity python client
- :param config: the tempest configuration file
- :param section_name: the section name in the configuration where the
+ :param config: a ConfigParser object representing the tempest config file
+ :param config_section: the section name in the configuration where the
arguments can be found
:return: a dictionary representing the needed arguments for the identity
client
"""
- username = config.get(section_name, 'admin_username')
- password = config.get(section_name, 'admin_password')
- tenant_name = config.get(section_name, 'admin_tenant_name')
- auth_url = config.get(section_name, 'uri')
- dscv = config.get(section_name, 'disable_ssl_certificate_validation')
+ username = config.get(config_section, 'admin_username')
+ password = config.get(config_section, 'admin_password')
+ tenant_name = config.get(config_section, 'admin_tenant_name')
+ auth_url = config.get(config_section, 'uri')
+ dscv = config.get(config_section, 'disable_ssl_certificate_validation')
kwargs = {'username': username,
'password': password,
'tenant_name': tenant_name,
@@ -185,21 +223,21 @@
def create_users_and_tenants(identity_client,
config,
- identity_section):
+ config_section):
"""
Creates the two non admin users and tenants for tempest
:param identity_client: openstack identity python client
- :param config: tempest configuration file
- :param identity_section: the section name of identity in the config
+ :param config: a ConfigParser object representing the tempest config file
+ :param config_section: the section name of identity in the config
"""
# Get the necessary params from the config file
- tenant_name = config.get(identity_section, 'tenant_name')
- username = config.get(identity_section, 'username')
- password = config.get(identity_section, 'password')
+ tenant_name = config.get(config_section, 'tenant_name')
+ username = config.get(config_section, 'username')
+ password = config.get(config_section, 'password')
- alt_tenant_name = config.get(identity_section, 'alt_tenant_name')
- alt_username = config.get(identity_section, 'alt_username')
- alt_password = config.get(identity_section, 'alt_password')
+ alt_tenant_name = config.get(config_section, 'alt_tenant_name')
+ alt_username = config.get(config_section, 'alt_username')
+ alt_password = config.get(config_section, 'alt_password')
# Create the necessary users for the test runs
create_user_with_tenant(identity_client, username, password, tenant_name)
@@ -207,28 +245,153 @@
alt_tenant_name)
+def get_image_client_kwargs(identity_client, config, config_section):
+ """
+ Get the required arguments for the image python client
+ :param identity_client: openstack identity python client
+ :param config: a ConfigParser object representing the tempest config file
+ :param config_section: the section name of identity in the config
+ :return: a dictionary representing the needed arguments for the image
+ client
+ """
+
+ token = identity_client.auth_token
+ endpoint = identity_client.\
+ service_catalog.url_for(service_type='image', endpoint_type='publicURL'
+ )
+ dscv = config.get(config_section, 'disable_ssl_certificate_validation')
+ kwargs = {'endpoint': endpoint,
+ 'token': token,
+ 'insecure': dscv}
+
+ return kwargs
+
+
+def images_exist(image_client):
+ """
+ Checks whether the images ID's located in the environment variable are
+ indeed registered
+ :param image_client: the openstack python client representing the image
+ client
+ """
+ exist = True
+ if not TEMPEST_IMAGE_ID or not TEMPEST_IMAGE_ID_ALT:
+ exist = False
+ else:
+ try:
+ image_client.images.get(TEMPEST_IMAGE_ID)
+ image_client.images.get(TEMPEST_IMAGE_ID_ALT)
+ except glance_exception.HTTPNotFound:
+ exist = False
+
+ return exist
+
+
+def download_and_register_uec_images(image_client, download_url,
+ download_folder):
+ """
+ Downloads and registered the UEC AKI/AMI/ARI images
+ :param image_client:
+ :param download_url: the url of the uec tar file
+ :param download_folder: the destination folder we wish to save the file to
+ """
+ basename = os.path.basename(download_url)
+ path = os.path.join(download_folder, basename)
+
+ request = urllib2.urlopen(download_url)
+
+ # First, download the file
+ with open(path, "wb") as fp:
+ while True:
+ chunk = request.read(IMAGE_DOWNLOAD_CHUNK_SIZE)
+ if not chunk:
+ break
+
+ fp.write(chunk)
+
+ # Then extract and register images
+ tar = tarfile.open(path, "r")
+ for name in tar.getnames():
+ file_obj = tar.extractfile(name)
+ format = "aki"
+
+ if file_obj.name.endswith(".img"):
+ format = "ami"
+
+ if file_obj.name.endswith("initrd"):
+ format = "ari"
+
+ # Register images in image client
+ image_client.images.create(name=file_obj.name, disk_format=format,
+ container_format=format, data=file_obj,
+ is_public="true")
+
+ tar.close()
+
+
+def create_images(image_client, config, config_section,
+ download_url=IMAGE_UEC_SOURCE_URL,
+ download_folder=TEMPEST_TEMP_DIR):
+ """
+ Creates images for tempest's use and registers the environment variables
+ IMAGE_ID and IMAGE_ID_ALT with registered images
+ :param image_client: Openstack python image client
+ :param config: a ConfigParser object representing the tempest config file
+ :param config_section: the section name where the IMAGE ids are set
+ :param download_url: the URL from which we should download the UEC tar
+ :param download_folder: the place where we want to save the download file
+ """
+ if not images_exist(image_client):
+ # Falls down to the default uec images
+ download_and_register_uec_images(image_client, download_url,
+ download_folder)
+ image_ids = []
+ for image in image_client.images.list():
+ image_ids.append(image.id)
+
+ os.environ["IMAGE_ID"] = image_ids[0]
+ os.environ["IMAGE_ID_ALT"] = image_ids[1]
+
+ params = {'image_ref': os.getenv("IMAGE_ID"),
+ 'image_ref_alt': os.getenv("IMAGE_ID_ALT")}
+
+ update_config_section_with_params(config, config_section, params)
+
+
def main():
"""
Main module to control the script
"""
- # TODO(tkammer): add support for existing config file
- config_sample = getTempestConfigSample()
- update_config_admin_credentials(config_sample, 'identity')
+ # Check if config file exists or fall to the default sample otherwise
+ path_to_config = TEMPEST_CONFIG_SAMPLE
+
+ if os.path.isfile(TEMPEST_CONFIG_FILE):
+ path_to_config = TEMPEST_CONFIG_FILE
+
+ config = get_tempest_config(path_to_config)
+ update_config_admin_credentials(config, 'identity')
client_manager = ClientManager()
# Set the identity related info for tempest
- identity_client_kwargs = get_identity_client_kwargs(config_sample,
+ identity_client_kwargs = get_identity_client_kwargs(config,
'identity')
identity_client = client_manager.get_identity_client(
**identity_client_kwargs)
# Create the necessary users and tenants for tempest run
- create_users_and_tenants(identity_client,
- config_sample,
- 'identity')
+ create_users_and_tenants(identity_client, config, 'identity')
- # TODO(tkammer): add image implementation
+ # Set the image related info for tempest
+ image_client_kwargs = get_image_client_kwargs(identity_client,
+ config,
+ 'identity')
+ image_client = client_manager.get_image_client(**image_client_kwargs)
+
+ # Create the necessary users and tenants for tempest run
+ create_images(image_client, config, 'compute')
+
+ # TODO(tkammer): add network implementation
if __name__ == "__main__":
main()