Implement OS::Heat::StructuredDeployments

This is a resource which subclasses SoftwareDeployments to deploy a group
of OS::Heat::StructuredDeployment resources

Change-Id: I5f9603c68bb7cd81ef60a6b2ba965e5b8e93887a
Implements-Blueprint: deployment-multiple-servers
This commit is contained in:
Steve Baker 2014-08-19 10:49:10 +12:00
parent 0f1e84ae9f
commit 727ec1ac64
2 changed files with 192 additions and 32 deletions

View File

@ -21,14 +21,6 @@ from heat.engine import properties
from heat.engine.resources.software_config import software_config as sc
from heat.engine.resources.software_config import software_deployment as sd
PROPERTIES = (
INPUT_KEY
) = (
'input_key'
)
DEFAULT_INPUT_KEY = 'get_input'
class StructuredConfig(sc.SoftwareConfig):
'''
@ -40,16 +32,26 @@ class StructuredConfig(sc.SoftwareConfig):
stored and returned by the software_configs API as parsed JSON.
'''
PROPERTIES = (
GROUP,
CONFIG,
OPTIONS,
INPUTS,
OUTPUTS
) = (
sc.SoftwareConfig.GROUP,
sc.SoftwareConfig.CONFIG,
sc.SoftwareConfig.OPTIONS,
sc.SoftwareConfig.INPUTS,
sc.SoftwareConfig.OUTPUTS
)
properties_schema = {
sc.SoftwareConfig.GROUP: sc.SoftwareConfig.properties_schema[
sc.SoftwareConfig.GROUP],
sc.SoftwareConfig.OPTIONS: sc.SoftwareConfig.properties_schema[
sc.SoftwareConfig.OPTIONS],
sc.SoftwareConfig.INPUTS: sc.SoftwareConfig.properties_schema[
sc.SoftwareConfig.INPUTS],
sc.SoftwareConfig.OUTPUTS: sc.SoftwareConfig.properties_schema[
sc.SoftwareConfig.OUTPUTS],
sc.SoftwareConfig.CONFIG: properties.Schema(
GROUP: sc.SoftwareConfig.properties_schema[GROUP],
OPTIONS: sc.SoftwareConfig.properties_schema[OPTIONS],
INPUTS: sc.SoftwareConfig.properties_schema[INPUTS],
OUTPUTS: sc.SoftwareConfig.properties_schema[OUTPUTS],
CONFIG: properties.Schema(
properties.Schema.MAP,
_('Map representing the configuration data structure which will '
'be serialized to JSON format.')
@ -73,32 +75,44 @@ class StructuredDeployment(sd.SoftwareDeployment):
different input_key property value can be specified.
'''
PROPERTIES = (
CONFIG,
SERVER,
INPUT_VALUES,
DEPLOY_ACTIONS,
NAME,
SIGNAL_TRANSPORT,
INPUT_KEY
) = (
sd.SoftwareDeployment.CONFIG,
sd.SoftwareDeployment.SERVER,
sd.SoftwareDeployment.INPUT_VALUES,
sd.SoftwareDeployment.DEPLOY_ACTIONS,
sd.SoftwareDeployment.NAME,
sd.SoftwareDeployment.SIGNAL_TRANSPORT,
'input_key'
)
_sd_ps = sd.SoftwareDeployment.properties_schema
properties_schema = {
sd.SoftwareDeployment.CONFIG: _sd_ps[
sd.SoftwareDeployment.CONFIG],
sd.SoftwareDeployment.SERVER: _sd_ps[
sd.SoftwareDeployment.SERVER],
sd.SoftwareDeployment.INPUT_VALUES: _sd_ps[
sd.SoftwareDeployment.INPUT_VALUES],
sd.SoftwareDeployment.DEPLOY_ACTIONS: _sd_ps[
sd.SoftwareDeployment.DEPLOY_ACTIONS],
sd.SoftwareDeployment.SIGNAL_TRANSPORT: _sd_ps[
sd.SoftwareDeployment.SIGNAL_TRANSPORT],
sd.SoftwareDeployment.NAME: _sd_ps[
sd.SoftwareDeployment.NAME],
CONFIG: _sd_ps[CONFIG],
SERVER: _sd_ps[SERVER],
INPUT_VALUES: _sd_ps[INPUT_VALUES],
DEPLOY_ACTIONS: _sd_ps[DEPLOY_ACTIONS],
SIGNAL_TRANSPORT: _sd_ps[SIGNAL_TRANSPORT],
NAME: _sd_ps[NAME],
INPUT_KEY: properties.Schema(
properties.Schema.STRING,
_('Name of key to use for substituting inputs during deployment'),
default=DEFAULT_INPUT_KEY,
default='get_input',
)
}
def _build_derived_config(self, action, source,
derived_inputs, derived_options):
cfg = source.get(sc.SoftwareConfig.CONFIG)
input_key = self.properties.get(INPUT_KEY)
input_key = self.properties.get(self.INPUT_KEY)
inputs = dict((i['name'], i['value']) for i in derived_inputs)
@ -123,8 +137,56 @@ class StructuredDeployment(sd.SoftwareDeployment):
return snippet
class StructuredDeployments(sd.SoftwareDeployments):
PROPERTIES = (
SERVERS,
CONFIG,
INPUT_VALUES,
DEPLOY_ACTIONS,
NAME,
SIGNAL_TRANSPORT,
INPUT_KEY,
) = (
sd.SoftwareDeployments.SERVERS,
sd.SoftwareDeployments.CONFIG,
sd.SoftwareDeployments.INPUT_VALUES,
sd.SoftwareDeployments.DEPLOY_ACTIONS,
sd.SoftwareDeployments.NAME,
sd.SoftwareDeployments.SIGNAL_TRANSPORT,
StructuredDeployment.INPUT_KEY
)
_sds_ps = sd.SoftwareDeployments.properties_schema
properties_schema = {
SERVERS: _sds_ps[SERVERS],
CONFIG: _sds_ps[CONFIG],
INPUT_VALUES: _sds_ps[INPUT_VALUES],
DEPLOY_ACTIONS: _sds_ps[DEPLOY_ACTIONS],
SIGNAL_TRANSPORT: _sds_ps[SIGNAL_TRANSPORT],
NAME: _sds_ps[NAME],
INPUT_KEY: StructuredDeployment.properties_schema[INPUT_KEY],
}
def _build_resource_definition(self, include_all=False):
p = self.properties
return {
self.RESOURCE_DEF_TYPE: 'OS::Heat::StructuredDeployment',
self.RESOURCE_DEF_PROPERTIES: {
self.CONFIG: p[self.CONFIG],
self.INPUT_VALUES: p[self.INPUT_VALUES],
self.DEPLOY_ACTIONS: p[self.DEPLOY_ACTIONS],
self.SIGNAL_TRANSPORT: p[self.SIGNAL_TRANSPORT],
self.NAME: p[self.NAME],
self.INPUT_KEY: p[self.INPUT_KEY],
}
}
def resource_mapping():
return {
'OS::Heat::StructuredConfig': StructuredConfig,
'OS::Heat::StructuredDeployment': StructuredDeployment,
'OS::Heat::StructuredDeployments': StructuredDeployments,
}

View File

@ -50,11 +50,13 @@ class StructuredConfigTestJSON(HeatTestCase):
def test_resource_mapping(self):
mapping = sc.resource_mapping()
self.assertEqual(2, len(mapping))
self.assertEqual(3, len(mapping))
self.assertEqual(sc.StructuredConfig,
mapping['OS::Heat::StructuredConfig'])
self.assertEqual(sc.StructuredDeployment,
mapping['OS::Heat::StructuredDeployment'])
self.assertEqual(sc.StructuredDeployments,
mapping['OS::Heat::StructuredDeployments'])
self.assertIsInstance(self.config, sc.StructuredConfig)
def test_handle_create(self):
@ -193,3 +195,99 @@ class StructuredDeploymentParseTest(HeatTestCase):
self.assertEqual(
self.result,
parse(self.inputs, self.input_key, self.config))
class StructuredDeploymentsTest(HeatTestCase):
template = {
'heat_template_version': '2013-05-23',
'resources': {
'deploy_mysql': {
'type': 'OS::Heat::StructuredDeployments',
'properties': {
'config': 'config_uuid',
'servers': {'server1': 'uuid1', 'server2': 'uuid2'},
}
}
}
}
def setUp(self):
HeatTestCase.setUp(self)
heat = mock.MagicMock()
self.deployments = heat.return_value.software_deployments
def test_build_resource_definition(self):
stack = utils.parse_stack(self.template)
snip = stack.t.resource_definitions(stack)['deploy_mysql']
resg = sc.StructuredDeployments('test', snip, stack)
expect = {
'type': 'OS::Heat::StructuredDeployment',
'properties': {
'actions': ['CREATE', 'UPDATE'],
'config': 'config_uuid',
'input_key': 'get_input',
'input_values': None,
'name': None,
'signal_transport': 'CFN_SIGNAL'
}
}
self.assertEqual(
expect, resg._build_resource_definition())
self.assertEqual(
expect, resg._build_resource_definition(include_all=True))
def test_resource_names(self):
stack = utils.parse_stack(self.template)
snip = stack.t.resource_definitions(stack)['deploy_mysql']
resg = sc.StructuredDeployments('test', snip, stack)
self.assertEqual(
set(('server1', 'server2')),
set(resg._resource_names())
)
self.assertEqual(
set(('s1', 's2', 's3')),
set(resg._resource_names({
'servers': {'s1': 'u1', 's2': 'u2', 's3': 'u3'}}))
)
def test_assemble_nested(self):
"""
Tests that the nested stack that implements the group is created
appropriately based on properties.
"""
stack = utils.parse_stack(self.template)
snip = stack.t.resource_definitions(stack)['deploy_mysql']
resg = sc.StructuredDeployments('test', snip, stack)
templ = {
"heat_template_version": "2013-05-23",
"resources": {
"server1": {
'type': 'OS::Heat::StructuredDeployment',
'properties': {
'server': 'uuid1',
'actions': ['CREATE', 'UPDATE'],
'config': 'config_uuid',
'input_key': 'get_input',
'input_values': None,
'name': None,
'signal_transport': 'CFN_SIGNAL'
}
},
"server2": {
'type': 'OS::Heat::StructuredDeployment',
'properties': {
'server': 'uuid2',
'actions': ['CREATE', 'UPDATE'],
'config': 'config_uuid',
'input_key': 'get_input',
'input_values': None,
'name': None,
'signal_transport': 'CFN_SIGNAL'
}
}
}
}
self.assertEqual(templ, resg._assemble_nested(['server1', 'server2']))