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:
parent
40a9ce5629
commit
e76adf3c6c
0
translator/hot/__init__.py
Normal file
0
translator/hot/__init__.py
Normal file
61
translator/hot/tosca_translator.py
Normal file
61
translator/hot/tosca_translator.py
Normal 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'])
|
110
translator/hot/translate_inputs.py
Normal file
110
translator/hot/translate_inputs.py
Normal 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
|
100
translator/hot/translate_node_templates.py
Normal file
100
translator/hot/translate_node_templates.py
Normal 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
|
45
translator/hot/translate_outputs.py
Normal file
45
translator/hot/translate_outputs.py
Normal 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
|
Loading…
Reference in New Issue
Block a user