Bottlenecks stack config parser.
[bottlenecks.git] / utils / infra_setup / heat / template.py
1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 """Heat template and stack management,
11 This file could manage stack include the function:
12 create stack delete stack and so on"""
13
14 import time
15 import sys
16 import logging
17
18 from heatclient import client as heatclient
19 import common as op_utils
20
21
22 log = logging.getLogger(__name__)
23
24
25 class HeatObject(object):
26     ''' base class for template and stack'''
27     def __init__(self):
28         self._heat_client = None
29         self.uuid = None
30
31     def _get_heat_client(self):
32         '''returns a heat client instance'''
33
34         if self._heat_client is None:
35             sess = op_utils.get_session()
36             heat_endpoint = op_utils.get_endpoint(service_type='orchestration')
37             self._heat_client = heatclient.Client(
38                 op_utils.get_heat_api_version(),
39                 endpoint=heat_endpoint, session=sess)
40
41         return self._heat_client
42
43     def status(self):
44         '''returns stack state as a string'''
45         heat = self._get_heat_client()
46         stack = heat.stacks.get(self.uuid)
47         return getattr(stack, 'stack_status')
48
49
50 class HeatStack(HeatObject):
51     ''' Represents a Heat stack (deployed template) '''
52     stacks = []
53
54     def __init__(self, name):
55         super(HeatStack, self).__init__()
56         self.uuid = None
57         self.name = name
58         self.outputs = None
59         HeatStack.stacks.append(self)
60
61     @staticmethod
62     def stacks_exist():
63         '''check if any stack has been deployed'''
64         return len(HeatStack.stacks) > 0
65
66     def _delete(self):
67         '''deletes a stack from the target cloud using heat'''
68         if self.uuid is None:
69             return
70
71         log.info("Deleting stack '%s', uuid:%s", self.name, self.uuid)
72         heat = self._get_heat_client()
73         template = heat.stacks.get(self.uuid)
74         start_time = time.time()
75         template.delete()
76         status = self.status()
77
78         while status != u'DELETE_COMPLETE':
79             log.debug("stack state %s", status)
80             if status == u'DELETE_FAILED':
81                 raise RuntimeError(
82                     heat.stacks.get(self.uuid).stack_status_reason)
83
84             time.sleep(2)
85             status = self.status()
86
87         end_time = time.time()
88         log.info("Deleted stack '%s' in %d secs", self.name,
89                  end_time - start_time)
90         self.uuid = None
91
92     def delete(self, block=True, retries=3):
93         '''deletes a stack in the target cloud using heat (with retry)
94         Sometimes delete fail with "InternalServerError" and the next attempt
95         succeeds. So it is worthwhile to test a couple of times.
96         '''
97         if self.uuid is None:
98             return
99
100         if not block:
101             try:
102                 self._delete()
103             except RuntimeError as err:
104                 log.warn(err.args)
105             HeatStack.stacks.remove(self)
106             return
107
108         i = 0
109         while i < retries:
110             try:
111                 self._delete()
112                 break
113             except RuntimeError as err:
114                 log.warn(err.args)
115                 time.sleep(2)
116             i += 1
117
118         if self.uuid is not None:
119             sys.exit("delete stack failed!!!")
120         else:
121             HeatStack.stacks.remove(self)
122
123     @staticmethod
124     def delete_all():
125         for stack in HeatStack.stacks[:]:
126             stack.delete()
127
128     def update(self):
129         '''update a stack'''
130         pass
131
132
133 class HeatTemplate(HeatObject):
134     '''Describes a Heat template and a method to deploy template to a stack'''
135
136     def __init__(self,
137                  name,
138                  template_file=None,
139                  heat_parameters=None,
140                  heat_template=None):
141         super(HeatTemplate, self).__init__()
142         self.name = name
143         self.state = "NOT_CREATED"
144         self.keystone_client = None
145         self.heat_client = None
146         self.heat_parameters = {}
147
148         # heat_parameters is passed to heat in stack create, empty dict when
149         # yardstick creates the template (no get_param in resources part)
150         if heat_parameters:
151             self.heat_parameters = heat_parameters
152
153         if template_file:
154             with open(template_file) as template:
155                 print "Parsing external template:", template_file
156                 template_str = template.read()
157                 self._template = template_str
158             self._parameters = heat_parameters
159         else:
160             if heat_template:
161                 self._template = heat_template
162             else:
163                 sys.exit("can't init template file!")
164
165         # holds results of requested output after deployment
166         self.outputs = {}
167
168         log.debug("template object '%s' created", name)
169
170     def create(self, block=True):
171         '''creates a template in the target cloud using heat
172         returns a dict with the requested output values from the template'''
173         log.info("Creating stack '%s'", self.name)
174
175         # create stack early to support cleanup, e.g. ctrl-c while waiting
176         stack = HeatStack(self.name)
177
178         heat = self._get_heat_client()
179         end_time = start_time = time.time()
180         print(self._template)
181         stack.uuid = self.uuid = heat.stacks.create(
182             stack_name=self.name, template=self._template,
183             parameters=self.heat_parameters)['stack']['id']
184
185         status = self.status()
186
187         if block:
188             while status != u'CREATE_COMPLETE':
189                 log.debug("stack state %s", status)
190                 if status == u'CREATE_FAILED':
191                     raise RuntimeError(getattr(heat.stacks.get(self.uuid),
192                                                'stack_status_reason'))
193
194                 time.sleep(2)
195                 status = self.status()
196
197             end_time = time.time()
198             outputs = getattr(heat.stacks.get(self.uuid), 'outputs')
199
200         for output in outputs:
201             self.outputs[output["output_key"].encode("ascii")] = \
202                 output["output_value"].encode("ascii")
203
204         log.info("Created stack '%s' in %d secs",
205                  self.name, end_time - start_time)
206
207         stack.outputs = self.outputs
208         return stack