a91a21c1c847fbedff5bb3dce1630139ee1acccd
[snaps.git] / snaps / openstack / utils / heat_utils.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain 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,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16
17 import yaml
18 from heatclient.client import Client
19 from heatclient.common.template_format import yaml_loader
20 from oslo_serialization import jsonutils
21
22 from snaps import file_utils
23 from snaps.domain.stack import Stack
24
25 from snaps.openstack.utils import keystone_utils
26
27 __author__ = 'spisarski'
28
29 logger = logging.getLogger('heat_utils')
30
31
32 def heat_client(os_creds):
33     """
34     Retrieves the Heat client
35     :param os_creds: the OpenStack credentials
36     :return: the client
37     """
38     logger.debug('Retrieving Nova Client')
39     return Client(os_creds.heat_api_version,
40                   session=keystone_utils.keystone_session(os_creds),
41                   region_name=os_creds.region_name)
42
43
44 def get_stack(heat_cli, stack_settings=None, stack_name=None):
45     """
46     Returns the first domain Stack object found. When stack_setting
47     is not None, the filter created will take the name attribute. When
48     stack_settings is None and stack_name is not, stack_name will be used
49     instead. When both are None, the first stack object received will be
50     returned, else None
51     :param heat_cli: the OpenStack heat client
52     :param stack_settings: a StackSettings object
53     :param stack_name: the name of the heat stack to return
54     :return: the Stack domain object else None
55     """
56
57     stack_filter = dict()
58     if stack_settings:
59         stack_filter['stack_name'] = stack_settings.name
60     elif stack_name:
61         stack_filter['stack_name'] = stack_name
62
63     stacks = heat_cli.stacks.list(**stack_filter)
64     for stack in stacks:
65         return Stack(name=stack.identifier, stack_id=stack.id)
66
67
68 def get_stack_by_id(heat_cli, stack_id):
69     """
70     Returns a domain Stack object for a given ID
71     :param heat_cli: the OpenStack heat client
72     :param stack_id: the ID of the heat stack to retrieve
73     :return: the Stack domain object else None
74     """
75     stack = heat_cli.stacks.get(stack_id)
76     return Stack(name=stack.identifier, stack_id=stack.id)
77
78
79 def get_stack_status(heat_cli, stack_id):
80     """
81     Returns the current status of the Heat stack
82     :param heat_cli: the OpenStack heat client
83     :param stack_id: the ID of the heat stack to retrieve
84     :return:
85     """
86     return heat_cli.stacks.get(stack_id).stack_status
87
88
89 def get_stack_outputs(heat_cli, stack_id):
90     """
91     Returns a domain Stack object for a given ID
92     :param heat_cli: the OpenStack heat client
93     :param stack_id: the ID of the heat stack to retrieve
94     :return: the Stack domain object else None
95     """
96     stack = heat_cli.stacks.get(stack_id)
97     return stack.outputs
98
99
100 def create_stack(heat_cli, stack_settings):
101     """
102     Executes an Ansible playbook to the given host
103     :param heat_cli: the OpenStack heat client object
104     :param stack_settings: the stack configuration
105     :return: the Stack domain object
106     """
107     args = dict()
108
109     if stack_settings.template:
110         args['template'] = stack_settings.template
111     else:
112         args['template'] = parse_heat_template_str(
113             file_utils.read_file(stack_settings.template_path))
114     args['stack_name'] = stack_settings.name
115
116     if stack_settings.env_values:
117         args['parameters'] = stack_settings.env_values
118
119     stack = heat_cli.stacks.create(**args)
120
121     return get_stack_by_id(heat_cli, stack_id=stack['stack']['id'])
122
123
124 def delete_stack(heat_cli, stack):
125     """
126     Deletes the Heat stack
127     :param heat_cli: the OpenStack heat client object
128     :param stack: the OpenStack Heat stack object
129     """
130     heat_cli.stacks.delete(stack.id)
131
132
133 def parse_heat_template_str(tmpl_str):
134     """
135     Takes a heat template string, performs some simple validation and returns a
136     dict containing the parsed structure. This function supports both JSON and
137     YAML Heat template formats.
138     """
139     if tmpl_str.startswith('{'):
140         tpl = jsonutils.loads(tmpl_str)
141     else:
142         try:
143             tpl = yaml.load(tmpl_str, Loader=yaml_loader)
144         except yaml.YAMLError as yea:
145             raise ValueError(yea)
146         else:
147             if tpl is None:
148                 tpl = {}
149     # Looking for supported version keys in the loaded template
150     if not ('HeatTemplateFormatVersion' in tpl or
151             'heat_template_version' in tpl or
152             'AWSTemplateFormatVersion' in tpl):
153         raise ValueError("Template format version not found.")
154     return tpl