Add support for dynamic parameters allocation in Heat stacks
- dynamically allocate subnet CIDR from configed pool Change-Id: I8a166cb348ca46b1267b1507e9b2f9a8b95eb2fe
This commit is contained in:
parent
d9e9bcab86
commit
ceb5420cf4
@ -41,17 +41,28 @@ DELETE_FAILED = 'DELETE_FAILED'
|
||||
TEMPLATE_FILE_SUFFIX = '.yaml'
|
||||
|
||||
|
||||
def _stack_parameters(obj, stack=None):
|
||||
if obj is None or isinstance(obj, collections.Mapping):
|
||||
parameters = HeatStackParametersFixture(stack, obj)
|
||||
else:
|
||||
parameters = tobiko.get_fixture(obj)
|
||||
if not isinstance(parameters, HeatStackParametersFixture):
|
||||
msg = "Object {!r} is not an HeatStackParametersFixture".format(
|
||||
parameters)
|
||||
raise TypeError(msg)
|
||||
return parameters
|
||||
|
||||
|
||||
class HeatStackFixture(tobiko.SharedFixture):
|
||||
"""Manages Heat stacks."""
|
||||
|
||||
client = None
|
||||
client_fixture = None
|
||||
retry_create_stack = 1
|
||||
wait_interval = 5
|
||||
stack_name = None
|
||||
template = None
|
||||
parameters = None
|
||||
stack = None
|
||||
parameters = None
|
||||
|
||||
def __init__(self, stack_name=None, template=None, parameters=None,
|
||||
wait_interval=None, client=None):
|
||||
@ -60,62 +71,24 @@ class HeatStackFixture(tobiko.SharedFixture):
|
||||
self.stack_name or
|
||||
self.fixture_name)
|
||||
|
||||
template = template or self.template
|
||||
if template:
|
||||
if isinstance(template, collections.Mapping):
|
||||
template = _template.heat_template(template)
|
||||
else:
|
||||
template = tobiko.get_fixture(template)
|
||||
if not isinstance(template, _template.HeatTemplateFixture):
|
||||
msg = "Object {!r} is not an HeatTemplateFixture".format(
|
||||
template)
|
||||
raise TypeError(msg)
|
||||
self.template = template
|
||||
|
||||
self._parameters = parameters
|
||||
|
||||
if tobiko.is_fixture(client):
|
||||
self.client_fixture = client
|
||||
elif client:
|
||||
self.client = client
|
||||
self.template = _template.heat_template(template or self.template)
|
||||
self.parameters = _stack_parameters(
|
||||
stack=self, obj=(parameters or self.parameters))
|
||||
self.client = client or self.client
|
||||
|
||||
if wait_interval:
|
||||
self.wait_interval = wait_interval
|
||||
|
||||
def setup_fixture(self):
|
||||
self.setup_template()
|
||||
self.setup_parameters()
|
||||
self.setup_client()
|
||||
self.setup_stack()
|
||||
|
||||
def setup_template(self):
|
||||
tobiko.setup_fixture(self.template)
|
||||
|
||||
def setup_parameters(self):
|
||||
self.parameters = {}
|
||||
# Merge all parameters dictionaries in the class hierarchy
|
||||
for cls in reversed(type(self).__mro__):
|
||||
parameters = cls.__dict__.get('parameters')
|
||||
if parameters:
|
||||
self.parameters.update(parameters)
|
||||
|
||||
if self._parameters:
|
||||
self.parameters.update(self._parameters)
|
||||
|
||||
# Add template's missing stack parameters
|
||||
template_parameters = set(self.template.template.get('parameters', []))
|
||||
missing_parameters = sorted(template_parameters - set(self.parameters))
|
||||
for name in missing_parameters:
|
||||
value = getattr(self, name, None)
|
||||
if value is not None:
|
||||
self.parameters[name] = value
|
||||
|
||||
def setup_client(self):
|
||||
client_fixture = self.client_fixture
|
||||
if client_fixture:
|
||||
self.client = tobiko.setup_fixture(client_fixture).client
|
||||
elif not self.client:
|
||||
self.client = _client.get_heat_client()
|
||||
self.client = _client.heat_client(self.client)
|
||||
|
||||
def setup_stack(self):
|
||||
self.create_stack()
|
||||
@ -159,13 +132,15 @@ class HeatStackFixture(tobiko.SharedFixture):
|
||||
expected_status={DELETE_COMPLETE})
|
||||
|
||||
self.stack = self._outputs = None
|
||||
# Compile template parameters
|
||||
parameters = tobiko.reset_fixture(self.parameters).values
|
||||
try:
|
||||
LOG.debug('Creating stack %r (re-tries left %d)...',
|
||||
self.stack_name, retry)
|
||||
stack_id = self.client.stacks.create(
|
||||
stack_name=self.stack_name,
|
||||
template=self.template.template_yaml,
|
||||
parameters=self.parameters)['stack']['id']
|
||||
parameters=parameters)['stack']['id']
|
||||
except exc.HTTPConflict:
|
||||
LOG.debug('Stack %r already exists.', self.stack_name)
|
||||
else:
|
||||
@ -252,80 +227,141 @@ class HeatStackFixture(tobiko.SharedFixture):
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self.get_stack_outputs().get_output(name)
|
||||
except InvalidHeatStackOutputKey:
|
||||
return self.get_stack_outputs().get_value(name)
|
||||
except HeatStackOutputKeyError:
|
||||
pass
|
||||
message = "Object {!r} has no attribute {!r}".format(self, name)
|
||||
raise AttributeError(message)
|
||||
|
||||
|
||||
class HeatStackOutputsFixture(tobiko.SharedFixture):
|
||||
class HeatStackKeyError(tobiko.TobikoException):
|
||||
message = "key {key!r} not found in stack {name!r}"
|
||||
|
||||
|
||||
class HeatStackParameterKeyError(HeatStackKeyError):
|
||||
message = "parameter key {key!r} not found in stack {name!r}"
|
||||
|
||||
|
||||
class HeatStackOutputKeyError(HeatStackKeyError):
|
||||
message = "output key {key!r} not found in stack {name!r}"
|
||||
|
||||
|
||||
class HeatStackNamespaceFixture(tobiko.SharedFixture):
|
||||
|
||||
key_error = HeatStackKeyError
|
||||
_keys = None
|
||||
_values = None
|
||||
|
||||
def __init__(self, stack):
|
||||
super(HeatStackOutputsFixture, self).__init__()
|
||||
super(HeatStackNamespaceFixture, self).__init__()
|
||||
if not isinstance(stack, HeatStackFixture):
|
||||
message = "Object {!r} is not an HeatStackFixture".format(stack)
|
||||
raise TypeError(message)
|
||||
self.stack = stack
|
||||
|
||||
def setup_fixture(self):
|
||||
self.get_output_keys()
|
||||
self.get_output_values()
|
||||
self.setup_keys()
|
||||
self.setup_values()
|
||||
|
||||
def get_output_keys(self):
|
||||
def setup_keys(self):
|
||||
keys = self._keys
|
||||
if keys is None:
|
||||
self._keys = keys = frozenset(tobiko.setup_fixture(
|
||||
self.stack.template).outputs.keys())
|
||||
self._keys = keys = self.get_keys()
|
||||
self.addCleanup(self.cleanup_keys)
|
||||
return keys
|
||||
|
||||
keys = property(get_output_keys)
|
||||
keys = tobiko.fixture_property(setup_keys)
|
||||
|
||||
def get_keys(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def cleanup_keys(self):
|
||||
del self._keys
|
||||
|
||||
def get_output_values(self):
|
||||
def setup_values(self):
|
||||
values = self._values
|
||||
if values is None:
|
||||
# Can't get output values before stack creation is complete
|
||||
self.stack.wait_for_create_complete()
|
||||
outputs = self.stack.get_stack(resolve_outputs=True).outputs
|
||||
self._values = values = {o['output_key']: o['output_value']
|
||||
for o in outputs}
|
||||
self.addCleanup(self.cleanup_output_values)
|
||||
self._values = values = self.get_values()
|
||||
self.addCleanup(self.cleanup_values)
|
||||
return values
|
||||
|
||||
def cleanup_output_values(self):
|
||||
values = tobiko.fixture_property(setup_values)
|
||||
|
||||
def get_values(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def cleanup_values(self):
|
||||
del self._values
|
||||
|
||||
values = property(get_output_values)
|
||||
|
||||
def get_output(self, key):
|
||||
# Match template outputs definition before fetching getting values
|
||||
if key not in self.keys:
|
||||
LOG.error('Output key %r not found in stack %r template', key,
|
||||
self.stack.stack_name)
|
||||
else:
|
||||
def get_value(self, key):
|
||||
# Match template outputs definition before getting value
|
||||
if key in self.keys:
|
||||
try:
|
||||
return self.values[key]
|
||||
except KeyError:
|
||||
LOG.error('Output key %r not found in stack %r outputs', key,
|
||||
LOG.error('Key %r not found in stack %r', key,
|
||||
self.stack.stack_name)
|
||||
raise InvalidHeatStackOutputKey(name=self.stack.stack_name, key=key)
|
||||
else:
|
||||
LOG.error('Key %r not found in template for stack %r', key,
|
||||
self.stack.stack_name)
|
||||
raise self.key_error(name=self.stack.stack_name, key=key)
|
||||
|
||||
def set_value(self, key, value):
|
||||
# Match template outputs definition before setting value
|
||||
if key in self.keys:
|
||||
self.values[key] = value
|
||||
else:
|
||||
LOG.error('Key %r not found in template for stack %r', key,
|
||||
self.stack.stack_name)
|
||||
raise self.key_error(name=self.stack.stack_name, key=key)
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self.get_output(name)
|
||||
except InvalidHeatStackOutputKey:
|
||||
return self.get_value(name)
|
||||
except self.key_error:
|
||||
pass
|
||||
message = "Object {!r} has no attribute {!r}".format(self, name)
|
||||
raise AttributeError(message)
|
||||
|
||||
|
||||
class HeatStackParametersFixture(HeatStackNamespaceFixture):
|
||||
|
||||
key_error = HeatStackParameterKeyError
|
||||
|
||||
def __init__(self, stack, parameters=None):
|
||||
super(HeatStackParametersFixture, self).__init__(stack)
|
||||
self.parameters = parameters and dict(parameters) or {}
|
||||
|
||||
def get_keys(self):
|
||||
template = tobiko.setup_fixture(self.stack.template)
|
||||
return frozenset(template.parameters or [])
|
||||
|
||||
def get_values(self):
|
||||
values = dict(self.parameters)
|
||||
missing_keys = sorted(self.keys - set(values))
|
||||
for key in missing_keys:
|
||||
value = getattr(self.stack, key, None)
|
||||
if value is not None:
|
||||
values[key] = value
|
||||
return values
|
||||
|
||||
|
||||
class HeatStackOutputsFixture(HeatStackNamespaceFixture):
|
||||
|
||||
key_error = HeatStackOutputKeyError
|
||||
|
||||
def get_keys(self):
|
||||
template = tobiko.setup_fixture(self.stack.template)
|
||||
return frozenset(template.outputs or [])
|
||||
|
||||
def get_values(self):
|
||||
# Can't get output values before stack creation is complete
|
||||
self.stack.wait_for_create_complete()
|
||||
outputs = self.stack.get_stack(resolve_outputs=True).outputs
|
||||
return {o['output_key']: o['output_value']
|
||||
for o in outputs}
|
||||
|
||||
|
||||
def check_stack_status(stack, expected):
|
||||
observed = stack.stack_status
|
||||
if observed not in expected:
|
||||
@ -341,10 +377,6 @@ def check_stack_status(stack, expected):
|
||||
status_reason=stack.stack_status_reason)
|
||||
|
||||
|
||||
class InvalidHeatStackOutputKey(tobiko.TobikoException, AttributeError):
|
||||
message = "output key {key!r} not found in stack {name!r}"
|
||||
|
||||
|
||||
class HeatStackNotFound(tobiko.TobikoException):
|
||||
message = "stack {name!r} not found"
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import collections
|
||||
import os
|
||||
import sys
|
||||
|
||||
@ -49,7 +50,12 @@ class HeatTemplateFixture(tobiko.SharedFixture):
|
||||
@property
|
||||
def outputs(self):
|
||||
template = self.template
|
||||
return template and template.get('outputs') or {}
|
||||
return template and template.get('outputs') or None
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
template = self.template
|
||||
return template and template.get('parameters') or None
|
||||
|
||||
|
||||
class HeatTemplateFileFixture(HeatTemplateFixture):
|
||||
@ -80,9 +86,17 @@ class HeatTemplateFileFixture(HeatTemplateFixture):
|
||||
super(HeatTemplateFileFixture, self).setup_template()
|
||||
|
||||
|
||||
def heat_template(template, template_files=None):
|
||||
return HeatTemplateFixture(template=template,
|
||||
template_files=template_files)
|
||||
def heat_template(obj, template_files=None):
|
||||
if isinstance(obj, collections.Mapping):
|
||||
template = HeatTemplateFixture(template=obj,
|
||||
template_files=template_files)
|
||||
else:
|
||||
template = tobiko.get_fixture(obj)
|
||||
|
||||
if not isinstance(template, HeatTemplateFixture):
|
||||
msg = "Object {!r} is not an HeatTemplateFixture".format(template)
|
||||
raise TypeError(msg)
|
||||
return template
|
||||
|
||||
|
||||
def heat_template_file(template_file, template_dirs=None):
|
||||
|
@ -35,7 +35,6 @@ class CIDRGeneratorFixture(tobiko.SharedFixture):
|
||||
client = None
|
||||
config = None
|
||||
cidr_generator = None
|
||||
subnet_cidrs = None
|
||||
|
||||
def __init__(self, cidr=None, prefixlen=None, client=None):
|
||||
super(CIDRGeneratorFixture, self).__init__()
|
||||
@ -48,7 +47,7 @@ class CIDRGeneratorFixture(tobiko.SharedFixture):
|
||||
|
||||
def setup_fixture(self):
|
||||
self.setup_config()
|
||||
self.setup_subnet_cidrs()
|
||||
self.setup_client()
|
||||
self.setup_cidr_generator()
|
||||
|
||||
def setup_config(self):
|
||||
@ -56,18 +55,16 @@ class CIDRGeneratorFixture(tobiko.SharedFixture):
|
||||
CONF = config.CONF
|
||||
self.config = CONF.tobiko.neutron
|
||||
|
||||
def setup_subnet_cidrs(self):
|
||||
client = _client.neutron_client(self.client)
|
||||
self.subnet_cidrs = set(_client.list_subnet_cidrs(client=client))
|
||||
def setup_client(self):
|
||||
self.client = _client.neutron_client(self.client)
|
||||
|
||||
def setup_cidr_generator(self):
|
||||
cidr = netaddr.IPNetwork(self.cidr)
|
||||
prefixlen = int(self.prefixlen)
|
||||
self.cidr_generator = cidr.subnet(prefixlen)
|
||||
self.cidr_generator = self.cidr.subnet(self.prefixlen)
|
||||
|
||||
def new_cidr(self):
|
||||
used_cidrs = set(_client.list_subnet_cidrs(client=self.client))
|
||||
for cidr in self.cidr_generator:
|
||||
if cidr not in self.subnet_cidrs:
|
||||
if cidr not in used_cidrs:
|
||||
return cidr
|
||||
raise NoSuchCIDRLeft(cidr=self.cidr, prefixlen=self.prefixlen)
|
||||
|
||||
@ -76,22 +73,22 @@ class IPv4CIDRGeneratorFixture(CIDRGeneratorFixture):
|
||||
|
||||
@property
|
||||
def cidr(self):
|
||||
return self.config.ipv4_cidr
|
||||
return netaddr.IPNetwork(self.config.ipv4_cidr)
|
||||
|
||||
@property
|
||||
def prefixlen(self):
|
||||
return self.config.ipv4_prefixlen
|
||||
return int(self.config.ipv4_prefixlen)
|
||||
|
||||
|
||||
class IPv6CIDRGeneratorFixture(CIDRGeneratorFixture):
|
||||
|
||||
@property
|
||||
def cidr(self):
|
||||
return self.config.ipv6_cidr
|
||||
return netaddr.IPNetwork(self.config.ipv6_cidr)
|
||||
|
||||
@property
|
||||
def prefixlen(self):
|
||||
return self.config.ipv6_prefixlen
|
||||
return int(self.config.ipv6_prefixlen)
|
||||
|
||||
|
||||
class NoSuchCIDRLeft(tobiko.TobikoException):
|
||||
|
@ -15,6 +15,7 @@
|
||||
# under the License.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import netaddr
|
||||
|
||||
import tobiko
|
||||
from tobiko import config
|
||||
@ -38,21 +39,33 @@ class NetworkStackFixture(heat.HeatStackFixture):
|
||||
#: Disable port security by default for new network ports
|
||||
port_security_enabled = False
|
||||
|
||||
#: Default IPv4 sub-net CIDR
|
||||
ipv4_cidr = '190.40.2.0/24'
|
||||
|
||||
@property
|
||||
def has_ipv4(self):
|
||||
"""Whenever to setup IPv4 subnet"""
|
||||
return bool(self.ipv4_cidr)
|
||||
return bool(CONF.tobiko.neutron.ipv4_cidr)
|
||||
|
||||
#: IPv6 sub-net CIDR
|
||||
ipv6_cidr = '2001:db8:1:2::/64'
|
||||
@property
|
||||
def ipv4_cidr(self):
|
||||
if self.has_ipv4:
|
||||
return neutron.new_ipv4_cidr()
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def has_ipv6(self):
|
||||
"""Whenever to setup IPv6 subnet"""
|
||||
return bool(self.ipv6_cidr)
|
||||
return bool(CONF.tobiko.neutron.ipv4_cidr)
|
||||
|
||||
@property
|
||||
def ipv6_cidr(self):
|
||||
if self.has_ipv6:
|
||||
return neutron.new_ipv6_cidr()
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def value_specs(self):
|
||||
return {}
|
||||
|
||||
#: Floating IP network where the Neutron floating IPs are created
|
||||
gateway_network = CONF.tobiko.neutron.floating_network
|
||||
@ -73,10 +86,18 @@ class NetworkStackFixture(heat.HeatStackFixture):
|
||||
def ipv4_subnet_details(self):
|
||||
return neutron.show_subnet(self.ipv4_subnet_id)
|
||||
|
||||
@property
|
||||
def ipv4_subnet_cidr(self):
|
||||
return netaddr.IPNetwork(self.ipv4_subnet_details['cidr'])
|
||||
|
||||
@property
|
||||
def ipv6_subnet_details(self):
|
||||
return neutron.show_subnet(self.ipv6_subnet_id)
|
||||
|
||||
@property
|
||||
def ipv6_subnet_cidr(self):
|
||||
return netaddr.IPNetwork(self.ipv6_subnet_details['cidr'])
|
||||
|
||||
@property
|
||||
def gateway_details(self):
|
||||
return neutron.show_router(self.gateway_id)
|
||||
@ -95,16 +116,11 @@ class NetworkWithNetMtuWriteStackFixture(NetworkStackFixture):
|
||||
#: Value for maximum transfer unit on the internal network
|
||||
mtu = 1000
|
||||
|
||||
def setup_parameters(self):
|
||||
"""Setup Heat template parameters"""
|
||||
super(NetworkWithNetMtuWriteStackFixture, self).setup_parameters()
|
||||
if self.mtu:
|
||||
self.setup_net_mtu_writable()
|
||||
|
||||
@neutron.skip_if_missing_networking_extensions('net-mtu-writable')
|
||||
def setup_net_mtu_writable(self):
|
||||
"""Setup maximum transfer unit size for the network"""
|
||||
self.parameters.setdefault('value_specs', {}).update(mtu=self.mtu)
|
||||
@property
|
||||
def value_specs(self):
|
||||
return dict(
|
||||
super(NetworkWithNetMtuWriteStackFixture, self).value_specs,
|
||||
mtu=int(self.mtu))
|
||||
|
||||
|
||||
@neutron.skip_if_missing_networking_extensions('security-group')
|
||||
|
@ -15,9 +15,6 @@
|
||||
# under the License.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
import netaddr
|
||||
import testtools
|
||||
|
||||
import tobiko
|
||||
@ -45,27 +42,19 @@ class NetworkTestCase(testtools.TestCase):
|
||||
self.assertEqual(self.stack.network_details['mtu'],
|
||||
self.stack.outputs.mtu)
|
||||
|
||||
@unittest.skip('Feature not implemented')
|
||||
def test_ipv4_subnet_cidr(self):
|
||||
if not self.stack.has_ipv4:
|
||||
tobiko.skip('Stack {!s} has no ipv4 subnet', self.stack.stack_name)
|
||||
|
||||
self.assertEqual(str(self.stack.ipv4_cidr),
|
||||
self.stack.ipv4_subnet_details['cidr'])
|
||||
|
||||
subnet = neutron.find_subnet(str(self.stack.ipv4_cidr),
|
||||
subnet = neutron.find_subnet(str(self.stack.ipv4_subnet_cidr),
|
||||
properties=['cidr'])
|
||||
self.assertEqual(neutron.show_subnet(self.stack.ipv4_subnet_id),
|
||||
subnet)
|
||||
|
||||
@unittest.skip('Feature not implemented')
|
||||
def test_ipv6_subnet_cidr(self):
|
||||
if not self.stack.has_ipv6:
|
||||
tobiko.skip('Stack {!s} has no ipv4 subnet', self.stack.stack_name)
|
||||
self.assertEqual(str(self.stack.ipv6_cidr),
|
||||
self.stack.ipv6_subnet_details['cidr'])
|
||||
|
||||
subnet = neutron.find_subnet(str(self.stack.ipv6_cidr),
|
||||
subnet = neutron.find_subnet(str(self.stack.ipv6_subnet_cidr),
|
||||
properties=['cidr'])
|
||||
self.assertEqual(neutron.show_subnet(self.stack.ipv6_subnet_id),
|
||||
subnet)
|
||||
@ -74,14 +63,14 @@ class NetworkTestCase(testtools.TestCase):
|
||||
if not self.stack.has_ipv4 or self.stack.has_gateway:
|
||||
tobiko.skip('Stack {!s} has no IPv4 gateway',
|
||||
self.stack.stack_name)
|
||||
self.assertEqual(str(netaddr.IPNetwork(self.stack.ipv4_cidr).ip + 1),
|
||||
self.assertEqual(str(self.stack.ipv4_cidr.ip + 1),
|
||||
self.stack.ipv4_subnet_details['gateway_ip'])
|
||||
|
||||
def test_ipv6_subnet_gateway_ip(self):
|
||||
if not self.stack.has_ipv6 or self.stack.has_gateway:
|
||||
tobiko.skip('Stack {!s} has no IPv6 gateway',
|
||||
self.stack.stack_name)
|
||||
self.assertEqual(str(netaddr.IPNetwork(self.stack.ipv6_cidr).ip + 1),
|
||||
self.assertEqual(str(self.stack.ipv6_cidr.ip + 1),
|
||||
self.stack.ipv6_subnet_details['gateway_ip'])
|
||||
|
||||
def test_gateway_network(self):
|
||||
|
@ -23,6 +23,7 @@ import yaml
|
||||
|
||||
import tobiko
|
||||
from tobiko.openstack import heat
|
||||
from tobiko.openstack.heat import _stack
|
||||
from tobiko.openstack import keystone
|
||||
from tobiko.tests.unit import openstack
|
||||
|
||||
@ -47,8 +48,16 @@ class MyTemplateFixture(heat.HeatTemplateFixture):
|
||||
template = {'template': 'from-class'}
|
||||
|
||||
|
||||
class MockClient(mock.NonCallableMagicMock):
|
||||
pass
|
||||
|
||||
|
||||
class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
|
||||
def setUp(self):
|
||||
super(HeatStackFixtureTest, self).setUp()
|
||||
self.patch(heatclient, 'Client', MockClient)
|
||||
|
||||
def test_init(self, fixture_class=MyStack, stack_name=None,
|
||||
template=None, parameters=None, wait_interval=None,
|
||||
client=None):
|
||||
@ -62,20 +71,13 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
|
||||
self.check_stack_template(stack=stack, template=template)
|
||||
|
||||
self.assertIs(fixture_class.parameters, stack.parameters)
|
||||
|
||||
self.assertIsInstance(stack.parameters,
|
||||
_stack.HeatStackParametersFixture)
|
||||
self.assertEqual(parameters or fixture_class.parameters or {},
|
||||
stack.parameters.parameters)
|
||||
self.assertEqual(wait_interval or fixture_class.wait_interval,
|
||||
stack.wait_interval)
|
||||
|
||||
if tobiko.is_fixture(client):
|
||||
self.assertIsNone(stack.client)
|
||||
self.assertIs(client, stack.client_fixture)
|
||||
elif client:
|
||||
self.assertIs(client, stack.client)
|
||||
self.assertIsNone(stack.client_fixture)
|
||||
else:
|
||||
self.assertIsNone(stack.client)
|
||||
self.assertIsNone(stack.client_fixture)
|
||||
self.assertIs(client or fixture_class.client, stack.client)
|
||||
|
||||
def test_init_with_stack_name(self):
|
||||
self.test_init(stack_name='my-stack-name')
|
||||
@ -112,12 +114,7 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
stack_name=None, parameters=None, wait_interval=None,
|
||||
stacks=None, create_conflict=False, call_create=True,
|
||||
call_delete=False, call_sleep=False):
|
||||
from tobiko.openstack.heat import _client
|
||||
|
||||
client = mock.MagicMock(specs=heatclient.Client)
|
||||
get_heat_client = self.patch(_client, 'get_heat_client',
|
||||
return_value=client)
|
||||
|
||||
client = MockClient()
|
||||
stacks = stacks or [
|
||||
exc.HTTPNotFound,
|
||||
mock_stack('CREATE_IN_PROGRESS')]
|
||||
@ -131,14 +128,14 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
|
||||
sleep = self.patch(time, 'sleep')
|
||||
stack = fixture_class(stack_name=stack_name, parameters=parameters,
|
||||
template=template, wait_interval=wait_interval)
|
||||
template=template, wait_interval=wait_interval,
|
||||
client=client)
|
||||
|
||||
stack.setUp()
|
||||
|
||||
self.assertIs(client, stack.client)
|
||||
self.assertEqual(wait_interval or fixture_class.wait_interval,
|
||||
stack.wait_interval)
|
||||
get_heat_client.assert_called_once_with()
|
||||
client.stacks.get.assert_has_calls([mock.call(stack.stack_name,
|
||||
resolve_outputs=False)])
|
||||
|
||||
@ -147,11 +144,11 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
else:
|
||||
client.stacks.delete.assert_not_called()
|
||||
|
||||
self.assertEqual(parameters or fixture_class.parameters or {},
|
||||
stack.parameters)
|
||||
parameters = parameters or fixture_class.parameters or {}
|
||||
self.assertEqual(parameters, stack.parameters.values)
|
||||
if call_create:
|
||||
client.stacks.create.assert_called_once_with(
|
||||
parameters=stack.parameters, stack_name=stack.stack_name,
|
||||
parameters=parameters, stack_name=stack.stack_name,
|
||||
template=yaml.safe_dump(stack.template.template))
|
||||
else:
|
||||
client.stacks.create.assert_not_called()
|
||||
@ -169,8 +166,8 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
self.test_setup(template={'other': 'template'})
|
||||
|
||||
def test_setup_with_template_fixture(self):
|
||||
self.test_setup(template=heat.heat_template(template={'template':
|
||||
'from-fixture'}))
|
||||
self.test_setup(template=heat.heat_template({'template':
|
||||
'from-fixture'}))
|
||||
|
||||
def test_setup_with_template_fixture_type(self):
|
||||
self.test_setup(template=MyTemplateFixture)
|
||||
@ -242,7 +239,7 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
self.test_setup(create_conflict=True)
|
||||
|
||||
def test_cleanup(self):
|
||||
client = mock.MagicMock(specs=heatclient.Client)
|
||||
client = MockClient()
|
||||
stack = MyStack(client=client)
|
||||
stack.cleanUp()
|
||||
client.stacks.delete.assert_called_once_with(stack.stack_name)
|
||||
@ -253,7 +250,7 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
'output_value': 'value1'},
|
||||
{'output_key': 'key2',
|
||||
'output_value': 'value2'}])
|
||||
client = mock.MagicMock(specs=heatclient.Client)
|
||||
client = MockClient()
|
||||
client.stacks.get.return_value = stack
|
||||
stack_fixture = MyStack(
|
||||
template={'outputs': {'key1': {}, 'key2': {}}},
|
||||
@ -264,6 +261,17 @@ class HeatStackFixtureTest(openstack.OpenstackTest):
|
||||
self.assertEqual('value1', outputs.key1)
|
||||
self.assertEqual('value2', outputs.key2)
|
||||
|
||||
def test_parameters(self):
|
||||
stack_fixture = MyStack(
|
||||
template={'parameters': {'key1': {}, 'key2': {}}},
|
||||
parameters={'key1': 'value1',
|
||||
'key2': 'value2'})
|
||||
|
||||
parameters = stack_fixture.parameters
|
||||
|
||||
self.assertEqual('value1', parameters.key1)
|
||||
self.assertEqual('value2', parameters.key2)
|
||||
|
||||
def check_stack_template(self, stack, template):
|
||||
expected_template = template or type(stack).template
|
||||
if tobiko.is_fixture(expected_template):
|
||||
|
Loading…
Reference in New Issue
Block a user