Validate occurrences in relationships

Check that the number of occurences of the relationships of a template is correct related with the definition of the type.
Also fix other tests that do not pass this new test.

Related-Bug: 2044266
Change-Id: I3dfa56d4e48454983daacc4b210017be1123683a
This commit is contained in:
micafer
2023-11-23 08:14:12 +01:00
committed by Miguel Caballer
parent 386c260206
commit fe0d9e9934
26 changed files with 218 additions and 10 deletions

View File

@@ -41,4 +41,8 @@ topology_template:
num_cpus: 1
disk_size: 10 GB
mem_size: 2 MB
requirements:
- host: my_server
my_server:
type: tosca.nodes.Compute

View File

@@ -1,5 +1,8 @@
tosca_definitions_version: tosca_simple_yaml_1_0
imports:
- elasticsearch: data/custom_types/elasticsearch.yaml
description: >
Logstash is a tool for receiving, processing and outputting logs. All kinds
of logs. System logs, webserver logs, error logs, application logs, and just

View File

@@ -13,6 +13,8 @@ topology_template:
id: vnf1
vendor: acmetelco
version: 1.0
requirements:
- virtualLink: PrivateNetwork
VDU1:
type: tosca.nodes.nfv.VDU
@@ -24,6 +26,11 @@ topology_template:
requirements:
- virtualLink: PrivateNetwork
- virtualBinding: VDU1
# Added to pass the requirement validation
# as the tosca.nodes.nfv.CP inherits from tosca.nodes.Port
# This type was removed from the final version of the NFV profile
- link: PrivateNetwork
- binding: VDU1
PrivateNetwork:
type: tosca.nodes.nfv.VL

View File

@@ -19,6 +19,11 @@ topology_template:
type: ServerNode
properties:
notification_port: 8000
requirements:
- host: my_server_host
my_server_host:
type: tosca.nodes.Compute
outputs:
ip_address:

View File

@@ -21,3 +21,8 @@ topology_template:
inputs:
param3:
type: string
requirements:
- host: my_server
my_server:
type: tosca.nodes.Compute

View File

@@ -17,3 +17,8 @@ topology_template:
inputs:
param3:
type: string
requirements:
- host: compute
compute:
type: tosca.nodes.Compute

View File

@@ -15,6 +15,11 @@ topology_template:
CustomOp:
notifications:
CustomNo:
requirements:
- host: compute
compute:
type: tosca.nodes.Compute
interface_types:
tosca.interfaces.CustomInterface:

View File

@@ -14,6 +14,11 @@ topology_template:
operations:
CustomOp: # operation from interface_type with additional outputs
outputs:
requirements:
- host: compute
compute:
type: tosca.nodes.Compute
interface_types:
tosca.interfaces.CustomInterface:

View File

@@ -30,6 +30,11 @@ topology_template:
node: VL1
- virtualBinding:
node: VDU1
# Added to pass the requirement validation
# as the tosca.nodes.nfv.CP inherits from tosca.nodes.Port
# This type was removed from the final version of the NFV profile
- link: VL1
- binding: VDU1
VDU2:
type: tosca.nodes.nfv.VDU.Tacker
@@ -50,7 +55,11 @@ topology_template:
node: VL1
- virtualBinding:
node: VDU2
# Added to pass the requirement validation
# as the tosca.nodes.nfv.CP inherits from tosca.nodes.Port
# This type was removed from the final version of the NFV profile
- link: VL1
- binding: VDU2
VL1:
type: tosca.nodes.nfv.VL
properties:

View File

@@ -22,6 +22,8 @@ topology_template:
node: mysql_database
relationship:
type: tosca.relationships.ConnectsTo
- host: my_webserver
- database_endpoint: mysql_database
mysql_database:
description: Specify requirement via a capability as an implicit relationship.
type: tosca.nodes.Database
@@ -31,8 +33,14 @@ topology_template:
relationship: tosca.relationships.HostedOn
my_dbms:
type: tosca.nodes.DBMS
requirements:
- host:
node: my_server
my_webserver:
type: tosca.nodes.WebServer
requirements:
- host:
node: my_server
my_server:
description: >
Specify requirement via a relationship template, as an explicit relationship.

View File

@@ -34,3 +34,8 @@ topology_template:
test_cap:
properties:
test: 1
requirements:
- host: test_server
test_server:
type: tosca.nodes.Compute

View File

@@ -10,4 +10,8 @@ topology_template:
node_templates:
test:
type: tosca.nodes.SomeNode
requirements:
- host: my_server
my_server:
type: tosca.nodes.Compute

View File

@@ -10,3 +10,8 @@ topology_template:
node_templates:
test:
type: tosca.nodes.SomeNode
requirements:
- host: test_server
test_server:
type: tosca.nodes.Compute

View File

