From 754ce1457182411e7e887b307fb66f280885ff7e Mon Sep 17 00:00:00 2001 From: liyin Date: Tue, 27 Dec 2016 15:43:06 +0800 Subject: bottlenecks openstack Newton support JIRA: BOTTLENECK-119 Change the file of template.py manager.py and common.py file This change helps Bottlenecks project support Newton. template.py support stack operation. manager.py support nova and glance operation. common.py support some operations of openstack Newton. Change-Id: Ibee110a2b7918c80b2651bb86a9fb7160414e842 Signed-off-by: liyin --- utils/infra_setup/heat/__init__.py | 2 +- utils/infra_setup/heat/common.py | 467 ++++++++----------------------------- utils/infra_setup/heat/manager.py | 140 +++++------ utils/infra_setup/heat/template.py | 300 ++++++++++++++---------- 4 files changed, 329 insertions(+), 580 deletions(-) mode change 100755 => 100644 utils/infra_setup/heat/__init__.py mode change 100755 => 100644 utils/infra_setup/heat/manager.py diff --git a/utils/infra_setup/heat/__init__.py b/utils/infra_setup/heat/__init__.py old mode 100755 new mode 100644 index 83b8d15d..6dbd8d79 --- a/utils/infra_setup/heat/__init__.py +++ b/utils/infra_setup/heat/__init__.py @@ -1,5 +1,5 @@ ############################################################################## -# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 diff --git a/utils/infra_setup/heat/common.py b/utils/infra_setup/heat/common.py index c4a78249..28257ac7 100755 --- a/utils/infra_setup/heat/common.py +++ b/utils/infra_setup/heat/common.py @@ -8,385 +8,106 @@ ############################################################################## import os -import re -import ConfigParser import logging -import fileinput -import consts.files as files -import consts.parameters as parameters +from keystoneauth1 import loading +from keystoneauth1 import session -# ------------------------------------------------------ -# List of common variables -# ------------------------------------------------------ +log = logging.getLogger(__name__) -LOG = None -CONF_FILE = None -DEPLOYMENT_UNIT = None -ITERATIONS = None +DEFAULT_HEAT_API_VERSION = '1' +DEFAULT_NOVA_API_VERSION = '2' +DEFAULT_GLANCE_API_VERSION = '2' -BASE_DIR = None -TEMPLATE_DIR = None -TEMPLATE_NAME = None -TEMPLATE_EXTENSION = None -# ------------------------------------------------------ -# Initialization and Input 'heat_templates/'validation -# ------------------------------------------------------ +def get_credentials(): + """Returns a creds dictionary filled with parsed from env""" + creds = {} + keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION') -def init(api=False): - global BASE_DIR - # BASE_DIR = os.getcwd() - BASE_DIR = os.path.dirname(os.path.abspath(__file__)) - BASE_DIR = BASE_DIR.replace('/heat', '') - BASE_DIR = InputValidation.validate_directory_exist_and_format( - BASE_DIR, "Error 000001") - - conf_file_init(api) - log_init() - general_vars_init(api) - - -def conf_file_init(api=False): - global CONF_FILE - if api: - CONF_FILE = ConfigurationFile(files.get_sections_api(), - '/tmp/bottlenecks.conf') - else: - CONF_FILE = ConfigurationFile(cf.get_sections(), - '/tmp/bottlenecks.conf') - - -def general_vars_init(api=False): - global TEMPLATE_EXTENSION - global TEMPLATE_NAME - global TEMPLATE_DIR - global ITERATIONS - - TEMPLATE_EXTENSION = '.yaml' - - # Check Section in Configuration File - InputValidation.validate_configuration_file_section( - files.GENERAL, - "Section " + files.GENERAL + - "is not present in configuration file") - - InputValidation.validate_configuration_file_section( - files.OPENSTACK, - "Section " + files.OPENSTACK + - "is not present in configuration file") - - TEMPLATE_DIR = '/tmp/heat_templates/' - - if not api: - # Validate template name - InputValidation.validate_configuration_file_parameter( - files.GENERAL, - files.TEMPLATE_NAME, - "Parameter " + files.TEMPLATE_NAME + - "is not present in configuration file") - TEMPLATE_NAME = CONF_FILE.get_variable(files.GENERAL, - files.TEMPLATE_NAME) - InputValidation.validate_file_exist( - TEMPLATE_DIR + TEMPLATE_NAME, - "The provided template file does not exist") - - # Validate and assign Iterations - if files.ITERATIONS in CONF_FILE.get_variable_list(files.GENERAL): - ITERATIONS = int(CONF_FILE.get_variable(files.GENERAL, - files.ITERATIONS)) + if keystone_api_version is None or keystone_api_version == '2': + keystone_v3 = False + tenant_env = 'OS_TENANT_NAME' + tenant = 'tenant_name' else: - ITERATIONS = 1 - - -def log_init(): - global LOG - LOG = logging.getLogger() - LOG.setLevel(level=logging.DEBUG) - log_formatter = logging.Formatter("%(asctime)s --- %(message)s") - file_handler = logging.FileHandler("{0}/{1}.log".format("./", "benchmark")) - file_handler.setFormatter(log_formatter) - file_handler.setLevel(logging.DEBUG) - LOG.addHandler(file_handler) - -# ------------------------------------------------------ -# Configuration file access -# ------------------------------------------------------ - - -class ConfigurationFile: - """ - Used to extract data from the configuration file - """ - - def __init__(self, sections, config_file='conf.cfg'): - """ - Reads configuration file sections - - :param sections: list of strings representing the sections to be - loaded - :param config_file: name of the configuration file (string) - :return: None - """ - InputValidation.validate_string( - config_file, "The configuration file name must be a string") - InputValidation.validate_file_exist( - config_file, 'The provided configuration file does not exist') - self.config = ConfigParser.ConfigParser() - self.config.read(config_file) - for section in sections: - setattr( - self, section, ConfigurationFile. - _config_section_map(section, self.config)) - - @staticmethod - def _config_section_map(section, config_file): - """ - Returns a dictionary with the configuration values for the specific - section - - :param section: section to be loaded (string) - :param config_file: name of the configuration file (string) - :return: dict - """ - dict1 = dict() - options = config_file.options(section) - for option in options: - dict1[option] = config_file.get(section, option) - return dict1 - - def get_variable(self, section, variable_name): - """ - Returns the value correspondent to a variable - - :param section: section to be loaded (string) - :param variable_name: name of the variable (string) - :return: string - """ - message = "The variable name must be a string" - InputValidation.validate_string(variable_name, message) - if variable_name in self.get_variable_list(section): - sect = getattr(self, section) - return sect[variable_name] - else: - exc_msg = 'Parameter {} is not in the {} section of the ' \ - 'conf file'.format(variable_name, section) - raise ValueError(exc_msg) - - def get_variable_list(self, section): - """ - Returns the list of the available variables in a section - :param section: section to be loaded (string) - :return: list - """ - try: - return getattr(self, section) - except: - msg = 'Section {} not found in the configuration file'.\ - format(section) - raise ValueError(msg) - -# ------------------------------------------------------ -# Manage files -# ------------------------------------------------------ - - -def get_heat_template_params(): - """ - Returns the list of deployment parameters from the configuration file - for the heat template - - :return: dict - """ - heat_parameters_list = CONF_FILE.get_variable_list( - files.DEPLOYMENT_PARAMETERS) - testcase_parameters = dict() - for param in heat_parameters_list: - testcase_parameters[param] = CONF_FILE.get_variable( - files.DEPLOYMENT_PARAMETERS, param) - return testcase_parameters - - -def get_testcase_params(): - """ - Returns the list of testcase parameters from the configuration file - - :return: dict - """ - testcase_parameters = dict() - parameters = CONF_FILE.get_variable_list(files.TESTCASE_PARAMETERS) - for param in parameters: - testcase_parameters[param] = CONF_FILE.get_variable( - files.TESTCASE_PARAMETERS, param) - return testcase_parameters - - -def get_file_first_line(file_name): - """ - Returns the first line of a file - - :param file_name: name of the file to be read (str) - :return: str - """ - message = "name of the file must be a string" - InputValidation.validate_string(file_name, message) - message = 'file {} does not exist'.format(file_name) - InputValidation.validate_file_exist(file_name, message) - res = open(file_name, 'r') - return res.readline() - - -def replace_in_file(file, text_to_search, text_to_replace): - """ - Replaces a string within a file - - :param file: name of the file (str) - :param text_to_search: text to be replaced - :param text_to_replace: new text that will replace the previous - :return: None - """ - message = 'text to be replaced in the file must be a string' - InputValidation.validate_string(text_to_search, message) - message = 'text to replace in the file must be a string' - InputValidation.validate_string(text_to_replace, message) - message = "name of the file must be a string" - InputValidation.validate_string(file, message) - message = "The file does not exist" - InputValidation.validate_file_exist(file, message) - for line in fileinput.input(file, inplace=True): - print(line.replace(text_to_search, text_to_replace).rstrip()) - -# ------------------------------------------------------ -# Shell interaction -# ------------------------------------------------------ - - -def run_command(command): - LOG.info("Running command: {}".format(command)) - return os.system(command) - -# ------------------------------------------------------ -# Expose variables to other modules -# ------------------------------------------------------ - - -def get_base_dir(): - return BASE_DIR - - -def get_template_dir(): - return TEMPLATE_DIR - -# ------------------------------------------------------ -# Configuration Variables from Config File -# ------------------------------------------------------ - - -def get_deployment_configuration_variables_from_conf_file(): - variables = dict() - types = dict() - all_variables = CONF_FILE.get_variable_list(files.EXPERIMENT_VNF) - for var in all_variables: - v = CONF_FILE.get_variable(files.EXPERIMENT_VNF, var) - type = re.findall(r'@\w*', v) - values = re.findall(r'\"(.+?)\"', v) - variables[var] = values - try: - types[var] = type[0][1:] - except IndexError: - LOG.debug("No type has been specified for variable " + var) - return variables - -# ------------------------------------------------------ -# benchmarks from Config File -# ------------------------------------------------------ - - -def get_benchmarks_from_conf_file(): - requested_benchmarks = list() - benchmarks = CONF_FILE.get_variable( - files.GENERAL, files.BENCHMARKS).split(', ') - for benchmark in benchmarks: - requested_benchmarks.append(benchmark) - return requested_benchmarks - - -class InputValidation(object): - - @staticmethod - def validate_string(param, message): - if not isinstance(param, str): - raise ValueError(message) - return True - - @staticmethod - def validate_integer(param, message): - if not isinstance(param, int): - raise ValueError(message) - return True - - @staticmethod - def validate_dictionary(param, message): - if not isinstance(param, dict): - raise ValueError(message) - return True - - @staticmethod - def validate_file_exist(file_name, message): - if not os.path.isfile(file_name): - raise ValueError(message + ' ' + file_name) - return True - - @staticmethod - def validate_directory_exist_and_format(directory, message): - if not os.path.isdir(directory): - raise ValueError(message) - if not directory.endswith('/'): - return directory + '/' - return directory - - @staticmethod - def validate_configuration_file_parameter(section, parameter, message): - params = CONF_FILE.get_variable_list(section) - if parameter not in params: - raise ValueError(message) - return True - - @staticmethod - def validate_configuration_file_section(section, message): - if section not in files.get_sections(): - raise ValueError(message) - return True - - @staticmethod - def validate_boolean(boolean, message): - if isinstance(boolean, bool): - return boolean - if isinstance(boolean, str): - if boolean == 'True': - return True - if boolean == 'False': - return False - raise ValueError(message) - - @staticmethod - def validate_os_credentials(credentials): - if not isinstance(credentials, dict): - raise ValueError( - 'The provided openstack_credentials ' - 'variable must be in dictionary format') - - credential_keys = ['user', 'password', 'ip_controller', 'heat_url', - 'auth_uri', 'project'] - missing = [ - credential_key - for credential_key in credential_keys - if credential_key not in credentials.keys() - ] - if len(missing) == 0: - return True - msg = 'OpenStack Credentials Error! ' \ - 'The following parameters are missing: {}'.\ - format(", ".join(missing)) - raise ValueError(msg) + keystone_v3 = True + tenant_env = 'OS_PROJECT_NAME' + tenant = 'project_name' + + # The most common way to pass these info to the script is to do it + # through environment variables. + creds.update({ + "username": os.environ.get("OS_USERNAME"), + "password": os.environ.get("OS_PASSWORD"), + "auth_url": os.environ.get("OS_AUTH_URL"), + tenant: os.environ.get(tenant_env) + }) + + if keystone_v3: + if os.getenv('OS_USER_DOMAIN_NAME') is not None: + creds.update({ + "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME') + }) + if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None: + creds.update({ + "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME') + }) + + cacert = os.environ.get("OS_CACERT") + + if cacert is not None: + # each openstack client uses differnt kwargs for this + creds.update({"cacert": cacert, + "ca_cert": cacert, + "https_ca_cert": cacert, + "https_cacert": cacert, + "ca_file": cacert}) + creds.update({"insecure": "True", "https_insecure": "True"}) + if not os.path.isfile(cacert): + log.info("WARNING: The 'OS_CACERT' environment variable is set\ + to %s but the file does not exist." % cacert) + + return creds + + +def get_session_auth(): + loader = loading.get_plugin_loader('password') + creds = get_credentials() + auth = loader.load_from_options(**creds) + return auth + + +def get_session(): + auth = get_session_auth() + return session.Session(auth=auth) + + +def get_endpoint(service_type, endpoint_type='publicURL'): + auth = get_session_auth() + return get_session().get_endpoint(auth=auth, + service_type=service_type, + endpoint_type=endpoint_type) + + +def get_heat_api_version(): + api_version = os.getenv('HEAT_API_VERSION') + if api_version is not None: + log.info("HEAT_API_VERSION is set in env as '%s'", api_version) + return api_version + return DEFAULT_HEAT_API_VERSION + +def get_nova_api_version(): + api_version = os.getenv('OS_COMPUTE_API_VERSION') + if api_version is not None: + log.info("NOVA_API_VERSION is set in env as '%s'", api_version) + return api_version + return DEFAULT_NOVA_API_VERSION + + +def get_glance_api_version(): + api_version = os.getenv('OS_IMAGE_API_VERSION') + if api_version is not None: + log.info("GLANCE_API_VERSION is set in env as '%s'", api_version) + return api_version + return DEFAULT_GLANCE_API_VERSION \ No newline at end of file diff --git a/utils/infra_setup/heat/manager.py b/utils/infra_setup/heat/manager.py old mode 100755 new mode 100644 index f5a9b88d..3a270ac1 --- a/utils/infra_setup/heat/manager.py +++ b/utils/infra_setup/heat/manager.py @@ -7,82 +7,68 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from heatclient import client as heat_client -from keystoneclient.v2_0 import client as keystone_client -from heatclient.common import template_utils - -import heat.common as common - - -class HeatManager: - - def __init__(self, credentials): - self.user = credentials['user'] - self.password = credentials['password'] - self.controller_ip = credentials['controller_ip'] - self.heat_url = credentials['heat_url'] - self.auth_uri = credentials['auth_uri'] - self.project_id = credentials['project'] - self.heat = None - - def heat_init(self): - keystone = keystone_client.Client(username=self.user, - password=self.password, - tenant_name=self.project_id, - auth_url=self.auth_uri) - auth_token = keystone.auth_token - self.heat_url = keystone.service_catalog.url_for( - service_type='orchestration') - self.heat = heat_client.Client('1', endpoint=self.heat_url, - token=auth_token) - - def stacks_list(self, name=None): - for stack in self.heat.stacks.list(): - if (name and stack.stack_name == name) or not name: - common.LOG.info("stack name " + stack.stack_name) - common.LOG.info("stack status " + stack.stack_status) - - def stack_generate(self, template_file, stack_name, parameters): - self.heat_init() - self.stacks_list() - tpl_files, template = template_utils.get_template_contents( - template_file) - - fields = { - 'template': template, - 'files': dict(list(tpl_files.items())) - } - self.heat.stacks.create(stack_name=stack_name, files=fields['files'], - template=template, parameters=parameters) - self.stacks_list(stack_name) - - def stack_is_deployed(self, stack_name): - self.heat_init() - if stack_name in self.heat.stacks.list(): - return True - return False +import time +import common as op_utils +from glanceclient.client import Client as GlanceClient +from novaclient.client import Client as NovaClient + + +def _get_glance_client(): + sess = op_utils.get_session() + return GlanceClient( + op_utils.get_glance_api_version(), + session=sess) + + +def _get_nova_client(): + sess = op_utils.get_session() + + return NovaClient( + op_utils.get_nova_api_version(), + session=sess) + + +def stack_create_images( + imagefile=None, + image_name="bottlenecks_image"): + print "========== Create image in OS ==========" - def stack_check_status(self, stack_name): - for stack in self.heat.stacks.list(): - if stack.stack_name == stack_name: - return stack.stack_status - return 'NOT_FOUND' - - def heat_validate_template(self, heat_template_file): - self.heat_init() - if not self.heat.stacks.validate(template=open(heat_template_file, - 'r').read()): - raise ValueError('The provided heat template "' + - heat_template_file + - '" is in the wrong format') - - def stack_delete(self, stack_name): - self.heat_init() - try: - for stack in self.heat.stacks.list(): - if stack.stack_name == stack_name: - self.heat.stacks.delete(stack.id) - return True - except: - pass + if imagefile is None: + print "imagefile not set/found" return False + + glance = _get_glance_client() + image = glance.images.create( + name=image_name, + disk_format="qcow2", + container_format="bare") + with open(imagefile) as fimage: + glance.images.upload(image.id, fimage) + + timeInQueue = 0 + img_status = image.status + while img_status == "queued" and timeInQueue < 30: + print " image's status: " + img_status + time.sleep(1) + timeInQueue = timeInQueue + 1 + img_status = glance.images.get(image.id).status + + print "After %d seconds,image status is [%s]" % (timeInQueue, img_status) + return True if img_status == "active" else False + + +def stack_create_keypairs(key_path, name="bottlenecks_keypair"): + print "========== Add keypairs in OS ==========" + nova = _get_nova_client() + with open(key_path) as pkey: + nova.keypairs.create(name=name, public_key=pkey.read()) + + +def stack_create_flavors( + name="bottlenecks_flavor", + ram=4096, + vcpus=2, + disk=10): + print "========== Create flavors in OS ==========" + nova = _get_nova_client() + nova.flavors.create(name=name, ram=ram, vcpus=vcpus, disk=disk) diff --git a/utils/infra_setup/heat/template.py b/utils/infra_setup/heat/template.py index f71e9166..28c20b72 100755 --- a/utils/infra_setup/heat/template.py +++ b/utils/infra_setup/heat/template.py @@ -7,151 +7,193 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -"""to create heat templates from the base template -""" -import os -import json -import shutil -import common -import consts.parameters as parameters +"""Heat template and stack management""" +import time +import sys +import logging -class TreeNode: +from heatclient import client as heatclient +import common as op_utils - def __init__(self): - self.up = None - self.down = [] - self.variable_name = '' - self.variable_value = 0 - def add_child(self, node): - node.up = self - self.down.append(node) +log = logging.getLogger(__name__) - def get_parent(self): - return self.up - def get_children(self): - if len(self.down) == 0: - return [] - return self.down +class HeatObject(object): + ''' base class for template and stack''' + def __init__(self): + self._heat_client = None + self.uuid = None - def get_variable_name(self): - return self.variable_name + def _get_heat_client(self): + '''returns a heat client instance''' - def get_variable_value(self): - return self.variable_value + if self._heat_client is None: + sess = op_utils.get_session() + heat_endpoint = op_utils.get_endpoint(service_type='orchestration') + self._heat_client = heatclient.Client( + op_utils.get_heat_api_version(), + endpoint=heat_endpoint, session=sess) - def set_variable_name(self, name): - self.variable_name = name + return self._heat_client - def set_variable_value(self, value): - self.variable_value = value + def status(self): + '''returns stack state as a string''' + heat = self._get_heat_client() + stack = heat.stacks.get(self.uuid) + return getattr(stack, 'stack_status') - def get_path(self): - ret_val = [] - if not self.up: - ret_val.append(self) - return ret_val - for node in self.up.get_path(): - ret_val.append(node) - ret_val.append(self) - return ret_val - def __str__(self): - return str(self.variable_name) + " --> " + str(self.variable_value) +class HeatStack(HeatObject): + ''' Represents a Heat stack (deployed template) ''' + stacks = [] - def __repr__(self): - return str(self.variable_name) + " = " + str(self.variable_value) + def __init__(self, name): + super(HeatStack, self).__init__() + self.uuid = None + self.name = name + self.outputs = None + HeatStack.stacks.append(self) @staticmethod - def _get_leaves(node, leaves): - children = node.get_children() - if len(children) == 0: - leaves.append(node) + def stacks_exist(): + '''check if any stack has been deployed''' + return len(HeatStack.stacks) > 0 + + def _delete(self): + '''deletes a stack from the target cloud using heat''' + if self.uuid is None: return - for child in children: - TreeNode._get_leaves(child, leaves) + + log.info("Deleting stack '%s', uuid:%s", self.name, self.uuid) + heat = self._get_heat_client() + template = heat.stacks.get(self.uuid) + start_time = time.time() + template.delete() + status = self.status() + + while status != u'DELETE_COMPLETE': + log.debug("stack state %s", status) + if status == u'DELETE_FAILED': + raise RuntimeError( + heat.stacks.get(self.uuid).stack_status_reason) + + time.sleep(2) + status = self.status() + + end_time = time.time() + log.info("Deleted stack '%s' in %d secs", self.name, + end_time - start_time) + self.uuid = None + + def delete(self, block=True, retries=3): + '''deletes a stack in the target cloud using heat (with retry) + Sometimes delete fail with "InternalServerError" and the next attempt + succeeds. So it is worthwhile to test a couple of times. + ''' + if self.uuid is None: + return + + if not block: + try: + self._delete() + except RuntimeError as err: + log.warn(err.args) + HeatStack.stacks.remove(self) + return + + i = 0 + while i < retries: + try: + self._delete() + break + except RuntimeError as err: + log.warn(err.args) + time.sleep(2) + i += 1 + + if self.uuid is not None: + sys.exit("delete stack failed!!!") + else: + HeatStack.stacks.remove(self) @staticmethod - def get_leaves(node): - leaves = list() - TreeNode._get_leaves(node, leaves) - return leaves - -template_name = parameters.TEST_TEMPLATE_NAME - - -def generates_templates(base_heat_template, deployment_configuration): - # parameters loaded from file - template_dir = common.get_template_dir() - template_extension = parameters.TEMPLATE_EXTENSION - template_base_name = base_heat_template - - variables = deployment_configuration - - # Delete the templates generated in previous running - common.LOG.info("Removing the heat templates already generated") - command = "rm {}{}_*".format(template_dir, template_name) - os.system(command) - - # Creation of the tree with all the new configurations - common.LOG.info("Creation of a tree with all new configurations") - tree = TreeNode() - for variable in variables: - leaves = TreeNode.get_leaves(tree) - common.LOG.debug("LEAVES: " + str(leaves)) - common.LOG.debug("VALUES: " + str(variables[variable])) - - for value in variables[variable]: - for leaf in leaves: - new_node = TreeNode() - new_node.set_variable_name(variable) - new_node.set_variable_value(value) - leaf.add_child(new_node) - - common.LOG.debug("CONFIGURATION TREE: " + str(tree)) - - common.LOG.info("Heat Template and metadata file creation") - leaves = TreeNode.get_leaves(tree) - counter = 1 - for leaf in leaves: - heat_template_vars = leaf.get_path() - if os.path.isabs(template_base_name): - base_template = template_base_name + def delete_all(): + for stack in HeatStack.stacks[:]: + stack.delete() + + def update(self): + '''update a stack''' + pass + + +class HeatTemplate(HeatObject): + '''Describes a Heat template and a method to deploy template to a stack''' + + def __init__(self, name, template_file=None, heat_parameters=None): + super(HeatTemplate, self).__init__() + self.name = name + self.state = "NOT_CREATED" + self.keystone_client = None + self.heat_client = None + self.heat_parameters = {} + + # heat_parameters is passed to heat in stack create, empty dict when + # yardstick creates the template (no get_param in resources part) + if heat_parameters: + self.heat_parameters = heat_parameters + + if template_file: + with open(template_file) as template: + print "Parsing external template:", template_file + template_str = template.read() + self._template = template_str + self._parameters = heat_parameters else: - base_template = template_dir + template_base_name - new_template = template_dir + template_name - new_template += "_" + str(counter) + template_extension - shutil.copy(base_template, new_template) - - metadata = dict() - for var in heat_template_vars: - if var.get_variable_name(): - common.replace_in_file(new_template, "#" + - var.get_variable_name(), - var.get_variable_value()) - metadata[var.get_variable_name()] = var.get_variable_value() - - # Save the metadata on a JSON file - with open(new_template + ".json", 'w') as outfile: - json.dump(metadata, outfile) - - common.LOG.debug("Heat Templates and Metadata file " + str(counter) + - " created") - counter += 1 - - # Creation of the template files - common.LOG.info(str(counter - 1) + " Heat Templates and Metadata files " - "created") - - -def get_all_heat_templates(template_dir, template_extension): - template_files = list() - for dirname, dirnames, filenames in os.walk(template_dir): - for filename in filenames: - if template_extension in filename and filename.endswith( - template_extension) and template_name in filename: - template_files.append(filename) - template_files.sort() - return template_files + sys.exit("\nno such template file.") + + # holds results of requested output after deployment + self.outputs = {} + + log.debug("template object '%s' created", name) + + def create(self, block=True): + '''creates a template in the target cloud using heat + returns a dict with the requested output values from the template''' + log.info("Creating stack '%s'", self.name) + + # create stack early to support cleanup, e.g. ctrl-c while waiting + stack = HeatStack(self.name) + + heat = self._get_heat_client() + end_time = start_time = time.time() + print(self._template) + stack.uuid = self.uuid = heat.stacks.create( + stack_name=self.name, template=self._template, + parameters=self.heat_parameters)['stack']['id'] + + status = self.status() + + if block: + while status != u'CREATE_COMPLETE': + log.debug("stack state %s", status) + if status == u'CREATE_FAILED': + raise RuntimeError(getattr(heat.stacks.get(self.uuid), + 'stack_status_reason')) + + time.sleep(2) + status = self.status() + + end_time = time.time() + outputs = getattr(heat.stacks.get(self.uuid), 'outputs') + + for output in outputs: + self.outputs[output["output_key"].encode("ascii")] = \ + output["output_value"].encode("ascii") + + log.info("Created stack '%s' in %d secs", + self.name, end_time - start_time) + + stack.outputs = self.outputs + return stack -- cgit 1.2.3-korg