]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Integrating all stack, template, and event calls with database
authorChris Alfonso <calfonso@redhat.com>
Thu, 5 Apr 2012 15:05:11 +0000 (11:05 -0400)
committerChris Alfonso <calfonso@redhat.com>
Wed, 11 Apr 2012 13:02:18 +0000 (09:02 -0400)
12 files changed:
bin/heat
bin/heat-db-setup-fedora
heat/api/v1/stacks.py
heat/client.py
heat/common/wsgi.py
heat/db/anydbm/api.py
heat/db/api.py
heat/db/sqlalchemy/api.py
heat/db/sqlalchemy/migrate_repo/versions/001_norwhal.py
heat/db/sqlalchemy/models.py
heat/engine/manager.py
heat/engine/resources.py

index 4bb758c1d4962efd158bd463d0c7f0887335edfe..9bc81710b5f0b734efc832277f0db75c9fa99e24 100755 (executable)
--- 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]),
index abbba56a97096e6866fa830c9808de4ef2c92e10..764c077c83eea4d5c03175d3165801f01c2d624a 100755 (executable)
@@ -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
index 0b6525d63a93c3fbd62f87ed02727d9c3751b964..00bd0b192ff589532b867b4a388d4c5ba31ebe4b 100644 (file)
@@ -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': ''}
index ed73b7c382c7a1789d6c6ccc409629d5d99ba80a..f305292fe8996159c7835d81273f17b94f0ae9d4 100644 (file)
@@ -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__)
 
 
index a2f5b7f51ddee7671cd51912d862986bda731309..45989e7e0c08047888fd90bef4e9dc51ccf78fa9 100644 (file)
@@ -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):
index bc88d7057c7369e6f7acee77d6c3aeadc20abb5a..93b5173ebf669922c247c0b59b8913e445743f0d 100644 (file)
@@ -97,4 +97,3 @@ def event_create(context, event):
     d[event['event_id']] = json.dumps(event)
 
     d.close()
-
index c3aa5006b3f5e19ac41344e2d8bbfdffddfd3520..e149cfcb4fc58a5af794dde2ad6587022e82720a 100644 (file)
@@ -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)
index ad8c2d4aa38af47d63b4aad9c05fbd1185f1cd22..5783cd1e3acfb3fe06524ce544d048b69c0aa6a7 100644 (file)
@@ -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).\
index 3311d86d8b73334690b20d6480250a76d9c9119b..5e7590af70832bf1cd63ad65d96b7c2c5a584dc8 100644 (file)
@@ -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()
index 2ab135612f97d34d3bf4e7f8c2825c1c7a789847..efc5ed301205a96d83b3fab53219f22b55ccf55e 100644 (file)
@@ -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'))
+
index bc2565c8b745a4a6b1493c3fcaacc9cfac96e2f4..60a0790daea01ff4d9246a4356c83d70cfb297ad 100644 (file)
@@ -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):
index 3f89c7139083a879e1193d205c601de7a95f0f31..6cecf88ad2de2580ef1b4e6a00ac26e608cb7582 100644 (file)
@@ -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):