diff options
author | 2017-01-04 06:36:33 +0000 | |
---|---|---|
committer | 2017-01-04 06:36:33 +0000 | |
commit | 5c859330549de91739b7c2ef31c040a208bb6840 (patch) | |
tree | 4c0d43ff90dfc40a9564cd6ca88dc1e3f6ec26ed /utils/infra_setup/heat | |
parent | e3f1c15416686a3e527529aa289d914811ebd66e (diff) | |
parent | 754ce1457182411e7e887b307fb66f280885ff7e (diff) |
Merge "bottlenecks openstack Newton support"
Diffstat (limited to 'utils/infra_setup/heat')
-rw-r--r--[-rwxr-xr-x] | utils/infra_setup/heat/__init__.py | 2 | ||||
-rwxr-xr-x | utils/infra_setup/heat/common.py | 467 | ||||
-rw-r--r--[-rwxr-xr-x] | utils/infra_setup/heat/manager.py | 140 | ||||
-rwxr-xr-x | utils/infra_setup/heat/template.py | 300 |
4 files changed, 329 insertions, 580 deletions
diff --git a/utils/infra_setup/heat/__init__.py b/utils/infra_setup/heat/__init__.py index 83b8d15d..6dbd8d79 100755..100644 --- 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 index f5a9b88d..3a270ac1 100755..100644 --- 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 |