Merge "Add extra data to Stack table for Convergence."

This commit is contained in:
Jenkins 2015-03-06 02:58:05 +00:00 committed by Gerrit Code Review
commit 7f0ff06247
7 changed files with 179 additions and 10 deletions

View File

@ -416,14 +416,21 @@ def stack_create(context, values):
def stack_update(context, stack_id, values):
stack = stack_get(context, stack_id)
if not stack:
if stack is None:
raise exception.NotFound(_('Attempt to update a stack with id: '
'%(id)s %(msg)s') % {
'id': stack_id,
'msg': 'that does not exist'})
stack.update(values)
stack.save(_session(context))
session = _session(context)
rows_updated = (session.query(models.Stack)
.filter(models.Stack.id == stack.id)
.filter(models.Stack.current_traversal
== stack.current_traversal)
.update(values, synchronize_session=False))
session.expire_all()
return (rows_updated is not None and rows_updated > 0)
def stack_delete(context, stack_id):

View File

@ -0,0 +1,140 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sqlalchemy
from heat.db.sqlalchemy import types as heat_db_types
from heat.db.sqlalchemy import utils as migrate_utils
from migrate import ForeignKeyConstraint
def upgrade(migrate_engine):
if migrate_engine.name == 'sqlite':
_upgrade_sqlite(migrate_engine)
return
meta = sqlalchemy.MetaData(bind=migrate_engine)
stack = sqlalchemy.Table('stack', meta, autoload=True)
prev_raw_template_id = sqlalchemy.Column('prev_raw_template_id',
sqlalchemy.Integer)
current_traversal = sqlalchemy.Column('current_traversal',
sqlalchemy.String(36))
current_deps = sqlalchemy.Column('current_deps', heat_db_types.Json)
prev_raw_template_id.create(stack)
current_traversal.create(stack)
current_deps.create(stack)
raw_template = sqlalchemy.Table('raw_template', meta, autoload=True)
fkey = ForeignKeyConstraint(columns=[stack.c.prev_raw_template_id],
refcolumns=[raw_template.c.id],
name='prev_raw_template_ref')
fkey.create()
def _upgrade_sqlite(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
stack = sqlalchemy.Table('stack', meta, autoload=True)
table_name = stack.name
newcols = [
sqlalchemy.Column('prev_raw_template_id', sqlalchemy.Integer,
sqlalchemy.ForeignKey('raw_template.id',
name='prev_raw_template_ref')),
sqlalchemy.Column('current_traversal', sqlalchemy.String(36)),
sqlalchemy.Column('current_deps', heat_db_types.Json),
]
new_stack = migrate_utils.clone_table(table_name + '__tmp__', stack,
meta, newcols=newcols)
# migrate stacks into new table
stacks = list(stack.select().order_by(
sqlalchemy.sql.expression.asc(stack.c.created_at))
.execute())
colnames = [c.name for c in stack.columns]
for s in stacks:
values = dict(zip(colnames,
map(lambda colname: getattr(s, colname),
colnames)))
migrate_engine.execute(new_stack.insert(values))
# Drop old tables and rename new ones
stack.drop()
new_stack.rename('stack')
# add the indexes back
_add_indexes(migrate_engine, new_stack)
def downgrade(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
stack = sqlalchemy.Table('stack', meta, autoload=True)
if migrate_engine.name == 'sqlite':
_downgrade_sqlite(migrate_engine)
else:
raw_template = sqlalchemy.Table('raw_template', meta, autoload=True)
fkey = ForeignKeyConstraint(columns=[stack.c.prev_raw_template_id],
refcolumns=[raw_template.c.id],
name='prev_raw_template_ref')
fkey.drop()
stack.c.prev_raw_template_id.drop()
stack.c.current_traversal.drop()
stack.c.current_deps.drop()
def _downgrade_sqlite(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
stack = sqlalchemy.Table('stack', meta, autoload=True)
table_name = stack.name
# ignore CheckConstraints and FK Constraint on prev_raw_template_id.
ignorecols = [
stack.c.prev_raw_template_id.name,
stack.c.current_traversal.name,
stack.c.current_deps.name,
]
ignorecons = [
'prev_raw_template_ref',
]
new_stack = migrate_utils.clone_table(table_name + '__tmp__', stack, meta,
ignorecols=ignorecols,
ignorecons=ignorecons)
migrate_data = """
INSERT INTO %s
SELECT id, created_at, updated_at, name, raw_template_id,
user_creds_id, username, owner_id, status, status_reason,
parameters, timeout, tenant, disable_rollback, action,
deleted_at, stack_user_project_id, backup, nested_depth,
convergence
FROM stack;""" % new_stack.name
migrate_engine.execute(migrate_data)
stack.drop()
new_stack.rename(table_name)
# add the indexes back to new table
_add_indexes(migrate_engine, new_stack)
def _add_indexes(migrate_engine, stack):
name_index = sqlalchemy.Index('ix_stack_name',
stack.c.name,
mysql_length=255)
tenant_index = sqlalchemy.Index('ix_stack_tenant',
stack.c.tenant,
mysql_length=255)
name_index.create(migrate_engine)
tenant_index.create(migrate_engine)

View File

@ -141,7 +141,14 @@ class Stack(BASE, HeatBase, SoftDelete, StateAware):
sqlalchemy.Integer,
sqlalchemy.ForeignKey('raw_template.id'),
nullable=False)
raw_template = relationship(RawTemplate, backref=backref('stack'))
raw_template = relationship(RawTemplate, backref=backref('stack'),
foreign_keys=[raw_template_id])
prev_raw_template_id = sqlalchemy.Column(
'prev_raw_template_id',
sqlalchemy.Integer,
sqlalchemy.ForeignKey('raw_template.id'))
prev_raw_template = relationship(RawTemplate,
foreign_keys=[prev_raw_template_id])
username = sqlalchemy.Column(sqlalchemy.String(256))
tenant = sqlalchemy.Column(sqlalchemy.String(256))
parameters = sqlalchemy.Column('parameters', types.Json)
@ -158,6 +165,9 @@ class Stack(BASE, HeatBase, SoftDelete, StateAware):
convergence = sqlalchemy.Column('convergence', sqlalchemy.Boolean)
tags = relationship(StackTag, cascade="all,delete",
backref=backref('stack'))
current_traversal = sqlalchemy.Column('current_traversal',
sqlalchemy.String(36))
current_deps = sqlalchemy.Column('current_deps', types.Json)
# Override timestamp column to store the correct value: it should be the
# time the create/update call was issued, not the time the DB entry is

View File

@ -79,7 +79,8 @@ class Stack(collections.Mapping):
created_time=None, updated_time=None,
user_creds_id=None, tenant_id=None,
use_stored_context=False, username=None,
nested_depth=0, strict_validate=True, convergence=False):
nested_depth=0, strict_validate=True, convergence=False,
current_traversal=None):
'''
Initialise from a context, name, Template object and (optionally)
Environment object. The database ID may also be initialised, if the
@ -121,6 +122,7 @@ class Stack(collections.Mapping):
self.nested_depth = nested_depth
self.strict_validate = strict_validate
self.convergence = convergence
self.current_traversal = current_traversal
if use_stored_context:
self.context = self.stored_context()
@ -322,13 +324,14 @@ class Stack(collections.Mapping):
updated_time=stack.updated_at,
user_creds_id=stack.user_creds_id, tenant_id=stack.tenant,
use_stored_context=use_stored_context,
username=stack.username, convergence=stack.convergence)
username=stack.username, convergence=stack.convergence,
current_traversal=stack.current_traversal)
@profiler.trace('Stack.store', hide_args=False)
def store(self, backup=False):
'''
Store the stack in the database and return its ID
If self.id is set, we update the existing stack
If self.id is set, we update the existing stack.
'''
s = {
'name': self._backup_name() if backup else self.name,
@ -347,7 +350,8 @@ class Stack(collections.Mapping):
'user_creds_id': self.user_creds_id,
'backup': backup,
'nested_depth': self.nested_depth,
'convergence': self.convergence
'convergence': self.convergence,
'current_traversal': self.current_traversal,
}
if self.id:
db_api.stack_update(self.context, self.id, s)

View File

@ -403,6 +403,11 @@ class HeatMigrationsCheckers(test_migrations.WalkVersionsMixin,
def _check_052(self, engine, data):
self.assertColumnExists(engine, 'stack', 'convergence')
def _check_055(self, engine, data):
self.assertColumnExists(engine, 'stack', 'prev_raw_template_id')
self.assertColumnExists(engine, 'stack', 'current_traversal')
self.assertColumnExists(engine, 'stack', 'current_deps')
class TestHeatMigrationsMySQL(HeatMigrationsCheckers,
test_base.MySQLOpportunisticTestCase):

View File

@ -1153,7 +1153,8 @@ class StackTest(common.HeatTestCase):
tenant_id='test_tenant_id',
use_stored_context=False,
username=mox.IgnoreArg(),
convergence=False)
convergence=False,
current_traversal=None)
self.m.ReplayAll()
parser.Stack.load(self.ctx, stack_id=self.stack.id,

View File

@ -1366,6 +1366,7 @@ class DBAPIStackTest(common.HeatTestCase):
'status': 'failed',
'status_reason': "update_failed",
'timeout': '90',
'current_traversal': 'dummy-uuid',
}
db_api.stack_update(self.ctx, stack.id, values)
stack = db_api.stack_get(self.ctx, stack.id)
@ -1373,7 +1374,8 @@ class DBAPIStackTest(common.HeatTestCase):
self.assertEqual('update', stack.action)
self.assertEqual('failed', stack.status)
self.assertEqual('update_failed', stack.status_reason)
self.assertEqual('90', stack.timeout)
self.assertEqual(90, stack.timeout)
self.assertEqual('dummy-uuid', stack.current_traversal)
self.assertRaises(exception.NotFound, db_api.stack_update, self.ctx,
UUID2, values)