diff --git a/toscaparser/common/exception.py b/toscaparser/common/exception.py index 724844b..13ccabd 100644 --- a/toscaparser/common/exception.py +++ b/toscaparser/common/exception.py @@ -114,6 +114,10 @@ class UnknownInputError(TOSCAException): msg_fmt = _('Unknown input "%(input_name)s".') +class UnknownOutputError(TOSCAException): + msg_fmt = _('Unknown output "%(output_name)s" in %(where)s.') + + class MissingRequiredInputError(TOSCAException): msg_fmt = _('%(what)s is missing required input definition ' 'of input "%(input_name)s".') @@ -129,6 +133,11 @@ class MissingDefaultValueError(TOSCAException): 'of input "%(input_name)s".') +class MissingRequiredOutputError(TOSCAException): + msg_fmt = _('%(what)s is missing required output definition ' + 'of output "%(output_name)s".') + + class InvalidPropertyValueError(TOSCAException): msg_fmt = _('Value of property "%(what)s" is invalid.') diff --git a/toscaparser/substitution_mappings.py b/toscaparser/substitution_mappings.py index e6377ed..1064143 100644 --- a/toscaparser/substitution_mappings.py +++ b/toscaparser/substitution_mappings.py @@ -18,6 +18,7 @@ from toscaparser.common.exception import MissingDefaultValueError from toscaparser.common.exception import MissingRequiredFieldError from toscaparser.common.exception import MissingRequiredInputError from toscaparser.common.exception import UnknownFieldError +from toscaparser.common.exception import UnknownOutputError from toscaparser.elements.nodetype import NodeType from toscaparser.utils.gettextutils import _ @@ -34,6 +35,8 @@ class SubstitutionMappings(object): SECTIONS = (NODE_TYPE, REQUIREMENTS, CAPABILITIES) = \ ('node_type', 'requirements', 'capabilities') + OPTIONAL_OUTPUTS = ['tosca_id', 'tosca_name', 'state'] + def __init__(self, sub_mapping_def, nodetemplates, inputs, outputs, sub_mapped_node_template, custom_defs): self.nodetemplates = nodetemplates @@ -189,14 +192,23 @@ class SubstitutionMappings(object): # 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)) + """validate the outputs of substitution mappings. + + The outputs defined by the topology template have to match the + attributes of the node type or the substituted node template, + and the observable attributes of the substituted node template + have to be defined as attributes of the node type or outputs in + the topology template. + """ + + # The outputs defined by the topology template have to match the + # attributes of the node type according to the specification, but + # it's reasonable that there are more inputs than the node type + # has properties, the specification will be amended? + for output in self.outputs: + if output.name not in self.node_definition.get_attributes_def(): + ExceptionCollector.appendException( + UnknownOutputError( + where=_('SubstitutionMappings with node_type ') + + self.node_type, + output_name=output.name)) diff --git a/toscaparser/tests/data/topology_template/databasesubsystem.yaml b/toscaparser/tests/data/topology_template/databasesubsystem.yaml index ebf1856..b6e9c42 100644 --- a/toscaparser/tests/data/topology_template/databasesubsystem.yaml +++ b/toscaparser/tests/data/topology_template/databasesubsystem.yaml @@ -75,15 +75,6 @@ topology_template: 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 ] diff --git a/toscaparser/tests/data/topology_template/queuingsubsystem.yaml b/toscaparser/tests/data/topology_template/queuingsubsystem.yaml index 76fa7e2..578aa4a 100644 --- a/toscaparser/tests/data/topology_template/queuingsubsystem.yaml +++ b/toscaparser/tests/data/topology_template/queuingsubsystem.yaml @@ -61,13 +61,13 @@ topology_template: 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 ] } + server_ip: + description: server_ip of the message receiver application + value: { get_input: server_ip } + + server_port: + description: server_port of the message receiver application + value: { get_input: server_port } groups: tran_server_group: diff --git a/toscaparser/tests/data/topology_template/system.yaml b/toscaparser/tests/data/topology_template/system.yaml index 00fb486..f4e10ae 100644 --- a/toscaparser/tests/data/topology_template/system.yaml +++ b/toscaparser/tests/data/topology_template/system.yaml @@ -21,8 +21,6 @@ topology_template: node_templates: mq: type: example.QueuingSubsystem - # properties: - # to be updated when substitution_mapping is validated later properties: server_ip: { get_input: mq_server_ip } server_port: { get_input: mq_server_port } @@ -36,9 +34,7 @@ topology_template: trans1: type: example.TransactionSubsystem properties: - # 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: @@ -49,9 +45,7 @@ topology_template: trans2: type: example.TransactionSubsystem properties: - # 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: diff --git a/toscaparser/tests/data/topology_template/transactionsubsystem.yaml b/toscaparser/tests/data/topology_template/transactionsubsystem.yaml index 0f145a3..42e100a 100644 --- a/toscaparser/tests/data/topology_template/transactionsubsystem.yaml +++ b/toscaparser/tests/data/topology_template/transactionsubsystem.yaml @@ -77,10 +77,10 @@ topology_template: 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 ] } + + receiver_port: + description: receiver_port of the message receiver application + value: { get_input: receiver_port } groups: webserver_group: diff --git a/toscaparser/tests/data/topology_template/validate/test_example_app_substitution_mappings.yaml b/toscaparser/tests/data/topology_template/validate/test_example_app_substitution_mappings.yaml new file mode 100644 index 0000000..718022a --- /dev/null +++ b/toscaparser/tests/data/topology_template/validate/test_example_app_substitution_mappings.yaml @@ -0,0 +1,70 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Template showing an example TOSCA type to demonstrate usage + of output in the substitution mappings. + +node_types: + example.app: + derived_from: tosca.nodes.WebApplication + properties: + mq_server_ip: + type: string + required: False + receiver_port: + type: integer + required: False + attributes: + receiver_ip: + type: string + receiver_port: + type: integer + +topology_template: + inputs: + 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: 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.app + + node_templates: + app: + type: example.app + properties: + mq_server_ip: { get_input: mq_server_ip } + receiver_port: { get_input: receiver_port } + requirements: + - host: + node: websrv + websrv: + type: tosca.nodes.WebServer + 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 diff --git a/toscaparser/tests/data/topology_template/validate/test_substitution_mappings_invalid_output.yaml b/toscaparser/tests/data/topology_template/validate/test_substitution_mappings_invalid_output.yaml new file mode 100644 index 0000000..ef21811 --- /dev/null +++ b/toscaparser/tests/data/topology_template/validate/test_substitution_mappings_invalid_output.yaml @@ -0,0 +1,31 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +imports: + - test_example_app_substitution_mappings.yaml + +topology_template: + description: > + Test template showing valid output section containing attribute defined + in the substitution mappings in the imported yaml file. + + 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 + default: 8080 + description: Port to be used for receiving messages. + + node_templates: + substitute_app: + type: example.app + properties: + mq_server_ip: { get_input: mq_server_ip } + receiver_port: { get_input: mq_server_port } + + outputs: + receiver_ip: + description: private IP address of the message receiver application + value: { get_attribute: [ substitute_app, my_cpu_output ] } \ No newline at end of file diff --git a/toscaparser/tests/data/topology_template/validate/test_substitution_mappings_valid_output.yaml b/toscaparser/tests/data/topology_template/validate/test_substitution_mappings_valid_output.yaml new file mode 100644 index 0000000..766ca87 --- /dev/null +++ b/toscaparser/tests/data/topology_template/validate/test_substitution_mappings_valid_output.yaml @@ -0,0 +1,31 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +imports: + - test_example_app_substitution_mappings.yaml + +topology_template: + description: > + Test template showing valid output section containing attribute defined + in the substitution mappings in the imported yaml file. + + 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 + default: 8080 + description: Port to be used for receiving messages. + + node_templates: + sustitute_app: + type: example.app + properties: + mq_server_ip: { get_input: mq_server_ip } + receiver_port: { get_input: mq_server_port } + + outputs: + receiver_ip: + description: private IP address of the message receiver application + value: { get_attribute: [ sustitute_app, receiver_ip ] } \ No newline at end of file diff --git a/toscaparser/tests/test_topology_template.py b/toscaparser/tests/test_topology_template.py index 99ca84c..f9e2122 100644 --- a/toscaparser/tests/test_topology_template.py +++ b/toscaparser/tests/test_topology_template.py @@ -154,7 +154,7 @@ class TopologyTemplateTest(TestCase): def test_outputs(self): self.assertEqual( - ['receiver_ip'], + sorted(['receiver_ip', 'receiver_port']), sorted([output.name for output in self.topo.outputs])) def test_groups(self): @@ -262,3 +262,22 @@ class TopologyTemplateTest(TestCase): lambda: ToscaTemplate(tpl_path1)) exception.ExceptionCollector.assertExceptionMessage( exception.MissingRequiredInputError, errormsg) + + def test_substitution_mappings_valid_output(self): + tpl_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/topology_template/validate/" + "test_substitution_mappings_valid_output.yaml") + self.assertIsNotNone(ToscaTemplate(tpl_path)) + + def test_system_with_unknown_output_validation(self): + tpl_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/topology_template/validate/" + "test_substitution_mappings_invalid_output.yaml") + errormsg = _('\'Attribute "my_cpu_output" was not found in node ' + 'template "substitute_app".\'') + self.assertRaises(exception.ValidationError, + lambda: ToscaTemplate(tpl_path)) + exception.ExceptionCollector.assertExceptionMessage( + KeyError, errormsg)