Add a config option to enable Convergence

This patch adds column 'convergence' to stack table
and configuration option 'convergence_engine'. If
'convergence_engine' equals True, new stacks are created,
updated and backuped with convergence column equals
True and old stacks are updated and backuped with
convergence column equals False. Otherwise convergence
column equals False.

blueprint convergence-config-option

Change-Id: I34d6fa3a0e387140914f5060c06be890640a970f
This commit is contained in:
Peter Razumovsky 2015-02-09 13:23:45 +03:00
parent fb32508af5
commit 6ec2759324
7 changed files with 130 additions and 31 deletions

View File

@ -151,6 +151,11 @@ engine_opts = [
cfg.BoolOpt('enable_stack_adopt',
default=False,
help=_('Enable the preview Stack Adopt feature.')),
cfg.BoolOpt('convergence_engine',
default=False,
help=_('Enables engine with convergence architecture. All '
'stacks with this option will be created using '
'convergence engine .')),
cfg.StrOpt('onready',
help=_('Deprecated.'))]

View File

@ -0,0 +1,63 @@
#
# 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
def upgrade(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
stack = sqlalchemy.Table('stack', meta, autoload=True)
convergence = sqlalchemy.Column('convergence', sqlalchemy.Boolean,
default=False)
convergence.create(stack)
def downgrade(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
stack = sqlalchemy.Table('stack', meta, autoload=True)
if migrate_engine.name == 'sqlite':
_downgrade_052_sqlite(migrate_engine, meta, stack)
else:
stack.c.convergence.drop()
def _downgrade_052_sqlite(migrate_engine, metadata, table):
table_name = table.name
constraints = [
c.copy() for c in table.constraints
if not isinstance(c, sqlalchemy.CheckConstraint)
]
columns = [c.copy() for c in table.columns if c.name != "convergence"]
new_table = sqlalchemy.Table(table_name + "__tmp__", metadata,
*(columns + constraints))
new_table.create()
migrate_data = """
INSERT INTO %s__tmp__
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,
tags
FROM stack;""" % table_name
migrate_engine.execute(migrate_data)
table.drop()
new_table.rename(table_name)

View File

@ -136,6 +136,7 @@ class Stack(BASE, HeatBase, SoftDelete, StateAware):
backup = sqlalchemy.Column('backup', sqlalchemy.Boolean)
nested_depth = sqlalchemy.Column('nested_depth', sqlalchemy.Integer)
tags = sqlalchemy.Column('tags', types.Json)
convergence = sqlalchemy.Column('convergence', sqlalchemy.Boolean)
# 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

@ -508,7 +508,8 @@ class EngineService(service.Service):
def _parse_template_and_validate_stack(self, cnxt, stack_name, template,
params, files, args, owner_id=None,
nested_depth=0, user_creds_id=None,
stack_user_project_id=None):
stack_user_project_id=None,
convergence=False):
# If it is stack-adopt, use parameters from adopt_stack_data
common_params = api.extract_args(args)
if (rpc_api.PARAM_ADOPT_STACK_DATA in common_params and
@ -531,6 +532,7 @@ class EngineService(service.Service):
nested_depth=nested_depth,
user_creds_id=user_creds_id,
stack_user_project_id=stack_user_project_id,
convergence=convergence,
**common_params)
self._validate_deferred_auth_context(cnxt, stack)
@ -554,12 +556,18 @@ class EngineService(service.Service):
"""
LOG.info(_LI('previewing stack %s'), stack_name)
conv_eng = cfg.CONF.convergence_engine
if conv_eng:
raise exception.NotSupported(feature=_('Convergence engine'))
stack = self._parse_template_and_validate_stack(cnxt,
stack_name,
template,
params,
files,
args)
args,
convergence=conv_eng)
return api.format_stack_preview(stack)
@ -612,16 +620,13 @@ class EngineService(service.Service):
else:
LOG.info(_LI("Stack create failed, status %s"), stack.status)
stack = self._parse_template_and_validate_stack(cnxt,
stack_name,
template,
params,
files,
args,
owner_id,
nested_depth,
user_creds_id,
stack_user_project_id)
convergence = cfg.CONF.convergence_engine
if convergence:
raise exception.NotSupported(feature=_('Convergence engine'))
stack = self._parse_template_and_validate_stack(
cnxt, stack_name, template, params, files, args, owner_id,
nested_depth, user_creds_id, stack_user_project_id, convergence)
stack.store()
@ -667,6 +672,7 @@ class EngineService(service.Service):
raise exception.RequestLimitExceeded(
message=exception.StackResourceLimitExceeded.msg_fmt)
stack_name = current_stack.name
convergence = current_stack.convergence
common_params = api.extract_args(args)
common_params.setdefault(rpc_api.PARAM_TIMEOUT,
current_stack.timeout_mins)
@ -678,7 +684,8 @@ class EngineService(service.Service):
current_stack.env,
args.get(rpc_api.PARAM_CLEAR_PARAMETERS, []))
updated_stack = parser.Stack(cnxt, stack_name, tmpl,
env, **common_params)
env, convergence=convergence,
**common_params)
updated_stack.parameters.set_stack_id(current_stack.identifier())
self._validate_deferred_auth_context(cnxt, updated_stack)

View File

@ -79,7 +79,7 @@ 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):
nested_depth=0, strict_validate=True, convergence=False):
'''
Initialise from a context, name, Template object and (optionally)
Environment object. The database ID may also be initialised, if the
@ -119,6 +119,7 @@ class Stack(collections.Mapping):
self.user_creds_id = user_creds_id
self.nested_depth = nested_depth
self.strict_validate = strict_validate
self.convergence = convergence
if use_stored_context:
self.context = self.stored_context()
@ -295,7 +296,7 @@ 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)
username=stack.username, convergence=stack.convergence)
@profiler.trace('Stack.store', hide_args=False)
def store(self, backup=False):
@ -319,7 +320,8 @@ class Stack(collections.Mapping):
'updated_at': self.updated_time,
'user_creds_id': self.user_creds_id,
'backup': backup,
'nested_depth': self.nested_depth
'nested_depth': self.nested_depth,
'convergence': self.convergence
}
if self.id:
db_api.stack_update(self.context, self.id, s)
@ -683,7 +685,8 @@ class Stack(collections.Mapping):
elif create_if_missing:
prev = type(self)(self.context, self.name, copy.deepcopy(self.t),
self.env, owner_id=self.id,
user_creds_id=self.user_creds_id)
user_creds_id=self.user_creds_id,
convergence=self.convergence)
prev.store(backup=True)
LOG.debug('Created new backup stack')
return prev
@ -755,7 +758,7 @@ class Stack(collections.Mapping):
# Oldstack is useless when the action is not UPDATE , so we don't
# need to build it, this can avoid some unexpected errors.
oldstack = Stack(self.context, self.name, copy.deepcopy(self.t),
self.env)
self.env, convergence=self.convergence)
backup_stack = self._backup_stack()
try:
update_task = update.StackUpdate(

View File

@ -453,7 +453,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
parser.Stack(self.ctx, stack.name,
stack.t, stack.env, owner_id=None,
nested_depth=0, user_creds_id=None,
stack_user_project_id=None).AndReturn(stack)
stack_user_project_id=None,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndReturn(None)
@ -507,7 +508,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
owner_id=None,
nested_depth=0,
user_creds_id=None,
stack_user_project_id=None).AndReturn(stack)
stack_user_project_id=None,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndRaise(exception.StackValidationFailed(
@ -620,6 +622,15 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
self.man.create_stack,
self.ctx, stack_name, stack.t.t, {}, None, {})
def test_stack_create_enabled_convergence_engine(self):
cfg.CONF.set_override('convergence_engine', True)
ex = self.assertRaises(dispatcher.ExpectedException,
self.man.create_stack, self.ctx, 'test',
wp_template, {}, None, {})
self.assertEqual(exception.NotSupported, ex.exc_info[0])
self.assertEqual('Convergence engine is not supported.',
six.text_type(ex.exc_info[1]))
def test_stack_create_invalid_resource_name(self):
stack_name = 'service_create_test_stack_invalid_res'
stack = get_wordpress_stack(stack_name, self.ctx)
@ -664,14 +675,16 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
parser.Stack(ctx_no_pwd, stack.name,
stack.t, stack.env, owner_id=None,
nested_depth=0, user_creds_id=None,
stack_user_project_id=None).AndReturn(stack)
stack_user_project_id=None,
convergence=False).AndReturn(stack)
templatem.Template(template, files=None).AndReturn(stack.t)
environment.Environment(params).AndReturn(stack.env)
parser.Stack(ctx_no_user, stack.name,
stack.t, stack.env, owner_id=None,
nested_depth=0, user_creds_id=None,
stack_user_project_id=None).AndReturn(stack)
stack_user_project_id=None,
convergence=False).AndReturn(stack)
self.m.ReplayAll()
@ -720,7 +733,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
owner_id=None,
nested_depth=0,
user_creds_id=None,
stack_user_project_id=None).AndReturn(stack)
stack_user_project_id=None,
convergence=False).AndReturn(stack)
self.m.ReplayAll()
@ -991,7 +1005,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
environment.Environment(params).AndReturn(stack.env)
parser.Stack(self.ctx, stack.name,
stack.t, stack.env,
timeout_mins=60, disable_rollback=True).AndReturn(stack)
timeout_mins=60, disable_rollback=True,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndReturn(None)
@ -1039,7 +1054,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
environment.Environment(no_params).AndReturn(old_stack.env)
parser.Stack(self.ctx, stack.name,
stack.t, old_stack.env,
timeout_mins=60, disable_rollback=True).AndReturn(stack)
timeout_mins=60, disable_rollback=True,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndReturn(None)
@ -1082,7 +1098,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
environment.Environment(params).AndReturn(stack.env)
parser.Stack(self.ctx, stack.name,
stack.t, stack.env,
timeout_mins=1, disable_rollback=False).AndReturn(stack)
timeout_mins=1, disable_rollback=False,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndReturn(None)
@ -1159,7 +1176,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
environment.Environment(params).AndReturn(stack.env)
parser.Stack(self.ctx, stack.name,
stack.t, stack.env,
timeout_mins=60, disable_rollback=True).AndReturn(stack)
timeout_mins=60, disable_rollback=True,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndReturn(None)
@ -1279,7 +1297,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
environment.Environment(params).AndReturn(stack.env)
parser.Stack(self.ctx, stack.name,
stack.t, stack.env,
timeout_mins=60, disable_rollback=True).AndReturn(stack)
timeout_mins=60, disable_rollback=True,
convergence=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndRaise(exception.StackValidationFailed(
@ -1335,8 +1354,8 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase):
environment.Environment(params).AndReturn(old_stack.env)
parser.Stack(self.ctx, old_stack.name,
old_stack.t, old_stack.env,
timeout_mins=60, disable_rollback=True
).AndReturn(old_stack)
timeout_mins=60, disable_rollback=True,
convergence=False).AndReturn(old_stack)
self.m.ReplayAll()

View File

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