Merge "Add extra data to Stack table for Convergence."
This commit is contained in:
commit
7f0ff06247
@ -416,14 +416,21 @@ def stack_create(context, values):
|
|||||||
def stack_update(context, stack_id, values):
|
def stack_update(context, stack_id, values):
|
||||||
stack = stack_get(context, stack_id)
|
stack = stack_get(context, stack_id)
|
||||||
|
|
||||||
if not stack:
|
if stack is None:
|
||||||
raise exception.NotFound(_('Attempt to update a stack with id: '
|
raise exception.NotFound(_('Attempt to update a stack with id: '
|
||||||
'%(id)s %(msg)s') % {
|
'%(id)s %(msg)s') % {
|
||||||
'id': stack_id,
|
'id': stack_id,
|
||||||
'msg': 'that does not exist'})
|
'msg': 'that does not exist'})
|
||||||
|
|
||||||
stack.update(values)
|
session = _session(context)
|
||||||
stack.save(_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):
|
def stack_delete(context, stack_id):
|
||||||
|
140
heat/db/sqlalchemy/migrate_repo/versions/055_stack_convg_data.py
Normal file
140
heat/db/sqlalchemy/migrate_repo/versions/055_stack_convg_data.py
Normal 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)
|
@ -141,7 +141,14 @@ class Stack(BASE, HeatBase, SoftDelete, StateAware):
|
|||||||
sqlalchemy.Integer,
|
sqlalchemy.Integer,
|
||||||
sqlalchemy.ForeignKey('raw_template.id'),
|
sqlalchemy.ForeignKey('raw_template.id'),
|
||||||
nullable=False)
|
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))
|
username = sqlalchemy.Column(sqlalchemy.String(256))
|
||||||
tenant = sqlalchemy.Column(sqlalchemy.String(256))
|
tenant = sqlalchemy.Column(sqlalchemy.String(256))
|
||||||
parameters = sqlalchemy.Column('parameters', types.Json)
|
parameters = sqlalchemy.Column('parameters', types.Json)
|
||||||
@ -158,6 +165,9 @@ class Stack(BASE, HeatBase, SoftDelete, StateAware):
|
|||||||
convergence = sqlalchemy.Column('convergence', sqlalchemy.Boolean)
|
convergence = sqlalchemy.Column('convergence', sqlalchemy.Boolean)
|
||||||
tags = relationship(StackTag, cascade="all,delete",
|
tags = relationship(StackTag, cascade="all,delete",
|
||||||
backref=backref('stack'))
|
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
|
# 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
|
# time the create/update call was issued, not the time the DB entry is
|
||||||
|
@ -79,7 +79,8 @@ class Stack(collections.Mapping):
|
|||||||
created_time=None, updated_time=None,
|
created_time=None, updated_time=None,
|
||||||
user_creds_id=None, tenant_id=None,
|
user_creds_id=None, tenant_id=None,
|
||||||
use_stored_context=False, username=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)
|
Initialise from a context, name, Template object and (optionally)
|
||||||
Environment object. The database ID may also be initialised, if the
|
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.nested_depth = nested_depth
|
||||||
self.strict_validate = strict_validate
|
self.strict_validate = strict_validate
|
||||||
self.convergence = convergence
|
self.convergence = convergence
|
||||||
|
self.current_traversal = current_traversal
|
||||||
|
|
||||||
if use_stored_context:
|
if use_stored_context:
|
||||||
self.context = self.stored_context()
|
self.context = self.stored_context()
|
||||||
@ -322,13 +324,14 @@ class Stack(collections.Mapping):
|
|||||||
updated_time=stack.updated_at,
|
updated_time=stack.updated_at,
|
||||||
user_creds_id=stack.user_creds_id, tenant_id=stack.tenant,
|
user_creds_id=stack.user_creds_id, tenant_id=stack.tenant,
|
||||||
use_stored_context=use_stored_context,
|
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)
|
@profiler.trace('Stack.store', hide_args=False)
|
||||||
def store(self, backup=False):
|
def store(self, backup=False):
|
||||||
'''
|
'''
|
||||||
Store the stack in the database and return its ID
|
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 = {
|
s = {
|
||||||
'name': self._backup_name() if backup else self.name,
|
'name': self._backup_name() if backup else self.name,
|
||||||
@ -347,7 +350,8 @@ class Stack(collections.Mapping):
|
|||||||
'user_creds_id': self.user_creds_id,
|
'user_creds_id': self.user_creds_id,
|
||||||
'backup': backup,
|
'backup': backup,
|
||||||
'nested_depth': self.nested_depth,
|
'nested_depth': self.nested_depth,
|
||||||
'convergence': self.convergence
|
'convergence': self.convergence,
|
||||||
|
'current_traversal': self.current_traversal,
|
||||||
}
|
}
|
||||||
if self.id:
|
if self.id:
|
||||||
db_api.stack_update(self.context, self.id, s)
|
db_api.stack_update(self.context, self.id, s)
|
||||||
|
@ -403,6 +403,11 @@ class HeatMigrationsCheckers(test_migrations.WalkVersionsMixin,
|
|||||||
def _check_052(self, engine, data):
|
def _check_052(self, engine, data):
|
||||||
self.assertColumnExists(engine, 'stack', 'convergence')
|
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,
|
class TestHeatMigrationsMySQL(HeatMigrationsCheckers,
|
||||||
test_base.MySQLOpportunisticTestCase):
|
test_base.MySQLOpportunisticTestCase):
|
||||||
|
@ -1153,7 +1153,8 @@ class StackTest(common.HeatTestCase):
|
|||||||
tenant_id='test_tenant_id',
|
tenant_id='test_tenant_id',
|
||||||
use_stored_context=False,
|
use_stored_context=False,
|
||||||
username=mox.IgnoreArg(),
|
username=mox.IgnoreArg(),
|
||||||
convergence=False)
|
convergence=False,
|
||||||
|
current_traversal=None)
|
||||||
|
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
parser.Stack.load(self.ctx, stack_id=self.stack.id,
|
parser.Stack.load(self.ctx, stack_id=self.stack.id,
|
||||||
|
@ -1366,6 +1366,7 @@ class DBAPIStackTest(common.HeatTestCase):
|
|||||||
'status': 'failed',
|
'status': 'failed',
|
||||||
'status_reason': "update_failed",
|
'status_reason': "update_failed",
|
||||||
'timeout': '90',
|
'timeout': '90',
|
||||||
|
'current_traversal': 'dummy-uuid',
|
||||||
}
|
}
|
||||||
db_api.stack_update(self.ctx, stack.id, values)
|
db_api.stack_update(self.ctx, stack.id, values)
|
||||||
stack = db_api.stack_get(self.ctx, stack.id)
|
stack = db_api.stack_get(self.ctx, stack.id)
|
||||||
@ -1373,7 +1374,8 @@ class DBAPIStackTest(common.HeatTestCase):
|
|||||||
self.assertEqual('update', stack.action)
|
self.assertEqual('update', stack.action)
|
||||||
self.assertEqual('failed', stack.status)
|
self.assertEqual('failed', stack.status)
|
||||||
self.assertEqual('update_failed', stack.status_reason)
|
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,
|
self.assertRaises(exception.NotFound, db_api.stack_update, self.ctx,
|
||||||
UUID2, values)
|
UUID2, values)
|
||||||
|
Loading…
Reference in New Issue
Block a user