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