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:
parent
aa23c05f04
commit
24c71e28e8
@ -22,12 +22,13 @@ class TypeValidation(object):
|
||||
DSL_DEFINITIONS, NODE_TYPES, REPOSITORIES,
|
||||
DATA_TYPES, ARTIFACT_TYPES, GROUP_TYPES,
|
||||
RELATIONSHIP_TYPES, CAPABILITY_TYPES,
|
||||
INTERFACE_TYPES, POLICY_TYPES) = \
|
||||
INTERFACE_TYPES, POLICY_TYPES,
|
||||
TOPOLOGY_TEMPLATE) = \
|
||||
('tosca_definitions_version', 'description', 'imports',
|
||||
'dsl_definitions', 'node_types', 'repositories',
|
||||
'data_types', 'artifact_types', 'group_types',
|
||||
'relationship_types', 'capability_types',
|
||||
'interface_types', 'policy_types')
|
||||
'interface_types', 'policy_types', 'topology_template')
|
||||
VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
|
||||
exttools = ExtTools()
|
||||
VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
|
||||
|
@ -724,11 +724,12 @@ def get_function(tosca_tpl, node_template, raw_function):
|
||||
parsing was unsuccessful.
|
||||
"""
|
||||
if is_function(raw_function):
|
||||
func_name = list(raw_function.keys())[0]
|
||||
if func_name in function_mappings:
|
||||
func = function_mappings[func_name]
|
||||
func_args = list(raw_function.values())[0]
|
||||
if not isinstance(func_args, list):
|
||||
func_args = [func_args]
|
||||
return func(tosca_tpl, node_template, func_name, func_args)
|
||||
if isinstance(raw_function, dict):
|
||||
func_name = list(raw_function.keys())[0]
|
||||
if func_name in function_mappings:
|
||||
func = function_mappings[func_name]
|
||||
func_args = list(raw_function.values())[0]
|
||||
if not isinstance(func_args, list):
|
||||
func_args = [func_args]
|
||||
return func(tosca_tpl, node_template, func_name, func_args)
|
||||
return raw_function
|
||||
|
@ -37,6 +37,7 @@ class ImportsLoader(object):
|
||||
tpl=None):
|
||||
self.importslist = importslist
|
||||
self.custom_defs = {}
|
||||
self.nested_tosca_tpls = []
|
||||
if not path and not tpl:
|
||||
msg = _('Input tosca template is not provided.')
|
||||
log.warning(msg)
|
||||
@ -56,6 +57,9 @@ class ImportsLoader(object):
|
||||
def get_custom_defs(self):
|
||||
return self.custom_defs
|
||||
|
||||
def get_nested_tosca_tpls(self):
|
||||
return self.nested_tosca_tpls
|
||||
|
||||
def _validate_and_load_imports(self):
|
||||
imports_names = set()
|
||||
|
||||
@ -77,8 +81,8 @@ class ImportsLoader(object):
|
||||
ValidationError(message=msg))
|
||||
imports_names.add(import_name)
|
||||
|
||||
custom_type = self._load_import_template(import_name,
|
||||
import_uri)
|
||||
full_file_name, custom_type = self._load_import_template(
|
||||
import_name, import_uri)
|
||||
namespace_prefix = None
|
||||
if isinstance(import_uri, dict):
|
||||
namespace_prefix = import_uri.get(
|
||||
@ -87,13 +91,15 @@ class ImportsLoader(object):
|
||||
TypeValidation(custom_type, import_def)
|
||||
self._update_custom_def(custom_type, namespace_prefix)
|
||||
else: # old style of imports
|
||||
custom_type = self._load_import_template(None,
|
||||
import_def)
|
||||
full_file_name, custom_type = self._load_import_template(
|
||||
None, import_def)
|
||||
if custom_type:
|
||||
TypeValidation(
|
||||
custom_type, import_def)
|
||||
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):
|
||||
outer_custom_types = {}
|
||||
for type_def in self.type_definition_list:
|
||||
@ -113,6 +119,11 @@ class ImportsLoader(object):
|
||||
else:
|
||||
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):
|
||||
if self.FILE not in import_uri_def.keys():
|
||||
log.warning(_('Missing keyname "file" in import "%(name)s".')
|
||||
@ -173,10 +184,10 @@ class ImportsLoader(object):
|
||||
% {'import_name': import_name})
|
||||
log.error(msg)
|
||||
ExceptionCollector.appendException(ValidationError(message=msg))
|
||||
return
|
||||
return None, None
|
||||
|
||||
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:
|
||||
import_template = None
|
||||
if self.path:
|
||||
@ -188,7 +199,7 @@ class ImportsLoader(object):
|
||||
% {'name': file_name, 'template': self.path})
|
||||
log.error(msg)
|
||||
ExceptionCollector.appendException(ImportError(msg))
|
||||
return
|
||||
return None, None
|
||||
import_template = toscaparser.utils.urlutils.UrlUtils.\
|
||||
join_url(self.path, file_name)
|
||||
a_file = False
|
||||
@ -231,7 +242,7 @@ class ImportsLoader(object):
|
||||
% {'name': file_name})
|
||||
log.error(msg)
|
||||
ExceptionCollector.appendException(ImportError(msg))
|
||||
return
|
||||
return None, None
|
||||
|
||||
if not import_template:
|
||||
log.error(_('Import "%(name)s" is not valid.') %
|
||||
@ -239,14 +250,14 @@ class ImportsLoader(object):
|
||||
ExceptionCollector.appendException(
|
||||
ImportError(_('Import "%s" is not valid.') %
|
||||
import_uri_def))
|
||||
return
|
||||
return YAML_LOADER(import_template, a_file)
|
||||
return None, None
|
||||
return import_template, YAML_LOADER(import_template, a_file)
|
||||
|
||||
if short_import_notation:
|
||||
log.error(_('Import "%(name)s" is not valid.') % import_uri_def)
|
||||
ExceptionCollector.appendException(
|
||||
ImportError(_('Import "%s" is not valid.') % import_uri_def))
|
||||
return
|
||||
return None, None
|
||||
|
||||
full_url = ""
|
||||
if repository:
|
||||
@ -264,10 +275,10 @@ class ImportsLoader(object):
|
||||
% {'n_uri': repository, 'tpl': import_name})
|
||||
log.error(msg)
|
||||
ExceptionCollector.appendException(ImportError(msg))
|
||||
return
|
||||
return None, None
|
||||
|
||||
if toscaparser.utils.urlutils.UrlUtils.validate_url(full_url):
|
||||
return YAML_LOADER(full_url, False)
|
||||
return full_url, YAML_LOADER(full_url, False)
|
||||
else:
|
||||
msg = (_('repository url "%(n_uri)s" is not valid in import '
|
||||
'definition "%(tpl)s".')
|
||||
|
@ -48,6 +48,7 @@ class NodeTemplate(EntityTemplate):
|
||||
self.available_rel_tpls = available_rel_tpls
|
||||
self.available_rel_types = available_rel_types
|
||||
self._relationships = {}
|
||||
self.sub_mapping_tosca_template = None
|
||||
|
||||
@property
|
||||
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
|
||||
|
||||
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:
|
||||
properties:
|
||||
mq_server_ip:
|
||||
@ -17,18 +39,8 @@ node_types:
|
||||
type: example.capabilities.Receiver
|
||||
requirements:
|
||||
- database_endpoint:
|
||||
capability: tosca.capabilities.Endpoint.Database
|
||||
node: tosca.nodes.Database
|
||||
relationship: tosca.relationships.ConnectsTo
|
||||
|
||||
example.QueuingSubsystem:
|
||||
derived_from: tosca.nodes.SoftwareComponent
|
||||
requirements:
|
||||
- receiver1:
|
||||
node: example.TransactionSubsystem
|
||||
relationship: tosca.relationships.ConnectsTo
|
||||
- receiver2:
|
||||
node: example.TransactionSubsystem
|
||||
capability: tosca.capabilities.Endpoint.Database
|
||||
relationship: tosca.relationships.ConnectsTo
|
||||
|
||||
example.DatabaseSubsystem:
|
||||
@ -44,6 +56,11 @@ node_types:
|
||||
capabilities:
|
||||
message_receiver:
|
||||
type: example.capabilities.Receiver
|
||||
requirements:
|
||||
- database:
|
||||
node: tosca.nodes.Database
|
||||
capability: tosca.capabilities.Endpoint.Database
|
||||
relationship: tosca.relationships.ConnectsTo
|
||||
|
||||
capability_types:
|
||||
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
|
||||
|
||||
imports:
|
||||
- definitions.yaml
|
||||
- queuingsubsystem.yaml
|
||||
- transactionsubsystem.yaml
|
||||
- databasesubsystem.yaml
|
||||
|
||||
topology_template:
|
||||
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:
|
||||
mq:
|
||||
type: example.QueuingSubsystem
|
||||
# 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:
|
||||
# message_queue_endpoint:
|
||||
# to be updated when substitution_mapping is implemented
|
||||
# to be updated when substitution_mapping is validated later
|
||||
requirements:
|
||||
- receiver1: trans1
|
||||
- receiver2: trans2
|
||||
@ -21,37 +36,33 @@ topology_template:
|
||||
trans1:
|
||||
type: example.TransactionSubsystem
|
||||
properties:
|
||||
# TODO to be updated when substitution_mapping is implemented
|
||||
# mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
||||
# for now, we will use the loopback address to avoid errors as
|
||||
# this property is required in the schema
|
||||
mq_server_ip: 127.0.0.1
|
||||
receiver_port: 8080
|
||||
# mq_server_ip: 127.0.0.1
|
||||
mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
||||
# receiver_port: 8080
|
||||
receiver_port: { get_attribute: [ mq, server_port ] }
|
||||
# capabilities:
|
||||
# message_receiver:
|
||||
# to be updated when substitution_mapping is implemented
|
||||
# to be updated when substitution_mapping is validated later
|
||||
requirements:
|
||||
- database_endpoint: dbsys
|
||||
|
||||
trans2:
|
||||
type: example.TransactionSubsystem
|
||||
properties:
|
||||
# TODO to be updated when substitution_mapping is implemented
|
||||
# mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
||||
# for now, we will use the loopback address to avoid errors as
|
||||
# this property is required in the schema
|
||||
mq_server_ip: 127.0.0.1
|
||||
receiver_port: 8080
|
||||
# mq_server_ip: 127.0.0.1
|
||||
mq_server_ip: { get_attribute: [ mq, server_ip ] }
|
||||
# receiver_port: 8080
|
||||
receiver_port: { get_attribute: [ mq, server_port ] }
|
||||
# capabilities:
|
||||
# message_receiver:
|
||||
# to be updated when substitution_mapping is implemented
|
||||
# to be updated when substitution_mapping is validated later
|
||||
requirements:
|
||||
- database_endpoint: dbsys
|
||||
|
||||
dbsys:
|
||||
type: example.DatabaseSubsystem
|
||||
# properties:
|
||||
# to be updated when substitution_mapping is implemented
|
||||
# to be updated when substitution_mapping is validated later
|
||||
# capabilities:
|
||||
# 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
|
||||
|
||||
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:
|
||||
- definitions.yaml
|
||||
@ -13,12 +15,15 @@ topology_template:
|
||||
mq_server_ip:
|
||||
type: string
|
||||
description: IP address of the message queuing server to receive messages from.
|
||||
default: 127.0.0.1
|
||||
receiver_port:
|
||||
type: string
|
||||
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 ]
|
||||
|
@ -27,7 +27,7 @@ class TopologyTemplateTest(TestCase):
|
||||
'''TOSCA template.'''
|
||||
self.tosca_tpl_path = os.path.join(
|
||||
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.topo_tpl = self.tpl.get('topology_template')
|
||||
self.imports = self.tpl.get('imports')
|
||||
@ -157,4 +157,8 @@ class TopologyTemplateTest(TestCase):
|
||||
tpl_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"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.policy import Policy
|
||||
from toscaparser.relationship_template import RelationshipTemplate
|
||||
from toscaparser.substitution_mappings import SubstitutionMappings
|
||||
from toscaparser.tpl_relationship_graph import ToscaGraph
|
||||
from toscaparser.utils.gettextutils import _
|
||||
|
||||
@ -41,8 +42,10 @@ class TopologyTemplate(object):
|
||||
|
||||
'''Load the template data.'''
|
||||
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.sub_mapped_node_template = sub_mapped_node_template
|
||||
if self.tpl:
|
||||
self.custom_defs = custom_defs
|
||||
self.rel_types = rel_types
|
||||
@ -58,6 +61,7 @@ class TopologyTemplate(object):
|
||||
self.groups = self._groups()
|
||||
self.policies = self._policies()
|
||||
self._process_intrinsic_functions()
|
||||
self.substitution_mappings = self._substitution_mappings()
|
||||
|
||||
def _inputs(self):
|
||||
inputs = []
|
||||
@ -105,7 +109,15 @@ class TopologyTemplate(object):
|
||||
return outputs
|
||||
|
||||
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):
|
||||
policies = []
|
||||
@ -177,13 +189,16 @@ class TopologyTemplate(object):
|
||||
# topology template can act like node template
|
||||
# it is exposed by substitution_mappings.
|
||||
def nodetype(self):
|
||||
pass
|
||||
return self.substitution_mappings.node_type \
|
||||
if self.substitution_mappings else None
|
||||
|
||||
def capabilities(self):
|
||||
pass
|
||||
return self.substitution_mappings.capabilities \
|
||||
if self.substitution_mappings else None
|
||||
|
||||
def requirements(self):
|
||||
pass
|
||||
return self.substitution_mappings.requirements \
|
||||
if self.substitution_mappings else None
|
||||
|
||||
def _tpl_description(self):
|
||||
description = self.tpl.get(DESCRIPTION)
|
||||
@ -278,3 +293,9 @@ class TopologyTemplate(object):
|
||||
func = functions.get_function(self, self.outputs, output.value)
|
||||
if isinstance(func, functions.GetAttribute):
|
||||
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.'''
|
||||
def __init__(self, path=None, parsed_params=None, a_file=True,
|
||||
yaml_dict_tpl=None):
|
||||
|
||||
ExceptionCollector.start()
|
||||
self.a_file = a_file
|
||||
self.input_path = None
|
||||
self.path = None
|
||||
self.tpl = None
|
||||
self.nested_tosca_tpls_with_topology = {}
|
||||
self.nested_tosca_templates_with_topology = []
|
||||
if path:
|
||||
self.input_path = path
|
||||
self.path = self._get_path(path)
|
||||
@ -101,6 +104,7 @@ class ToscaTemplate(object):
|
||||
self.relationship_templates = self._relationship_templates()
|
||||
self.nodetemplates = self._nodetemplates()
|
||||
self.outputs = self._outputs()
|
||||
self._handle_nested_tosca_templates_with_topology()
|
||||
self.graph = ToscaGraph(self.nodetemplates)
|
||||
|
||||
ExceptionCollector.stop()
|
||||
@ -110,7 +114,8 @@ class ToscaTemplate(object):
|
||||
return TopologyTemplate(self._tpl_topology_template(),
|
||||
self._get_all_custom_defs(),
|
||||
self.relationship_types,
|
||||
self.parsed_params)
|
||||
self.parsed_params,
|
||||
None)
|
||||
|
||||
def _inputs(self):
|
||||
return self.topology_template.inputs
|
||||
@ -188,9 +193,14 @@ class ToscaTemplate(object):
|
||||
imports = self._tpl_imports()
|
||||
|
||||
if imports:
|
||||
custom_defs = toscaparser.imports.\
|
||||
custom_service = toscaparser.imports.\
|
||||
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:
|
||||
return
|
||||
|
||||
@ -202,6 +212,33 @@ class ToscaTemplate(object):
|
||||
custom_defs.update(inner_custom_types)
|
||||
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):
|
||||
version = self._tpl_version()
|
||||
if not version:
|
||||
@ -264,3 +301,28 @@ class ToscaTemplate(object):
|
||||
msg = _('The pre-parsed input successfully passed validation.')
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user