# -*- coding: utf-8 -*- ''' Module for handling Heat stacks. :depends: - python-heatclient>=0.2.3 Python module :configuration: This module is not usable until the following are specified either in a pillar or in the minion's config file:: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.insecure: False #(optional) keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' If configuration for multiple openstack accounts is required, they can be set up as different configuration profiles: For example:: openstack1: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' openstack2: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.2:5000/v2.0/' With this configuration in place, any of the heat functions can make use of a configuration profile by declaring it explicitly. For example:: salt '*' heat.stack_list profile=openstack1 ''' from __future__ import absolute_import import logging LOG = logging.getLogger(__name__) # Import third party libs HAS_HEAT = False try: from heatclient.v1 import client HAS_HEAT = True except Exception, e: LOG.trace("heatclient or keystone is not installed %s" % e) import json import glob from os.path import basename from yaml import load, dump HEAT_ROOT = "/srv/heat/env" TEMPLATE_PATH = "template" ENV_PATH ="env" HOT = ".hot" ENV = ".env" HOT_MASK = "*%s" % HOT ENV_MASK = "*%s" % ENV def _autheticate(func_name): ''' Authenticate requests with the salt keystone module and format return data ''' @wraps(func_name) def decorator_method(*args, **kwargs): ''' Authenticate request and format return data ''' connection_args = {'profile': kwargs.get('profile', None)} nkwargs = {} for kwarg in kwargs: if 'connection_' in kwarg: connection_args.update({kwarg: kwargs[kwarg]}) elif '__' not in kwarg: nkwargs.update({kwarg: kwargs[kwarg]}) kstone = __salt__['keystone.auth'](**connection_args) token = kstone.auth_token endpoint = kstone.service_catalog.url_for( service_type='orchestration', endpoint_type='publicURL') heat_interface = client.Client( endpoint_url=endpoint, token=token) return_data = func_name(heat_interface, *args, **nkwargs) if isinstance(return_data, list): # format list as a dict for rendering return {data.get('name', None) or data['id']: data for data in return_data} return return_data return decorator_method def _filename(path): """ helper return filename without extension """ return basename(path).split(".")[0] def _get_templates(choices=True): """ if choices is False return array of full path """ path = "/".join([HEAT_ROOT, TEMPLATE_PATH]) templates = [] for path in glob.glob("/".join([path, HOT_MASK])): name = filename(path) templates.append((name, name.replace("_", " ").capitalize())) return sorted(templates) def _get_environments(template_name=None): """return environments choices """ path = "/".join([HEAT_ROOT, ENV_PATH]) environments = [] if template_name: join = [path, template_name, ENV_MASK] else: join = [path, ENV_MASK] for path in glob.glob("/".join(join)): name = filename(path) environments.append((name, name.replace("_", " ").capitalize())) return sorted(environments) def _get_template_data(name): """ load and return template data """ path = "/".join([ HEAT_ROOT, TEMPLATE_PATH, "".join([name, HOT]) ]) try: f = open(path, 'r') data = load(f) except Exception, e: raise e return data def _get_environment_data(template_name, name): """ load and return parameters data """ path = "/".join([ HEAT_ROOT, ENV_PATH, template_name, "".join([name, ENV]) ]) try: f = open(path, 'r') data = load(f) except Exception, e: raise e return data def __virtual__(): ''' Only load this module if Heat is installed on this minion. ''' if HAS_HEAT: return 'heat' return False __opts__ = {} def stack_list(tenant=None, **kwargs): heat = heatclient() ret = {} ret["result"] = heat.stacks.list() return ret def stack_create(template, environment=None, name=None, parameters=None, timeout_mins=5, enable_rollback=True, **kwargs): ''' Return a specific endpoint (gitlab endpoint-get) :params template: template name :params name: if not provided template will be used CLI Example: .. code-block:: bash salt '*' heat.stack_create template_name ''' heat = heatclient() # get template template_data = get_template_data(template) # Validate the template and get back the params. kwargs = {} kwargs['template'] = str(json.dumps(template_data, cls=CustomEncoder)) try: validated = heat.stacks.validate(**kwargs) except Exception as e: LOG.error("Template not valid %s" % e) fields = { 'stack_name': name, 'template': json.dumps(template_data, cls=CustomEncoder), 'environment': parameters, 'parameters': parameters, 'timeout_mins': timeout_mins, 'disable_rollback': enable_rollback, } #LOG.debug(dir(heat)) heat.stacks.create(**fields) return {'status': result} def stack_delete(template, name=None, parameters=None, **kwargs): return {'Error': 'Could not delete stack.'}