Connect metadata server to the engine via RPC

Similarly to the way heat-api works, the engine does all the heavy lifting (db
access, etc.) while the metadata server provides the API layer that communicates
with the engine.

Signed-off-by: Tomas Sedovic <tomas@sedovic.cz>
This commit is contained in:
Tomas Sedovic 2012-05-04 15:16:11 +02:00
parent 247266e7bc
commit 9d69b20402
5 changed files with 109 additions and 27 deletions

View File

@ -34,6 +34,7 @@ if os.path.exists(os.path.join(possible_topdir, 'heat', '__init__.py')):
gettext.install('heat', unicode=1) gettext.install('heat', unicode=1)
import logging import logging
from heat import rpc
from heat.common import config from heat.common import config
from heat.common import wsgi from heat.common import wsgi
from paste import httpserver from paste import httpserver
@ -45,6 +46,7 @@ if __name__ == '__main__':
conf = config.HeatMetadataConfigOpts() conf = config.HeatMetadataConfigOpts()
conf() conf()
config.FLAGS = conf config.FLAGS = conf
rpc.configure(conf)
config.setup_logging(conf) config.setup_logging(conf)
app = config.load_paste_app(conf) app = config.load_paste_app(conf)

View File

@ -148,7 +148,9 @@ class HeatMetadataConfigOpts(cfg.CommonConfigOpts):
version='%%prog %s' % version.version_string(), version='%%prog %s' % version.version_string(),
default_config_files=default_config_files, default_config_files=default_config_files,
**kwargs) **kwargs)
self.register_cli_opts([cfg.IntOpt('bind_port', default=8000)]) opts = [cfg.IntOpt('bind_port', default=8000)]
opts.extend(rpc_opts)
self.register_cli_opts(opts)
class HeatEngineConfigOpts(cfg.CommonConfigOpts): class HeatEngineConfigOpts(cfg.CommonConfigOpts):

View File

@ -89,6 +89,16 @@ class HeatBase(object):
for k, v in values.iteritems(): for k, v in values.iteritems():
setattr(self, k, v) setattr(self, k, v)
def update_and_save(self, values, session=None):
if not session:
session = Session.object_session(self)
if not session:
session = get_session()
session.begin()
for k, v in values.iteritems():
setattr(self, k, v)
session.commit()
def iteritems(self): def iteritems(self):
"""Make the model object behave like a dict. """Make the model object behave like a dict.

View File

@ -15,6 +15,7 @@
import contextlib import contextlib
from copy import deepcopy
import functools import functools
import os import os
import socket import socket
@ -214,3 +215,61 @@ class EngineManager(manager.Manager):
'ResourceStatus': e.name} 'ResourceStatus': e.name}
return {'events': [parse_event(e) for e in events]} return {'events': [parse_event(e) for e in events]}
def metadata_list_stacks(self, context):
"""
Return the names of the stacks registered with Heat.
"""
stacks = db_api.stack_get_all(None)
return [s.name for s in stacks]
def metadata_list_resources(self, context, stack_name):
"""
Return the resource IDs of the given stack.
"""
stack = db_api.stack_get(None, stack_name)
if stack:
return [r.name for r in stack.resources]
else:
return None
def metadata_get_resource(self, context, stack_name, resource_id):
"""
Get the metadata for the given resource.
"""
s = db_api.stack_get(None, stack_name)
if not s:
return ['stack', None]
raw_template = db_api.raw_template_get(None, s.raw_template_id)
template = raw_template.template
if not resource_id in template.get('Resources', {}):
return ['resource', None]
metadata = template['Resources'][resource_id].get('Metadata', {})
return [None, metadata]
def metadata_update(self, context, stack_name, resource_id, metadata):
"""
Update the metadata for the given resource.
"""
s = db_api.stack_get(None, stack_name)
if not s:
return ['stack', None]
raw_template = db_api.raw_template_get(None, s.raw_template_id)
if not resource_id in raw_template.template.get('Resources', {}):
return ['resource', None]
# TODO(shadower) deep copy of the template is required here. Without it,
# we directly modify raw_template.template by assigning the new
# metadata. When we then call raw_template.update_and_save, the session
# will detect no changes and thus not update the database.
# Just updating the values and calling save didn't seem to work either.
# There's probably an idiomatic way I'm missing right now.
t = deepcopy(raw_template.template)
t['Resources'][resource_id]['Metadata'] = metadata
raw_template.update_and_save({'template': t})
return [None, metadata]

View File

@ -19,9 +19,11 @@ import json
from webob.exc import Response from webob.exc import Response
from heat.common import wsgi from heat.common import wsgi
from heat import context
from heat.metadata import db as db_api from heat.metadata import db as db_api
from heat.metadata.db import (ConflictError, StackNotFoundError, from heat.metadata.db import (ConflictError, StackNotFoundError,
ResourceNotFoundError) ResourceNotFoundError)
from heat import rpc
def json_response(http_status, data): def json_response(http_status, data):
@ -47,39 +49,46 @@ class MetadataController:
} }
def list_stacks(self, req): def list_stacks(self, req):
return db_api.list_stacks() con = context.get_admin_context()
resp = rpc.call(con, 'engine',
{'method': 'metadata_list_stacks'})
return resp
def list_resources(self, req, stack_name): def list_resources(self, req, stack_name):
try: con = context.get_admin_context()
resources = db_api.list_resources(stack_name) resources = rpc.call(con, 'engine',
except StackNotFoundError: {'method': 'metadata_list_resources',
'args': {'stack_name': stack_name}})
if resources:
return resources
else:
return json_error(404, 'The stack "%s" does not exist.' % stack_name) return json_error(404, 'The stack "%s" does not exist.' % stack_name)
return resources
def get_resource(self, req, stack_name, resource_id): def get_resource(self, req, stack_name, resource_id):
try: con = context.get_admin_context()
resource = db_api.get_resource(stack_name, resource_id) [error, metadata] = rpc.call(con, 'engine',
except StackNotFoundError: {'method': 'metadata_get_resource',
return json_error(404, 'The stack "%s" does not exist.' % stack_name) 'args': {'stack_name': stack_name,
except ResourceNotFoundError: 'resource_id': resource_id}})
return json_error(404, 'The resource "%s" does not exist.' % resource_id) if error:
return resource if error == 'stack':
return json_error(404, 'The stack "%s" does not exist.' % stack_name)
def create_stack(self, req, body, stack_name): else:
try: return json_error(404, 'The resource "%s" does not exist.' % resource_id)
stack = db_api.create_stack(stack_name, body) return metadata
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): def update_metadata(self, req, body, stack_name, resource_id):
try: con = context.get_admin_context()
db_api.update_resource_metadata(stack_name, resource_id, body) [error, metadata] = rpc.call(con, 'engine',
except StackNotFoundError: {'method': 'metadata_update',
return json_error(409, 'The stack "%s" does not exist.' % stack_name) 'args': {'stack_name': stack_name,
except ResourceNotFoundError: 'resource_id': resource_id,
# The resource doesn't exit yet, create it. 'metadata': body}})
db_api.create_resource_metadata(stack_name, resource_id, body) if error:
if error == 'stack':
return json_error(404, 'The stack "%s" does not exist.' % stack_name)
else:
return json_error(404, 'The resource "%s" does not exist.' % resource_id)
return json_response(201, { return json_response(201, {
'resource': resource_id, 'resource': resource_id,
'metadata': body, 'metadata': body,