Main translation code to handle parameters
Changes to the backend Heat generator to process the parameters from the TOSCA input section, which would be passed in from the CLI or accept the default value. Thi also handles the similar get_attribute function in the output section. Add handler for the nodejs type, needed for the monitoring use case. Fix the test_blockstorage unit test to match the new expected generator output, due to a change in the Heat Nova server resource type. Change-Id: Id73aba9ebaa6828c676a7a2afdee7ec6ef2579d3
This commit is contained in:
parent
e814b85f37
commit
314fb8f2ed
30
translator/hot/tosca/tosca_nodejs.py
Executable file
30
translator/hot/tosca/tosca_nodejs.py
Executable 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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user