@@ -12,15 +12,43 @@ topology_template:
node_templates:
wordpress:
type: tosca.nodes.WebApplication.WordPress
requirements:
- host: server
- database_endpoint: wordpress_db
testrsyslogtype:
type: tosca.nodes.SoftwareComponent.Rsyslog.TestRsyslogType
requirements:
- log_endpoint: logstash
- host: server
rsyslog:
type: Test2ndRsyslogType
requirements:
- log_endpoint: logstash
- host: server
logstash:
type: tosca.nodes.SoftwareComponent.Logstash
requirements:
- search_endpoint: elasticsearch
- host: server
kibana:
type: tosca.nodes.SoftwareComponent.Kibana
requirements:
- search_endpoint: elasticsearch
- host: server
elasticsearch:
type: tosca.nodes.SoftwareComponent.Elasticsearch
requirements:
- host: server
server:
type: tosca.nodes.Compute
wordpress_db:
type: tosca.nodes.Database
requirements:
- host: server

View File

@@ -17,6 +17,7 @@ topology_template:
type: sample.SC
requirements:
- custom_host: Compute1
- host: Compute1
Compute1:
type: tosca.nodes.Compute
SC2:

View File

@@ -0,0 +1,9 @@
tosca_definitions_version: tosca_simple_yaml_1_0
description: >
Webserver without host requirement
topology_template:
node_templates:
webserver:
type: tosca.nodes.WebServer

View File

@@ -30,6 +30,7 @@ topology_template:
requirements:
- receiver1: trans1
- receiver2: trans2
- host: server
trans1:
type: example.TransactionSubsystem
@@ -60,3 +61,8 @@ topology_template:
# capabilities:
# database_endpoint:
# to be updated when substitution_mapping is validated later
requirements:
- host: server
server:
type: tosca.nodes.Compute

View File

@@ -47,6 +47,14 @@ topology_template:
requirements:
- host:
node: websrv
- database:
node: db
db:
type: example.DatabaseSubsystem
requirements:
- host:
node: server
websrv:
type: tosca.nodes.WebServer

View File

@@ -83,7 +83,7 @@ class NodeTemplate(EntityTemplate):
' can not be full-filled.') \
% {'node': node, 'name': self.name}
if (node in list(self.type_definition.TOSCA_DEF.keys())
or node in self.custom_def):
or (self.custom_def and node in self.custom_def)):
ExceptionCollector.appendException(NotImplementedError(msg))
return
@@ -221,6 +221,51 @@ class NodeTemplate(EntityTemplate):
self._common_validate_field(req, allowed_reqs,
'requirements')
def _validate_relationship_occurrences(self):
type_requires = self.type_definition.get_all_requirements()
requires = self.type_definition.get_value(self.REQUIREMENTS,
self.entity_tpl)
if requires is None:
requires = []
# in case of substitution mapping, we need to get also the substitution
if self.sub_mapping_tosca_template:
mapping_reqs = self.sub_mapping_tosca_template.requirements
if mapping_reqs:
if isinstance(mapping_reqs, list):
requires.extend(mapping_reqs)
else:
requires.append(mapping_reqs)
for tpl in self.sub_mapping_tosca_template.nodetemplates:
if tpl.type == self.type:
if tpl.requirements:
if isinstance(tpl.requirements, list):
requires.extend(tpl.requirements)
else:
requires.append(tpl.requirements)
break
if type_requires:
for treq in type_requires:
for key, value in treq.items():
occurrences = value.get('occurrences', [1, 'UNBOUNDED'])
min_occurrences = occurrences[0]
max_occurrences = occurrences[1]
if max_occurrences == "UNBOUNDED":
max_occurrences = 9999999999
count = 0
for req in requires:
for req_name in list(req.keys()):
if req_name == key:
count += 1
if count < min_occurrences or count > max_occurrences:
ExceptionCollector.appendException(
ValidationError(
message='Relationship "%s" in template "%s"'
' has a wrong number of '
'occurrences' % (key, self.name)))
def _validate_requirements_properties(self, requirements):
# TODO(anyone): Only occurrences property of the requirements is
# validated here. Validation of other requirement properties are being

View File

