Merge "Main translation code to handle parameters"

This commit is contained in:
Jenkins
2015-01-08 14:05:34 +00:00
committed by Gerrit Code Review
6 changed files with 111 additions and 55 deletions

View File

@@ -0,0 +1,30 @@
#
# 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_resource import HotResource
class ToscaNodejs(HotResource):
'''Translate TOSCA node type tosca.nodes.SoftwareComponent.Nodejs.'''
# TODO(anyone): this is a custom TOSCA type so it should be kept separate
# from the TOSCA base types; need to come up with a scheme so new custom
# types can be added by users.
toscatype = 'tosca.nodes.SoftwareComponent.Nodejs'
def __init__(self, nodetemplate):
super(ToscaNodejs, self).__init__(nodetemplate)
pass
def handle_properties(self):
pass

View File

@@ -25,12 +25,15 @@ class TOSCATranslator(object):
self.tosca = tosca
self.hot_template = HotTemplate()
self.parsed_params = parsed_params
self.node_translator = None
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.node_translator = TranslateNodeTemplates(self.tosca.nodetemplates,
self.hot_template)
self.hot_template.resources = self.node_translator.translate()
self.hot_template.outputs = self._translate_outputs()
return self.hot_template.output_to_yaml()
@@ -38,13 +41,8 @@ class TOSCATranslator(object):
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)
translator = TranslateOutputs(self.tosca.outputs, self.node_translator)
return translator.translate()
# check all properties for all node and ensure they are resolved

View File

