]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Initial metadata server API
authorTomas Sedovic <tomas@sedovic.cz>
Wed, 2 May 2012 11:51:08 +0000 (13:51 +0200)
committerTomas Sedovic <tomas@sedovic.cz>
Wed, 2 May 2012 15:47:51 +0000 (17:47 +0200)
This implements the basic capabilities we need from the metadata server. Each
API call returns proper HTTP responses (404, 409, etc.).

The server is currently not connected to a real database. Rather, it uses a
simple mock. This allows for quick initial API changes before things stabilize.

The immediate next steps are to integrate the server with the cfn tools
(cfn-metadata being the prime candidate) to see what may be wrong/missing.

And then to connect the server to a real database.

Signed-off-by: Tomas Sedovic <tomas@sedovic.cz>
heat/metadata/api/v1/__init__.py
heat/metadata/api/v1/metadata.py
heat/metadata/db.py [new file with mode: 0644]

index 7c1631d4e73b3cc33c0fd4275ce70f8a47451886..c0957f966e10b3d365deedbcaf64cdfb9236d472 100644 (file)
@@ -30,7 +30,27 @@ class API(wsgi.Router):
         mapper = routes.Mapper()
         metadata_controller = metadata.create_resource(conf)
 
-        mapper.connect('/', controller=metadata_controller, action='index',
+        mapper.connect('/',
+                       controller=metadata_controller, action='entry_point',
                        conditions=dict(method=['GET']))
+        mapper.connect('/stacks/',
+                       controller=metadata_controller, action='list_stacks',
+                       conditions=dict(method=['GET']))
+        mapper.connect('/stacks/:stack_name/resources/',
+                       controller=metadata_controller, action='list_resources',
+                       conditions=dict(method=['GET']))
+        mapper.connect('/stacks/:stack_name/resources/:resource_id',
+                       controller=metadata_controller, action='get_resource',
+                       conditions=dict(method=['GET']))
+        mapper.connect('/stacks/:stack_name',
+                       controller=metadata_controller, action='create_stack',
+                       conditions=dict(method=['PUT']))
+        mapper.connect('/stacks/:stack_name/resources/:resource_id',
+                       controller=metadata_controller, action='update_metadata',
+                       conditions=dict(method=['PUT']))
+
+        # TODO(shadower): make sure all responses are JSON-encoded
+        # currently, calling an unknown route uses the default handler which
+        # produces a HTML response.
 
         super(API, self).__init__(mapper)
index 25b47284243a9ebd609399529161468cb0df5ec2..8c114a153c9e2db64f3bc1cf503f1321c8d8b126 100644 (file)
 #    under the License.
 
 import logging
+import json
+
+from webob.exc import Response
 
 from heat.common import wsgi
+from heat.metadata import db as db_api
+from heat.metadata.db import (ConflictError, StackNotFoundError,
+                              ResourceNotFoundError)
+
 
+def json_response(http_status, data):
+    """Create a JSON response with a specific HTTP code."""
+    response = Response(json.dumps(data))
+    response.status = http_status
+    response.content_type = 'application/json'
+    return response
+
+def json_error(http_status, message):
+    """Create a JSON error response."""
+    body = {'error': message}
+    return json_response(http_status, body)
 
 class MetadataController:
     def __init__(self, options):
         self.options = options
 
-    def index(self, req):
-        return []
+    def entry_point(self, req):
+        return {
+            'name': 'Heat Metadata Server API',
+            'version': '1',
+        }
+
+    def list_stacks(self, req):
+        return db_api.list_stacks()
+
+    def list_resources(self, req, stack_name):
+        try:
+            resources = db_api.list_resources(stack_name)
+        except StackNotFoundError:
+            return json_error(404, 'The stack "%s" does not exist.' % stack_name)
+        return resources
+
+    def get_resource(self, req, stack_name, resource_id):
+        try:
+            resource = db_api.get_resource(stack_name, resource_id)
+        except StackNotFoundError:
+            return json_error(404, 'The stack "%s" does not exist.' % stack_name)
+        except ResourceNotFoundError:
+            return json_error(404, 'The resource "%s" does not exist.' % resource_id)
+        return resource
+
+    def create_stack(self, req, body, stack_name):
+        try:
+            stack = db_api.create_stack(stack_name, body)
+        except ConflictError:
+            return json_error(409, 'The stack "%s" already exists.' % stack_name)
+        return json_response(201, stack)
 
+    def update_metadata(self, req, body, stack_name, resource_id):
+        try:
+            db_api.update_resource_metadata(stack_name, resource_id, body)
+        except StackNotFoundError:
+            return json_error(409, 'The stack "%s" does not exist.' % stack_name)
+        except ResourceNotFoundError:
+            # The resource doesn't exit yet, create it.
+            db_api.create_resource_metadata(stack_name, resource_id, body)
+        return json_response(201, {
+            'resource': resource_id,
+            'metadata': body,
+        })
 
 def create_resource(options):
     """
diff --git a/heat/metadata/db.py b/heat/metadata/db.py
new file mode 100644 (file)
index 0000000..732422c
--- /dev/null
@@ -0,0 +1,64 @@
+DB = {}
+
+
+class ConflictError(Exception):
+    pass
+
+class StackNotFoundError(Exception):
+    pass
+
+class ResourceNotFoundError(Exception):
+    pass
+
+
+def list_stacks():
+    return DB.keys()
+
+def create_stack(name, stack):
+    global DB
+    if name in DB:
+        raise ConflictError(name)
+    data = {}
+    # TODO(shadower): validate the stack input format
+    data['name'] = name
+    data['heat_id'] = stack['id']
+    data['resources'] = {}
+    DB[name] = data
+    return data
+
+def list_resources(stack_name):
+    if not stack_name in DB:
+        raise StackNotFoundError(stack_name)
+    stack = DB[stack_name]
+    try:
+        resources = stack['resources'].keys()
+    except:
+        resources = []
+    return resources
+
+def get_resource(stack_name, resource_id):
+    if not stack_name in DB:
+        raise StackNotFoundError(stack_name)
+    stack = DB[stack_name]
+
+    if not resource_id in stack['resources']:
+        raise ResourceNotFoundError(resource_id)
+    return stack['resources'][resource_id]
+
+def create_resource_metadata(stack_name, resource_id, metadata):
+    if not stack_name in DB:
+        raise StackNotFoundError(stack_name)
+    stack = DB[stack_name]
+
+    if resource_id in stack['resources']:
+        raise ConflictError(resource_id)
+    stack['resources'][resource_id] = metadata
+
+def update_resource_metadata(stack_name, resource_id, metadata):
+    if not stack_name in DB:
+        raise StackNotFoundError(stack_name)
+    stack = DB[stack_name]
+
+    if not resource_id in stack['resources']:
+        raise ResourceNotFoundError(resource_id)
+    stack['resources'][resource_id] = metadata