Fixed missing license headers
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / heat_dummy_api.py
1 """
2 Copyright (c) 2017 SONATA-NFV and Paderborn University
3 ALL RIGHTS RESERVED.
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 Neither the name of the SONATA-NFV, Paderborn University
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
20 permission.
21
22 This work has been performed in the framework of the SONATA project,
23 funded by the European Commission under Grant number 671517 through
24 the Horizon 2020 and 5G-PPP programmes. The authors would like to
25 acknowledge the contributions of their colleagues of the SONATA
26 partner consortium (www.sonata-nfv.eu).
27 """
28 from flask import request, Response
29 from flask_restful import Resource
30 from emuvim.api.openstack.resources import Stack
31 from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
32 from emuvim.api.openstack.helper import get_host
33 from datetime import datetime
34 from emuvim.api.openstack.heat_parser import HeatParser
35 import logging
36 import json
37
38
39 LOG = logging.getLogger("api.openstack.heat")
40
41
42 class HeatDummyApi(BaseOpenstackDummy):
43 def __init__(self, in_ip, in_port, compute):
44 super(HeatDummyApi, self).__init__(in_ip, in_port)
45 self.compute = compute
46
47 self.api.add_resource(Shutdown, "/shutdown")
48 self.api.add_resource(HeatListAPIVersions, "/",
49 resource_class_kwargs={'api': self})
50 self.api.add_resource(HeatCreateStack, "/v1/<tenant_id>/stacks",
51 resource_class_kwargs={'api': self})
52 self.api.add_resource(HeatShowStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
53 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
54 resource_class_kwargs={'api': self})
55 self.api.add_resource(HeatShowStackTemplate, "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>/template",
56 resource_class_kwargs={'api': self})
57 self.api.add_resource(HeatShowStackResources, "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>/resources",
58 resource_class_kwargs={'api': self})
59 self.api.add_resource(HeatUpdateStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
60 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
61 resource_class_kwargs={'api': self})
62 self.api.add_resource(HeatDeleteStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
63 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
64 resource_class_kwargs={'api': self})
65
66 @self.app.after_request
67 def add_access_control_header(response):
68 response.headers['Access-Control-Allow-Origin'] = '*'
69 return response
70
71
72 def _start_flask(self):
73 LOG.info("Starting %s endpoint @ http://%s:%d" % (__name__, self.ip, self.port))
74 if self.app is not None:
75 self.app.before_request(self.dump_playbook)
76 self.app.run(self.ip, self.port, debug=True, use_reloader=False)
77
78
79 class Shutdown(Resource):
80 """
81 A get request to /shutdown will shut down this endpoint.
82 """
83
84 def get(self):
85 LOG.debug(("%s is beeing shut down") % (__name__))
86 func = request.environ.get('werkzeug.server.shutdown')
87 if func is None:
88 raise RuntimeError('Not running with the Werkzeug Server')
89 func()
90
91
92 class HeatListAPIVersions(Resource):
93 def __init__(self, api):
94 self.api = api
95
96 def get(self):
97 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
98 resp = dict()
99
100 resp['versions'] = dict()
101 resp['versions'] = [{
102 "status": "CURRENT",
103 "id": "v1.0",
104 "links": [
105 {
106 "href": "http://%s:%d/v2.0" % (get_host(request), self.api.port),
107 "rel": "self"
108 }
109 ]
110 }]
111
112 return Response(json.dumps(resp), status=200, mimetype="application/json")
113
114
115 class HeatCreateStack(Resource):
116 def __init__(self, api):
117 self.api = api
118
119 def post(self, tenant_id):
120 """
121 Create and deploy a new stack.
122
123 :param tenant_id:
124 :return: 409, if the stack name was already used.
125 400, if the heat template could not be parsed properly.
126 500, if any exception occurred while creation.
127 201, if everything worked out.
128 """
129 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
130
131 try:
132 stack_dict = json.loads(request.data)
133 for stack in self.api.compute.stacks.values():
134 if stack.stack_name == stack_dict['stack_name']:
135 return [], 409
136 stack = Stack()
137 stack.stack_name = stack_dict['stack_name']
138
139 reader = HeatParser(self.api.compute)
140 if isinstance(stack_dict['template'], str) or isinstance(stack_dict['template'], unicode):
141 stack_dict['template'] = json.loads(stack_dict['template'])
142 if not reader.parse_input(stack_dict['template'], stack, self.api.compute.dc.label):
143 self.api.compute.clean_broken_stack(stack)
144 return 'Could not create stack.', 400
145
146 stack.template = stack_dict['template']
147 stack.creation_time = str(datetime.now())
148 stack.status = "CREATE_COMPLETE"
149
150 return_dict = {"stack": {"id": stack.id,
151 "links": [
152 {
153 "href": "http://%s:%s/v1/%s/stacks/%s"
154 % (get_host(request), self.api.port, tenant_id, stack.id),
155 "rel": "self"
156 }]}}
157
158 self.api.compute.add_stack(stack)
159 self.api.compute.deploy_stack(stack.id)
160 return Response(json.dumps(return_dict), status=201, mimetype="application/json")
161
162 except Exception as ex:
163 LOG.exception("Heat: Create Stack exception.")
164 return ex.message, 500
165
166 def get(self, tenant_id):
167 """
168 Calculates information about the requested stack.
169
170 :param tenant_id:
171 :return: Returns a json response which contains information like the stack id, name, status, creation time.
172 500, if any exception occurred.
173 200, if everything worked out.
174 """
175 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
176 try:
177 return_stacks = dict()
178 return_stacks['stacks'] = list()
179 for stack in self.api.compute.stacks.values():
180 return_stacks['stacks'].append(
181 {"creation_time": stack.creation_time,
182 "description": "desc of " + stack.id,
183 "id": stack.id,
184 "links": [],
185 "stack_name": stack.stack_name,
186 "stack_status": stack.status,
187 "stack_status_reason": "Stack CREATE completed successfully",
188 "updated_time": stack.update_time,
189 "tags": ""
190 })
191
192 return Response(json.dumps(return_stacks), status=200, mimetype="application/json")
193 except Exception as ex:
194 LOG.exception("Heat: List Stack exception.")
195 return ex.message, 500
196
197
198 class HeatShowStack(Resource):
199 def __init__(self, api):
200 self.api = api
201
202 def get(self, tenant_id, stack_name_or_id, stack_id=None):
203 """
204 Calculates detailed information about the requested stack.
205
206 :param tenant_id:
207 :param stack_name_or_id:
208 :param stack_id:
209 :return: Returns a json response which contains information like the stack id, name, status, creation time.
210 500, if any exception occurred.
211 200, if everything worked out.
212 """
213 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
214 try:
215 stack = None
216 if stack_name_or_id in self.api.compute.stacks:
217 stack = self.api.compute.stacks[stack_name_or_id]
218 else:
219 for tmp_stack in self.api.compute.stacks.values():
220 if tmp_stack.stack_name == stack_name_or_id:
221 stack = tmp_stack
222 if stack is None:
223 return 'Could not resolve Stack - ID', 404
224
225 return_stack = {
226 "stack": {
227 "capabilities": [],
228 "creation_time": stack.creation_time,
229 "description": "desc of " + stack.stack_name,
230 "disable_rollback": True,
231 "id": stack.id,
232 "links": [
233 {
234 "href": "http://%s:%s/v1/%s/stacks/%s"
235 % (get_host(request), self.api.port, tenant_id, stack.id),
236 "rel": "self"
237 }
238 ],
239 "notification_topics": [],
240 "outputs": [],
241 "parameters": {
242 "OS::project_id": "3ab5b02f-a01f-4f95-afa1-e254afc4a435", # add real project id
243 "OS::stack_id": stack.id,
244 "OS::stack_name": stack.stack_name
245 },
246 "stack_name": stack.stack_name,
247 "stack_owner": "The owner of the stack.", # add stack owner
248 "stack_status": stack.status,
249 "stack_status_reason": "The reason for the current status of the stack.", # add status reason
250 "template_description": "The description of the stack template.",
251 "stack_user_project_id": "The project UUID of the stack user.",
252 "timeout_mins": "",
253 "updated_time": "",
254 "parent": "",
255 "tags": ""
256 }
257 }
258
259 return Response(json.dumps(return_stack), status=200, mimetype="application/json")
260
261 except Exception as ex:
262 LOG.exception("Heat: Show stack exception.")
263 return ex.message, 500
264
265
266 class HeatShowStackTemplate(Resource):
267 def __init__(self, api):
268 self.api = api
269
270 def get(self, tenant_id, stack_name_or_id, stack_id=None):
271 """
272 Returns template of given stack.
273
274 :param tenant_id:
275 :param stack_name_or_id:
276 :param stack_id:
277 :return: Returns a json response which contains the stack's template.
278 """
279 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
280 try:
281 stack = None
282 if stack_name_or_id in self.api.compute.stacks:
283 stack = self.api.compute.stacks[stack_name_or_id]
284 else:
285 for tmp_stack in self.api.compute.stacks.values():
286 if tmp_stack.stack_name == stack_name_or_id:
287 stack = tmp_stack
288 if stack is None:
289 return 'Could not resolve Stack - ID', 404
290 #LOG.debug("STACK: {}".format(stack))
291 #LOG.debug("TEMPLATE: {}".format(stack.template))
292 return Response(json.dumps(stack.template), status=200, mimetype="application/json")
293
294 except Exception as ex:
295 LOG.exception("Heat: Show stack template exception.")
296 return ex.message, 500
297
298
299 class HeatShowStackResources(Resource):
300 def __init__(self, api):
301 self.api = api
302
303 def get(self, tenant_id, stack_name_or_id, stack_id=None):
304 """
305 Returns template of given stack.
306
307 :param tenant_id:
308 :param stack_name_or_id:
309 :param stack_id:
310 :return: Returns a json response which contains the stack's template.
311 """
312 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
313 try:
314 stack = None
315 if stack_name_or_id in self.api.compute.stacks:
316 stack = self.api.compute.stacks[stack_name_or_id]
317 else:
318 for tmp_stack in self.api.compute.stacks.values():
319 if tmp_stack.stack_name == stack_name_or_id:
320 stack = tmp_stack
321 if stack is None:
322 return 'Could not resolve Stack - ID', 404
323
324 response = {"resources": []}
325
326 return Response(json.dumps(response), status=200, mimetype="application/json")
327
328 except Exception as ex:
329 LOG.exception("Heat: Show stack template exception.")
330 return ex.message, 500
331
332
333 class HeatUpdateStack(Resource):
334 def __init__(self, api):
335 self.api = api
336
337 def put(self, tenant_id, stack_name_or_id, stack_id=None):
338 LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
339 return self.update_stack(tenant_id, stack_name_or_id, stack_id)
340
341 def patch(self, tenant_id, stack_name_or_id, stack_id=None):
342 LOG.debug("API CALL: %s PATCH" % str(self.__class__.__name__))
343 return self.update_stack(tenant_id, stack_name_or_id, stack_id)
344
345 def update_stack(self, tenant_id, stack_name_or_id, stack_id=None):
346 """
347 Updates an existing stack with a new heat template.
348
349 :param tenant_id:
350 :param stack_name_or_id: Specifies the stack, which should be updated.
351 :param stack_id:
352 :return: 404, if the requested stack could not be found.
353 400, if the stack creation (because of errors in the heat template) or the stack update failed.
354 500, if any exception occurred while updating.
355 202, if everything worked out.
356 """
357 try:
358 old_stack = None
359 if stack_name_or_id in self.api.compute.stacks:
360 old_stack = self.api.compute.stacks[stack_name_or_id]
361 else:
362 for tmp_stack in self.api.compute.stacks.values():
363 if tmp_stack.stack_name == stack_name_or_id:
364 old_stack = tmp_stack
365 if old_stack is None:
366 return 'Could not resolve Stack - ID', 404
367
368 stack_dict = json.loads(request.data)
369
370 stack = Stack()
371 stack.stack_name = old_stack.stack_name
372 stack.id = old_stack.id
373 stack.creation_time = old_stack.creation_time
374 stack.update_time = str(datetime.now())
375 stack.status = "UPDATE_COMPLETE"
376
377 reader = HeatParser(self.api.compute)
378 if isinstance(stack_dict['template'], str) or isinstance(stack_dict['template'], unicode):
379 stack_dict['template'] = json.loads(stack_dict['template'])
380 if not reader.parse_input(stack_dict['template'], stack, self.api.compute.dc.label, stack_update=True):
381 return 'Could not create stack.', 400
382 stack.template = stack_dict['template']
383
384 if not self.api.compute.update_stack(old_stack.id, stack):
385 return 'Could not update stack.', 400
386
387 return Response(status=202, mimetype="application/json")
388
389 except Exception as ex:
390 LOG.exception("Heat: Update Stack exception")
391 return ex.message, 500
392
393
394 class HeatDeleteStack(Resource):
395 def __init__(self, api):
396 self.api = api
397
398 def delete(self, tenant_id, stack_name_or_id, stack_id=None):
399 """
400 Deletes an existing stack.
401
402 :param tenant_id:
403 :param stack_name_or_id: Specifies the stack, which should be deleted.
404 :param stack_id:
405 :return: 500, if any exception occurred while deletion.
406 204, if everything worked out.
407 """
408 LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
409 try:
410 if stack_name_or_id in self.api.compute.stacks:
411 self.api.compute.delete_stack(stack_name_or_id)
412 return Response('Deleted Stack: ' + stack_name_or_id, 204)
413
414 for stack in self.api.compute.stacks.values():
415 if stack.stack_name == stack_name_or_id:
416 self.api.compute.delete_stack(stack.id)
417 return Response('Deleted Stack: ' + stack_name_or_id, 204)
418
419 except Exception as ex:
420 LOG.exception("Heat: Delete Stack exception")
421 return ex.message, 500