Manually added OpenStack API code
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / heat_dummy_api.py
diff --git a/src/emuvim/api/openstack/openstack_dummies/heat_dummy_api.py b/src/emuvim/api/openstack/openstack_dummies/heat_dummy_api.py
new file mode 100644 (file)
index 0000000..f696442
--- /dev/null
@@ -0,0 +1,310 @@
+from flask import request, Response
+from flask_restful import Resource
+from emuvim.api.openstack.resources import Stack
+from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
+from datetime import datetime
+from emuvim.api.openstack.heat_parser import HeatParser
+import logging
+import json
+
+
+class HeatDummyApi(BaseOpenstackDummy):
+    def __init__(self, in_ip, in_port, compute):
+        super(HeatDummyApi, self).__init__(in_ip, in_port)
+        self.compute = compute
+
+        self.api.add_resource(Shutdown, "/shutdown")
+        self.api.add_resource(HeatListAPIVersions, "/",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(HeatCreateStack, "/v1/<tenant_id>/stacks",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(HeatShowStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
+                              "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(HeatUpdateStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
+                              "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(HeatDeleteStack, "/v1/<tenant_id>/stacks/<stack_name_or_id>",
+                              "/v1/<tenant_id>/stacks/<stack_name_or_id>/<stack_id>",
+                              resource_class_kwargs={'api': self})
+
+        @self.app.after_request
+        def add_access_control_header(response):
+            response.headers['Access-Control-Allow-Origin'] = '*'
+            return response
+
+
+    def _start_flask(self):
+        logging.info("Starting %s endpoint @ http://%s:%d" % (__name__, self.ip, self.port))
+        if self.app is not None:
+            self.app.before_request(self.dump_playbook)
+            self.app.run(self.ip, self.port, debug=True, use_reloader=False)
+
+
+class Shutdown(Resource):
+    """
+    A get request to /shutdown will shut down this endpoint.
+    """
+
+    def get(self):
+        logging.debug(("%s is beeing shut down") % (__name__))
+        func = request.environ.get('werkzeug.server.shutdown')
+        if func is None:
+            raise RuntimeError('Not running with the Werkzeug Server')
+        func()
+
+
+class HeatListAPIVersions(Resource):
+    def __init__(self, api):
+        self.api = api
+
+    def get(self):
+        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        resp = dict()
+
+        resp['versions'] = dict()
+        resp['versions'] = [{
+            "status": "CURRENT",
+            "id": "v1.0",
+            "links": [
+                {
+                    "href": "http://%s:%d/v2.0" % (self.api.ip, self.api.port),
+                    "rel": "self"
+                }
+            ]
+        }]
+
+        return Response(json.dumps(resp), status=200, mimetype="application/json")
+
+
+class HeatCreateStack(Resource):
+    def __init__(self, api):
+        self.api = api
+
+    def post(self, tenant_id):
+        """
+        Create and deploy a new stack.
+
+        :param tenant_id:
+        :return: 409, if the stack name was already used.
+            400, if the heat template could not be parsed properly.
+            500, if any exception occurred while creation.
+            201, if everything worked out.
+        """
+        logging.debug("API CALL: %s POST" % str(self.__class__.__name__))
+
+        try:
+            stack_dict = json.loads(request.data)
+            for stack in self.api.compute.stacks.values():
+                if stack.stack_name == stack_dict['stack_name']:
+                    return [], 409
+            stack = Stack()
+            stack.stack_name = stack_dict['stack_name']
+            reader = HeatParser(self.api.compute)
+
+            if isinstance(stack_dict['template'], str) or isinstance(stack_dict['template'], unicode):
+                stack_dict['template'] = json.loads(stack_dict['template'])
+            if not reader.parse_input(stack_dict['template'], stack, self.api.compute.dc.label):
+                self.api.compute.clean_broken_stack(stack)
+                return 'Could not create stack.', 400
+
+            stack.creation_time = str(datetime.now())
+            stack.status = "CREATE_COMPLETE"
+
+            return_dict = {"stack": {"id": stack.id,
+                                     "links": [
+                                         {
+                                             "href": "http://%s:%s/v1/%s/stacks/%s"
+                                                     % (self.api.ip, self.api.port, tenant_id, stack.id),
+                                             "rel": "self"
+                                         }]}}
+
+            self.api.compute.add_stack(stack)
+            self.api.compute.deploy_stack(stack.id)
+            return Response(json.dumps(return_dict), status=201, mimetype="application/json")
+
+        except Exception as ex:
+            logging.exception("Heat: Create Stack exception.")
+            return ex.message, 500
+
+    def get(self, tenant_id):
+        """
+        Calculates information about the requested stack.
+
+        :param tenant_id:
+        :return: Returns a json response which contains information like the stack id, name, status, creation time.
+            500, if any exception occurred.
+            200, if everything worked out.
+        """
+        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        try:
+            return_stacks = dict()
+            return_stacks['stacks'] = list()
+            for stack in self.api.compute.stacks.values():
+                return_stacks['stacks'].append(
+                    {"creation_time": stack.creation_time,
+                     "description": "desc of " + stack.id,
+                     "id": stack.id,
+                     "links": [],
+                     "stack_name": stack.stack_name,
+                     "stack_status": stack.status,
+                     "stack_status_reason": "Stack CREATE completed successfully",
+                     "updated_time": stack.update_time,
+                     "tags": ""
+                     })
+
+            return Response(json.dumps(return_stacks), status=200, mimetype="application/json")
+        except Exception as ex:
+            logging.exception("Heat: List Stack exception.")
+            return ex.message, 500
+
+
+class HeatShowStack(Resource):
+    def __init__(self, api):
+        self.api = api
+
+    def get(self, tenant_id, stack_name_or_id, stack_id=None):
+        """
+        Calculates detailed information about the requested stack.
+
+        :param tenant_id:
+        :param stack_name_or_id:
+        :param stack_id:
+        :return: Returns a json response which contains information like the stack id, name, status, creation time.
+            500, if any exception occurred.
+            200, if everything worked out.
+        """
+        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        try:
+            stack = None
+            if stack_name_or_id in self.api.compute.stacks:
+                stack = self.api.compute.stacks[stack_name_or_id]
+            else:
+                for tmp_stack in self.api.compute.stacks.values():
+                    if tmp_stack.stack_name == stack_name_or_id:
+                        stack = tmp_stack
+            if stack is None:
+                return 'Could not resolve Stack - ID', 404
+
+            return_stack = {
+                "stack": {
+                    "capabilities": [],
+                    "creation_time": stack.creation_time,
+                    "description": "desc of " + stack.stack_name,
+                    "disable_rollback": True,
+                    "id": stack.id,
+                    "links": [
+                        {
+                            "href": "http://%s:%s/v1/%s/stacks/%s"
+                                    % (self.api.ip, self.api.port, tenant_id, stack.id),
+                            "rel": "self"
+                        }
+                    ],
+                    "notification_topics": [],
+                    "outputs": [],
+                    "parameters": {
+                        "OS::project_id": "3ab5b02f-a01f-4f95-afa1-e254afc4a435",  # add real project id
+                        "OS::stack_id": stack.id,
+                        "OS::stack_name": stack.stack_name
+                    },
+                    "stack_name": stack.stack_name,
+                    "stack_owner": "The owner of the stack.",  # add stack owner
+                    "stack_status": stack.status,
+                    "stack_status_reason": "The reason for the current status of the stack.",  # add status reason
+                    "template_description": "The description of the stack template.",
+                    "stack_user_project_id": "The project UUID of the stack user.",
+                    "timeout_mins": "",
+                    "updated_time": "",
+                    "parent": "",
+                    "tags": ""
+                }
+            }
+
+            return Response(json.dumps(return_stack), status=200, mimetype="application/json")
+
+        except Exception as ex:
+            logging.exception("Heat: Show stack exception.")
+            return ex.message, 500
+
+
+class HeatUpdateStack(Resource):
+    def __init__(self, api):
+        self.api = api
+
+    def put(self, tenant_id, stack_name_or_id, stack_id=None):
+        """
+        Updates an existing stack with a new heat template.
+
+        :param tenant_id:
+        :param stack_name_or_id: Specifies the stack, which should be updated.
+        :param stack_id:
+        :return: 404, if the requested stack could not be found.
+            400, if the stack creation (because of errors in the heat template) or the stack update failed.
+            500, if any exception occurred while updating.
+            202, if everything worked out.
+        """
+        logging.debug("API CALL: %s PUT" % str(self.__class__.__name__))
+        try:
+            old_stack = None
+            if stack_name_or_id in self.api.compute.stacks:
+                old_stack = self.api.compute.stacks[stack_name_or_id]
+            else:
+                for tmp_stack in self.api.compute.stacks.values():
+                    if tmp_stack.stack_name == stack_name_or_id:
+                        old_stack = tmp_stack
+            if old_stack is None:
+                return 'Could not resolve Stack - ID', 404
+
+            stack_dict = json.loads(request.data)
+
+            stack = Stack()
+            stack.stack_name = old_stack.stack_name
+            stack.id = old_stack.id
+            stack.creation_time = old_stack.creation_time
+            stack.update_time = str(datetime.now())
+            stack.status = "UPDATE_COMPLETE"
+
+            reader = HeatParser(self.api.compute)
+            if isinstance(stack_dict['template'], str) or isinstance(stack_dict['template'], unicode):
+                stack_dict['template'] = json.loads(stack_dict['template'])
+            if not reader.parse_input(stack_dict['template'], stack, self.api.compute.dc.label, stack_update=True):
+                return 'Could not create stack.', 400
+
+            if not self.api.compute.update_stack(old_stack.id, stack):
+                return 'Could not update stack.', 400
+
+            return Response(status=202, mimetype="application/json")
+
+        except Exception as ex:
+            logging.exception("Heat: Update Stack exception")
+            return ex.message, 500
+
+
+class HeatDeleteStack(Resource):
+    def __init__(self, api):
+        self.api = api
+
+    def delete(self, tenant_id, stack_name_or_id, stack_id=None):
+        """
+        Deletes an existing stack.
+
+        :param tenant_id:
+        :param stack_name_or_id: Specifies the stack, which should be deleted.
+        :param stack_id:
+        :return: 500, if any exception occurred while deletion.
+            204, if everything worked out.
+        """
+        logging.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
+        try:
+            if stack_name_or_id in self.api.compute.stacks:
+                self.api.compute.delete_stack(stack_name_or_id)
+                return Response('Deleted Stack: ' + stack_name_or_id, 204)
+
+            for stack in self.api.compute.stacks.values():
+                if stack.stack_name == stack_name_or_id:
+                    self.api.compute.delete_stack(stack.id)
+                    return Response('Deleted Stack: ' + stack_name_or_id, 204)
+
+        except Exception as ex:
+            logging.exception("Heat: Delete Stack exception")
+            return ex.message, 500