@@ -12,6 +12,7 @@
# under the License.
from translator.hot.syntax.hot_parameter import HotParameter
from translator.toscalib.utils.gettextutils import _
INPUT_CONSTRAINTS = (CONSTRAINTS, DESCRIPTION, LENGTH, RANGE,
@@ -68,11 +69,17 @@ class TranslateInputs():
hc, hvalue = self._translate_constraints(
constraint.constraint_key, constraint.constraint_value)
hot_constraints.append({hc: hvalue})
cli_value = self.parsed_params[input.name]
if input.name in self.parsed_params:
hot_default = self.parsed_params[input.name]
elif input.default is not None:
hot_default = input.default
else:
raise Exception(_("Need to specify a value "
"for input {0}").format(input.name))
hot_inputs.append(HotParameter(name=input.name,
type=hot_input_type,
description=input.description,
default=cli_value,
default=hot_default,
constraints=hot_constraints))
return hot_inputs

View File

@@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from translator.hot.tosca.tosca_block_storage import ToscaBlockStorage
from translator.hot.tosca.tosca_block_storage_attachment import (
ToscaBlockStorageAttachment
@@ -18,8 +19,12 @@ from translator.hot.tosca.tosca_block_storage_attachment import (
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_nodejs import ToscaNodejs
from translator.hot.tosca.tosca_webserver import ToscaWebserver
from translator.hot.tosca.tosca_wordpress import ToscaWordpress
from translator.toscalib.functions import GetAttribute
from translator.toscalib.functions import GetInput
from translator.toscalib.functions import GetProperty
from translator.toscalib.relationship_template import RelationshipTemplate
SECTIONS = (TYPE, PROPERTIES, REQUIREMENTS, INTERFACES, LIFECYCLE, INPUT) = \
@@ -45,7 +50,8 @@ TOSCA_TO_HOT_TYPE = {'tosca.nodes.Compute': ToscaCompute,
'tosca.nodes.DBMS': ToscaDbms,
'tosca.nodes.Database': ToscaDatabase,
'tosca.nodes.WebApplication.WordPress': ToscaWordpress,
'tosca.nodes.BlockStorage': ToscaBlockStorage}
'tosca.nodes.BlockStorage': ToscaBlockStorage,
'tosca.nodes.SoftwareComponent.Nodejs': ToscaNodejs}
TOSCA_TO_HOT_REQUIRES = {'container': 'server', 'host': 'server',
'dependency': 'depends_on', "connects": 'depends_on'}
@@ -59,20 +65,22 @@ class TranslateNodeTemplates():
def __init__(self, nodetemplates, hot_template):
self.nodetemplates = nodetemplates
self.hot_template = hot_template
# list of all HOT resources generated
self.hot_resources = []
# mapping between TOSCA nodetemplate and HOT resource
self.hot_lookup = {}
def translate(self):
return self._translate_nodetemplates()
def _translate_nodetemplates(self):
hot_resources = []
hot_lookup = {}
suffix = 0
# 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
self.hot_resources.append(hot_node)
self.hot_lookup[node] = hot_node
# BlockStorage Attachment is a special case,
# which doesn't match to Heat Resources 1 to 1.
@@ -80,7 +88,7 @@ class TranslateNodeTemplates():
volume_name = None
requirements = node.requirements
if requirements:
# Find the name of associated BlockStorage node
# Find the name of associated BlockStorage node
for requires in requirements:
for value in requires.values():
for n in self.nodetemplates:
@@ -92,16 +100,16 @@ class TranslateNodeTemplates():
suffix,
volume_name)
if attachment_node:
hot_resources.append(attachment_node)
self.hot_resources.append(attachment_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:
for resource in self.hot_resources:
expanded = resource.handle_life_cycle()
if expanded:
lifecycle_resources += expanded
hot_resources += lifecycle_resources
self.hot_resources += lifecycle_resources
# Copy the initial dependencies based on the relationship in
# the TOSCA template
@@ -110,22 +118,44 @@ class TranslateNodeTemplates():
# 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}
self.hot_lookup[node].properties['server'] = \
{'get_resource': self.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())
self.hot_lookup[node].depends_on.append(
self.hot_lookup[node_depend].top_of_chain())
# handle hosting relationship
for resource in hot_resources:
for resource in self.hot_resources:
resource.handle_hosting()
# Handle properties
for resource in hot_resources:
# handle built-in properties of HOT resources
for resource in self.hot_resources:
resource.handle_properties()
return hot_resources
# Resolve function calls: GetProperty, GetAttribute, GetInput
# at this point, all the HOT resources should have been created
# in the graph.
for resource in self.hot_resources:
# traverse the reference chain to get the actual value
inputs = resource.properties.get('input_values')
if inputs:
for name, value in six.iteritems(inputs):
if isinstance(value, GetAttribute):
# for the attribute
# get the proper target type to perform the translation
args = value.result()
target = args[0]
hot_target = self.find_hot_resource(target)
inputs[name] = hot_target.get_hot_attribute(args[1],
args)
else:
if isinstance(value, GetProperty) or \
isinstance(value, GetInput):
inputs[name] = value.result()
return self.hot_resources
def _get_attachment_node(self, node, suffix, volume_name):
attach = False
@@ -145,3 +175,8 @@ class TranslateNodeTemplates():
volume_name
)
return hot_node
def find_hot_resource(self, name):
for resource in self.hot_resources:
if resource.name == name:
return resource

View File

@@ -12,8 +12,6 @@
# under the License.
from translator.hot.syntax.hot_output import HotOutput
from translator.toscalib import functions
from translator.toscalib.utils.gettextutils import _
TOSCA_TO_HOT_GET_ATTRS = {'ip_address': 'first_address'}
@@ -21,8 +19,9 @@ TOSCA_TO_HOT_GET_ATTRS = {'ip_address': 'first_address'}
class TranslateOutputs():
'''Translate TOSCA Outputs to Heat Outputs.'''
def __init__(self, outputs):
def __init__(self, outputs, node_translator):
self.outputs = outputs
self.nodes = node_translator
def translate(self):
return self._translate_outputs()
@@ -30,29 +29,16 @@ class TranslateOutputs():
def _translate_outputs(self):
hot_outputs = []
for output in self.outputs:
hot_value = {}
if isinstance(output.value, functions.GetAttribute):
func = output.value
get_parameters = [
func.get_referenced_node_template().name,
self._translate_attribute_name(func.attribute_name)]
hot_value['get_attr'] = get_parameters
elif isinstance(output.value, functions.GetProperty):
func = output.value
if func.req_or_cap:
raise NotImplementedError(_(
'get_property with requirement/capability in outputs '
'translation is not supported'))
get_parameters = [
func.node_template_name,
self._translate_attribute_name(func.property_name)]
hot_value['get_attr'] = get_parameters
if output.value.name == 'get_attribute':
get_parameters = output.value.args
hot_target = self.nodes.find_hot_resource(get_parameters[0])
hot_value = hot_target.get_hot_attribute(get_parameters[1],
get_parameters)
hot_outputs.append(HotOutput(output.name,
hot_value,
output.description))
else:
hot_value['get_attr'] = output.value
hot_outputs.append(HotOutput(output.name,
hot_value,
output.description))
hot_outputs.append(HotOutput(output.name,
output.value,
output.description))
return hot_outputs
def _translate_attribute_name(self, attribute_name):
return TOSCA_TO_HOT_GET_ATTRS.get(attribute_name, attribute_name)

View File

@@ -51,12 +51,12 @@ class ToscaBlockStorageTest(TestCase):
self.assertEqual(
'Public IP address of the newly created compute instance.',
outputs['public_ip']['description'])
self.assertEqual({'get_attr': ['my_server', 'first_address']},
self.assertEqual({'get_attr': ['my_server', 'networks', 'private', 0]},
outputs['public_ip']['value'])
self.assertIn('volume_id', outputs)
self.assertEqual('The volume id of the block storage instance.',
outputs['volume_id']['description'])
self.assertEqual({'get_attr': ['my_storage', 'volume_id']},
self.assertEqual({'get_resource': 'my_storage'},
outputs['volume_id']['value'])
def test_translate_multi_storage(self):