TOSCA generator top level code

This set is invoked after the parser has created the TOSCA graph as the
intermediate representation.  It iterates over the TOSCA graph and creates
the graph for the Heat template.  It invokes the classes in the tosca
and syntax directories.

Partially implements blueprint heat-translator-tosca
Change-Id: Idbbbf2a9992ffac51bdaf93a68039b0fecc928dc
This commit is contained in:
Ton Ngo 2014-06-10 12:30:09 -07:00
parent 40a9ce5629
commit e76adf3c6c
5 changed files with 316 additions and 0 deletions

View File

View File

@ -0,0 +1,61 @@
#
# 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 translator.hot.syntax.hot_template import HotTemplate
from translator.hot.translate_inputs import TranslateInputs
from translator.hot.translate_node_templates import TranslateNodeTemplates
from translator.hot.translate_outputs import TranslateOutputs
class TOSCATranslator(object):
'''Invokes translation methods.'''
def __init__(self, tosca, parsed_params):
super(TOSCATranslator, self).__init__()
self.tosca = tosca
self.hot_template = HotTemplate()
self.parsed_params = parsed_params
def translate(self):
self._resolve_input()
self.hot_template.description = self.tosca.description
self.hot_template.parameters = self._translate_inputs()
self.hot_template.resources = self._translate_node_templates()
self.hot_template.outputs = self._translate_outputs()
return self.hot_template.output_to_yaml()
def _translate_inputs(self):
translator = TranslateInputs(self.tosca.inputs, self.parsed_params)
return translator.translate()
def _translate_node_templates(self):
translator = TranslateNodeTemplates(self.tosca.nodetemplates,
self.hot_template)
return translator.translate()
def _translate_outputs(self):
translator = TranslateOutputs(self.tosca.outputs)
return translator.translate()
# check all properties for all node and ensure they are resolved
# to actual value
def _resolve_input(self):
for n in self.tosca.nodetemplates:
for node_prop in n.properties:
if isinstance(node_prop.value, dict):
try:
self.parsed_params[node_prop.value['get_input']]
except Exception:
raise ValueError('Must specify all input values in \
TOSCA template, missing %s' %
node_prop.value['get_input'])

View File

@ -0,0 +1,110 @@
#
# 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 translator.hot.syntax.hot_parameter import HotParameter
INPUT_CONSTRAINTS = (CONSTRAINTS, DESCRIPTION, LENGTH, RANGE,
MIN, MAX, ALLOWED_VALUES, ALLOWED_PATTERN) = \
('constraints', 'description', 'length', 'range',
'min', 'max', 'allowed_values', 'allowed_pattern')
TOSCA_CONSTRAINT_OPERATORS = (EQUAL, GREATER_THAN, GREATER_OR_EQUAL, LESS_THAN,
LESS_OR_EQUAL, IN_RANGE, VALID_VALUES, LENGTH,
MIN_LENGTH, MAX_LENGTH, PATTERN) = \
('equal', 'greater_than', 'greater_or_equal',
'less_than', 'less_or_equal', 'in_range',
'valid_values', 'length', 'min_length',
'max_length', 'pattern')
TOSCA_TO_HOT_CONSTRAINTS_ATTRS = {'equal': 'allowed_values',
'greater_than': 'range',
'greater_or_equal': 'range',
'less_than': 'range',
'less_or_equal': 'range',
'in_range': 'range',
'valid_values': 'allowed_values',
'length': 'length',
'min_length': 'length',
'max_length': 'length',
'pattern': 'allowed_pattern'}
TOSCA_TO_HOT_INPUT_TYPES = {'string': 'string',
'integer': 'number',
'float': 'number',
'boolean': 'boolean',
'timestamp': 'string',
'null': 'string'}
class TranslateInputs():
'''Translate TOSCA Inputs to Heat Parameters.'''
def __init__(self, inputs, parsed_params):
self.inputs = inputs
self.parsed_params = parsed_params
def translate(self):
return self._translate_inputs()
def _translate_inputs(self):
hot_inputs = []
for input in self.inputs:
hot_input_type = TOSCA_TO_HOT_INPUT_TYPES[input.type]
hot_constraints = []
if input.constraints:
for constraint in input.constraints:
constraint_name, value = constraint.iteritems().next()
hc, hvalue = self._translate_constraints(constraint_name,
value)
hot_constraints.append({hc: hvalue})
cli_value = self.parsed_params[input.name]
hot_inputs.append(HotParameter(name=input.name,
type=hot_input_type,
description=input.description,
default=cli_value,
constraints=hot_constraints))
return hot_inputs
def _translate_constraints(self, name, value):
hot_constraint = TOSCA_TO_HOT_CONSTRAINTS_ATTRS[name]
# Offset used to support less_than and greater_than.
# TODO(anyone): when parser supports float, verify this works
offset = 1
if name == EQUAL:
hot_value = [value]
elif name == GREATER_THAN:
hot_value = {"min": value + offset}
elif name == GREATER_OR_EQUAL:
hot_value = {"min": value}
elif name == LESS_THAN:
hot_value = {"max": value - offset}
elif name == LESS_OR_EQUAL:
hot_value = {"max": value}
elif name == IN_RANGE:
range_values = value.keys()
min_value = min(range_values)
max_value = max(range_values)
hot_value = {"min": min_value, "max": max_value}
elif name == LENGTH:
hot_value = {"min": value, "max": value}
elif name == MIN_LENGTH:
hot_value = {"min": value}
elif name == MAX_LENGTH:
hot_value = {"max": value}
else:
hot_value = value
return hot_constraint, hot_value

