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
11 LOG
= logging
.getLogger("api.openstack.heat")
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
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(HeatShowStackResources
, "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>/resources",
30 resource_class_kwargs
={'api': self
})
31 self
.api
.add_resource(HeatUpdateStack
, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
32 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
33 resource_class_kwargs
={'api': self
})
34 self
.api
.add_resource(HeatDeleteStack
, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
35 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
36 resource_class_kwargs
={'api': self
})
38 @self.app
.after_request
39 def add_access_control_header(response
):
40 response
.headers
['Access-Control-Allow-Origin'] = '*'
44 def _start_flask(self
):
45 LOG
.info("Starting %s endpoint @ http://%s:%d" % (__name__
, self
.ip
, self
.port
))
46 if self
.app
is not None:
47 self
.app
.before_request(self
.dump_playbook
)
48 self
.app
.run(self
.ip
, self
.port
, debug
=True, use_reloader
=False)
51 class Shutdown(Resource
):
53 A get request to /shutdown will shut down this endpoint.
57 LOG
.debug(("%s is beeing shut down") % (__name__
))
58 func
= request
.environ
.get('werkzeug.server.shutdown')
60 raise RuntimeError('Not running with the Werkzeug Server')
64 class HeatListAPIVersions(Resource
):
65 def __init__(self
, api
):
69 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
72 resp
['versions'] = dict()
78 "href": "http://%s:%d/v2.0" % (self
.api
.ip
, self
.api
.port
),
84 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
87 class HeatCreateStack(Resource
):
88 def __init__(self
, api
):
91 def post(self
, tenant_id
):
93 Create and deploy a new stack.
96 :return: 409, if the stack name was already used.
97 400, if the heat template could not be parsed properly.
98 500, if any exception occurred while creation.
99 201, if everything worked out.
101 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
104 stack_dict
= json
.loads(request
.data
)
105 for stack
in self
.api
.compute
.stacks
.values():
106 if stack
.stack_name
== stack_dict
['stack_name']:
109 stack
.stack_name
= stack_dict
['stack_name']
111 reader
= HeatParser(self
.api
.compute
)
112 if isinstance(stack_dict
['template'], str) or isinstance(stack_dict
['template'], unicode):
113 stack_dict
['template'] = json
.loads(stack_dict
['template'])
114 if not reader
.parse_input(stack_dict
['template'], stack
, self
.api
.compute
.dc
.label
):
115 self
.api
.compute
.clean_broken_stack(stack
)
116 return 'Could not create stack.', 400
118 stack
.template
= stack_dict
['template']
119 stack
.creation_time
= str(datetime
.now())
120 stack
.status
= "CREATE_COMPLETE"
122 return_dict
= {"stack": {"id": stack
.id,
125 "href": "http://%s:%s/v1/%s/stacks/%s"
126 % (self
.api
.ip
, self
.api
.port
, tenant_id
, stack
.id),
130 self
.api
.compute
.add_stack(stack
)
131 self
.api
.compute
.deploy_stack(stack
.id)
132 return Response(json
.dumps(return_dict
), status
=201, mimetype
="application/json")
134 except Exception as ex
:
135 LOG
.exception("Heat: Create Stack exception.")
136 return ex
.message
, 500
138 def get(self
, tenant_id
):
140 Calculates information about the requested stack.
143 :return: Returns a json response which contains information like the stack id, name, status, creation time.
144 500, if any exception occurred.
145 200, if everything worked out.
147 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
149 return_stacks
= dict()
150 return_stacks
['stacks'] = list()
151 for stack
in self
.api
.compute
.stacks
.values():
152 return_stacks
['stacks'].append(
153 {"creation_time": stack
.creation_time
,
154 "description": "desc of " + stack
.id,
157 "stack_name": stack
.stack_name
,
158 "stack_status": stack
.status
,
159 "stack_status_reason": "Stack CREATE completed successfully",
160 "updated_time": stack
.update_time
,
164 return Response(json
.dumps(return_stacks
), status
=200, mimetype
="application/json")
165 except Exception as ex
:
166 LOG
.exception("Heat: List Stack exception.")
167 return ex
.message
, 500
170 class HeatShowStack(Resource
):
171 def __init__(self
, api
):
174 def get(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
176 Calculates detailed information about the requested stack.
179 :param stack_name_or_id:
181 :return: Returns a json response which contains information like the stack id, name, status, creation time.
182 500, if any exception occurred.
183 200, if everything worked out.
185 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
188 if stack_name_or_id
in self
.api
.compute
.stacks
:
189 stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
191 for tmp_stack
in self
.api
.compute
.stacks
.values():
192 if tmp_stack
.stack_name
== stack_name_or_id
:
195 return 'Could not resolve Stack - ID', 404
200 "creation_time": stack
.creation_time
,
201 "description": "desc of " + stack
.stack_name
,
202 "disable_rollback": True,
206 "href": "http://%s:%s/v1/%s/stacks/%s"
207 % (self
.api
.ip
, self
.api
.port
, tenant_id
, stack
.id),
211 "notification_topics": [],
214 "OS::project_id": "3ab5b02f-a01f-4f95-afa1-e254afc4a435", # add real project id
215 "OS::stack_id": stack
.id,
216 "OS::stack_name": stack
.stack_name
218 "stack_name": stack
.stack_name
,
219 "stack_owner": "The owner of the stack.", # add stack owner
220 "stack_status": stack
.status
,
221 "stack_status_reason": "The reason for the current status of the stack.", # add status reason
222 "template_description": "The description of the stack template.",
223 "stack_user_project_id": "The project UUID of the stack user.",
231 return Response(json
.dumps(return_stack
), status
=200, mimetype
="application/json")
233 except Exception as ex
:
234 LOG
.exception("Heat: Show stack exception.")
235 return ex
.message
, 500
238 class HeatShowStackTemplate(Resource
):
239 def __init__(self
, api
):
242 def get(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
244 Returns template of given stack.
247 :param stack_name_or_id:
249 :return: Returns a json response which contains the stack's template.
251 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
254 if stack_name_or_id
in self
.api
.compute
.stacks
:
255 stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
257 for tmp_stack
in self
.api
.compute
.stacks
.values():
258 if tmp_stack
.stack_name
== stack_name_or_id
:
261 return 'Could not resolve Stack - ID', 404
263 return Response(json
.dumps(stack
.template
), status
=200, mimetype
="application/json")
265 except Exception as ex
:
266 LOG
.exception("Heat: Show stack template exception.")
267 return ex
.message
, 500
270 class HeatShowStackResources(Resource
):
271 def __init__(self
, api
):
274 def get(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
276 Returns template of given stack.
279 :param stack_name_or_id:
281 :return: Returns a json response which contains the stack's template.
283 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
286 if stack_name_or_id
in self
.api
.compute
.stacks
:
287 stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
289 for tmp_stack
in self
.api
.compute
.stacks
.values():
290 if tmp_stack
.stack_name
== stack_name_or_id
:
293 return 'Could not resolve Stack - ID', 404
295 response
= {"resources": []}
297 return Response(json
.dumps(response
), status
=200, mimetype
="application/json")
299 except Exception as ex
:
300 LOG
.exception("Heat: Show stack template exception.")
301 return ex
.message
, 500
304 class HeatUpdateStack(Resource
):
305 def __init__(self
, api
):
308 def put(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
309 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
310 return self
.update_stack(tenant_id
, stack_name_or_id
, stack_id
)
312 def patch(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
313 LOG
.debug("API CALL: %s PATCH" % str(self
.__class
__.__name
__))
314 return self
.update_stack(tenant_id
, stack_name_or_id
, stack_id
)
316 def update_stack(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
318 Updates an existing stack with a new heat template.
321 :param stack_name_or_id: Specifies the stack, which should be updated.
323 :return: 404, if the requested stack could not be found.
324 400, if the stack creation (because of errors in the heat template) or the stack update failed.
325 500, if any exception occurred while updating.
326 202, if everything worked out.
330 if stack_name_or_id
in self
.api
.compute
.stacks
:
331 old_stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
333 for tmp_stack
in self
.api
.compute
.stacks
.values():
334 if tmp_stack
.stack_name
== stack_name_or_id
:
335 old_stack
= tmp_stack
336 if old_stack
is None:
337 return 'Could not resolve Stack - ID', 404
339 stack_dict
= json
.loads(request
.data
)
342 stack
.stack_name
= old_stack
.stack_name
343 stack
.id = old_stack
.id
344 stack
.creation_time
= old_stack
.creation_time
345 stack
.update_time
= str(datetime
.now())
346 stack
.status
= "UPDATE_COMPLETE"
348 reader
= HeatParser(self
.api
.compute
)
349 if isinstance(stack_dict
['template'], str) or isinstance(stack_dict
['template'], unicode):
350 stack_dict
['template'] = json
.loads(stack_dict
['template'])
351 if not reader
.parse_input(stack_dict
['template'], stack
, self
.api
.compute
.dc
.label
, stack_update
=True):
352 return 'Could not create stack.', 400
354 if not self
.api
.compute
.update_stack(old_stack
.id, stack
):
355 return 'Could not update stack.', 400
357 return Response(status
=202, mimetype
="application/json")
359 except Exception as ex
:
360 LOG
.exception("Heat: Update Stack exception")
361 return ex
.message
, 500
364 class HeatDeleteStack(Resource
):
365 def __init__(self
, api
):
368 def delete(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
370 Deletes an existing stack.
373 :param stack_name_or_id: Specifies the stack, which should be deleted.
375 :return: 500, if any exception occurred while deletion.
376 204, if everything worked out.
378 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
380 if stack_name_or_id
in self
.api
.compute
.stacks
:
381 self
.api
.compute
.delete_stack(stack_name_or_id
)
382 return Response('Deleted Stack: ' + stack_name_or_id
, 204)
384 for stack
in self
.api
.compute
.stacks
.values():
385 if stack
.stack_name
== stack_name_or_id
:
386 self
.api
.compute
.delete_stack(stack
.id)
387 return Response('Deleted Stack: ' + stack_name_or_id
, 204)
389 except Exception as ex
:
390 LOG
.exception("Heat: Delete Stack exception")
391 return ex
.message
, 500