@@ -213,6 +213,11 @@ class PropertyTest(TestCase):
type: tosca.nodes.SoftwareComponent.MySoftware
properties:
component_version: 3.1
requirements:
- host: my_server_host
my_server_host:
type: tosca.nodes.Compute
'''
expected_properties = ['component_version',

View File

@@ -84,16 +84,18 @@ class TopologyTemplateTest(TestCase):
def test_node_tpls(self):
'''Test nodetemplate names.'''
self.assertEqual(
['app', 'server', 'websrv'],
['app', 'db', 'server', 'websrv'],
sorted([tpl.name for tpl in self.topo.nodetemplates]))
tpl_name = "app"
expected_type = "example.SomeApp"
expected_properties = ['admin_user', 'pool_size']
expected_capabilities = ['app_endpoint', 'feature', 'message_receiver']
expected_requirements = [{'host': {'node': 'websrv'}}]
expected_relationshp = ['tosca.relationships.HostedOn']
expected_host = ['websrv']
expected_requirements = [{'host': {'node': 'websrv'}},
{'database': {'node': 'db'}}]
expected_relationshp = ['tosca.relationships.HostedOn',
'tosca.relationships.ConnectsTo']
expected_host = ['websrv', 'db']
for tpl in self.topo.nodetemplates:
if tpl_name == tpl.name:
'''Test node type.'''

View File

@@ -154,12 +154,12 @@ class ToscaTemplateTest(TestCase):
# Nodes that contain "relationship" in "requirements"
depend_node_types = (
"tosca.nodes.SoftwareComponent",
"sample.SC",
)
# Nodes that do not contain "relationship" in "requirements"
non_depend_node_types = (
"tosca.nodes.Compute",
"sample.SC",
)
tosca_tpl = utils.get_sample_test_path(
@@ -327,6 +327,8 @@ class ToscaTemplateTest(TestCase):
if node_tpl.name == 'my_app':
expected_relationship = [
('tosca.relationships.ConnectsTo', 'mysql_database'),
('tosca.relationships.ConnectsTo', 'mysql_database'),
('tosca.relationships.HostedOn', 'my_webserver'),
('tosca.relationships.HostedOn', 'my_webserver')]
actual_relationship = sorted([
(relation.type, node.name) for
@@ -487,8 +489,14 @@ class ToscaTemplateTest(TestCase):
"test": 1
}
}
}
}
},
"requirements": [
{
"host": "test_server"
}
]
},
'test_server': {'type': 'tosca.nodes.Compute'}
}
tosca_tpl = utils.get_sample_test_path(
@@ -618,7 +626,8 @@ class ToscaTemplateTest(TestCase):
tosca_tpl = utils.get_sample_test_path(
"data/test_instance_nested_imports.yaml")
tosca = ToscaTemplate(tosca_tpl)
expected_custom_types = ['tosca.nodes.SoftwareComponent.Kibana',
expected_custom_types = ['tosca.nodes.SoftwareComponent.Elasticsearch',
'tosca.nodes.SoftwareComponent.Kibana',
'tosca.nodes.WebApplication.WordPress',
'test_namespace_prefix.Rsyslog',
'Test2ndRsyslogType',

View File

@@ -750,6 +750,8 @@ tosca-parser/master/toscaparser/tests/data/custom_types/wordpress.yaml
properties:
root_password: aaa
port: 3376
requirements:
- host: server
groups:
webserver_group:
@@ -773,6 +775,8 @@ tosca-parser/master/toscaparser/tests/data/custom_types/wordpress.yaml
properties:
root_password: aaa
port: 3376
requirements:
- host: server
groups:
webserver_group:
@@ -799,6 +803,8 @@ tosca-parser/master/toscaparser/tests/data/custom_types/wordpress.yaml
properties:
root_password: aaa
port: 3376
requirements:
- host: server
groups:
webserver_group:
@@ -826,6 +832,8 @@ tosca-parser/master/toscaparser/tests/data/custom_types/wordpress.yaml
properties:
root_password: aaa
port: 3376
requirements:
- host: server
groups:
webserver_group:
@@ -854,6 +862,8 @@ tosca-parser/master/toscaparser/tests/data/custom_types/wordpress.yaml
properties:
root_password: aaa
port: 3376
requirements:
- host: server
groups:
webserver_group:
@@ -2139,3 +2149,12 @@ tosca-parser/master/toscaparser/tests/data/custom_types/wordpress.yaml
"data/test_scalar_unit_without_unit.yaml")
self.assertRaises(exception.ValidationError,
lambda: ToscaTemplate(tpl_path))
def test_template_without_requirement(self):
tpl_path = utils.get_sample_test_path(
"data/test_template_without_requirement.yaml")
err = self.assertRaises(exception.ValidationError,
lambda: ToscaTemplate(tpl_path))
expectedmessage = _('Relationship "host" in template "webserver" has '
'a wrong number of occurrences')
self.assertIn(expectedmessage, err.message)

View File

@@ -114,10 +114,16 @@ class ToscaTemplate(object):
self.policies = self._policies()
self._handle_nested_tosca_templates_with_topology()
self.graph = ToscaGraph(self.nodetemplates)
self._validate_relationship_occurences()
ExceptionCollector.stop()
self.verify_template()
def _validate_relationship_occurences(self):
for tpl in self.nodetemplates:
# Check if the requirements has a correct number of occurrences
tpl._validate_relationship_occurrences()
def _topology_template(self):
return TopologyTemplate(self._tpl_topology_template(),
self._get_all_custom_defs(),