convergence scenario tests
Adding convergence prototype scenarios tests to run against actual heat code base. These tests are not modified and should test the sanity of covergence implementation. Change-Id: I69373a423da85f23597623457a7a7f1d5c2b0d2a Implements: blueprint convergence-simulator-tests Co-Authored-By: Anant Patil <anant.patil@hp.com>
This commit is contained in:
parent
01c334f017
commit
f38dc98ffd
|
@ -0,0 +1,103 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
from heat.db import api as db_api
|
||||
from heat.engine import service
|
||||
from heat.engine import stack
|
||||
from heat.tests.convergence.framework import message_processor
|
||||
from heat.tests.convergence.framework import message_queue
|
||||
from heat.tests.convergence.framework import scenario_template
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
class Engine(message_processor.MessageProcessor):
|
||||
'''
|
||||
Wrapper to the engine service. Methods of this
|
||||
class will be called from the scenario tests.
|
||||
'''
|
||||
|
||||
queue = message_queue.MessageQueue('engine')
|
||||
|
||||
def __init__(self):
|
||||
super(Engine, self).__init__('engine')
|
||||
|
||||
def scenario_template_to_hot(self, scenario_tmpl):
|
||||
'''
|
||||
Converts the scenario template into hot template.
|
||||
'''
|
||||
hot_tmpl = {"heat_template_version": "2013-05-23"}
|
||||
resources = {}
|
||||
for res_name, res_def in scenario_tmpl.resources.iteritems():
|
||||
props = getattr(res_def, 'properties')
|
||||
depends = getattr(res_def, 'depends_on')
|
||||
res_defn = {"type": "OS::Heat::TestResource"}
|
||||
if props:
|
||||
props_def = {}
|
||||
for prop_name, prop_value in props.items():
|
||||
if type(prop_value) == scenario_template.GetRes:
|
||||
prop_res = getattr(prop_value, "target_name")
|
||||
prop_value = {'get_resource': prop_res}
|
||||
elif type(prop_value) == scenario_template.GetAtt:
|
||||
prop_res = getattr(prop_value, "target_name")
|
||||
prop_attr = getattr(prop_value, "attr")
|
||||
prop_value = {'get_attr': [prop_res, prop_attr]}
|
||||
props_def[prop_name] = prop_value
|
||||
res_defn["properties"] = props_def
|
||||
if depends:
|
||||
res_defn["depends_on"] = depends
|
||||
resources[res_name] = res_defn
|
||||
hot_tmpl['resources'] = resources
|
||||
return hot_tmpl
|
||||
|
||||
@message_processor.asynchronous
|
||||
def create_stack(self, stack_name, scenario_tmpl):
|
||||
cnxt = utils.dummy_context()
|
||||
srv = service.EngineService("host", "engine")
|
||||
thread_group_mgr = service.ThreadGroupManager()
|
||||
srv.thread_group_mgr = thread_group_mgr
|
||||
hot_tmpl = self.scenario_template_to_hot(scenario_tmpl)
|
||||
srv.create_stack(cnxt, stack_name, hot_tmpl,
|
||||
params={}, files={}, args={})
|
||||
|
||||
@message_processor.asynchronous
|
||||
def update_stack(self, stack_name, scenario_tmpl):
|
||||
cnxt = utils.dummy_context()
|
||||
db_stack = db_api.stack_get_by_name(cnxt, stack_name)
|
||||
srv = service.EngineService("host", "engine")
|
||||
thread_group_mgr = service.ThreadGroupManager()
|
||||
srv.thread_group_mgr = thread_group_mgr
|
||||
hot_tmpl = self.scenario_template_to_hot(scenario_tmpl)
|
||||
stack_identity = {'stack_name': stack_name,
|
||||
'stack_id': db_stack.id,
|
||||
'tenant': db_stack.tenant,
|
||||
'path': ''}
|
||||
srv.update_stack(cnxt, stack_identity, hot_tmpl,
|
||||
params={}, files={}, args={})
|
||||
|
||||
@message_processor.asynchronous
|
||||
def delete_stack(self, stack_name):
|
||||
cnxt = utils.dummy_context()
|
||||
db_stack = db_api.stack_get_by_name(cnxt, stack_name)
|
||||
stack_identity = {'stack_name': stack_name,
|
||||
'stack_id': db_stack.id,
|
||||
'tenant': db_stack.tenant,
|
||||
'path': ''}
|
||||
srv = service.EngineService("host", "engine")
|
||||
srv.delete_stack(cnxt, stack_identity)
|
||||
|
||||
@message_processor.asynchronous
|
||||
def rollback_stack(self, stack_name):
|
||||
cntxt = utils.dummy_context()
|
||||
db_stack = db_api.stack_get_by_name(cntxt, stack_name)
|
||||
stk = stack.Stack.load(cntxt, stack=db_stack)
|
||||
stk.rollback()
|
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class EventLoop(object):
|
||||
|
||||
def __init__(self, *processors):
|
||||
self.processors = processors
|
||||
|
||||
def __call__(self):
|
||||
while any([processor() for processor in self.processors]):
|
||||
continue
|
|
@ -0,0 +1,96 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.engine import attributes
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestResource(resource.Resource):
|
||||
|
||||
PROPERTIES = (
|
||||
A, C, CA, rA, rB
|
||||
) = (
|
||||
'a', 'c', 'ca', '!a', '!b'
|
||||
)
|
||||
|
||||
ATTRIBUTES = (
|
||||
A, rA
|
||||
) = (
|
||||
'a', '!a'
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
A: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fake property a.'),
|
||||
default='a',
|
||||
update_allowed=True
|
||||
),
|
||||
C: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fake property c.'),
|
||||
update_allowed=True,
|
||||
default='c'
|
||||
),
|
||||
CA: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fake property ca.'),
|
||||
update_allowed=True,
|
||||
default='ca'
|
||||
),
|
||||
rA: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fake property !a.'),
|
||||
update_allowed=True,
|
||||
default='!a'
|
||||
),
|
||||
rB: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fake property !c.'),
|
||||
update_allowed=True,
|
||||
default='!b'
|
||||
),
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
A: attributes.Schema(
|
||||
_('Fake attribute a.'),
|
||||
cache_mode=attributes.Schema.CACHE_NONE
|
||||
),
|
||||
rA: attributes.Schema(
|
||||
_('Fake attribute !a.'),
|
||||
cache_mode=attributes.Schema.CACHE_NONE
|
||||
),
|
||||
}
|
||||
|
||||
def handle_create(self):
|
||||
for prop in self.properties.props.keys():
|
||||
self.data_set(prop, self.properties.get(prop), redact=False)
|
||||
|
||||
self.resource_id_set(self.physical_resource_name())
|
||||
|
||||
def handle_update(self, json_snippet=None, tmpl_diff=None, prop_diff=None):
|
||||
for prop in prop_diff:
|
||||
if '!' in prop:
|
||||
raise exception.UpdateReplace(self.name)
|
||||
self.data_set(prop, prop_diff.get(prop), redact=False)
|
||||
|
||||
def _resolve_attribute(self, name):
|
||||
if name in self.attributes:
|
||||
return self.data().get(name)
|
|
@ -0,0 +1,109 @@
|
|||
#
|
||||
# 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 collections
|
||||
import functools
|
||||
import inspect
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def asynchronous(function):
|
||||
'''Decorator for MessageProcessor methods to make them asynchronous.
|
||||
|
||||
To use, simply call the method as usual. Instead of being executed
|
||||
immediately, it will be placed on the queue for the MessageProcessor and
|
||||
run on a future iteration of the event loop.
|
||||
'''
|
||||
arg_names = inspect.getargspec(function).args
|
||||
MessageData = collections.namedtuple(function.__name__, arg_names[1:])
|
||||
|
||||
@functools.wraps(function)
|
||||
def call_or_send(processor, *args, **kwargs):
|
||||
if len(args) == 1 and not kwargs and isinstance(args[0], MessageData):
|
||||
try:
|
||||
return function(processor, **args[0]._asdict())
|
||||
except Exception as exc:
|
||||
LOG.exception('[%s] Exception in "%s": %s',
|
||||
processor.name, function.__name__, exc)
|
||||
raise
|
||||
else:
|
||||
data = inspect.getcallargs(function, processor, *args, **kwargs)
|
||||
data.pop(arg_names[0]) # lose self
|
||||
return processor.queue.send(function.__name__,
|
||||
MessageData(**data))
|
||||
|
||||
call_or_send.MessageData = MessageData
|
||||
return call_or_send
|
||||
|
||||
|
||||
class MessageProcessor(object):
|
||||
|
||||
queue = None
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __call__(self):
|
||||
message = self.queue.get()
|
||||
if message is None:
|
||||
LOG.debug('[%s] No messages' % self.name)
|
||||
return False
|
||||
|
||||
try:
|
||||
method = getattr(self, message.name)
|
||||
except AttributeError:
|
||||
LOG.error('[%s] Bad message name "%s"' % (self.name,
|
||||
message.name))
|
||||
raise
|
||||
else:
|
||||
LOG.info('[%s] %r' % (self.name, message.data))
|
||||
|
||||
method(message.data)
|
||||
return True
|
||||
|
||||
@asynchronous
|
||||
def noop(self, count=1):
|
||||
'''
|
||||
Insert <count> No-op operations in the message queue.
|
||||
'''
|
||||
assert isinstance(count, int)
|
||||
if count > 1:
|
||||
self.queue.send_priority('noop',
|
||||
self.noop.MessageData(count - 1))
|
||||
|
||||
@asynchronous
|
||||
def _execute(self, func):
|
||||
'''
|
||||
Insert a function call in the message queue.
|
||||
|
||||
The function takes no arguments, so use functools.partial to curry the
|
||||
arguments before passing it here.
|
||||
'''
|
||||
func()
|
||||
|
||||
def call(self, func, *args, **kwargs):
|
||||
'''
|
||||
Insert a function call in the message queue.
|
||||
'''
|
||||
self._execute(functools.partial(func, *args, **kwargs))
|
||||
|
||||
def clear(self):
|
||||
'''
|
||||
Delete all the messages from the queue.
|
||||
'''
|
||||
self.queue.clear()
|
||||
|
||||
__all__ = ['MessageProcessor', 'asynchronous']
|
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# 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 collections
|
||||
|
||||
|
||||
Message = collections.namedtuple('Message', ['name', 'data'])
|
||||
|
||||
|
||||
class MessageQueue(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self._queue = collections.deque()
|
||||
|
||||
def send(self, name, data=None):
|
||||
self._queue.append(Message(name, data))
|
||||
|
||||
def send_priority(self, name, data=None):
|
||||
self._queue.appendleft(Message(name, data))
|
||||
|
||||
def get(self):
|
||||
try:
|
||||
return self._queue.popleft()
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def clear(self):
|
||||
self._queue.clear()
|
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
from heat.tests.convergence.framework import engine_wrapper
|
||||
from heat.tests.convergence.framework import event_loop as event_loop_module
|
||||
from heat.tests.convergence.framework import worker_wrapper
|
||||
|
||||
|
||||
engine = None
|
||||
worker = None
|
||||
event_loop = None
|
||||
|
||||
|
||||
class Processes(object):
|
||||
|
||||
def __init__(self):
|
||||
global engine
|
||||
global worker
|
||||
global event_loop
|
||||
|
||||
engine = engine_wrapper.Engine()
|
||||
worker = worker_wrapper.Worker()
|
||||
|
||||
event_loop = event_loop_module.EventLoop(engine, worker)
|
||||
|
||||
self.engine = engine
|
||||
self.worker = worker
|
||||
self.event_loop = event_loop
|
||||
|
||||
def clear(self):
|
||||
self.engine.clear()
|
||||
self.worker.clear()
|
||||
|
||||
Processes()
|
|
@ -0,0 +1,51 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
from heat.common import exception
|
||||
from heat.db import api as db_api
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
class RealityStore(object):
|
||||
|
||||
def __init__(self):
|
||||
self.cntxt = utils.dummy_context()
|
||||
|
||||
def resources_by_logical_name(self, logical_name):
|
||||
ret = []
|
||||
resources = db_api.resource_get_all(self.cntxt)
|
||||
for res in resources:
|
||||
if (res.name == logical_name and res.action in ("CREATE", "UPDATE")
|
||||
and res.status == "COMPLETE"):
|
||||
ret.append(res)
|
||||
return ret
|
||||
|
||||
def all_resources(self):
|
||||
try:
|
||||
resources = db_api.resource_get_all(self.cntxt)
|
||||
except exception.NotFound:
|
||||
return []
|
||||
|
||||
ret = []
|
||||
for res in resources:
|
||||
if res.action in ("CREATE", "UPDATE") and res.status == "COMPLETE":
|
||||
ret.append(res)
|
||||
return ret
|
||||
|
||||
def resource_properties(self, res, prop_name):
|
||||
res_data = db_api.resource_data_get_by_key(self.cntxt,
|
||||
res.id,
|
||||
prop_name)
|
||||
return res_data.value
|
||||
|
||||
reality = RealityStore()
|
|
@ -0,0 +1,49 @@
|
|||
#
|
||||
# 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 os
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def list_all():
|
||||
scenario_dir = os.path.join(os.path.dirname(__file__), '../scenarios')
|
||||
if not os.path.isdir(scenario_dir):
|
||||
LOG.error('Scenario directory "%s" not found', scenario_dir)
|
||||
return
|
||||
|
||||
for root, dirs, files in os.walk(scenario_dir):
|
||||
for filename in files:
|
||||
name, ext = os.path.splitext(filename)
|
||||
if ext == '.py':
|
||||
LOG.debug('Found scenario "%s"', name)
|
||||
yield name, os.path.join(root, filename)
|
||||
|
||||
|
||||
class Scenario(object):
|
||||
|
||||
def __init__(self, name, path):
|
||||
self.name = name
|
||||
|
||||
with open(path) as f:
|
||||
source = f.read()
|
||||
|
||||
self.code = compile(source, path, 'exec')
|
||||
LOG.debug('Loaded scenario %s', self.name)
|
||||
|
||||
def __call__(self, _event_loop, **global_env):
|
||||
LOG.info('*** Beginning scenario "%s"', self.name)
|
||||
|
||||
exec(self.code, global_env, {})
|
||||
_event_loop()
|
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class GetRes(object):
|
||||
def __init__(self, target_name):
|
||||
self.target_name = target_name
|
||||
|
||||
|
||||
class GetAtt(GetRes):
|
||||
def __init__(self, target_name, attr):
|
||||
super(GetAtt, self).__init__(target_name)
|
||||
self.attr = attr
|
||||
|
||||
|
||||
class RsrcDef(object):
|
||||
def __init__(self, properties, depends_on):
|
||||
self.properties = properties
|
||||
self.depends_on = depends_on
|
||||
|
||||
|
||||
class Template(object):
|
||||
|
||||
def __init__(self, resources={}, key=None):
|
||||
self.key = key
|
||||
self.resources = resources
|
||||
|
||||
def __repr__(self):
|
||||
return 'Template(%r)' % self.resources
|
|
@ -0,0 +1,70 @@
|
|||
#
|
||||
# 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 functools
|
||||
from oslo_log import log as logging
|
||||
|
||||
from heat.tests.convergence.framework import reality
|
||||
from heat.tests.convergence.framework import scenario_template
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def verify(test, reality, tmpl):
|
||||
for name in tmpl.resources:
|
||||
rsrc_count = len(reality.resources_by_logical_name(name))
|
||||
test.assertEqual(1, rsrc_count,
|
||||
'Found %d copies of resource "%s"' % (rsrc_count,
|
||||
name))
|
||||
|
||||
all_rsrcs = reality.all_resources()
|
||||
|
||||
for name, defn in tmpl.resources.items():
|
||||
phys_rsrc = reality.resources_by_logical_name(name)[0]
|
||||
|
||||
for prop_name, prop_def in defn.properties.items():
|
||||
real_value = reality.resource_properties(phys_rsrc, prop_name)
|
||||
|
||||
if isinstance(prop_def, scenario_template.GetAtt):
|
||||
targs = reality.resources_by_logical_name(prop_def.target_name)
|
||||
att_value = targs[0].properties_data[prop_def.attr]
|
||||
test.assertEqual(att_value, real_value)
|
||||
|
||||
elif isinstance(prop_def, scenario_template.GetRes):
|
||||
targs = reality.resources_by_logical_name(prop_def.target_name)
|
||||
test.assertEqual(targs[0].nova_instance, real_value)
|
||||
|
||||
else:
|
||||
test.assertEqual(prop_def, real_value)
|
||||
|
||||
test.assertEqual(len(defn.properties), len(phys_rsrc.properties_data))
|
||||
|
||||
test.assertEqual(len(tmpl.resources), len(all_rsrcs))
|
||||
|
||||
|
||||
def scenario_globals(procs, testcase):
|
||||
return {
|
||||
'test': testcase,
|
||||
'reality': reality.reality,
|
||||
'verify': functools.partial(verify,
|
||||
testcase,
|
||||
reality.reality),
|
||||
|
||||
'Template': scenario_template.Template,
|
||||
'RsrcDef': scenario_template.RsrcDef,
|
||||
'GetRes': scenario_template.GetRes,
|
||||
'GetAtt': scenario_template.GetAtt,
|
||||
|
||||
'engine': procs.engine,
|
||||
'worker': procs.worker,
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
from heat.engine import worker
|
||||
from heat.tests.convergence.framework import message_processor
|
||||
from heat.tests.convergence.framework import message_queue
|
||||
|
||||
|
||||
class Worker(message_processor.MessageProcessor):
|
||||
|
||||
queue = message_queue.MessageQueue('worker')
|
||||
|
||||
def __init__(self):
|
||||
super(Worker, self).__init__('worker')
|
||||
|
||||
@message_processor.asynchronous
|
||||
def check_resource(self, ctxt, resource_id,
|
||||
current_traversal, data,
|
||||
is_update, adopt_stack_data):
|
||||
worker.WorkerService("fake_host", "fake_topic",
|
||||
"fake_engine", "tgm").check_resource(
|
||||
ctxt, resource_id,
|
||||
current_traversal,
|
||||
data, is_update,
|
||||
adopt_stack_data)
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(3)
|
||||
engine.rollback_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template())
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def check_resource_count(expected_count):
|
||||
test.assertEqual(expected_count, len(reality.all_resources()))
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(2)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.call(check_resource_count, 3)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template2)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(2)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
|
@ -0,0 +1,50 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template_shrunk = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_shrunk)
|
||||
engine.noop(10)
|
||||
engine.call(verify, example_template_shrunk)
|
||||
|
||||
example_template_long = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template_long)
|
||||
engine.noop(12)
|
||||
engine.call(verify, example_template_long)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template2)
|
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def check_resource_count(expected_count):
|
||||
test.assertEqual(expected_count, len(reality.all_resources()))
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(2)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.call(check_resource_count, 3)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template2)
|
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
'F': RsrcDef({}, ['A']),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.noop(4)
|
||||
|
||||
engine.rollback_stack('foo')
|
||||
engine.noop(8)
|
||||
engine.call(verify, example_template)
|
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
'F': RsrcDef({}, ['D']),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.noop(4)
|
||||
|
||||
engine.rollback_stack('foo')
|
||||
engine.noop(8)
|
||||
engine.call(verify, example_template)
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.noop(9)
|
||||
engine.call(verify, example_template2)
|
|
@ -0,0 +1,51 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
b_uuid = None
|
||||
|
||||
def store_b_uuid():
|
||||
global b_uuid
|
||||
b_uuid = next(iter(reality.resources_by_logical_name('B'))).uuid
|
||||
|
||||
def check_b_not_replaced():
|
||||
test.assertEqual(b_uuid,
|
||||
next(iter(reality.resources_by_logical_name('B'))).uuid)
|
||||
test.assertIsNot(b_uuid, None)
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A', 'B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
engine.call(store_b_uuid)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({}, []),
|
||||
'C': RsrcDef({'a': '4alpha'}, ['A']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', 'a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.noop(2)
|
||||
|
||||
engine.rollback_stack('foo')
|
||||
engine.noop(10)
|
||||
|
||||
engine.call(verify, example_template)
|
||||
engine.call(check_b_not_replaced)
|
|
@ -0,0 +1,65 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
c_uuid = None
|
||||
|
||||
def store_c_uuid():
|
||||
global c_uuid
|
||||
c_uuid = next(iter(reality.resources_by_logical_name('C'))).uuid
|
||||
|
||||
def check_c_replaced():
|
||||
test.assertNotEqual(c_uuid,
|
||||
next(iter(reality.resources_by_logical_name('C'))).uuid)
|
||||
test.assertIsNot(c_uuid, None)
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'a': 'initial'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
engine.call(store_c_uuid)
|
||||
|
||||
example_template_updated = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_updated)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template_updated)
|
||||
|
||||
example_template_long = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template_long)
|
||||
engine.noop(12)
|
||||
engine.call(verify, example_template_long)
|
||||
engine.call(check_c_replaced)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def check_resource_counts(count_map):
|
||||
for name, count in count_map.items():
|
||||
test.assertEqual(count,
|
||||
len(list(reality.resources_by_logical_name(name))))
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'!a': 'initial'}, []),
|
||||
'B': RsrcDef({'!b': 'first'}, ['A']),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(4)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template_inverted = Template({
|
||||
'A': RsrcDef({'!a': 'updated'}, ['B']),
|
||||
'B': RsrcDef({'!b': 'second'}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_inverted)
|
||||
engine.noop(4)
|
||||
engine.call(check_resource_counts, {'A': 2, 'B': 1})
|
||||
engine.noop(2)
|
||||
engine.call(verify, example_template_inverted)
|
||||
engine.call(check_resource_counts, {'A': 1, 'B': 1})
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(3)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,55 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def check_c_count(expected_count):
|
||||
test.assertEqual(expected_count,
|
||||
len(reality.resources_by_logical_name('C')))
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'a': 'initial'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template_shrunk = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_shrunk)
|
||||
engine.noop(7)
|
||||
|
||||
example_template_long = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template_long)
|
||||
engine.call(check_c_count, 2)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template_long)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(12)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def check_c_count(expected_count):
|
||||
test.assertEqual(expected_count,
|
||||
len(reality.resources_by_logical_name('C')))
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'a': 'initial'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template_shrunk = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_shrunk)
|
||||
engine.noop(7)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.call(check_c_count, 2)
|
||||
engine.noop(11)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def check_c_count(expected_count):
|
||||
test.assertEqual(expected_count,
|
||||
len(reality.resources_by_logical_name('C')))
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'a': 'initial'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template2 = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template2)
|
||||
engine.noop(4)
|
||||
|
||||
engine.rollback_stack('foo')
|
||||
engine.call(check_c_count, 2)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(12)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,65 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
c_uuid = None
|
||||
|
||||
def store_c_uuid():
|
||||
global c_uuid
|
||||
c_uuid = next(iter(reality.resources_by_logical_name('C'))).uuid
|
||||
|
||||
def check_c_replaced():
|
||||
test.assertNotEqual(c_uuid,
|
||||
next(iter(reality.resources_by_logical_name('newC'))).uuid)
|
||||
test.assertIsNot(c_uuid, None)
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'a': 'initial'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
engine.call(store_c_uuid)
|
||||
|
||||
example_template_updated = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'newC': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('newC')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('newC', '!a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_updated)
|
||||
engine.noop(11)
|
||||
engine.call(verify, example_template_updated)
|
||||
|
||||
example_template_long = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'newC': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('newC')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('newC', '!a')}, []),
|
||||
'F': RsrcDef({}, ['D', 'E']),
|
||||
})
|
||||
engine.update_stack('foo', example_template_long)
|
||||
engine.noop(12)
|
||||
engine.call(verify, example_template_long)
|
||||
engine.call(check_c_replaced)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
example_template = Template({
|
||||
'A': RsrcDef({'a': 'initial'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'C': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('C')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('C', '!a')}, []),
|
||||
})
|
||||
engine.create_stack('foo', example_template)
|
||||
engine.noop(5)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
example_template_updated = Template({
|
||||
'A': RsrcDef({'a': 'updated'}, []),
|
||||
'B': RsrcDef({}, []),
|
||||
'newC': RsrcDef({'!a': GetAtt('A', 'a')}, ['B']),
|
||||
'D': RsrcDef({'c': GetRes('newC')}, []),
|
||||
'E': RsrcDef({'ca': GetAtt('newC', '!a')}, []),
|
||||
})
|
||||
engine.update_stack('foo', example_template_updated)
|
||||
engine.noop(3)
|
||||
|
||||
engine.rollback_stack('foo')
|
||||
engine.noop(12)
|
||||
engine.call(verify, example_template)
|
||||
|
||||
engine.delete_stack('foo')
|
||||
engine.noop(6)
|
||||
engine.call(verify, Template({}))
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# 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.
|
||||
|
||||
from heat.engine import resource
|
||||
from heat.tests import common
|
||||
from heat.tests.convergence.framework import fake_resource
|
||||
from heat.tests.convergence.framework import processes
|
||||
from heat.tests.convergence.framework import scenario
|
||||
from heat.tests.convergence.framework import testutils
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
class ScenarioTest(common.HeatTestCase):
|
||||
|
||||
scenarios = [(name, {'name': name, 'path': path})
|
||||
for name, path in scenario.list_all()]
|
||||
|
||||
def setUp(self):
|
||||
super(ScenarioTest, self).setUp()
|
||||
resource._register_class('OS::Heat::TestResource',
|
||||
fake_resource.TestResource)
|
||||
self.procs = processes.Processes()
|
||||
po = self.patch("heat.rpc.worker_client.WorkerClient.check_resource")
|
||||
po.side_effect = self.procs.worker.check_resource
|
||||
cfg.CONF.set_default('convergence_engine', True)
|
||||
|
||||
def tearDown(self):
|
||||
super(ScenarioTest, self).tearDown()
|
||||
|
||||
def test_scenario(self):
|
||||
self.procs.clear()
|
||||
runner = scenario.Scenario(self.name, self.path)
|
||||
runner(self.procs.event_loop,
|
||||
**testutils.scenario_globals(self.procs, self))
|
2
tox.ini
2
tox.ini
|
@ -73,7 +73,7 @@ commands = bandit -c bandit.yaml -r heat -n5 -p heat_conservative
|
|||
# H405 multi line docstring summary not separated with an empty line
|
||||
ignore = H404,H405
|
||||
show-source = true
|
||||
exclude=.*,dist,*openstack/common*,*lib/python*,*egg,tools,build
|
||||
exclude=.*,dist,*openstack/common*,*lib/python*,*egg,tools,build,*convergence/scenarios/*
|
||||
max-complexity=20
|
||||
|
||||
[hacking]
|
||||
|
|
Loading…
Reference in New Issue