Implement substitution mappings
Add validation for substitute_mappings and a test example. The substitution is responsibility of an orchestrator so work needs to be done in heat-translator to translate substitute_mapping in HOT. Partially example of substitute_mappings is given in the test/data/topology_template/subsystem.yaml Refer to the spec section 'Using node template substitution for model composition' and related sections to read detail on the substitute_mappings. The related heat-translator blueprint is https://blueprints.launchpad.net/heat-translator/+spec/ nested-templates Partially-Implements: blueprint tosca-substitution-mappings Co-Authored-By: Sahdev Zala <spzala@us.ibm.com> Change-Id: I7baadef4f0b4c7cd426f710f4c74c198d02bc030 Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
This commit is contained in:
@@ -22,12 +22,13 @@ class TypeValidation(object):
|
|||||||
DSL_DEFINITIONS, NODE_TYPES, REPOSITORIES,
|
DSL_DEFINITIONS, NODE_TYPES, REPOSITORIES,
|
||||||
DATA_TYPES, ARTIFACT_TYPES, GROUP_TYPES,
|
DATA_TYPES, ARTIFACT_TYPES, GROUP_TYPES,
|
||||||
RELATIONSHIP_TYPES, CAPABILITY_TYPES,
|
RELATIONSHIP_TYPES, CAPABILITY_TYPES,
|
||||||
INTERFACE_TYPES, POLICY_TYPES) = \
|
INTERFACE_TYPES, POLICY_TYPES,
|
||||||
|
TOPOLOGY_TEMPLATE) = \
|
||||||
('tosca_definitions_version', 'description', 'imports',
|
('tosca_definitions_version', 'description', 'imports',
|
||||||
'dsl_definitions', 'node_types', 'repositories',
|
'dsl_definitions', 'node_types', 'repositories',
|
||||||
'data_types', 'artifact_types', 'group_types',
|
'data_types', 'artifact_types', 'group_types',
|
||||||
'relationship_types', 'capability_types',
|
'relationship_types', 'capability_types',
|
||||||
'interface_types', 'policy_types')
|
'interface_types', 'policy_types', 'topology_template')
|
||||||
VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
|
VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
|
||||||
exttools = ExtTools()
|
exttools = ExtTools()
|
||||||
VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
|
VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
|
||||||
|
|||||||
@@ -724,11 +724,12 @@ def get_function(tosca_tpl, node_template, raw_function):
|
|||||||
parsing was unsuccessful.
|
parsing was unsuccessful.
|
||||||
"""
|
"""
|
||||||
if is_function(raw_function):
|
if is_function(raw_function):
|
||||||
func_name = list(raw_function.keys())[0]
|
if isinstance(raw_function, dict):
|
||||||
if func_name in function_mappings:
|
func_name = list(raw_function.keys())[0]
|
||||||
func = function_mappings[func_name]
|
if func_name in function_mappings:
|
||||||
func_args = list(raw_function.values())[0]
|
func = function_mappings[func_name]
|
||||||
if not isinstance(func_args, list):
|
func_args = list(raw_function.values())[0]
|
||||||
func_args = [func_args]
|
if not isinstance(func_args, list):
|
||||||
return func(tosca_tpl, node_template, func_name, func_args)
|
func_args = [func_args]
|
||||||
|
return func(tosca_tpl, node_template, func_name, func_args)
|
||||||
return raw_function
|
return raw_function
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class ImportsLoader(object):
|
|||||||
tpl=None):
|
tpl=None):
|
||||||
self.importslist = importslist
|
self.importslist = importslist
|
||||||
self.custom_defs = {}
|
self.custom_defs = {}
|
||||||
|
self.nested_tosca_tpls = []
|
||||||
if not path and not tpl:
|
if not path and not tpl:
|
||||||
msg = _('Input tosca template is not provided.')
|
msg = _('Input tosca template is not provided.')
|
||||||
log.warning(msg)
|
log.warning(msg)
|
||||||
@@ -56,6 +57,9 @@ class ImportsLoader(object):
|
|||||||
def get_custom_defs(self):
|
def get_custom_defs(self):
|
||||||
return self.custom_defs
|
return self.custom_defs
|
||||||
|
|
||||||
|
def get_nested_tosca_tpls(self):
|
||||||
|
return self.nested_tosca_tpls
|
||||||
|
|
||||||
def _validate_and_load_imports(self):
|
def _validate_and_load_imports(self):
|
||||||
imports_names = set()
|
imports_names = set()
|
||||||
|
|
||||||
@@ -77,8 +81,8 @@ class ImportsLoader(object):
|
|||||||
ValidationError(message=msg))
|
ValidationError(message=msg))
|
||||||
imports_names.add(import_name)
|
imports_names.add(import_name)
|
||||||
|
|
||||||
custom_type = self._load_import_template(import_name,
|
full_file_name, custom_type = self._load_import_template(
|
||||||
import_uri)
|
import_name, import_uri)
|
||||||
namespace_prefix = None
|
namespace_prefix = None
|
||||||
if isinstance(import_uri, dict):
|
if isinstance(import_uri, dict):
|
||||||
namespace_prefix = import_uri.get(
|
namespace_prefix = import_uri.get(
|
||||||
@@ -87,13 +91,15 @@ class ImportsLoader(object):
|
|||||||
TypeValidation(custom_type, import_def)
|
TypeValidation(custom_type, import_def)
|
||||||
self._update_custom_def(custom_type, namespace_prefix)
|
self._update_custom_def(custom_type, namespace_prefix)
|
||||||
else: # old style of imports
|
else: # old style of imports
|
||||||
custom_type = self._load_import_template(None,
|
full_file_name, custom_type = self._load_import_template(
|
||||||
import_def)
|
None, import_def)
|
||||||
if custom_type:
|
if custom_type:
|
||||||
TypeValidation(
|
TypeValidation(
|
||||||
custom_type, import_def)
|
custom_type, import_def)
|
||||||
self._update_custom_def(custom_type, None)
|
self._update_custom_def(custom_type, None)
|
||||||
|
|
||||||
|
self._update_nested_tosca_tpls(full_file_name, custom_type)
|
||||||
|
|
||||||
def _update_custom_def(self, custom_type, namespace_prefix):
|
def _update_custom_def(self, custom_type, namespace_prefix):
|
||||||
outer_custom_types = {}
|
outer_custom_types = {}
|
||||||
for type_def in self.type_definition_list:
|
for type_def in self.type_definition_list:
|
||||||
@@ -113,6 +119,11 @@ class ImportsLoader(object):
|
|||||||
else:
|
else:
|
||||||
self.custom_defs.update(outer_custom_types)
|
self.custom_defs.update(outer_custom_types)
|
||||||
|
|
||||||
|
def _update_nested_tosca_tpls(self, full_file_name, custom_tpl):
|
||||||
|
if full_file_name and custom_tpl:
|
||||||
|
topo_tpl = {full_file_name: custom_tpl}
|
||||||
|
self.nested_tosca_tpls.append(topo_tpl)
|
||||||
|
|
||||||
def _validate_import_keys(self, import_name, import_uri_def):
|
def _validate_import_keys(self, import_name, import_uri_def):
|
||||||
if self.FILE not in import_uri_def.keys():
|
if self.FILE not in import_uri_def.keys():
|
||||||
log.warning(_('Missing keyname "file" in import "%(name)s".')
|
log.warning(_('Missing keyname "file" in import "%(name)s".')
|
||||||
@@ -173,10 +184,10 @@ class ImportsLoader(object):
|
|||||||
% {'import_name': import_name})
|
% {'import_name': import_name})
|
||||||
log.error(msg)
|
log.error(msg)
|
||||||
ExceptionCollector.appendException(ValidationError(message=msg))
|
ExceptionCollector.appendException(ValidationError(message=msg))
|
||||||
return
|
return None, None
|
||||||
|
|
||||||
if toscaparser.utils.urlutils.UrlUtils.validate_url(file_name):
|
if toscaparser.utils.urlutils.UrlUtils.validate_url(file_name):
|
||||||
return YAML_LOADER(file_name, False)
|
return file_name, YAML_LOADER(file_name, False)
|
||||||
elif not repository:
|
elif not repository:
|
||||||
import_template = None
|
import_template = None
|
||||||
if self.path:
|
if self.path:
|
||||||
@@ -188,7 +199,7 @@ class ImportsLoader(object):
|
|||||||
% {'name': file_name, 'template': self.path})
|
% {'name': file_name, 'template': self.path})
|
||||||
log.error(msg)
|
log.error(msg)
|
||||||
ExceptionCollector.appendException(ImportError(msg))
|
ExceptionCollector.appendException(ImportError(msg))
|
||||||
return
|
return None, None
|
||||||
import_template = toscaparser.utils.urlutils.UrlUtils.\
|
import_template = toscaparser.utils.urlutils.UrlUtils.\
|
||||||
join_url(self.path, file_name)
|
join_url(self.path, file_name)
|
||||||
a_file = False
|
a_file = False
|
||||||
@@ -231,7 +242,7 @@ class ImportsLoader(object):
|
|||||||
% {'name': file_name})
|
% {'name': file_name})
|
||||||
log.error(msg)
|
log.error(msg)
|
||||||
ExceptionCollector.appendException(ImportError(msg))
|
ExceptionCollector.appendException(ImportError(msg))
|
||||||
return
|
return None, None
|
||||||
|
|
||||||
if not import_template:
|
if not import_template:
|
||||||
log.error(_('Import "%(name)s" is not valid.') %
|
log.error(_('Import "%(name)s" is not valid.') %
|
||||||
@@ -239,14 +250,14 @@ class ImportsLoader(object):
|
|||||||
ExceptionCollector.appendException(
|
ExceptionCollector.appendException(
|
||||||
ImportError(_('Import "%s" is not valid.') %
|
ImportError(_('Import "%s" is not valid.') %
|
||||||
import_uri_def))
|
import_uri_def))
|
||||||
return
|
return None, None
|
||||||
return YAML_LOADER(import_template, a_file)
|
return import_template, YAML_LOADER(import_template, a_file)
|
||||||
|
|
||||||
if short_import_notation:
|
if short_import_notation:
|
||||||
log.error(_('Import "%(name)s" is not valid.') % import_uri_def)
|
log.error(_('Import "%(name)s" is not valid.') % import_uri_def)
|
||||||
ExceptionCollector.appendException(
|
ExceptionCollector.appendException(
|
||||||
ImportError(_('Import "%s" is not valid.') % import_uri_def))
|
ImportError(_('Import "%s" is not valid.') % import_uri_def))
|
||||||
return
|
return None, None
|
||||||
|
|
||||||
full_url = ""
|
full_url = ""
|
||||||
if repository:
|
if repository:
|
||||||
@@ -264,10 +275,10 @@ class ImportsLoader(object):
|
|||||||
% {'n_uri': repository, 'tpl': import_name})
|
% {'n_uri': repository, 'tpl': import_name})
|
||||||
log.error(msg)
|
log.error(msg)
|
||||||
ExceptionCollector.appendException(ImportError(msg))
|
ExceptionCollector.appendException(ImportError(msg))
|
||||||
return
|
return None, None
|
||||||
|
|
||||||
if toscaparser.utils.urlutils.UrlUtils.validate_url(full_url):
|
if toscaparser.utils.urlutils.UrlUtils.validate_url(full_url):
|
||||||
return YAML_LOADER(full_url, False)
|
return full_url, YAML_LOADER(full_url, False)
|
||||||
else:
|
else:
|
||||||
msg = (_('repository url "%(n_uri)s" is not valid in import '
|
msg = (_('repository url "%(n_uri)s" is not valid in import '
|
||||||
'definition "%(tpl)s".')
|
'definition "%(tpl)s".')
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class NodeTemplate(EntityTemplate):
|
|||||||
self.available_rel_tpls = available_rel_tpls
|
self.available_rel_tpls = available_rel_tpls
|
||||||
self.available_rel_types = available_rel_types
|
self.available_rel_types = available_rel_types
|
||||||
self._relationships = {}
|
self._relationships = {}
|
||||||
|
self.sub_mapping_tosca_template = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def relationships(self):
|
def relationships(self):
|
||||||
|
|||||||
157
toscaparser/substitution_mappings.py
Normal file
157
toscaparser/substitution_mappings.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# 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 logging
|
||||||
|
|
||||||
|
from toscaparser.common.exception import ExceptionCollector
|
||||||
|
from toscaparser.common.exception import InvalidNodeTypeError
|
||||||
|
from toscaparser.common.exception import MissingRequiredFieldError
|
||||||
|
from toscaparser.common.exception import UnknownFieldError
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger('tosca')
|
||||||
|
|
||||||
|
|
||||||
|
class SubstitutionMappings(object):
|
||||||
|
'''SubstitutionMappings class declaration
|
||||||
|
|
||||||
|
SubstitutionMappings exports the topology template as an
|
||||||
|
implementation of a Node type.
|
||||||
|
'''
|
||||||
|
|
||||||
|
SECTIONS = (NODE_TYPE, REQUIREMENTS, CAPABILITIES) = \
|
||||||
|
('node_type', 'requirements', 'capabilities')
|
||||||
|
|
||||||
|
def __init__(self, sub_mapping_def, nodetemplates, inputs, outputs,
|
||||||
|
sub_mapped_node_template, custom_defs):
|
||||||
|
self.nodetemplates = nodetemplates
|
||||||
|
self.sub_mapping_def = sub_mapping_def
|
||||||
|
self.inputs = inputs or []
|
||||||
|
self.outputs = outputs or []
|
||||||
|
self.sub_mapped_node_template = sub_mapped_node_template
|
||||||
|
self.custom_defs = custom_defs or {}
|
||||||
|
self._validate()
|
||||||
|
|
||||||
|
self._capabilities = None
|
||||||
|
self._requirements = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
if self.sub_mapping_def:
|
||||||
|
return self.sub_mapping_def['node_type']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_node_type(cls, sub_mapping_def):
|
||||||
|
if isinstance(sub_mapping_def, dict):
|
||||||
|
return sub_mapping_def.get(cls.NODE_TYPE)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def node_type(self):
|
||||||
|
return self.sub_mapping_def.get(self.NODE_TYPE)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def capabilities(self):
|
||||||
|
return self.sub_mapping_def.get(self.CAPABILITIES)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def requirements(self):
|
||||||
|
return self.sub_mapping_def.get(self.REQUIREMENTS)
|
||||||
|
|
||||||
|
def _validate(self):
|
||||||
|
self._validate_keys()
|
||||||
|
self._validate_type()
|
||||||
|
self._validate_inputs()
|
||||||
|
self._validate_capabilities()
|
||||||
|
self._validate_requirements()
|
||||||
|
self._validate_outputs()
|
||||||
|
|
||||||
|
def _validate_keys(self):
|
||||||
|
"""validate the keys of substitution mappings."""
|
||||||
|
for key in self.sub_mapping_def.keys():
|
||||||
|
if key not in self.SECTIONS:
|
||||||
|
ExceptionCollector.appendException(
|
||||||
|
UnknownFieldError(what='SubstitutionMappings',
|
||||||
|
field=key))
|
||||||
|
|
||||||
|
def _validate_type(self):
|
||||||
|
"""validate the node_type of substitution mappings."""
|
||||||
|
node_type = self.sub_mapping_def.get(self.NODE_TYPE)
|
||||||
|
if not node_type:
|
||||||
|
ExceptionCollector.appendException(
|
||||||
|
MissingRequiredFieldError(
|
||||||
|
what=_('SubstitutionMappings used in topology_template'),
|
||||||
|
required=self.NODE_TYPE))
|
||||||
|
|
||||||
|
node_type_def = self.custom_defs.get(node_type)
|
||||||
|
if not node_type_def:
|
||||||
|
ExceptionCollector.appendException(
|
||||||
|
InvalidNodeTypeError(what=node_type_def))
|
||||||
|
|
||||||
|
def _validate_inputs(self):
|
||||||
|
"""validate the inputs of substitution mappings."""
|
||||||
|
|
||||||
|
# The inputs in service template which defines substutition mappings
|
||||||
|
# must be in properties of node template which is mapped or provide
|
||||||
|
# defualt value. Currently the input.name is not restrict to be the
|
||||||
|
# same as properte's name in specification, but they should be equal
|
||||||
|
# for current implementation.
|
||||||
|
property_names = list(self.sub_mapped_node_template
|
||||||
|
.get_properties().keys()
|
||||||
|
if self.sub_mapped_node_template else [])
|
||||||
|
for input in self.inputs:
|
||||||
|
if input.name not in property_names and input.default is None:
|
||||||
|
ExceptionCollector.appendException(
|
||||||
|
UnknownFieldError(what='SubstitutionMappings',
|
||||||
|
field=input.name))
|
||||||
|
|
||||||
|
def _validate_capabilities(self):
|
||||||
|
"""validate the capabilities of substitution mappings."""
|
||||||
|
|
||||||
|
# The capabilites must be in node template wchich be mapped.
|
||||||
|
tpls_capabilities = self.sub_mapping_def.get(self.CAPABILITIES)
|
||||||
|
node_capabiliteys = self.sub_mapped_node_template.get_capabilities() \
|
||||||
|
if self.sub_mapped_node_template else None
|
||||||
|
for cap in node_capabiliteys.keys() if node_capabiliteys else []:
|
||||||
|
if (tpls_capabilities and
|
||||||
|
cap not in list(tpls_capabilities.keys())):
|
||||||
|
pass
|
||||||
|
# ExceptionCollector.appendException(
|
||||||
|
# UnknownFieldError(what='SubstitutionMappings',
|
||||||
|
# field=cap))
|
||||||
|
|
||||||
|
def _validate_requirements(self):
|
||||||
|
"""validate the requirements of substitution mappings."""
|
||||||
|
|
||||||
|
# The requirements must be in node template wchich be mapped.
|
||||||
|
tpls_requirements = self.sub_mapping_def.get(self.REQUIREMENTS)
|
||||||
|
node_requirements = self.sub_mapped_node_template.requirements \
|
||||||
|
if self.sub_mapped_node_template else None
|
||||||
|
for req in node_requirements if node_requirements else []:
|
||||||
|
if (tpls_requirements and
|
||||||
|
req not in list(tpls_requirements.keys())):
|
||||||
|
pass
|
||||||
|
# ExceptionCollector.appendException(
|
||||||
|
# UnknownFieldError(what='SubstitutionMappings',
|
||||||
|
# field=req))
|
||||||
|
|
||||||
|
def _validate_outputs(self):
|
||||||
|
"""validate the outputs of substitution mappings."""
|
||||||
|
pass
|
||||||
|
# The outputs in service template which defines substutition mappings
|
||||||
|
# must be in atrributes of node template wchich be mapped.
|
||||||
|
# outputs_names = self.sub_mapped_node_template.get_properties().
|
||||||
|
# keys() if self.sub_mapped_node_template else None
|
||||||
|
# for name in outputs_names:
|
||||||
|
# if name not in [output.name for input in self.outputs]:
|
||||||
|
# ExceptionCollector.appendException(
|
||||||
|
# UnknownFieldError(what='SubstitutionMappings',
|
||||||
|
# field=name))
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Database subsystem, which is service template with topology_template,
|
||||||
|
act as a nested system inside another system and also act as stand
|
||||||
|
alone service template.
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- definitions.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
description: Template of a database including its hosting stack.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
user:
|
||||||
|
type: string
|
||||||
|
description: the user name of database.
|
||||||
|
default: test
|
||||||
|
port:
|
||||||
|
type: integer
|
||||||
|
description: the port of database.
|
||||||
|
default: 3306
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: the name of database.
|
||||||
|
default: test
|
||||||
|
my_cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
default: 2
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
|
||||||
|
substitution_mappings:
|
||||||
|
node_type: example.DatabaseSubsystem
|
||||||
|
capabilities:
|
||||||
|
database_endpoint: [ db_app, database_endpoint ]
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
db_app:
|
||||||
|
type: tosca.nodes.Database
|
||||||
|
properties:
|
||||||
|
user: { get_input: user }
|
||||||
|
port: { get_input: port }
|
||||||
|
name: { get_input: name }
|
||||||
|
capabilities:
|
||||||
|
database_endpoint:
|
||||||
|
properties:
|
||||||
|
port: 1234
|
||||||
|
requirements:
|
||||||
|
- host:
|
||||||
|
node: dbms
|
||||||
|
|
||||||
|
dbms:
|
||||||
|
type: tosca.nodes.DBMS
|
||||||
|
properties:
|
||||||
|
port: 3306
|
||||||
|
root_password: 123456789
|
||||||
|
requirements:
|
||||||
|
- host:
|
||||||
|
node: server
|
||||||
|
|
||||||
|
server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
capabilities:
|
||||||
|
host:
|
||||||
|
properties:
|
||||||
|
disk_size: 10 GB
|
||||||
|
num_cpus: { get_input: my_cpus }
|
||||||
|
mem_size: 4096 MB
|
||||||
|
os:
|
||||||
|
properties:
|
||||||
|
architecture: x86_64
|
||||||
|
type: Linux
|
||||||
|
distribution: Ubuntu
|
||||||
|
version: 14.04
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
receiver_ip:
|
||||||
|
description: private IP address of the database application
|
||||||
|
value: { get_attribute: [ server, private_address ] }
|
||||||
|
# It seems current _process_intrisic_function can not handle more than 2 arguments, save it for later
|
||||||
|
# receiver_port:
|
||||||
|
# description: Port of the message receiver endpoint
|
||||||
|
# value: { get_attribute: [ app, data_endpoint, port_name ] }
|
||||||
|
|
||||||
|
groups:
|
||||||
|
dbserver_group:
|
||||||
|
members: [ dbms, server ]
|
||||||
|
type: tosca.groups.Root
|
||||||
@@ -1,6 +1,28 @@
|
|||||||
tosca_definitions_version: tosca_simple_yaml_1_0
|
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||||
|
|
||||||
node_types:
|
node_types:
|
||||||
|
example.QueuingSubsystem:
|
||||||
|
derived_from: tosca.nodes.SoftwareComponent
|
||||||
|
properties:
|
||||||
|
server_ip:
|
||||||
|
type: string
|
||||||
|
server_port:
|
||||||
|
type: integer
|
||||||
|
attributes:
|
||||||
|
server_ip:
|
||||||
|
type: string
|
||||||
|
server_port:
|
||||||
|
type: integer
|
||||||
|
requirements:
|
||||||
|
- receiver1:
|
||||||
|
node: example.TransactionSubsystem
|
||||||
|
capability: example.capabilities.Receiver
|
||||||
|
relationship: tosca.relationships.ConnectsTo
|
||||||
|
- receiver2:
|
||||||
|
node: example.TransactionSubsystem
|
||||||
|
capability: example.capabilities.Receiver
|
||||||
|
relationship: tosca.relationships.ConnectsTo
|
||||||
|
|
||||||
example.TransactionSubsystem:
|
example.TransactionSubsystem:
|
||||||
properties:
|
properties:
|
||||||
mq_server_ip:
|
mq_server_ip:
|
||||||
@@ -17,18 +39,8 @@ node_types:
|
|||||||
type: example.capabilities.Receiver
|
type: example.capabilities.Receiver
|
||||||
requirements:
|
requirements:
|
||||||
- database_endpoint:
|
- database_endpoint:
|
||||||
capability: tosca.capabilities.Endpoint.Database
|
|
||||||
node: tosca.nodes.Database
|
node: tosca.nodes.Database
|
||||||
relationship: tosca.relationships.ConnectsTo
|
capability: tosca.capabilities.Endpoint.Database
|
||||||
|
|
||||||
example.QueuingSubsystem:
|
|
||||||
derived_from: tosca.nodes.SoftwareComponent
|
|
||||||
requirements:
|
|
||||||
- receiver1:
|
|
||||||
node: example.TransactionSubsystem
|
|
||||||
relationship: tosca.relationships.ConnectsTo
|
|
||||||
- receiver2:
|
|
||||||
node: example.TransactionSubsystem
|
|
||||||
relationship: tosca.relationships.ConnectsTo
|
relationship: tosca.relationships.ConnectsTo
|
||||||
|
|
||||||
example.DatabaseSubsystem:
|
example.DatabaseSubsystem:
|
||||||
@@ -44,6 +56,11 @@ node_types:
|
|||||||
capabilities:
|
capabilities:
|
||||||
message_receiver:
|
message_receiver:
|
||||||
type: example.capabilities.Receiver
|
type: example.capabilities.Receiver
|
||||||
|
requirements:
|
||||||
|
- database:
|
||||||
|
node: tosca.nodes.Database
|
||||||
|
capability: tosca.capabilities.Endpoint.Database
|
||||||
|
relationship: tosca.relationships.ConnectsTo
|
||||||
|
|
||||||
capability_types:
|
capability_types:
|
||||||
example.capabilities.Receiver:
|
example.capabilities.Receiver:
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Queue subsystem, which is service template with topology_template,
|
||||||
|
act as a nested system inside another system and also act as stand
|
||||||
|
alone service template.
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- definitions.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
description: Template of a database including its hosting stack.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
server_ip:
|
||||||
|
type: string
|
||||||
|
description: IP address of the message queuing server to receive messages from.
|
||||||
|
default: 127.0.0.1
|
||||||
|
server_port:
|
||||||
|
type: integer
|
||||||
|
description: Port to be used for receiving messages.
|
||||||
|
default: 8080
|
||||||
|
my_cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
default: 2
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
|
||||||
|
substitution_mappings:
|
||||||
|
node_type: example.QueuingSubsystem
|
||||||
|
# capabilities:
|
||||||
|
# message_receiver: [ app, message_receiver ]
|
||||||
|
requirements:
|
||||||
|
receiver1: [ tran_app, receiver1 ]
|
||||||
|
receiver2: [ tran_app, receiver2 ]
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
tran_app:
|
||||||
|
type: example.QueuingSubsystem
|
||||||
|
properties:
|
||||||
|
server_ip: { get_input: server_ip }
|
||||||
|
server_port: { get_input: server_port }
|
||||||
|
requirements:
|
||||||
|
- host:
|
||||||
|
node: server
|
||||||
|
|
||||||
|
server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
capabilities:
|
||||||
|
host:
|
||||||
|
properties:
|
||||||
|
disk_size: 10 GB
|
||||||
|
num_cpus: { get_input: my_cpus }
|
||||||
|
mem_size: 4096 MB
|
||||||
|
os:
|
||||||
|
properties:
|
||||||
|
architecture: x86_64
|
||||||
|
type: Linux
|
||||||
|
distribution: Ubuntu
|
||||||
|
version: 14.04
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
receiver_ip:
|
||||||
|
description: private IP address of the message receiver application
|
||||||
|
value: { get_attribute: [ server, private_address ] }
|
||||||
|
# It seems current _process_intrisic_function can not handle more than 2 arguments, save it for later
|
||||||
|
# receiver_port:
|
||||||
|
# description: Port of the message receiver endpoint
|
||||||
|
# value: { get_attribute: [ app, data_endpoint, port_name ] }
|
||||||
|
|
||||||
|
groups:
|
||||||
|
tran_server_group:
|
||||||
|
members: [ tran_app, server ]
|
||||||
|
type: tosca.groups.Root
|
||||||
@@ -1,19 +1,34 @@
|
|||||||
tosca_definitions_version: tosca_simple_yaml_1_0
|
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||||
|
|
||||||
imports:
|
imports:
|
||||||
- definitions.yaml
|
- queuingsubsystem.yaml
|
||||||
|
- transactionsubsystem.yaml
|
||||||
|
- databasesubsystem.yaml
|
||||||
|
|
||||||
topology_template:
|
topology_template:
|
||||||
description: Template of online transaction processing service.
|
description: Template of online transaction processing service.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
mq_server_ip:
|
||||||
|
type: string
|
||||||
|
default: 127.0.0.1
|
||||||
|
description: IP address of the message queuing server to receive messages from.
|
||||||
|
mq_server_port:
|
||||||
|
type: integer
|
||||||
|
default1: 8080
|
||||||
|
description: Port to be used for receiving messages.
|
||||||
|
|
||||||
node_templates:
|
node_templates:
|
||||||
mq:
|
mq:
|
||||||
type: example.QueuingSubsystem
|
type: example.QueuingSubsystem
|
||||||
# properties:
|
# properties:
|
||||||
# to be updated when substitution_mapping is implemented
|
# to be updated when substitution_mapping is validated later
|
||||||
|
properties:
|
||||||
|
server_ip: { get_input: mq_server_ip }
|
||||||
|
server_port: { get_input: mq_server_port }
|
||||||
# capabilities:
|
# capabilities:
|
||||||
# message_queue_endpoint:
|
# message_queue_endpoint:
|
||||||
# to be updated when substitution_mapping is implemented
|
# to be updated when substitution_mapping is validated later
|
||||||
requirements:
|
requirements:
|
||||||
- receiver1: trans1
|
- receiver1: trans1
|
||||||
- receiver2: trans2
|
- receiver2: trans2
|
||||||
@@ -21,37 +36,33 @@ topology_template:
|
|||||||
trans1:
|
trans1:
|
||||||
type: example.TransactionSubsystem
|
type: example.TransactionSubsystem
|
||||||
properties:
|
properties:
|
||||||
# TODO to be updated when substitution_mapping is implemented
|
# mq_server_ip: 127.0.0.1
|
||||||
# mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
||||||
# for now, we will use the loopback address to avoid errors as
|
# receiver_port: 8080
|
||||||
# this property is required in the schema
|
receiver_port: { get_attribute: [ mq, server_port ] }
|
||||||
mq_server_ip: 127.0.0.1
|
|
||||||
receiver_port: 8080
|
|
||||||
# capabilities:
|
# capabilities:
|
||||||
# message_receiver:
|
# message_receiver:
|
||||||
# to be updated when substitution_mapping is implemented
|
# to be updated when substitution_mapping is validated later
|
||||||
requirements:
|
requirements:
|
||||||
- database_endpoint: dbsys
|
- database_endpoint: dbsys
|
||||||
|
|
||||||
trans2:
|
trans2:
|
||||||
type: example.TransactionSubsystem
|
type: example.TransactionSubsystem
|
||||||
properties:
|
properties:
|
||||||
# TODO to be updated when substitution_mapping is implemented
|
# mq_server_ip: 127.0.0.1
|
||||||
# mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
||||||
# for now, we will use the loopback address to avoid errors as
|
# receiver_port: 8080
|
||||||
# this property is required in the schema
|
receiver_port: { get_attribute: [ mq, server_port ] }
|
||||||
mq_server_ip: 127.0.0.1
|
|
||||||
receiver_port: 8080
|
|
||||||
# capabilities:
|
# capabilities:
|
||||||
# message_receiver:
|
# message_receiver:
|
||||||
# to be updated when substitution_mapping is implemented
|
# to be updated when substitution_mapping is validated later
|
||||||
requirements:
|
requirements:
|
||||||
- database_endpoint: dbsys
|
- database_endpoint: dbsys
|
||||||
|
|
||||||
dbsys:
|
dbsys:
|
||||||
type: example.DatabaseSubsystem
|
type: example.DatabaseSubsystem
|
||||||
# properties:
|
# properties:
|
||||||
# to be updated when substitution_mapping is implemented
|
# to be updated when substitution_mapping is validated later
|
||||||
# capabilities:
|
# capabilities:
|
||||||
# database_endpoint:
|
# database_endpoint:
|
||||||
# to be updated when substitution_mapping is implemented
|
# to be updated when substitution_mapping is validated later
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
tosca_definitions_version: tosca_simple_yaml_1_0
|
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||||
|
|
||||||
description: >
|
description: >
|
||||||
Service template with topology_template, act as a nested system inside another system.
|
Transaction subsystem, which is service template with topology_template,
|
||||||
|
act as a nested system inside another system and also act as stand
|
||||||
|
alone service template.
|
||||||
|
|
||||||
imports:
|
imports:
|
||||||
- definitions.yaml
|
- definitions.yaml
|
||||||
@@ -13,12 +15,15 @@ topology_template:
|
|||||||
mq_server_ip:
|
mq_server_ip:
|
||||||
type: string
|
type: string
|
||||||
description: IP address of the message queuing server to receive messages from.
|
description: IP address of the message queuing server to receive messages from.
|
||||||
|
default: 127.0.0.1
|
||||||
receiver_port:
|
receiver_port:
|
||||||
type: string
|
type: integer
|
||||||
description: Port to be used for receiving messages.
|
description: Port to be used for receiving messages.
|
||||||
|
default: 8080
|
||||||
my_cpus:
|
my_cpus:
|
||||||
type: integer
|
type: integer
|
||||||
description: Number of CPUs for the server.
|
description: Number of CPUs for the server.
|
||||||
|
default: 2
|
||||||
constraints:
|
constraints:
|
||||||
- valid_values: [ 1, 2, 4, 8 ]
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ class TopologyTemplateTest(TestCase):
|
|||||||
'''TOSCA template.'''
|
'''TOSCA template.'''
|
||||||
self.tosca_tpl_path = os.path.join(
|
self.tosca_tpl_path = os.path.join(
|
||||||
os.path.dirname(os.path.abspath(__file__)),
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
"data/topology_template/subsystem.yaml")
|
"data/topology_template/transactionsubsystem.yaml")
|
||||||
self.tpl = YAML_LOADER(self.tosca_tpl_path)
|
self.tpl = YAML_LOADER(self.tosca_tpl_path)
|
||||||
self.topo_tpl = self.tpl.get('topology_template')
|
self.topo_tpl = self.tpl.get('topology_template')
|
||||||
self.imports = self.tpl.get('imports')
|
self.imports = self.tpl.get('imports')
|
||||||
@@ -157,4 +157,8 @@ class TopologyTemplateTest(TestCase):
|
|||||||
tpl_path = os.path.join(
|
tpl_path = os.path.join(
|
||||||
os.path.dirname(os.path.abspath(__file__)),
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
"data/topology_template/system.yaml")
|
"data/topology_template/system.yaml")
|
||||||
self.assertIsNotNone(ToscaTemplate(tpl_path))
|
system_tosca_template = ToscaTemplate(tpl_path)
|
||||||
|
self.assertIsNotNone(system_tosca_template)
|
||||||
|
self.assertEqual(
|
||||||
|
len(system_tosca_template.
|
||||||
|
nested_tosca_templates_with_topology), 4)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from toscaparser.parameters import Input
|
|||||||
from toscaparser.parameters import Output
|
from toscaparser.parameters import Output
|
||||||
from toscaparser.policy import Policy
|
from toscaparser.policy import Policy
|
||||||
from toscaparser.relationship_template import RelationshipTemplate
|
from toscaparser.relationship_template import RelationshipTemplate
|
||||||
|
from toscaparser.substitution_mappings import SubstitutionMappings
|
||||||
from toscaparser.tpl_relationship_graph import ToscaGraph
|
from toscaparser.tpl_relationship_graph import ToscaGraph
|
||||||
from toscaparser.utils.gettextutils import _
|
from toscaparser.utils.gettextutils import _
|
||||||
|
|
||||||
@@ -41,8 +42,10 @@ class TopologyTemplate(object):
|
|||||||
|
|
||||||
'''Load the template data.'''
|
'''Load the template data.'''
|
||||||
def __init__(self, template, custom_defs,
|
def __init__(self, template, custom_defs,
|
||||||
rel_types=None, parsed_params=None):
|
rel_types=None, parsed_params=None,
|
||||||
|
sub_mapped_node_template=None):
|
||||||
self.tpl = template
|
self.tpl = template
|
||||||
|
self.sub_mapped_node_template = sub_mapped_node_template
|
||||||
if self.tpl:
|
if self.tpl:
|
||||||
self.custom_defs = custom_defs
|
self.custom_defs = custom_defs
|
||||||
self.rel_types = rel_types
|
self.rel_types = rel_types
|
||||||
@@ -58,6 +61,7 @@ class TopologyTemplate(object):
|
|||||||
self.groups = self._groups()
|
self.groups = self._groups()
|
||||||
self.policies = self._policies()
|
self.policies = self._policies()
|
||||||
self._process_intrinsic_functions()
|
self._process_intrinsic_functions()
|
||||||
|
self.substitution_mappings = self._substitution_mappings()
|
||||||
|
|
||||||
def _inputs(self):
|
def _inputs(self):
|
||||||
inputs = []
|
inputs = []
|
||||||
@@ -105,7 +109,15 @@ class TopologyTemplate(object):
|
|||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
def _substitution_mappings(self):
|
def _substitution_mappings(self):
|
||||||
pass
|
tpl_substitution_mapping = self._tpl_substitution_mappings()
|
||||||
|
# if tpl_substitution_mapping and self.sub_mapped_node_template:
|
||||||
|
if tpl_substitution_mapping:
|
||||||
|
return SubstitutionMappings(tpl_substitution_mapping,
|
||||||
|
self.nodetemplates,
|
||||||
|
self.inputs,
|
||||||
|
self.outputs,
|
||||||
|
self.sub_mapped_node_template,
|
||||||
|
self.custom_defs)
|
||||||
|
|
||||||
def _policies(self):
|
def _policies(self):
|
||||||
policies = []
|
policies = []
|
||||||
@@ -177,13 +189,16 @@ class TopologyTemplate(object):
|
|||||||
# topology template can act like node template
|
# topology template can act like node template
|
||||||
# it is exposed by substitution_mappings.
|
# it is exposed by substitution_mappings.
|
||||||
def nodetype(self):
|
def nodetype(self):
|
||||||
pass
|
return self.substitution_mappings.node_type \
|
||||||
|
if self.substitution_mappings else None
|
||||||
|
|
||||||
def capabilities(self):
|
def capabilities(self):
|
||||||
pass
|
return self.substitution_mappings.capabilities \
|
||||||
|
if self.substitution_mappings else None
|
||||||
|
|
||||||
def requirements(self):
|
def requirements(self):
|
||||||
pass
|
return self.substitution_mappings.requirements \
|
||||||
|
if self.substitution_mappings else None
|
||||||
|
|
||||||
def _tpl_description(self):
|
def _tpl_description(self):
|
||||||
description = self.tpl.get(DESCRIPTION)
|
description = self.tpl.get(DESCRIPTION)
|
||||||
@@ -278,3 +293,9 @@ class TopologyTemplate(object):
|
|||||||
func = functions.get_function(self, self.outputs, output.value)
|
func = functions.get_function(self, self.outputs, output.value)
|
||||||
if isinstance(func, functions.GetAttribute):
|
if isinstance(func, functions.GetAttribute):
|
||||||
output.attrs[output.VALUE] = func
|
output.attrs[output.VALUE] = func
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_sub_mapping_node_type(cls, topology_tpl):
|
||||||
|
if topology_tpl and isinstance(topology_tpl, dict):
|
||||||
|
submap_tpl = topology_tpl.get(SUBSTITUION_MAPPINGS)
|
||||||
|
return SubstitutionMappings.get_node_type(submap_tpl)
|
||||||
|
|||||||
@@ -65,11 +65,14 @@ class ToscaTemplate(object):
|
|||||||
'''Load the template data.'''
|
'''Load the template data.'''
|
||||||
def __init__(self, path=None, parsed_params=None, a_file=True,
|
def __init__(self, path=None, parsed_params=None, a_file=True,
|
||||||
yaml_dict_tpl=None):
|
yaml_dict_tpl=None):
|
||||||
|
|
||||||
ExceptionCollector.start()
|
ExceptionCollector.start()
|
||||||
self.a_file = a_file
|
self.a_file = a_file
|
||||||
self.input_path = None
|
self.input_path = None
|
||||||
self.path = None
|
self.path = None
|
||||||
self.tpl = None
|
self.tpl = None
|
||||||
|
self.nested_tosca_tpls_with_topology = {}
|
||||||
|
self.nested_tosca_templates_with_topology = []
|
||||||
if path:
|
if path:
|
||||||
self.input_path = path
|
self.input_path = path
|
||||||
self.path = self._get_path(path)
|
self.path = self._get_path(path)
|
||||||
@@ -101,6 +104,7 @@ class ToscaTemplate(object):
|
|||||||
self.relationship_templates = self._relationship_templates()
|
self.relationship_templates = self._relationship_templates()
|
||||||
self.nodetemplates = self._nodetemplates()
|
self.nodetemplates = self._nodetemplates()
|
||||||
self.outputs = self._outputs()
|
self.outputs = self._outputs()
|
||||||
|
self._handle_nested_tosca_templates_with_topology()
|
||||||
self.graph = ToscaGraph(self.nodetemplates)
|
self.graph = ToscaGraph(self.nodetemplates)
|
||||||
|
|
||||||
ExceptionCollector.stop()
|
ExceptionCollector.stop()
|
||||||
@@ -110,7 +114,8 @@ class ToscaTemplate(object):
|
|||||||
return TopologyTemplate(self._tpl_topology_template(),
|
return TopologyTemplate(self._tpl_topology_template(),
|
||||||
self._get_all_custom_defs(),
|
self._get_all_custom_defs(),
|
||||||
self.relationship_types,
|
self.relationship_types,
|
||||||
self.parsed_params)
|
self.parsed_params,
|
||||||
|
None)
|
||||||
|
|
||||||
def _inputs(self):
|
def _inputs(self):
|
||||||
return self.topology_template.inputs
|
return self.topology_template.inputs
|
||||||
@@ -188,9 +193,14 @@ class ToscaTemplate(object):
|
|||||||
imports = self._tpl_imports()
|
imports = self._tpl_imports()
|
||||||
|
|
||||||
if imports:
|
if imports:
|
||||||
custom_defs = toscaparser.imports.\
|
custom_service = toscaparser.imports.\
|
||||||
ImportsLoader(imports, self.path,
|
ImportsLoader(imports, self.path,
|
||||||
type_defs, self.tpl).get_custom_defs()
|
type_defs, self.tpl)
|
||||||
|
|
||||||
|
nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
|
||||||
|
self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
|
||||||
|
|
||||||
|
custom_defs = custom_service.get_custom_defs()
|
||||||
if not custom_defs:
|
if not custom_defs:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -202,6 +212,33 @@ class ToscaTemplate(object):
|
|||||||
custom_defs.update(inner_custom_types)
|
custom_defs.update(inner_custom_types)
|
||||||
return custom_defs
|
return custom_defs
|
||||||
|
|
||||||
|
def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
|
||||||
|
for tpl in nested_tosca_tpls:
|
||||||
|
filename, tosca_tpl = list(tpl.items())[0]
|
||||||
|
if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
|
||||||
|
filename not in list(
|
||||||
|
self.nested_tosca_tpls_with_topology.keys())):
|
||||||
|
self.nested_tosca_tpls_with_topology.update(tpl)
|
||||||
|
|
||||||
|
def _handle_nested_tosca_templates_with_topology(self):
|
||||||
|
for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
|
||||||
|
for nodetemplate in self.nodetemplates:
|
||||||
|
if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
|
||||||
|
topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE)
|
||||||
|
topology_with_sub_mapping = TopologyTemplate(
|
||||||
|
topology_tpl,
|
||||||
|
self._get_all_custom_defs(),
|
||||||
|
self.relationship_types,
|
||||||
|
self.parsed_params,
|
||||||
|
nodetemplate)
|
||||||
|
if topology_with_sub_mapping.substitution_mappings:
|
||||||
|
# Record nested topo templates in top level template
|
||||||
|
self.nested_tosca_templates_with_topology.\
|
||||||
|
append(topology_with_sub_mapping)
|
||||||
|
# Set substitution mapping object for mapped node
|
||||||
|
nodetemplate.sub_mapping_tosca_template = \
|
||||||
|
topology_with_sub_mapping.substitution_mappings
|
||||||
|
|
||||||
def _validate_field(self):
|
def _validate_field(self):
|
||||||
version = self._tpl_version()
|
version = self._tpl_version()
|
||||||
if not version:
|
if not version:
|
||||||
@@ -264,3 +301,28 @@ class ToscaTemplate(object):
|
|||||||
msg = _('The pre-parsed input successfully passed validation.')
|
msg = _('The pre-parsed input successfully passed validation.')
|
||||||
|
|
||||||
log.info(msg)
|
log.info(msg)
|
||||||
|
|
||||||
|
def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
|
||||||
|
"""Return True if the nodetemple is substituted."""
|
||||||
|
if (nodetemplate and not nodetemplate.sub_mapping_tosca_template and
|
||||||
|
self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
|
||||||
|
and len(nodetemplate.interfaces) < 1):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_sub_mapping_node_type(self, tosca_tpl):
|
||||||
|
"""Return substitution mappings node type."""
|
||||||
|
if tosca_tpl:
|
||||||
|
return TopologyTemplate.get_sub_mapping_node_type(
|
||||||
|
tosca_tpl.get(TOPOLOGY_TEMPLATE))
|
||||||
|
|
||||||
|
def has_substitution_mappings(self):
|
||||||
|
"""Return True if the template has valid substitution mappings."""
|
||||||
|
return self.topology_template is not None and \
|
||||||
|
self.topology_template.substitution_mappings is not None
|
||||||
|
|
||||||
|
def has_nested_templates(self):
|
||||||
|
"""Return True if the tosca template has nested templates."""
|
||||||
|
return self.nested_tosca_templates_with_topology is not None and \
|
||||||
|
len(self.nested_tosca_templates_with_topology) >= 1
|
||||||
|
|||||||
Reference in New Issue
Block a user