300ce90bfffd069ec4564f7d4135b7cecd21b66a
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / heat_dummy_api.py
1 from flask import request, Response
2 from flask_restful import Resource
3 from emuvim.api.openstack.resources import Stack
4 from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
5 from datetime import datetime
6 from emuvim.api.openstack.heat_parser import HeatParser
7 import logging
8 import json
9
10
11 LOG = logging.getLogger("api.openstack.heat")
12
13
14 class HeatDummyApi(BaseOpenstackDummy):
15 def __init__(self, in_ip, in_port, compute):
16 super(HeatDummyApi, self).__init__(in_ip, in_port)
17 self.compute = compute
18
19 self.api.add_resource(Shutdown, "/shutdown")
20 self.api.add_resource(HeatListAPIVersions, "/",
21 resource_class_kwargs={'api': self})
22 self.api.add_resource(HeatCreateStack, "/v1/<tenant_id>/stacks",
23 resource_class_kwargs={'api': self})
24 self.api.add_resource(HeatShowStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
25 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
26 resource_class_kwargs={'api': self})
27 self.api.add_resource(HeatShowStackTemplate, "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>/template",
28 resource_class_kwargs={'api': self})
29 self.api.add_resource(HeatUpdateStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
30 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
31 resource_class_kwargs={'api': self})
32 self.api.add_resource(HeatDeleteStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
33 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
34 resource_class_kwargs={'api': self})
35
36 @self.app.after_request
37 def add_access_control_header(response):
38 response.headers['Access-Control-Allow-Origin'] = '*'
39 return response
40
41
42 def _start_flask(self):
43 LOG.info("Starting %s endpoint @ http://%s:%d" % (__name__, self.ip, self.port))
44 if self.app is not None:
45 self.app.before_request(self.dump_playbook)
46 self.app.run(self.ip, self.port, debug=True, use_reloader=False)
47
48
49 class Shutdown(Resource):
50 """
51 A get request to /shutdown will shut down this endpoint.
52 """
53
54 def get(self):
55 LOG.debug(("%s is beeing shut down") % (__name__))
56 func = request.environ.get('werkzeug.server.shutdown')
57 if func is None:
58 raise RuntimeError('Not running with the Werkzeug Server')
59 func()
60
61
62 class HeatListAPIVersions(Resource):
63 def __init__(self, api):
64 self.api = api
65
66 def get(self):
67 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
68 resp = dict()
69
70 resp['versions'] = dict()
71 resp['versions'] = [{
72 "status": "CURRENT",
73 "id": "v1.0",
74 "links": [
75 {
76 "href": "http://%s:%d/v2.0" % (self.api.ip, self.api.port),
77 "rel": "self"
78 }
79 ]
80 }]
81
82 return Response(json.dumps(resp), status=200, mimetype="application/json")
83
84
85 class HeatCreateStack(Resource):
86 def __init__(self, api):
87 self.api = api
88
89 def post(self, tenant_id):
90 """
91 Create and deploy a new stack.
92
93 :param tenant_id:
94 :return: 409, if the stack name was already used.
95 400, if the heat template could not be parsed properly.
96 500, if any exception occurred while creation.
97 201, if everything worked out.
98 """
99 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
100
101 try:
102 stack_dict = json.loads(request.data)
103 for stack in self.api.compute.stacks.values():
104 if stack.stack_name == stack_dict['stack_name']:
105 return [], 409
106 stack = Stack()
107 stack.stack_name = stack_dict['stack_name']
108
109 reader = HeatParser(self.api.compute)
110 if isinstance(stack_dict['template'], str) or isinstance(stack_dict['template'], unicode):
111 stack_dict['template'] = json.loads(stack_dict['template'])
112 if not reader.parse_input(stack_dict['template'], stack, self.api.compute.dc.label):
113 self.api.compute.clean_broken_stack(stack)
114 return 'Could not create stack.', 400
115
116 stack.template = stack_dict['template']
117 stack.creation_time = str(datetime.now())
118 stack.status = "CREATE_COMPLETE"
119
120 return_dict = {"stack": {"id": stack.id,
121 "links": [
122 {
123 "href": "http://%s:%s/v1/%s/stacks/%s"
124 % (self.api.ip, self.api.port, tenant_id, stack.id),
125 "rel": "self"
126 }]}}
127
128 self.api.compute.add_stack(stack)
129 self.api.compute.deploy_stack(stack.id)
130 return Response(json.dumps(return_dict), status=201, mimetype="application/json")
131
132 except Exception as ex:
133 LOG.exception("Heat: Create Stack exception.")
134 return ex.message, 500
135
136 def get(self, tenant_id):
137 """
138 Calculates information about the requested stack.
139
140 :param tenant_id:
141 :return: Returns a json response which contains information like the stack id, name, status, creation time.
142 500, if any exception occurred.
143 200, if everything worked out.
144 """
145 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
146 try:
147 return_stacks = dict()
148 return_stacks['stacks'] = list()
149 for stack in self.api.compute.stacks.values():
150 return_stacks['stacks'].append(
151 {"creation_time": stack.creation_time,
152 "description": "desc of " + stack.id,
153 "id": stack.id,
154 "links": [],
155 "stack_name": stack.stack_name,
156 "stack_status": stack.status,
157 "stack_status_reason": "Stack CREATE completed successfully",
158 "updated_time": stack.update_time,
159 "tags": ""
160 })
161
162 return Response(json.dumps(return_stacks), status=200, mimetype="application/json")
163 except Exception as ex:
164 LOG.exception("Heat: List Stack exception.")
165 return ex.message, 500
166
167
168 class HeatShowStack(Resource):
169 def __init__(self, api):
170 self.api = api
171
172 def get(self, tenant_id, stack_name_or_id, stack_id=None):
173 """
174 Calculates detailed information about the requested stack.
175
176 :param tenant_id:
177 :param stack_name_or_id:
178 :param stack_id:
179 :return: Returns a json response which contains information like the stack id, name, status, creation time.
180 500, if any exception occurred.
181 200, if everything worked out.
182 """
183 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
184 try:
185 stack = None
186 if stack_name_or_id in self.api.compute.stacks:
187 stack = self.api.compute.stacks[stack_name_or_id]
188 else:
189 for tmp_stack in self.api.compute.stacks.values():
190 if tmp_stack.stack_name == stack_name_or_id:
191 stack = tmp_stack
192 if stack is None:
193 return 'Could not resolve Stack - ID', 404
194
195 return_stack = {
196 "stack": {
197 "capabilities": [],
198 "creation_time": stack.creation_time,
199 "description": "desc of " + stack.stack_name,
200 "disable_rollback": True,
201 "id": stack.id,
202 "links": [
203 {
204 "href": "http://%s:%s/v1/%s/stacks/%s"
205 % (self.api.ip, self.api.port, tenant_id, stack.id),
206 "rel": "self"
207 }
208 ],
209 "notification_topics": [],
210 "outputs": [],
211 "parameters": {
212 "OS::project_id": "3ab5b02f-a01f-4f95-afa1-e254afc4a435", # add real project id
213 "OS::stack_id": stack.id,
214 "OS::stack_name": stack.stack_name
215 },
216 "stack_name": stack.stack_name,
217 "stack_owner": "The owner of the stack.", # add stack owner
218 "stack_status": stack.status,
219 "stack_status_reason": "The reason for the current status of the stack.", # add status reason
220 "template_description": "The description of the stack template.",
221 "stack_user_project_id": "The project UUID of the stack user.",
222 "timeout_mins": "",
223 "updated_time": "",
224 "parent": "",
225 "tags": ""
226 }
227 }
228
229 return Response(json.dumps(return_stack), status=200, mimetype="application/json")
230
231 except Exception as ex:
232 LOG.exception("Heat: Show stack exception.")
233 return ex.message, 500
234
235
236 class HeatShowStackTemplate(Resource):
237 def __init__(self, api):
238 self.api = api
239
240 def get(self, tenant_id, stack_name_or_id, stack_id=None):
241 """
242 Returns template of given stack.
243
244 :param tenant_id:
245 :param stack_name_or_id:
246 :param stack_id:
247 :return: Returns a json response which contains the stack's template.
248 """
249 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
250 try:
251 stack = None
252 if stack_name_or_id in self.api.compute.stacks:
253 stack = self.api.compute.stacks[stack_name_or_id]
254 else:
255 for tmp_stack in self.api.compute.stacks.values():
256 if tmp_stack.stack_name == stack_name_or_id:
257 stack = tmp_stack
258 if stack is None:
259 return 'Could not resolve Stack - ID', 404
260
261 return Response(json.dumps(stack.template), status=200, mimetype="application/json")
262
263 except Exception as ex:
264 LOG.exception("Heat: Show stack template exception.")
265 return ex.message, 500
266
267
268 class HeatUpdateStack(Resource):
269 def __init__(self, api):
270 self.api = api
271
272 def put(self, tenant_id, stack_name_or_id, stack_id=None):
273 """
274 Updates an existing stack with a new heat template.
275
276 :param tenant_id:
277 :param stack_name_or_id: Specifies the stack, which should be updated.
278 :param stack_id:
279 :return: 404, if the requested stack could not be found.
280 400, if the stack creation (because of errors in the heat template) or the stack update failed.
281 500, if any exception occurred while updating.
282 202, if everything worked out.
283 """
284 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
285 try:
286 old_stack = None
287 if stack_name_or_id in self.api.compute.stacks:
288 old_stack = self.api.compute.stacks[stack_name_or_id]
289 else:
290 for tmp_stack in self.api.compute.stacks.values():
291 if tmp_stack.stack_name == stack_name_or_id:
292 old_stack = tmp_stack
293 if old_stack is None:
294 return 'Could not resolve Stack - ID', 404
295
296 stack_dict = json.loads(request.data)
297
298 stack = Stack()
299 stack.stack_name = old_stack.stack_name
300 stack.id = old_stack.id
301 stack.creation_time = old_stack.creation_time
302 stack.update_time = str(datetime.now())
303 stack.status = "UPDATE_COMPLETE"
304
305 reader = HeatParser(self.api.compute)
306 if isinstance(stack_dict['template'], str) or isinstance(stack_dict['template'], unicode):
307 stack_dict['template'] = json.loads(stack_dict['template'])
308 if not reader.parse_input(stack_dict['template'], stack, self.api.compute.dc.label, stack_update=True):
309 return 'Could not create stack.', 400
310
311 if not self.api.compute.update_stack(old_stack.id, stack):
312 return 'Could not update stack.', 400
313
314 return Response(status=202, mimetype="application/json")
315
316 except Exception as ex:
317 LOG.exception("Heat: Update Stack exception")
318 return ex.message, 500
319
320
321 class HeatDeleteStack(Resource):
322 def __init__(self, api):
323 self.api = api
324
325 def delete(self, tenant_id, stack_name_or_id, stack_id=None):
326 """
327 Deletes an existing stack.
328
329 :param tenant_id:
330 :param stack_name_or_id: Specifies the stack, which should be deleted.
331 :param stack_id:
332 :return: 500, if any exception occurred while deletion.
333 204, if everything worked out.
334 """
335 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
336 try:
337 if stack_name_or_id in self.api.compute.stacks:
338 self.api.compute.delete_stack(stack_name_or_id)
339 return Response('Deleted Stack: ' + stack_name_or_id, 204)
340
341 for stack in self.api.compute.stacks.values():
342 if stack.stack_name == stack_name_or_id:
343 self.api.compute.delete_stack(stack.id)
344 return Response('Deleted Stack: ' + stack_name_or_id, 204)
345
346 except Exception as ex:
347 LOG.exception("Heat: Delete Stack exception")
348 return ex.message, 500