From: Chris Alfonso Date: Thu, 5 Apr 2012 15:05:11 +0000 (-0400) Subject: Integrating all stack, template, and event calls with database X-Git-Tag: 2014.1~2058 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=77a004167ac34b7d9073d7606392a15b188733b3;p=openstack-build%2Fheat-build.git Integrating all stack, template, and event calls with database --- diff --git a/bin/heat b/bin/heat index 4bb758c1..9bc81710 100755 --- a/bin/heat +++ b/bin/heat @@ -19,7 +19,6 @@ import base64 import libxml2 from urlparse import urlparse - # If ../heat/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), diff --git a/bin/heat-db-setup-fedora b/bin/heat-db-setup-fedora index abbba56a..764c077c 100755 --- a/bin/heat-db-setup-fedora +++ b/bin/heat-db-setup-fedora @@ -216,11 +216,12 @@ if [ "${MYSQL_HEAT_PW}" != "${MYSQL_HEAT_PW_DEFAULT}" ] ; then fi #create the schema using sqlalchemy-migrate -if test $1 == "rpm"; then - pushd /usr/lib/python2.7/site-packages/heat/db/sqlalchemy -else +#if [ $1='rpm' ]; +#then +# pushd /usr/lib/python2.7/site-packages/heat/db/sqlalchemy +#else pushd /usr/lib/python2.7/site-packages/heat-0.0.1-py2.7.egg/heat/db/sqlalchemy/ -fi +#fi python migrate_repo/manage.py version_control mysql://heat:heat@localhost/heat migrate_repo python manage.py upgrade diff --git a/heat/api/v1/stacks.py b/heat/api/v1/stacks.py index 0b6525d6..00bd0b19 100644 --- a/heat/api/v1/stacks.py +++ b/heat/api/v1/stacks.py @@ -23,17 +23,14 @@ import os import socket import sys import urlparse - import webob from webob.exc import (HTTPNotFound, HTTPConflict, HTTPBadRequest) - from heat.common import wsgi from heat.common import config from heat import rpc from heat import context - logger = logging.getLogger('heat.api.v1.stacks') @@ -52,7 +49,9 @@ class StackController(object): Returns the following information for all stacks: """ con = context.get_admin_context() - stack_list = rpc.call(con, 'engine', {'method': 'list_stacks'}) + stack_list = rpc.call(con, 'engine', + {'method': 'list_stacks', + 'args': {'params': dict(req.params)}}) res = {'ListStacksResponse': {'ListStacksResult': {'StackSummaries': [] } } } summaries = res['ListStacksResponse']['ListStacksResult']['StackSummaries'] @@ -69,7 +68,8 @@ class StackController(object): stack_list = rpc.call(con, 'engine', {'method': 'show_stack', - 'args': {'stack_name': req.params['StackName']}}) + 'args': {'stack_name': req.params['StackName'], + 'params': dict(req.params)}}) res = {'DescribeStacksResult': {'Stacks': [] } } stacks = res['DescribeStacksResult']['Stacks'] for s in stack_list['stacks']: @@ -160,7 +160,8 @@ class StackController(object): res = rpc.call(con, 'engine', {'method': 'delete_stack', - 'args': {'stack_name': req.params['StackName']}}) + 'args': {'stack_name': req.params['StackName'], + 'params': dict(req.params)}}) if res == None: return {'DeleteStackResult': ''} diff --git a/heat/client.py b/heat/client.py index ed73b7c3..f305292f 100644 --- a/heat/client.py +++ b/heat/client.py @@ -20,12 +20,10 @@ Client classes for callers of a heat system import json import logging import os - from heat.common import client as base_client from heat.common import exception from heat.cloudformations import * - logger = logging.getLogger(__name__) diff --git a/heat/common/wsgi.py b/heat/common/wsgi.py index a2f5b7f5..45989e7e 100644 --- a/heat/common/wsgi.py +++ b/heat/common/wsgi.py @@ -43,7 +43,6 @@ from heat.common import exception from heat.openstack.common import cfg from heat.openstack.common import utils - bind_opts = [ cfg.StrOpt('bind_host', default='0.0.0.0'), cfg.IntOpt('bind_port'), @@ -489,7 +488,6 @@ class Resource(object): method = getattr(obj, action) except AttributeError: method = getattr(obj, 'default') - return method(*args, **kwargs) def get_action_args(self, request_environment): diff --git a/heat/db/anydbm/api.py b/heat/db/anydbm/api.py index bc88d705..93b5173e 100644 --- a/heat/db/anydbm/api.py +++ b/heat/db/anydbm/api.py @@ -97,4 +97,3 @@ def event_create(context, event): d[event['event_id']] = json.dumps(event) d.close() - diff --git a/heat/db/api.py b/heat/db/api.py index c3aa5006..e149cfcb 100644 --- a/heat/db/api.py +++ b/heat/db/api.py @@ -27,7 +27,6 @@ supported backend. ''' from heat.openstack.common import utils - def configure(conf): global IMPL global SQL_CONNECTION @@ -67,7 +66,7 @@ def resource_create(context, values): def stack_get(context, stack_id): - return IMPL.resource_get(context, resource_id) + return IMPL.stack_get(context, stack_id) def stack_get_all(context): return IMPL.stack_get_all(context) @@ -75,6 +74,8 @@ def stack_get_all(context): def stack_create(context, values): return IMPL.stack_create(context, values) +def stack_delete(context, stack_name): + return IMPL.stack_delete(context, stack_name) def event_get(context, event_id): return IMPL.event_get(context, event_id) diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py index ad8c2d4a..5783cd1e 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/sqlalchemy/api.py @@ -20,33 +20,13 @@ from heat.db.sqlalchemy import models from heat.db.sqlalchemy.session import get_session def model_query(context, *args, **kwargs): - """Query helper that accounts for context's `read_deleted` field. - - :param context: context to query under + """ :param session: if present, the session to use - :param read_deleted: if present, overrides context's read_deleted field. - :param project_only: if present and context is user-type, then restrict - query to match the context's project_id. """ session = kwargs.get('session') or get_session() - read_deleted = kwargs.get('read_deleted') or context.read_deleted - project_only = kwargs.get('project_only') query = session.query(*args) - if read_deleted == 'no': - query = query.filter_by(deleted=False) - elif read_deleted == 'yes': - pass # omit the filter to include deleted and active - elif read_deleted == 'only': - query = query.filter_by(deleted=True) - else: - raise Exception( - _("Unrecognized read_deleted value '%s'") % read_deleted) - - if project_only and is_user_context(context): - query = query.filter_by(project_id=context.project_id) - return query def raw_template_get(context, template_id): @@ -120,19 +100,11 @@ def resource_create(context, values): def stack_get(context, stack_id): result = model_query(context, models.Stack).\ - filter_by(id=stack_id).first() - - if not result: - raise Exception("stack with id %s not found" % stack_id) - + filter_by(name=stack_id).first() return result def stack_get_all(context): results = model_query(context, models.Stack).all() - - if not results: - raise Exception('no stacks were found') - return results def stack_create(context, values): @@ -140,6 +112,15 @@ def stack_create(context, values): stack_ref.update(values) stack_ref.save() return stack_ref + +def stack_delete(context, stack_name): + s = stack_get(context, stack_name) + if not s: + raise Exception('Attempt to delete a stack with id: %s that does not exist' % stack_name) + + for e in s.events: + e.delete() + s.delete() def event_get(context, event_id): result = model_query(context, models.Event).\ diff --git a/heat/db/sqlalchemy/migrate_repo/versions/001_norwhal.py b/heat/db/sqlalchemy/migrate_repo/versions/001_norwhal.py index 3311d86d..5e7590af 100644 --- a/heat/db/sqlalchemy/migrate_repo/versions/001_norwhal.py +++ b/heat/db/sqlalchemy/migrate_repo/versions/001_norwhal.py @@ -21,6 +21,7 @@ def upgrade(migrate_engine): Column('name', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False)), + Column('raw_template_id', Integer, ForeignKey("raw_template.id"), nullable=False), ) event = Table( @@ -47,17 +48,17 @@ def upgrade(migrate_engine): assert_unicode=None, unicode_error=None, _warn_on_bytestring=False)), + Column('parsed_template_id', Integer, ForeignKey("parsed_template.id"), nullable=False), ) parsedtemplate = Table( 'parsed_template', meta, Column('id', Integer, primary_key=True), - Column('resource_id', Integer, ForeignKey("resource.id"),\ - nullable=False), + Column('raw_template_id', Integer, ForeignKey("raw_template.id"), nullable=False), Column('template', Text()), ) - tables = [rawtemplate, stack, event, resource, parsedtemplate] + tables = [rawtemplate, stack, event, parsedtemplate, resource] for table in tables: try: table.create() @@ -73,6 +74,7 @@ def downgrade(migrate_engine): event = Table('event', meta, autoload=True) resource = Table('resource', meta, autoload=True) parsedtemplate = Table('parsed_template', meta, autoload=True) + stack = Table('stack', meta, autoload=True) - for table in (rawtemplate, event, stack, parsedtemplate, resource): + for table in (event, stack, resource, parsedtemplate, rawtemplate): table.drop() diff --git a/heat/db/sqlalchemy/models.py b/heat/db/sqlalchemy/models.py index 2ab13561..efc5ed30 100644 --- a/heat/db/sqlalchemy/models.py +++ b/heat/db/sqlalchemy/models.py @@ -16,15 +16,26 @@ SQLAlchemy models for heat data. """ from sqlalchemy import * +from sqlalchemy.orm import relationship, backref from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.schema import ForeignKeyConstraint - +from sqlalchemy import types as types +from json import dumps, loads from nova import utils from heat.db.sqlalchemy.session import get_session BASE = declarative_base() +class Json(types.TypeDecorator, types.MutableType): + impl=types.Text + + def process_bind_param(self, value, dialect): + return dumps(value) + + def process_result_value(self, value, dialect): + return loads(value) + class HeatBase(object): """Base class for Heat Models.""" __table_args__ = {'mysql_engine': 'InnoDB'} @@ -88,14 +99,18 @@ class RawTemplate(BASE, HeatBase): __tablename__ = 'raw_template' id = Column(Integer, primary_key=True) - template = Text() + template = Column(Json) + parsed_template = relationship("ParsedTemplate",\ + uselist=False, backref="raw_template", cascade="all, delete", passive_deletes=True) class ParsedTemplate(BASE, HeatBase): """Represents a parsed template.""" __tablename__ = 'parsed_template' id = Column(Integer, primary_key=True) - resource_id = Column('resource_id', Integer) + template = Column(Json) + raw_template_id = Column(Integer, ForeignKey('raw_template.id'),\ + nullable=False) class Stack(BASE, HeatBase): """Represents an generated by the heat engine.""" @@ -103,7 +118,11 @@ class Stack(BASE, HeatBase): __tablename__ = 'stack' id = Column(Integer, primary_key=True) - name = Column(String) + name = Column(String, unique=True) + raw_template_id = Column(Integer, ForeignKey('raw_template.id'),\ + nullable=False) + raw_template = relationship(RawTemplate, + backref=backref('stack'), cascade="all, delete", passive_deletes=True) class Event(BASE, HeatBase): """Represents an event generated by the heat engine.""" @@ -111,7 +130,11 @@ class Event(BASE, HeatBase): __tablename__ = 'event' id = Column(Integer, primary_key=True) - stack_id = Column(Integer) + stack_id = Column(Integer, ForeignKey('stack.id'),\ + nullable=False) + stack = relationship(Stack, + backref=backref('events'), cascade="all, delete", passive_deletes=True) + name = Column(String) class Resource(BASE, HeatBase): @@ -122,3 +145,8 @@ class Resource(BASE, HeatBase): id = Column(Integer, primary_key=True) state = Column(String) state_description = Column('state_description', String) + parsed_template_id = Column(Integer, ForeignKey('parsed_template.id'),\ + nullable=False) + parsed_template = relationship(ParsedTemplate, + backref=backref('resources')) + diff --git a/heat/engine/manager.py b/heat/engine/manager.py index bc2565c8..60a0790d 100644 --- a/heat/engine/manager.py +++ b/heat/engine/manager.py @@ -23,21 +23,12 @@ import tempfile import time import traceback import logging - -from eventlet import greenthread - -import heat.context -from heat.common import exception +import webob from heat import manager -from heat.openstack.common import cfg -from heat import rpc from heat.engine import parser from heat.db import api as db_api - logger = logging.getLogger('heat.engine.manager') -stack_db = {} - class EngineManager(manager.Manager): """Manages the running instances from creation to destruction.""" @@ -45,57 +36,69 @@ class EngineManager(manager.Manager): """Load configuration options and connect to the hypervisor.""" pass - def list_stacks(self, context): + def list_stacks(self, context, params): logger.info('context is %s' % context) res = {'stacks': [] } - for s in stack_db: + stacks = db_api.stack_get_all(None) + for s in stacks: + ps = parser.Stack(s.name, s.raw_template.template, params) mem = {} - mem['stack_id'] = s - mem['stack_name'] = s - mem['created_at'] = 'now' + mem['stack_id'] = s.id + mem['stack_name'] = s.name + mem['created_at'] = str(s.created_at) try: - mem['template_description'] = stack_db[s].t['Description'] - mem['stack_status'] = stack_db[s].t['StackStatus'] + mem['template_description'] = s.template.description + mem['stack_status'] = ps.t['StackStatus'] except: mem['template_description'] = 'No description' mem['stack_status'] = 'unknown' res['stacks'].append(mem) return res - - def show_stack(self, context, stack_name): - + + def show_stack(self, context, stack_name, params): res = {'stacks': [] } - if stack_db.has_key(stack_name): + s = db_api.stack_get(None, id) + if s: + ps = parser.Stack(s.name, s.raw_template.template, params) mem = {} - mem['stack_id'] = stack_name - mem['stack_name'] = stack_name - mem['creation_at'] = 'TODO' - mem['updated_at'] = 'TODO' + mem['stack_id'] = s.id + mem['stack_name'] = s.name + mem['creation_at'] = s.created_at + mem['updated_at'] = s.updated_at mem['NotificationARNs'] = 'TODO' - mem['Outputs'] = stack_db[stack_name].get_outputs() - mem['Parameters'] = stack_db[stack_name].t['Parameters'] + mem['Outputs'] = ps.get_outputs() + mem['Parameters'] = ps.t['Parameters'] mem['StackStatusReason'] = 'TODO' mem['TimeoutInMinutes'] = 'TODO' try: - mem['TemplateDescription'] = stack_db[stack_name].t['Description'] - mem['StackStatus'] = stack_db[stack_name].t['StackStatus'] + mem['TemplateDescription'] = ps.t['Description'] + mem['StackStatus'] = ps.t['StackStatus'] except: mem['TemplateDescription'] = 'No description' mem['StackStatus'] = 'unknown' res['stacks'].append(mem) return res - + def create_stack(self, context, stack_name, template, params): - if stack_db.has_key(stack_name): - return {'Error': 'Stack already exists with that name.'} - logger.info('template is %s' % template) - stack_db[stack_name] = parser.Stack(stack_name, template, params) - stack_db[stack_name].create() + if db_api.stack_get(None, stack_name): + return {'Error': 'Stack already exists with that name.'} - return {'stack': {'id': stack_name}} + stack = parser.Stack(stack_name, template, params) + rt = {} + rt['template'] = template + new_rt = db_api.raw_template_create(None, rt) + s = {} + s['name'] = stack_name + s['raw_template_id'] = new_rt.id + new_s = db_api.stack_create(None, s) + stack.id = new_s.id + stack.start() + + return {'stack': {'id': new_s.id, 'name': new_s.name,\ + 'created_at': new_s.created_at}} def validate_template(self, req, body=None): @@ -109,13 +112,16 @@ class EngineManager(manager.Manager): return res - def delete_stack(self, context, stack_name): - if not stack_db.has_key(stack_name): - return {'Error': 'No stack by that name'} + def delete_stack(self, context, stack_name, params): + s = db_api.stack_get(None, stack_name) + if not s: + return {'Error': 'No stack by that name'} logger.info('deleting stack %s' % stack_name) - stack_db[stack_name].delete() - del stack_db[stack_name] + + ps = parser.Stack(s.name, s.raw_template.template, params) + ps.stop() + db_api.stack_delete(None, stack_name) return None def list_events(self, context, stack_name): diff --git a/heat/engine/resources.py b/heat/engine/resources.py index 3f89c713..6cecf88a 100644 --- a/heat/engine/resources.py +++ b/heat/engine/resources.py @@ -23,6 +23,7 @@ from novaclient.v1_1 import client from heat.common import exception from heat.db import api as db_api +from heat.common.config import HeatEngineConfigOpts logger = logging.getLogger('heat.engine.resources') @@ -88,11 +89,10 @@ class Resource(object): ev['stack_id'] = self.stack.id ev['stack_name'] = self.stack.name ev['resource_status'] = new_state + ev['name'] = new_state ev['resource_status_reason'] = reason ev['resource_type'] = self.t['Type'] ev['resource_properties'] = self.t['Properties'] - new_stack = db_api.stack_create(None, ev) - ev['stack_id'] = new_stack.id db_api.event_create(None, ev) self.state = new_state @@ -412,21 +412,10 @@ class Instance(Resource): self.state_set(self.CREATE_FAILED) def delete(self): - - if self.state == self.DELETE_IN_PROGRESS or self.state == self.DELETE_COMPLETE: - return - self.state_set(self.DELETE_IN_PROGRESS) - Resource.delete(self) - - if self.instance_id == None: - self.state_set(self.DELETE_COMPLETE) - return - + Resource.stop(self) server = self.nova().servers.get(self.instance_id) server.delete() self.instance_id = None - self.state_set(self.DELETE_COMPLETE) - def insert_package_and_services(self, r, new_script):