a5b6402b2d69591a6dfd5b00bdbb021eda66be9d
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 emuvim
.api
.openstack
.helper
import get_host
6 from datetime
import datetime
7 from emuvim
.api
.openstack
.heat_parser
import HeatParser
12 LOG
= logging
.getLogger("api.openstack.heat")
15 class HeatDummyApi(BaseOpenstackDummy
):
16 def __init__(self
, in_ip
, in_port
, compute
):
17 super(HeatDummyApi
, self
).__init
__(in_ip
, in_port
)
18 self
.compute
= compute
20 self
.api
.add_resource(Shutdown
, "/shutdown")
21 self
.api
.add_resource(HeatListAPIVersions
, "/",
22 resource_class_kwargs
={'api': self
})
23 self
.api
.add_resource(HeatCreateStack
, "/v1/<tenant_id>/stacks",
24 resource_class_kwargs
={'api': self
})
25 self
.api
.add_resource(HeatShowStack
, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
26 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
27 resource_class_kwargs
={'api': self
})
28 self
.api
.add_resource(HeatShowStackTemplate
, "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>/template",
29 resource_class_kwargs
={'api': self
})
30 self
.api
.add_resource(HeatShowStackResources
, "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>/resources",
31 resource_class_kwargs
={'api': self
})
32 self
.api
.add_resource(HeatUpdateStack
, "/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 self
.api
.add_resource(HeatDeleteStack
, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
36 "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
37 resource_class_kwargs
={'api': self
})
39 @self.app
.after_request
40 def add_access_control_header(response
):
41 response
.headers
['Access-Control-Allow-Origin'] = '*'
45 def _start_flask(self
):
46 LOG
.info("Starting %s endpoint @ http://%s:%d" % (__name__
, self
.ip
, self
.port
))
47 if self
.app
is not None:
48 self
.app
.before_request(self
.dump_playbook
)
49 self
.app
.run(self
.ip
, self
.port
, debug
=True, use_reloader
=False)
52 class Shutdown(Resource
):
54 A get request to /shutdown will shut down this endpoint.
58 LOG
.debug(("%s is beeing shut down") % (__name__
))
59 func
= request
.environ
.get('werkzeug.server.shutdown')
61 raise RuntimeError('Not running with the Werkzeug Server')
65 class HeatListAPIVersions(Resource
):
66 def __init__(self
, api
):
70 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
73 resp
['versions'] = dict()
79 "href": "http://%s:%d/v2.0" % (get_host(request
), self
.api
.port
),
85 return Response(json
.dumps(resp
), status
=200, mimetype
="application/json")
88 class HeatCreateStack(Resource
):
89 def __init__(self
, api
):
92 def post(self
, tenant_id
):
94 Create and deploy a new stack.
97 :return: 409, if the stack name was already used.
98 400, if the heat template could not be parsed properly.
99 500, if any exception occurred while creation.
100 201, if everything worked out.
102 LOG
.debug("API CALL: %s POST" % str(self
.__class
__.__name
__))
105 stack_dict
= json
.loads(request
.data
)
106 for stack
in self
.api
.compute
.stacks
.values():
107 if stack
.stack_name
== stack_dict
['stack_name']:
110 stack
.stack_name
= stack_dict
['stack_name']
112 reader
= HeatParser(self
.api
.compute
)
113 if isinstance(stack_dict
['template'], str) or isinstance(stack_dict
['template'], unicode):
114 stack_dict
['template'] = json
.loads(stack_dict
['template'])
115 if not reader
.parse_input(stack_dict
['template'], stack
, self
.api
.compute
.dc
.label
):
116 self
.api
.compute
.clean_broken_stack(stack
)
117 return 'Could not create stack.', 400
119 stack
.template
= stack_dict
['template']
120 stack
.creation_time
= str(datetime
.now())
121 stack
.status
= "CREATE_COMPLETE"
123 return_dict
= {"stack": {"id": stack
.id,
126 "href": "http://%s:%s/v1/%s/stacks/%s"
127 % (get_host(request
), self
.api
.port
, tenant_id
, stack
.id),
131 self
.api
.compute
.add_stack(stack
)
132 self
.api
.compute
.deploy_stack(stack
.id)
133 return Response(json
.dumps(return_dict
), status
=201, mimetype
="application/json")
135 except Exception as ex
:
136 LOG
.exception("Heat: Create Stack exception.")
137 return ex
.message
, 500
139 def get(self
, tenant_id
):
141 Calculates information about the requested stack.
144 :return: Returns a json response which contains information like the stack id, name, status, creation time.
145 500, if any exception occurred.
146 200, if everything worked out.
148 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
150 return_stacks
= dict()
151 return_stacks
['stacks'] = list()
152 for stack
in self
.api
.compute
.stacks
.values():
153 return_stacks
['stacks'].append(
154 {"creation_time": stack
.creation_time
,
155 "description": "desc of " + stack
.id,
158 "stack_name": stack
.stack_name
,
159 "stack_status": stack
.status
,
160 "stack_status_reason": "Stack CREATE completed successfully",
161 "updated_time": stack
.update_time
,
165 return Response(json
.dumps(return_stacks
), status
=200, mimetype
="application/json")
166 except Exception as ex
:
167 LOG
.exception("Heat: List Stack exception.")
168 return ex
.message
, 500
171 class HeatShowStack(Resource
):
172 def __init__(self
, api
):
175 def get(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
177 Calculates detailed information about the requested stack.
180 :param stack_name_or_id:
182 :return: Returns a json response which contains information like the stack id, name, status, creation time.
183 500, if any exception occurred.
184 200, if everything worked out.
186 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
189 if stack_name_or_id
in self
.api
.compute
.stacks
:
190 stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
192 for tmp_stack
in self
.api
.compute
.stacks
.values():
193 if tmp_stack
.stack_name
== stack_name_or_id
:
196 return 'Could not resolve Stack - ID', 404
201 "creation_time": stack
.creation_time
,
202 "description": "desc of " + stack
.stack_name
,
203 "disable_rollback": True,
207 "href": "http://%s:%s/v1/%s/stacks/%s"
208 % (get_host(request
), self
.api
.port
, tenant_id
, stack
.id),
212 "notification_topics": [],
215 "OS::project_id": "3ab5b02f-a01f-4f95-afa1-e254afc4a435", # add real project id
216 "OS::stack_id": stack
.id,
217 "OS::stack_name": stack
.stack_name
219 "stack_name": stack
.stack_name
,
220 "stack_owner": "The owner of the stack.", # add stack owner
221 "stack_status": stack
.status
,
222 "stack_status_reason": "The reason for the current status of the stack.", # add status reason
223 "template_description": "The description of the stack template.",
224 "stack_user_project_id": "The project UUID of the stack user.",
232 return Response(json
.dumps(return_stack
), status
=200, mimetype
="application/json")
234 except Exception as ex
:
235 LOG
.exception("Heat: Show stack exception.")
236 return ex
.message
, 500
239 class HeatShowStackTemplate(Resource
):
240 def __init__(self
, api
):
243 def get(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
245 Returns template of given stack.
248 :param stack_name_or_id:
250 :return: Returns a json response which contains the stack's template.
252 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
255 if stack_name_or_id
in self
.api
.compute
.stacks
:
256 stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
258 for tmp_stack
in self
.api
.compute
.stacks
.values():
259 if tmp_stack
.stack_name
== stack_name_or_id
:
262 return 'Could not resolve Stack - ID', 404
263 #LOG.debug("STACK: {}".format(stack))
264 #LOG.debug("TEMPLATE: {}".format(stack.template))
265 return Response(json
.dumps(stack
.template
), status
=200, mimetype
="application/json")
267 except Exception as ex
:
268 LOG
.exception("Heat: Show stack template exception.")
269 return ex
.message
, 500
272 class HeatShowStackResources(Resource
):
273 def __init__(self
, api
):
276 def get(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
278 Returns template of given stack.
281 :param stack_name_or_id:
283 :return: Returns a json response which contains the stack's template.
285 LOG
.debug("API CALL: %s GET" % str(self
.__class
__.__name
__))
288 if stack_name_or_id
in self
.api
.compute
.stacks
:
289 stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
291 for tmp_stack
in self
.api
.compute
.stacks
.values():
292 if tmp_stack
.stack_name
== stack_name_or_id
:
295 return 'Could not resolve Stack - ID', 404
297 response
= {"resources": []}
299 return Response(json
.dumps(response
), status
=200, mimetype
="application/json")
301 except Exception as ex
:
302 LOG
.exception("Heat: Show stack template exception.")
303 return ex
.message
, 500
306 class HeatUpdateStack(Resource
):
307 def __init__(self
, api
):
310 def put(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
311 LOG
.debug("API CALL: %s PUT" % str(self
.__class
__.__name
__))
312 return self
.update_stack(tenant_id
, stack_name_or_id
, stack_id
)
314 def patch(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
315 LOG
.debug("API CALL: %s PATCH" % str(self
.__class
__.__name
__))
316 return self
.update_stack(tenant_id
, stack_name_or_id
, stack_id
)
318 def update_stack(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
320 Updates an existing stack with a new heat template.
323 :param stack_name_or_id: Specifies the stack, which should be updated.
325 :return: 404, if the requested stack could not be found.
326 400, if the stack creation (because of errors in the heat template) or the stack update failed.
327 500, if any exception occurred while updating.
328 202, if everything worked out.
332 if stack_name_or_id
in self
.api
.compute
.stacks
:
333 old_stack
= self
.api
.compute
.stacks
[stack_name_or_id
]
335 for tmp_stack
in self
.api
.compute
.stacks
.values():
336 if tmp_stack
.stack_name
== stack_name_or_id
:
337 old_stack
= tmp_stack
338 if old_stack
is None:
339 return 'Could not resolve Stack - ID', 404
341 stack_dict
= json
.loads(request
.data
)
344 stack
.stack_name
= old_stack
.stack_name
345 stack
.id = old_stack
.id
346 stack
.creation_time
= old_stack
.creation_time
347 stack
.update_time
= str(datetime
.now())
348 stack
.status
= "UPDATE_COMPLETE"
350 reader
= HeatParser(self
.api
.compute
)
351 if isinstance(stack_dict
['template'], str) or isinstance(stack_dict
['template'], unicode):
352 stack_dict
['template'] = json
.loads(stack_dict
['template'])
353 if not reader
.parse_input(stack_dict
['template'], stack
, self
.api
.compute
.dc
.label
, stack_update
=True):
354 return 'Could not create stack.', 400
355 stack
.template
= stack_dict
['template']
357 if not self
.api
.compute
.update_stack(old_stack
.id, stack
):
358 return 'Could not update stack.', 400
360 return Response(status
=202, mimetype
="application/json")
362 except Exception as ex
:
363 LOG
.exception("Heat: Update Stack exception")
364 return ex
.message
, 500
367 class HeatDeleteStack(Resource
):
368 def __init__(self
, api
):
371 def delete(self
, tenant_id
, stack_name_or_id
, stack_id
=None):
373 Deletes an existing stack.
376 :param stack_name_or_id: Specifies the stack, which should be deleted.
378 :return: 500, if any exception occurred while deletion.
379 204, if everything worked out.
381 LOG
.debug("API CALL: %s DELETE" % str(self
.__class
__.__name
__))
383 if stack_name_or_id
in self
.api
.compute
.stacks
:
384 self
.api
.compute
.delete_stack(stack_name_or_id
)
385 return Response('Deleted Stack: ' + stack_name_or_id
, 204)
387 for stack
in self
.api
.compute
.stacks
.values():
388 if stack
.stack_name
== stack_name_or_id
:
389 self
.api
.compute
.delete_stack(stack
.id)
390 return Response('Deleted Stack: ' + stack_name_or_id
, 204)
392 except Exception as ex
:
393 LOG
.exception("Heat: Delete Stack exception")
394 return ex
.message
, 500