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