TOSCA generator template syntax
A set of classes to hold the required data structure for a Heat template. The generator uses these classes to construct the HOT graph, then print the yaml text Partially implements blueprint heat-translator-tosca Change-Id: If9def2d90483d84824e405ed69b61c9fb757d8ac
This commit is contained in:
parent
78ded47386
commit
89c397901c
0
translator/hot/syntax/__init__.py
Normal file
0
translator/hot/syntax/__init__.py
Normal file
25
translator/hot/syntax/hot_output.py
Normal file
25
translator/hot/syntax/hot_output.py
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# 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 HotOutput(object):
|
||||
'''Attributes for HOT output section.'''
|
||||
|
||||
def __init__(self, name, value, description=None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.description = description
|
||||
|
||||
def get_dict_output(self):
|
||||
return {self.name: {'value': self.value,
|
||||
'description': self.description}}
|
44
translator/hot/syntax/hot_parameter.py
Normal file
44
translator/hot/syntax/hot_parameter.py
Normal file
@ -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.
|
||||
|
||||
KEYS = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS, HIDDEN, LABEL) = \
|
||||
('type', 'description', 'default', 'constraints', 'hidden', 'label')
|
||||
|
||||
|
||||
class HotParameter(object):
|
||||
'''Attributes for HOT parameter section.'''
|
||||
|
||||
def __init__(self, name, type, label=None, description=None, default=None,
|
||||
hidden=None, constraints=None):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.label = label
|
||||
self.description = description
|
||||
self.default = default
|
||||
self.hidden = hidden
|
||||
self.constraints = constraints
|
||||
|
||||
def get_dict_output(self):
|
||||
param_sections = {TYPE: self.type}
|
||||
if self.label:
|
||||
param_sections[LABEL] = self.label
|
||||
if self.description:
|
||||
param_sections[DESCRIPTION] = self.description
|
||||
if self.default:
|
||||
param_sections[DEFAULT] = self.default
|
||||
if self.hidden:
|
||||
param_sections[HIDDEN] = self.hidden
|
||||
if self.constraints:
|
||||
param_sections[CONSTRAINTS] = self.constraints
|
||||
|
||||
return {self.name: param_sections}
|
166
translator/hot/syntax/hot_resource.py
Normal file
166
translator/hot/syntax/hot_resource.py
Normal file
@ -0,0 +1,166 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
SECTIONS = (TYPE, PROPERTIES, MEDADATA, DEPENDS_ON, UPDATE_POLICY,
|
||||
DELETION_POLICY) = \
|
||||
('type', 'properties', 'metadata',
|
||||
'depends_on', 'update_policy', 'deletion_policy')
|
||||
|
||||
|
||||
class HotResource(object):
|
||||
'''Base class for TOSCA node type translation to Heat resource type.'''
|
||||
|
||||
def __init__(self, nodetemplate, name=None, type=None, properties=None,
|
||||
metadata=None, depends_on=None,
|
||||
update_policy=None, deletion_policy=None):
|
||||
self.nodetemplate = nodetemplate
|
||||
if name:
|
||||
self.name = name
|
||||
else:
|
||||
self.name = nodetemplate.name
|
||||
self.type = type
|
||||
self.properties = properties
|
||||
# special case for HOT softwareconfig
|
||||
if type == 'OS::Heat::SoftwareConfig':
|
||||
self.properties['group'] = 'script'
|
||||
self.metadata = metadata
|
||||
if depends_on:
|
||||
self.depends_on = depends_on
|
||||
else:
|
||||
self.depends_on = []
|
||||
self.update_policy = update_policy
|
||||
self.deletion_policy = deletion_policy
|
||||
self.group_dependencies = {}
|
||||
|
||||
def handle_properties(self):
|
||||
# the property can hold a value or the intrinsic function get_input
|
||||
# for value, copy it
|
||||
# for get_input, convert to get_param
|
||||
for prop in self.nodetemplate.properties:
|
||||
pass
|
||||
|
||||
def handle_life_cycle(self):
|
||||
hot_resources = []
|
||||
deploy_lookup = {}
|
||||
interfaces_deploy_sequence = ['create', 'start', 'configure']
|
||||
|
||||
# create HotResource for each interface used for deployment:
|
||||
# create, start, configure
|
||||
# ignore the other interfaces
|
||||
# observe the order: create, start, configure
|
||||
# use the current HotResource for the first interface in this order
|
||||
|
||||
# hold the original name since it will be changed during
|
||||
# the transformation
|
||||
node_name = self.name
|
||||
reserve_current = 'NONE'
|
||||
interfaces_actual = []
|
||||
for interface in self.nodetemplate.interfaces:
|
||||
interfaces_actual.append(interface.name)
|
||||
for operation in interfaces_deploy_sequence:
|
||||
if operation in interfaces_actual:
|
||||
reserve_current = operation
|
||||
break
|
||||
|
||||
# create the set of SoftwareDeployment and SoftwareConfig for
|
||||
# the interface operations
|
||||
for interface in self.nodetemplate.interfaces:
|
||||
if interface.name in interfaces_deploy_sequence:
|
||||
config_name = node_name + '_' + interface.name + '_config'
|
||||
deploy_name = node_name + '_' + interface.name + '_deploy'
|
||||
hot_resources.append(
|
||||
HotResource(self.nodetemplate,
|
||||
config_name,
|
||||
'OS::Heat::SoftwareConfig',
|
||||
{'config':
|
||||
{'get_file': interface.implementation}}))
|
||||
if interface.name == reserve_current:
|
||||
deploy_resource = self
|
||||
self.name = deploy_name
|
||||
self.type = 'OS::Heat::SoftwareDeployment'
|
||||
self.properties = {'config': {'get_resource': config_name}}
|
||||
deploy_lookup[interface.name] = self
|
||||
else:
|
||||
deploy_resource = \
|
||||
HotResource(self.nodetemplate,
|
||||
deploy_name,
|
||||
'OS::Heat::SoftwareDeployment',
|
||||
{'config': {'get_resource': config_name}})
|
||||
hot_resources.append(deploy_resource)
|
||||
deploy_lookup[interface.name] = deploy_resource
|
||||
|
||||
# Add dependencies for the set of HOT resources in the sequence defined
|
||||
# in interfaces_deploy_sequence
|
||||
# TODO(anyone): find some better way to encode this implicit sequence
|
||||
group = {}
|
||||
for op, hot in deploy_lookup.iteritems():
|
||||
# position to determine potential preceding nodes
|
||||
op_index = interfaces_deploy_sequence.index(op)
|
||||
for preceding_op in \
|
||||
reversed(interfaces_deploy_sequence[:op_index]):
|
||||
preceding_hot = deploy_lookup.get(preceding_op)
|
||||
if preceding_hot:
|
||||
hot.depends_on.append(preceding_hot)
|
||||
group[preceding_hot] = hot
|
||||
break
|
||||
|
||||
# save this dependency chain in the set of HOT resources
|
||||
self.group_dependencies.update(group)
|
||||
for hot in hot_resources:
|
||||
hot.group_dependencies.update(group)
|
||||
|
||||
return hot_resources
|
||||
|
||||
def handle_hosting(self):
|
||||
# handle hosting server for the OS:HEAT::SoftwareDeployment
|
||||
# from the TOSCA nodetemplate, traverse the relationship chain
|
||||
# down to the server
|
||||
if self.type == 'OS::Heat::SoftwareDeployment':
|
||||
# skip if already have hosting
|
||||
host_server = self.properties.get('server')
|
||||
if host_server is None:
|
||||
host_server = self.bottom_of_chain().\
|
||||
properties['server']['get_resource']
|
||||
self.properties['server'] = {'get_resource': host_server}
|
||||
|
||||
def top_of_chain(self):
|
||||
dependent = self.group_dependencies.get(self)
|
||||
if dependent is None:
|
||||
return self
|
||||
else:
|
||||
return dependent.top_of_chain()
|
||||
|
||||
# TODO(anyone): traverse using the relationship requirement:host in TOSCA
|
||||
def bottom_of_chain(self):
|
||||
if len(self.depends_on) == 0:
|
||||
return self
|
||||
else:
|
||||
for preceding in self.depends_on:
|
||||
return preceding.bottom_of_chain()
|
||||
|
||||
def get_dict_output(self):
|
||||
resource_sections = {TYPE: self.type}
|
||||
if self.properties:
|
||||
resource_sections[PROPERTIES] = self.properties
|
||||
if self.metadata:
|
||||
resource_sections[MEDADATA] = self.metadata
|
||||
if self.depends_on:
|
||||
resource_sections[DEPENDS_ON] = []
|
||||
for depend in self.depends_on:
|
||||
resource_sections[DEPENDS_ON].append(depend.name)
|
||||
if self.update_policy:
|
||||
resource_sections[UPDATE_POLICY] = self.update_policy
|
||||
if self.deletion_policy:
|
||||
resource_sections[DELETION_POLICY] = self.deletion_policy
|
||||
|
||||
return {self.name: resource_sections}
|
67
translator/hot/syntax/hot_template.py
Normal file
67
translator/hot/syntax/hot_template.py
Normal file
@ -0,0 +1,67 @@
|
||||
#
|
||||
# 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 textwrap
|
||||
import yaml
|
||||
|
||||
|
||||
class HotTemplate(object):
|
||||
'''Container for full Heat Orchestration template.'''
|
||||
|
||||
SECTIONS = (VERSION, DESCRIPTION, PARAMETER_GROUPS, PARAMETERS,
|
||||
RESOURCES, OUTPUTS, MAPPINGS) = \
|
||||
('heat_template_version', 'description', 'parameter_groups',
|
||||
'parameters', 'resources', 'outputs', '__undefined__')
|
||||
|
||||
VERSIONS = (LATEST,) = ('2013-05-23',)
|
||||
|
||||
def __init__(self):
|
||||
self.resources = []
|
||||
self.outputs = []
|
||||
self.parameters = []
|
||||
self.description = ""
|
||||
|
||||
def output_to_yaml(self):
|
||||
dict_output = {}
|
||||
# Version
|
||||
version_string = self.VERSION + ": " + self.LATEST + "\n\n"
|
||||
|
||||
# Description
|
||||
desc_str = ""
|
||||
if self.description:
|
||||
# Wrap the text to a new line if the line exceeds 80 characters.
|
||||
wrapped_txt = "\n ".join(textwrap.wrap(self.description, 80))
|
||||
desc_str = self.DESCRIPTION + ": >\n " + wrapped_txt + "\n\n"
|
||||
|
||||
# Parameters
|
||||
all_params = {}
|
||||
for parameter in self.parameters:
|
||||
all_params.update(parameter.get_dict_output())
|
||||
dict_output.update({self.PARAMETERS: all_params})
|
||||
|
||||
# Resources
|
||||
all_resources = {}
|
||||
for resource in self.resources:
|
||||
all_resources.update(resource.get_dict_output())
|
||||
dict_output.update({self.RESOURCES: all_resources})
|
||||
|
||||
# Outputs
|
||||
all_outputs = {}
|
||||
for output in self.outputs:
|
||||
all_outputs.update(output.get_dict_output())
|
||||
dict_output.update({self.OUTPUTS: all_outputs})
|
||||
|
||||
yaml_string = yaml.dump(dict_output, default_flow_style=False)
|
||||
# get rid of the '' from yaml.dump around numbers
|
||||
yaml_string = yaml_string.replace('\'', '')
|
||||
return version_string + desc_str + yaml_string
|
Loading…
x
Reference in New Issue
Block a user