View File

@ -0,0 +1,100 @@
#
# 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 translator.hot.tosca.tosca_compute import ToscaCompute
from translator.hot.tosca.tosca_database import ToscaDatabase
from translator.hot.tosca.tosca_dbms import ToscaDbms
from translator.hot.tosca.tosca_webserver import ToscaWebserver
from translator.hot.tosca.tosca_wordpress import ToscaWordpress
SECTIONS = (TYPE, PROPERTIES, REQUIREMENTS, INTERFACES, LIFECYCLE, INPUT) = \
('type', 'properties', 'requirements',
'interfaces', 'lifecycle', 'input')
# TODO(anyone): the following requirement names should not be hard-coded
# in the translator. Since they are basically arbitrary names, we have to get
# them from TOSCA type definitions.
# To be fixed with the blueprint:
# https://blueprints.launchpad.net/heat-translator/+spec/tosca-custom-types
REQUIRES = (CONTAINER, DEPENDENCY, DATABASE_ENDPOINT, CONNECTION, HOST) = \
('container', 'dependency', 'database_endpoint',
'connection', 'host')
INTERFACES_STATE = (CREATE, START, CONFIGURE, START, DELETE) = \
('create', 'stop', 'configure', 'start', 'delete')
# dict to look up HOT translation class,
# TODO(replace with function to scan the classes in translator.hot.tosca)
TOSCA_TO_HOT_TYPE = {'tosca.nodes.Compute': ToscaCompute,
'tosca.nodes.WebServer': ToscaWebserver,
'tosca.nodes.DBMS': ToscaDbms,
'tosca.nodes.Database': ToscaDatabase,
'tosca.nodes.WebApplication.WordPress': ToscaWordpress}
TOSCA_TO_HOT_REQUIRES = {'container': 'server', 'host': 'server',
'dependency': 'depends_on', "connects": 'depends_on'}
TOSCA_TO_HOT_PROPERTIES = {'properties': 'input'}
class TranslateNodeTemplates():
'''Translate TOSCA NodeTemplates to Heat Resources.'''
def __init__(self, nodetemplates, hot_template):
self.nodetemplates = nodetemplates
self.hot_template = hot_template
def translate(self):
return self._translate_nodetemplates()
def _translate_nodetemplates(self):
hot_resources = []
hot_lookup = {}
# Copy the TOSCA graph: nodetemplate
for node in self.nodetemplates:
hot_node = TOSCA_TO_HOT_TYPE[node.type](node)
hot_resources.append(hot_node)
hot_lookup[node] = hot_node
# Handle life cycle operations: this may expand each node into
# multiple HOT resources and may change their name
lifecycle_resources = []
for resource in hot_resources:
expanded = resource.handle_life_cycle()
lifecycle_resources += expanded
hot_resources += lifecycle_resources
# Copy the initial dependencies based on the relationship in
# the TOSCA template
for node in self.nodetemplates:
for node_depend in node.related_nodes:
# if the source of dependency is a server, add dependency
# as properties.get_resource
if node_depend.type == 'tosca.nodes.Compute':
hot_lookup[node].properties['server'] = \
{'get_resource': hot_lookup[node_depend].name}
# for all others, add dependency as depends_on
else:
hot_lookup[node].depends_on.append(hot_lookup[node_depend].
top_of_chain())
# handle hosting relationship
for resource in hot_resources:
resource.handle_hosting()
# Handle properties
for resource in hot_resources:
resource.handle_properties()
return hot_resources

View File

@ -0,0 +1,45 @@
#
# 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 translator.hot.syntax.hot_output import HotOutput
TOSCA_TO_HOT_GET_ATTRS = {'ip_address': 'first_address'}
class TranslateOutputs():
'''Translate TOSCA Outputs to Heat Outputs.'''
def __init__(self, outputs):
self.outputs = outputs
def translate(self):
return self._translate_outputs()
def _translate_outputs(self):
hot_outputs = []
for output in self.outputs:
hot_value = {}
if 'get_property' in output.value:
get_parameters = output.value['get_property']
if get_parameters[1] in TOSCA_TO_HOT_GET_ATTRS:
get_parameters[1] = \
TOSCA_TO_HOT_GET_ATTRS[get_parameters[1]]
hot_value['get_attr'] = get_parameters
hot_outputs.append(HotOutput(output.name,
hot_value,
output.description))
else:
hot_outputs.append(HotOutput(output.name,
output.value,
output.description))
return hot_outputs