Validation of imported templates
Validated First level sections of imported templates,First level sub-sections of node_types and policy_types and added testcases for the same Change-Id: I51f4e2496fea567fb0e5672f77899334c7556909 Partially Implements: #1516177
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
from toscaparser.common.exception import ExceptionCollector
|
||||
from toscaparser.common.exception import ValidationError
|
||||
from toscaparser.extensions.exttools import ExtTools
|
||||
import toscaparser.utils.yamlparser
|
||||
|
||||
@@ -82,7 +84,7 @@ class EntityType(object):
|
||||
value = None
|
||||
if defs is None:
|
||||
if not hasattr(self, 'defs'):
|
||||
return
|
||||
return None
|
||||
defs = self.defs
|
||||
if ndtype in defs:
|
||||
value = defs[ndtype]
|
||||
@@ -111,8 +113,13 @@ class EntityType(object):
|
||||
|
||||
def get_definition(self, ndtype):
|
||||
value = None
|
||||
if not hasattr(self, 'defs'):
|
||||
defs = None
|
||||
ExceptionCollector.appendException(
|
||||
ValidationError(message="defs is " + str(defs)))
|
||||
else:
|
||||
defs = self.defs
|
||||
if ndtype in defs:
|
||||
if defs is not None and ndtype in defs:
|
||||
value = defs[ndtype]
|
||||
p = self.parent_type
|
||||
if p:
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from toscaparser.common.exception import ExceptionCollector
|
||||
from toscaparser.common.exception import UnknownFieldError
|
||||
from toscaparser.common.exception import ValidationError
|
||||
from toscaparser.elements.capabilitytype import CapabilityTypeDef
|
||||
import toscaparser.elements.interfaces as ifaces
|
||||
from toscaparser.elements.interfaces import InterfacesDef
|
||||
@@ -19,16 +22,22 @@ from toscaparser.elements.statefulentitytype import StatefulEntityType
|
||||
|
||||
class NodeType(StatefulEntityType):
|
||||
'''TOSCA built-in node type.'''
|
||||
SECTIONS = (DERIVED_FROM, METADATA, PROPERTIES, VERSION, DESCRIPTION, ATTRIBUTES, REQUIREMENTS, CAPABILITIES, INTERFACES, ARTIFACTS) = \
|
||||
('derived_from', 'metadata', 'properties', 'version',
|
||||
'description', 'attributes', 'requirements', 'capabilities',
|
||||
'interfaces', 'artifacts')
|
||||
|
||||
def __init__(self, ntype, custom_def=None):
|
||||
super(NodeType, self).__init__(ntype, self.NODE_PREFIX, custom_def)
|
||||
self.ntype = ntype
|
||||
self.custom_def = custom_def
|
||||
self._validate_keys()
|
||||
|
||||
@property
|
||||
def parent_type(self):
|
||||
'''Return a node this node is derived from.'''
|
||||
if not hasattr(self, 'defs'):
|
||||
return
|
||||
return None
|
||||
pnode = self.derived_from(self.defs)
|
||||
if pnode:
|
||||
return NodeType(pnode, self.custom_def)
|
||||
@@ -153,6 +162,11 @@ class NodeType(StatefulEntityType):
|
||||
parent_node = self.parent_type
|
||||
if requires is None:
|
||||
requires = self.get_value(self.REQUIREMENTS, None, True)
|
||||
if parent_node is None:
|
||||
ExceptionCollector.appendException(
|
||||
ValidationError(message="parent_node is "
|
||||
+ str(parent_node)))
|
||||
else:
|
||||
parent_node = parent_node.parent_type
|
||||
if parent_node:
|
||||
while parent_node.type != 'tosca.nodes.Root':
|
||||
@@ -200,3 +214,11 @@ class NodeType(StatefulEntityType):
|
||||
captype = self.get_capability(name)
|
||||
if captype and name in captype.keys():
|
||||
return captype[name].value
|
||||
|
||||
def _validate_keys(self):
|
||||
if self.defs:
|
||||
for key in self.defs.keys():
|
||||
if key not in self.SECTIONS:
|
||||
ExceptionCollector.appendException(
|
||||
UnknownFieldError(what='Nodetype"%s"' % self.ntype,
|
||||
field=key))
|
||||
|
||||
@@ -89,7 +89,7 @@ class PolicyType(StatefulEntityType):
|
||||
for key in self.defs.keys():
|
||||
if key not in self.SECTIONS:
|
||||
ExceptionCollector.appendException(
|
||||
UnknownFieldError(what='Policy "%s"' % self.name,
|
||||
UnknownFieldError(what='Policy "%s"' % self.type,
|
||||
field=key))
|
||||
|
||||
def _validate_targets(self, targets_list, custom_def):
|
||||
|
||||
@@ -39,6 +39,7 @@ class StatefulEntityType(EntityType):
|
||||
elif custom_def and entitytype in list(custom_def.keys()):
|
||||
self.defs = custom_def[entitytype]
|
||||
else:
|
||||
self.defs = None
|
||||
ExceptionCollector.appendException(
|
||||
InvalidTypeError(what=entitytype))
|
||||
self.type = entitytype
|
||||
|
||||
59
toscaparser/elements/tosca_type_validation.py
Normal file
59
toscaparser/elements/tosca_type_validation.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from toscaparser.common.exception import ExceptionCollector
|
||||
from toscaparser.common.exception import InvalidTemplateVersion
|
||||
from toscaparser.common.exception import UnknownFieldError
|
||||
from toscaparser.extensions.exttools import ExtTools
|
||||
|
||||
|
||||
class TypeValidation(object):
|
||||
|
||||
ALLOWED_TYPE_SECTIONS = (DEFINITION_VERSION, DESCRIPTION, IMPORTS,
|
||||
DSL_DEFINITIONS, NODE_TYPES, REPOSITORIES,
|
||||
DATA_TYPES, ARTIFACT_TYPES, GROUP_TYPES,
|
||||
RELATIONSHIP_TYPES, CAPABILITY_TYPES,
|
||||
INTERFCAE_TYPES, POLICY_TYPES, DATATYPE_DEFINITIONS) = \
|
||||
('tosca_definitions_version', 'description',
|
||||
'imports', 'dsl_definitions', 'node_types',
|
||||
'repositories', 'data_types', 'group_types',
|
||||
'artifact_types', 'relationship_types',
|
||||
'capability_types', 'interface_types',
|
||||
'policy_types', 'datatype_definitions')
|
||||
VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
|
||||
exttools = ExtTools()
|
||||
VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
|
||||
|
||||
def __init__(self, custom_types, import_def):
|
||||
self.import_def = import_def
|
||||
self._validate_type_keys(custom_types)
|
||||
|
||||
def _validate_type_keys(self, custom_type):
|
||||
version = custom_type[self.DEFINITION_VERSION] \
|
||||
if self.DEFINITION_VERSION in custom_type \
|
||||
else None
|
||||
if version:
|
||||
self._validate_type_version(version)
|
||||
self.version = version
|
||||
|
||||
for name in custom_type:
|
||||
if name not in self.ALLOWED_TYPE_SECTIONS:
|
||||
ExceptionCollector.appendException(
|
||||
UnknownFieldError(what='Template ' + (self.import_def),
|
||||
field=name))
|
||||
|
||||
def _validate_type_version(self, version):
|
||||
if version not in self.VALID_TEMPLATE_VERSIONS:
|
||||
ExceptionCollector.appendException(
|
||||
InvalidTemplateVersion(
|
||||
what=version + ' in ' + self.import_def,
|
||||
valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
|
||||
@@ -17,6 +17,7 @@ from toscaparser.common.exception import ExceptionCollector
|
||||
from toscaparser.common.exception import MissingRequiredFieldError
|
||||
from toscaparser.common.exception import UnknownFieldError
|
||||
from toscaparser.common.exception import ValidationError
|
||||
from toscaparser.elements.tosca_type_validation import TypeValidation
|
||||
from toscaparser.utils.gettextutils import _
|
||||
import toscaparser.utils.urlutils
|
||||
import toscaparser.utils.yamlparser
|
||||
@@ -79,11 +80,14 @@ class ImportsLoader(object):
|
||||
namespace_prefix = import_uri.get(
|
||||
self.NAMESPACE_PREFIX)
|
||||
if custom_type:
|
||||
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)
|
||||
if custom_type:
|
||||
TypeValidation(
|
||||
custom_type, import_def)
|
||||
self._update_custom_def(custom_type, None)
|
||||
|
||||
def _update_custom_def(self, custom_type, namespace_prefix):
|
||||
|
||||
@@ -18,6 +18,7 @@ from toscaparser.common.exception import InvalidPropertyValueError
|
||||
from toscaparser.common.exception import MissingRequiredFieldError
|
||||
from toscaparser.common.exception import TypeMismatchError
|
||||
from toscaparser.common.exception import UnknownFieldError
|
||||
from toscaparser.common.exception import ValidationError
|
||||
from toscaparser.dataentity import DataEntity
|
||||
from toscaparser.elements.interfaces import CONFIGURE
|
||||
from toscaparser.elements.interfaces import CONFIGURE_SHORTNAME
|
||||
@@ -93,6 +94,11 @@ class NodeTemplate(EntityTemplate):
|
||||
# check if it's type has relationship defined
|
||||
if not relationship:
|
||||
parent_reqs = self.type_definition.get_all_requirements()
|
||||
if parent_reqs is None:
|
||||
ExceptionCollector.appendException(
|
||||
ValidationError(message='parent_req is ' +
|
||||
str(parent_reqs)))
|
||||
else:
|
||||
for key in req.keys():
|
||||
for req_dict in parent_reqs:
|
||||
if key in req_dict.keys():
|
||||
|
||||
34
toscaparser/tests/data/custom_types/imported_sample.yaml
Normal file
34
toscaparser/tests/data/custom_types/imported_sample.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
tosca1_definitions_version: tosca_simple_yaml_1_0
|
||||
tosca_definitions_version: tosca_simple_yaml_1_10
|
||||
|
||||
descriptions: >
|
||||
Pizza store app that allows you to explore the features provided by PayPal's REST APIs.
|
||||
More detail can be found at https://github.com/paypal/rest-api-sample-app-nodejs/
|
||||
|
||||
node_typess:
|
||||
node_types:
|
||||
tosca.nodes.SoftwareComponent.Logstash:
|
||||
derived_from: tosca.nodes.SoftwareComponent
|
||||
requirements:
|
||||
- search_endpoint:
|
||||
capability: tosca.capabilities.Endpoint
|
||||
node: tosca.nodes.SoftwareComponent.Elasticsearch
|
||||
relationship:
|
||||
type: tosca.relationships.ConnectsTo
|
||||
interfaces:
|
||||
Configure:
|
||||
pre_configure_source:
|
||||
inputs:
|
||||
elasticsearch_ip:
|
||||
type: string
|
||||
capabilities1:
|
||||
log_endpoint:
|
||||
type: tosca.capabilities.Endpoint
|
||||
policy_types1:
|
||||
policy_types:
|
||||
mycompany.mytypes.myScalingPolicy:
|
||||
derived1_from: tosca.policies.Scaling
|
||||
metadata:
|
||||
type: map
|
||||
entry_schema:
|
||||
type: string
|
||||
39
toscaparser/tests/data/tosca_imports_validation.yaml
Normal file
39
toscaparser/tests/data/tosca_imports_validation.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||
|
||||
description: Template to test invalid imports.
|
||||
|
||||
imports:
|
||||
- custom_types/imported_sample.yaml
|
||||
|
||||
topology_template:
|
||||
node_templates:
|
||||
logstash:
|
||||
type: tosca.nodes.SoftwareComponent.Logstash
|
||||
requirements:
|
||||
- search_endpoint:
|
||||
capability: search_endpoint
|
||||
relationship:
|
||||
type: tosca.relationships.ConnectsTo
|
||||
interfaces:
|
||||
Configure:
|
||||
pre_configure_source:
|
||||
implementation: logstash/configure_elasticsearch.py
|
||||
inputs:
|
||||
elasticsearch_ip: { get_attribute: [elasticsearch_server, private_address] }
|
||||
interfaces:
|
||||
Standard:
|
||||
create: logstash/create.sh
|
||||
start: logstash/start.sh
|
||||
policies:
|
||||
- my_compute_placement_policy:
|
||||
type: tosca.policies.Placement
|
||||
description: Apply placement policy to servers
|
||||
metadata: { user1: 1001, user2: 1002 }
|
||||
targets: [ my_server_1, my_server_2 ]
|
||||
- my_groups_placement:
|
||||
type: mycompany.mytypes.myScalingPolicy
|
||||
targets: [ webserver_group ]
|
||||
description: my company scaling policy
|
||||
metadata:
|
||||
user1: 1001
|
||||
user2: 1003
|
||||
@@ -54,6 +54,48 @@ class ToscaTemplateValidationTest(TestCase):
|
||||
_('Template contains unknown field "node_template". Refer to the '
|
||||
'definition to verify valid values.'))
|
||||
|
||||
def test_template_with_imports_validation(self):
|
||||
tpl_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"data/tosca_imports_validation.yaml")
|
||||
self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path)
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.UnknownFieldError,
|
||||
_('Template custom_types/imported_sample.yaml contains unknown '
|
||||
'field "descriptions". Refer to the definition'
|
||||
' to verify valid values.'))
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.UnknownFieldError,
|
||||
_('Template custom_types/imported_sample.yaml contains unknown '
|
||||
'field "node_typess". Refer to the definition to '
|
||||
'verify valid values.'))
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.UnknownFieldError,
|
||||
_('Template custom_types/imported_sample.yaml contains unknown '
|
||||
'field "tosca1_definitions_version". Refer to the definition'
|
||||
' to verify valid values.'))
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.InvalidTemplateVersion,
|
||||
_('The template version "tosca_simple_yaml_1_10 in '
|
||||
'custom_types/imported_sample.yaml" is invalid. '
|
||||
'Valid versions are "tosca_simple_yaml_1_0, '
|
||||
'tosca_simple_profile_for_nfv_1_0_0".'))
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.UnknownFieldError,
|
||||
_('Template custom_types/imported_sample.yaml contains unknown '
|
||||
'field "policy_types1". Refer to the definition to '
|
||||
'verify valid values.'))
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.UnknownFieldError,
|
||||
_('Nodetype"tosca.nodes.SoftwareComponent.Logstash" contains '
|
||||
'unknown field "capabilities1". Refer to the definition '
|
||||
'to verify valid values.'))
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
exception.UnknownFieldError,
|
||||
_('Policy "mycompany.mytypes.myScalingPolicy" contains unknown '
|
||||
'field "derived1_from". Refer to the definition to '
|
||||
'verify valid values.'))
|
||||
|
||||
def test_inputs(self):
|
||||
tpl_snippet = '''
|
||||
inputs:
|
||||
|
||||
Reference in New Issue
Block a user