summaryrefslogtreecommitdiffstats
path: root/tosca2heat/tosca-parser
diff options
context:
space:
mode:
authorshangxdy <shang.xiaodong@zte.com.cn>2016-07-08 15:15:00 +0800
committershangxdy <shang.xiaodong@zte.com.cn>2016-07-10 00:38:59 +0800
commit0997552722dc4845a854e0e6f8d7f18058e26380 (patch)
treeb90d1e808bb326612211ba56b3b941516493398d /tosca2heat/tosca-parser
parent7fe3011a67a239f7dc04153c54eaff78ef967eaf (diff)
Synchronise the openstack bugs
When run unittests through tox, some test cases are always error, the errors are already done in openstack community, so it's necessary to synchronise the fixes. Change-Id: Ib29078e6cc138a474e89c6a2cc90ad7a1db1bb46 JIRA: PARSER-63 Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
Diffstat (limited to 'tosca2heat/tosca-parser')
-rw-r--r--tosca2heat/tosca-parser/.gitignore56
-rw-r--r--tosca2heat/tosca-parser/.gitreview4
-rw-r--r--tosca2heat/tosca-parser/.mailmap3
-rw-r--r--tosca2heat/tosca-parser/requirements.txt3
-rw-r--r--tosca2heat/tosca-parser/test-requirements.txt4
-rw-r--r--tosca2heat/tosca-parser/toscaparser/common/exception.py9
-rw-r--r--tosca2heat/tosca-parser/toscaparser/dataentity.py25
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml9
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/capabilitytype.py12
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/constraints.py8
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/datatype.py3
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/entity_type.py3
-rw-r--r--tosca2heat/tosca-parser/toscaparser/elements/portspectype.py86
-rw-r--r--tosca2heat/tosca-parser/toscaparser/entity_template.py9
-rw-r--r--tosca2heat/tosca-parser/toscaparser/extensions/exttools.py2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/extensions/nfv/TOSCA_nfv_definition_1_0.yaml13
-rw-r--r--tosca2heat/tosca-parser/toscaparser/functions.py211
-rw-r--r--tosca2heat/tosca-parser/toscaparser/groups.py2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/imports.py8
-rw-r--r--tosca2heat/tosca-parser/toscaparser/parameters.py14
-rw-r--r--tosca2heat/tosca-parser/toscaparser/properties.py3
-rw-r--r--tosca2heat/tosca-parser/toscaparser/repositories.py52
-rw-r--r--tosca2heat/tosca-parser/toscaparser/shell.py16
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/compute_with_nested_atributes.yaml22
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml5
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/datatypes/test_datatype_portspec_add_req.yaml41
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_index_error.yaml2
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_nested_params.yaml23
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token.yaml15
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token_invalid.yaml17
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/test_containers.yaml44
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/test_credential_datatype.yaml77
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/test_invalid_input_defaults.yaml12
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/test_repositories_definition.yaml6
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/subsystem.yaml3
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/tosca_repositories_test_definition.yaml26
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py135
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_functions.py46
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_toscatpl.py62
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py145
-rw-r--r--tosca2heat/tosca-parser/toscaparser/topology_template.py4
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tosca_template.py11
-rw-r--r--tosca2heat/tosca-parser/toscaparser/utils/validateutils.py69
43 files changed, 1183 insertions, 137 deletions
diff --git a/tosca2heat/tosca-parser/.gitignore b/tosca2heat/tosca-parser/.gitignore
new file mode 100644
index 0000000..c172104
--- /dev/null
+++ b/tosca2heat/tosca-parser/.gitignore
@@ -0,0 +1,56 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+.testrepository
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+# Complexity
+output/*.html
+output/*/index.html
+
+# Sphinx
+doc/build
+
+# pbr generates these
+AUTHORS
+ChangeLog
+
+# Editors
+*~
+.*.swp
+.idea
+*.iml
+
+# OSX
+.DS_Store \ No newline at end of file
diff --git a/tosca2heat/tosca-parser/.gitreview b/tosca2heat/tosca-parser/.gitreview
new file mode 100644
index 0000000..9105f23
--- /dev/null
+++ b/tosca2heat/tosca-parser/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=review.openstack.org
+port=29418
+project=openstack/tosca-parser.git
diff --git a/tosca2heat/tosca-parser/.mailmap b/tosca2heat/tosca-parser/.mailmap
new file mode 100644
index 0000000..cc92f17
--- /dev/null
+++ b/tosca2heat/tosca-parser/.mailmap
@@ -0,0 +1,3 @@
+# Format is:
+# <preferred e-mail> <other e-mail 1>
+# <preferred e-mail> <other e-mail 2> \ No newline at end of file
diff --git a/tosca2heat/tosca-parser/requirements.txt b/tosca2heat/tosca-parser/requirements.txt
index dd27a53..b30cc51 100644
--- a/tosca2heat/tosca-parser/requirements.txt
+++ b/tosca2heat/tosca-parser/requirements.txt
@@ -2,8 +2,9 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr>=1.6 # Apache-2.0
-Babel>=1.3 # BSD
+Babel>=2.3.4 # BSD
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
PyYAML>=3.1.0 # MIT
python-dateutil>=2.4.2 # BSD
six>=1.9.0 # MIT
+requests>=2.10.0 # Apache-2.0
diff --git a/tosca2heat/tosca-parser/test-requirements.txt b/tosca2heat/tosca-parser/test-requirements.txt
index 2b9cae7..70a261b 100644
--- a/tosca2heat/tosca-parser/test-requirements.txt
+++ b/tosca2heat/tosca-parser/test-requirements.txt
@@ -4,11 +4,11 @@
hacking<0.11,>=0.10.0
coverage>=3.6 # Apache-2.0
discover # BSD
-fixtures>=1.3.1 # Apache-2.0/BSD
+fixtures>=3.0.0 # Apache-2.0/BSD
oslotest>=1.10.0 # Apache-2.0
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
-sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
+sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
diff --git a/tosca2heat/tosca-parser/toscaparser/common/exception.py b/tosca2heat/tosca-parser/toscaparser/common/exception.py
index 4f99dda..bd0ed9c 100644
--- a/tosca2heat/tosca-parser/toscaparser/common/exception.py
+++ b/tosca2heat/tosca-parser/toscaparser/common/exception.py
@@ -88,6 +88,15 @@ class InvalidTypeError(TOSCAException):
msg_fmt = _('Type "%(what)s" is not a valid type.')
+class InvalidTypeAdditionalRequirementsError(TOSCAException):
+ msg_fmt = _('Additional requirements for type "%(type)s" not met.')
+
+
+class RangeValueError(TOSCAException):
+ msg_fmt = _('The value "%(pvalue)s" of property "%(pname)s" is out of '
+ 'range "(min:%(vmin)s, max:%(vmax)s)".')
+
+
class InvalidSchemaError(TOSCAException):
msg_fmt = _('%(message)s')
diff --git a/tosca2heat/tosca-parser/toscaparser/dataentity.py b/tosca2heat/tosca-parser/toscaparser/dataentity.py
index 6e7d59e..507c899 100644
--- a/tosca2heat/tosca-parser/toscaparser/dataentity.py
+++ b/tosca2heat/tosca-parser/toscaparser/dataentity.py
@@ -16,10 +16,10 @@ from toscaparser.common.exception import TypeMismatchError
from toscaparser.common.exception import UnknownFieldError
from toscaparser.elements.constraints import Schema
from toscaparser.elements.datatype import DataType
+from toscaparser.elements.portspectype import PortSpec
from toscaparser.elements.scalarunit import ScalarUnit_Frequency
from toscaparser.elements.scalarunit import ScalarUnit_Size
from toscaparser.elements.scalarunit import ScalarUnit_Time
-
from toscaparser.utils.gettextutils import _
from toscaparser.utils import validateutils
@@ -27,11 +27,13 @@ from toscaparser.utils import validateutils
class DataEntity(object):
'''A complex data value entity.'''
- def __init__(self, datatypename, value_dict, custom_def=None):
+ def __init__(self, datatypename, value_dict, custom_def=None,
+ prop_name=None):
self.custom_def = custom_def
self.datatype = DataType(datatypename, custom_def)
self.schema = self.datatype.get_all_properties()
self.value = value_dict
+ self.property_name = prop_name
def validate(self):
'''Validate the value by the definition of the datatype.'''
@@ -43,7 +45,7 @@ class DataEntity(object):
self.value,
None,
self.custom_def)
- schema = Schema(None, self.datatype.defs)
+ schema = Schema(self.property_name, self.datatype.defs)
for constraint in schema.constraints:
constraint.validate(self.value)
# If the datatype has 'properties' definition
@@ -89,7 +91,10 @@ class DataEntity(object):
# check every field
for name, value in list(self.value.items()):
- prop_schema = Schema(name, self._find_schema(name))
+ schema_name = self._find_schema(name)
+ if not schema_name:
+ continue
+ prop_schema = Schema(name, schema_name)
# check if field value meets type defined
DataEntity.validate_datatype(prop_schema.type, value,
prop_schema.entry_schema,
@@ -110,12 +115,16 @@ class DataEntity(object):
return self.schema[name].schema
@staticmethod
- def validate_datatype(type, value, entry_schema=None, custom_def=None):
+ def validate_datatype(type, value, entry_schema=None, custom_def=None,
+ prop_name=None):
'''Validate value with given type.
If type is list or map, validate its entry by entry_schema(if defined)
If type is a user-defined complex datatype, custom_def is required.
'''
+ from toscaparser.functions import is_function
+ if is_function(value):
+ return value
if type == Schema.STRING:
return validateutils.validate_string(value)
elif type == Schema.INTEGER:
@@ -123,7 +132,7 @@ class DataEntity(object):
elif type == Schema.FLOAT:
return validateutils.validate_float(value)
elif type == Schema.NUMBER:
- return validateutils.validate_number(value)
+ return validateutils.validate_numeric(value)
elif type == Schema.BOOLEAN:
return validateutils.validate_boolean(value)
elif type == Schema.RANGE:
@@ -149,6 +158,10 @@ class DataEntity(object):
if entry_schema:
DataEntity.validate_entry(value, entry_schema, custom_def)
return value
+ elif type == Schema.PORTSPEC:
+ # TODO(TBD) bug 1567063, validate source & target as PortDef type
+ # as complex types not just as integers
+ PortSpec.validate_additional_req(value, prop_name, custom_def)
else:
data = DataEntity(type, value, custom_def)
return data.validate()
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml b/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
index c1c1cd4..ede5fa5 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
@@ -390,7 +390,7 @@ tosca.nodes.Container.Application:
requirements:
- host:
capability: tosca.capabilities.Container
- node: tosca.nodes.Container
+ node: tosca.nodes.Container.Runtime
relationship: tosca.relationships.HostedOn
tosca.nodes.Container.Runtime:
@@ -544,7 +544,7 @@ tosca.capabilities.Endpoint:
tosca.capabilities.Endpoint.Admin:
derived_from: tosca.capabilities.Endpoint
properties:
- secure:
+ secure:
type: boolean
default: true
constraints:
@@ -815,14 +815,19 @@ tosca.datatypes.Credential:
properties:
protocol:
type: string
+ required: false
token_type:
type: string
+ default: password
+ required: true
token:
type: string
+ required: true
keys:
type: map
entry_schema:
type: string
+ required: false
user:
type: string
required: false
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/capabilitytype.py b/tosca2heat/tosca-parser/toscaparser/elements/capabilitytype.py
index 0413443..865690e 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/capabilitytype.py
+++ b/tosca2heat/tosca-parser/toscaparser/elements/capabilitytype.py
@@ -16,6 +16,7 @@ from toscaparser.elements.statefulentitytype import StatefulEntityType
class CapabilityTypeDef(StatefulEntityType):
'''TOSCA built-in capabilities type.'''
+ TOSCA_TYPEURI_CAPABILITY_ROOT = 'tosca.capabilities.Root'
def __init__(self, name, ctype, ntype, custom_def=None):
self.name = name
@@ -23,6 +24,7 @@ class CapabilityTypeDef(StatefulEntityType):
custom_def)
self.nodetype = ntype
self.properties = None
+ self.custom_def = custom_def
if self.PROPERTIES in self.defs:
self.properties = self.defs[self.PROPERTIES]
self.parent_capabilities = self._get_parent_capabilities(custom_def)
@@ -61,7 +63,8 @@ class CapabilityTypeDef(StatefulEntityType):
capabilities = {}
parent_cap = self.parent_type
if parent_cap:
- while parent_cap != 'tosca.capabilities.Root':
+ parent_cap = parent_cap.type
+ while parent_cap != self.TOSCA_TYPEURI_CAPABILITY_ROOT:
if parent_cap in self.TOSCA_DEF.keys():
capabilities[parent_cap] = self.TOSCA_DEF[parent_cap]
elif custom_def and parent_cap in custom_def.keys():
@@ -72,4 +75,9 @@ class CapabilityTypeDef(StatefulEntityType):
@property
def parent_type(self):
'''Return a capability this capability is derived from.'''
- return self.derived_from(self.defs)
+ if not hasattr(self, 'defs'):
+ return None
+ pnode = self.derived_from(self.defs)
+ if pnode:
+ return CapabilityTypeDef(self.name, pnode,
+ self.nodetype, self.custom_def)
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/constraints.py b/tosca2heat/tosca-parser/toscaparser/elements/constraints.py
index 9883da3..8594b85 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/constraints.py
+++ b/tosca2heat/tosca-parser/toscaparser/elements/constraints.py
@@ -18,6 +18,7 @@ import toscaparser
from toscaparser.common.exception import ExceptionCollector
from toscaparser.common.exception import InvalidSchemaError
from toscaparser.common.exception import ValidationError
+from toscaparser.elements.portspectype import PortSpec
from toscaparser.elements import scalarunit
from toscaparser.utils.gettextutils import _
@@ -36,12 +37,12 @@ class Schema(collections.Mapping):
INTEGER, STRING, BOOLEAN, FLOAT, RANGE,
NUMBER, TIMESTAMP, LIST, MAP,
SCALAR_UNIT_SIZE, SCALAR_UNIT_FREQUENCY, SCALAR_UNIT_TIME,
- PORTDEF, VERSION
+ VERSION, PORTDEF, PORTSPEC
) = (
'integer', 'string', 'boolean', 'float', 'range',
'number', 'timestamp', 'list', 'map',
'scalar-unit.size', 'scalar-unit.frequency', 'scalar-unit.time',
- 'PortDef', 'version'
+ 'version', 'PortDef', PortSpec.SHORTNAME
)
SCALAR_UNIT_SIZE_DEFAULT = 'B'
@@ -127,8 +128,6 @@ class Constraint(object):
'less_or_equal', 'in_range', 'valid_values', 'length',
'min_length', 'max_length', 'pattern')
- UNBOUNDED = 'UNBOUNDED'
-
def __new__(cls, property_name, property_type, constraint):
if cls is not Constraint:
return super(Constraint, cls).__new__(cls)
@@ -370,6 +369,7 @@ class InRange(Constraint):
Constrains a property or parameter to a value in range of (inclusive)
the two values declared.
"""
+ UNBOUNDED = 'UNBOUNDED'
constraint_key = Constraint.IN_RANGE
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/datatype.py b/tosca2heat/tosca-parser/toscaparser/elements/datatype.py
index 7e05a69..93d1b3a 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/datatype.py
+++ b/tosca2heat/tosca-parser/toscaparser/elements/datatype.py
@@ -18,7 +18,8 @@ class DataType(StatefulEntityType):
'''TOSCA built-in and user defined complex data type.'''
def __init__(self, datatypename, custom_def=None):
- super(DataType, self).__init__(datatypename, self.DATATYPE_PREFIX,
+ super(DataType, self).__init__(datatypename,
+ self.DATATYPE_NETWORK_PREFIX,
custom_def)
self.custom_def = custom_def
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/entity_type.py b/tosca2heat/tosca-parser/toscaparser/elements/entity_type.py
index 72e7e3f..5947b1c 100644
--- a/tosca2heat/tosca-parser/toscaparser/elements/entity_type.py
+++ b/tosca2heat/tosca-parser/toscaparser/elements/entity_type.py
@@ -56,7 +56,8 @@ class EntityType(object):
GROUP_PREFIX = 'tosca.groups.'
# currently the data types are defined only for network
# but may have changes in the future.
- DATATYPE_PREFIX = 'tosca.datatypes.network.'
+ DATATYPE_PREFIX = 'tosca.datatypes.'
+ DATATYPE_NETWORK_PREFIX = DATATYPE_PREFIX + 'network.'
TOSCA = 'tosca'
def derived_from(self, defs):
diff --git a/tosca2heat/tosca-parser/toscaparser/elements/portspectype.py b/tosca2heat/tosca-parser/toscaparser/elements/portspectype.py
new file mode 100644
index 0000000..d32e97e
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/elements/portspectype.py
@@ -0,0 +1,86 @@
+# 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 InvalidTypeAdditionalRequirementsError
+from toscaparser.utils.gettextutils import _
+import toscaparser.utils.validateutils as validateutils
+
+log = logging.getLogger('tosca')
+
+
+class PortSpec(object):
+ '''Parent class for tosca.datatypes.network.PortSpec type.'''
+
+ SHORTNAME = 'PortSpec'
+ TYPE_URI = 'tosca.datatypes.network.' + SHORTNAME
+
+ PROPERTY_NAMES = (
+ PROTOCOL, SOURCE, SOURCE_RANGE,
+ TARGET, TARGET_RANGE
+ ) = (
+ 'protocol', 'source', 'source_range',
+ 'target', 'target_range'
+ )
+
+ # TODO(TBD) May want to make this a subclass of DataType
+ # and change init method to set PortSpec's properties
+ def __init__(self):
+ pass
+
+ # The following additional requirements MUST be tested:
+ # 1) A valid PortSpec MUST have at least one of the following properties:
+ # target, target_range, source or source_range.
+ # 2) A valid PortSpec MUST have a value for the source property that
+ # is within the numeric range specified by the property source_range
+ # when source_range is specified.
+ # 3) A valid PortSpec MUST have a value for the target property that is
+ # within the numeric range specified by the property target_range
+ # when target_range is specified.
+ @staticmethod
+ def validate_additional_req(properties, prop_name, custom_def=None, ):
+ try:
+ source = properties.get(PortSpec.SOURCE)
+ source_range = properties.get(PortSpec.SOURCE_RANGE)
+ target = properties.get(PortSpec.TARGET)
+ target_range = properties.get(PortSpec.TARGET_RANGE)
+
+ # verify one of the specified values is set
+ if source is None and source_range is None and \
+ target is None and target_range is None:
+ ExceptionCollector.appendException(
+ InvalidTypeAdditionalRequirementsError(
+ type=PortSpec.TYPE_URI))
+ # Validate source value is in specified range
+ if source and source_range:
+ validateutils.validate_value_in_range(source, source_range,
+ PortSpec.SOURCE)
+ else:
+ from toscaparser.dataentity import DataEntity
+ portdef = DataEntity('PortDef', source, None, PortSpec.SOURCE)
+ portdef.validate()
+ # Validate target value is in specified range
+ if target and target_range:
+ validateutils.validate_value_in_range(target, target_range,
+ PortSpec.TARGET)
+ else:
+ from toscaparser.dataentity import DataEntity
+ portdef = DataEntity('PortDef', source, None, PortSpec.TARGET)
+ portdef.validate()
+ except Exception:
+ msg = _('"%(value)s" do not meet requirements '
+ 'for type "%(type)s".') \
+ % {'value': properties, 'type': PortSpec.SHORTNAME}
+ ExceptionCollector.appendException(
+ ValueError(msg))
diff --git a/tosca2heat/tosca-parser/toscaparser/entity_template.py b/tosca2heat/tosca-parser/toscaparser/entity_template.py
index f416c99..7488c33 100644
--- a/tosca2heat/tosca-parser/toscaparser/entity_template.py
+++ b/tosca2heat/tosca-parser/toscaparser/entity_template.py
@@ -81,15 +81,11 @@ class EntityTemplate(object):
def type(self):
if self.type_definition:
return self.type_definition.type
- else:
- return None
@property
def parent_type(self):
if self.type_definition:
return self.type_definition.parent_type
- else:
- return None
@property
def requirements(self):
@@ -189,7 +185,10 @@ class EntityTemplate(object):
def _validate_capabilities_properties(self, capabilities):
for cap, props in capabilities.items():
- capabilitydef = self.get_capability(cap).definition
+ capability = self.get_capability(cap)
+ if not capability:
+ continue
+ capabilitydef = capability.definition
self._common_validate_properties(capabilitydef,
props[self.PROPERTIES])
diff --git a/tosca2heat/tosca-parser/toscaparser/extensions/exttools.py b/tosca2heat/tosca-parser/toscaparser/extensions/exttools.py
index 963b958..5310422 100644
--- a/tosca2heat/tosca-parser/toscaparser/extensions/exttools.py
+++ b/tosca2heat/tosca-parser/toscaparser/extensions/exttools.py
@@ -36,7 +36,7 @@ class ExtTools(object):
extdirs = [e for e in os.listdir(abs_path) if
not e.startswith('tests') and
- not e.endswith('.pyc') and not e.endswith('.py')]
+ os.path.isdir(os.path.join(abs_path, e))]
for e in extdirs:
log.info(e)
diff --git a/tosca2heat/tosca-parser/toscaparser/extensions/nfv/TOSCA_nfv_definition_1_0.yaml b/tosca2heat/tosca-parser/toscaparser/extensions/nfv/TOSCA_nfv_definition_1_0.yaml
index 660cdc0..dc986e5 100644
--- a/tosca2heat/tosca-parser/toscaparser/extensions/nfv/TOSCA_nfv_definition_1_0.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/extensions/nfv/TOSCA_nfv_definition_1_0.yaml
@@ -235,16 +235,3 @@ tosca.groups.nfv.VNFFG:
type: string
required: true
description: Reference to a list of VNFD used in this VNF Forwarding Graph
-
- targets:
- type: list
- entry_schema:
- type: string
- required: false
- description: list of Network Forwarding Path within the VNFFG
-
- requirements:
- - forwarder:
- capability: tosca.capabilities.nfv.Forwarder
- relationship: tosca.relationships.nfv.ForwardsTo
-
diff --git a/tosca2heat/tosca-parser/toscaparser/functions.py b/tosca2heat/tosca-parser/toscaparser/functions.py
index 011efde..1b64416 100644
--- a/tosca2heat/tosca-parser/toscaparser/functions.py
+++ b/tosca2heat/tosca-parser/toscaparser/functions.py
@@ -18,6 +18,8 @@ import six
from toscaparser.common.exception import ExceptionCollector
from toscaparser.common.exception import UnknownInputError
from toscaparser.dataentity import DataEntity
+from toscaparser.elements.constraints import Schema
+from toscaparser.elements.datatype import DataType
from toscaparser.elements.entity_type import EntityType
from toscaparser.elements.relationshiptype import RelationshipType
from toscaparser.utils.gettextutils import _
@@ -27,6 +29,7 @@ GET_PROPERTY = 'get_property'
GET_ATTRIBUTE = 'get_attribute'
GET_INPUT = 'get_input'
CONCAT = 'concat'
+TOKEN = 'token'
SELF = 'SELF'
HOST = 'HOST'
@@ -125,30 +128,67 @@ class GetAttribute(Function):
* { get_attribute: [ server, private_address ] }
* { get_attribute: [ HOST, private_address ] }
* { get_attribute: [ HOST, private_address, 0 ] }
+ * { get_attribute: [ HOST, private_address, 0, some_prop] }
"""
def validate(self):
- if len(self.args) != 2 and len(self.args) != 3:
+ if len(self.args) < 2:
ExceptionCollector.appendException(
ValueError(_('Illegal arguments for function "{0}". Expected '
- 'arguments: "node-template-name", '
- '"attribute-name"').format(GET_ATTRIBUTE)))
- node_tpl = self._find_node_template_containing_attribute()
- if len(self.args) > 2:
- # Currently we only check the first level
- attrs_def = node_tpl.type_definition.get_attributes_def()
- attr_def = attrs_def[self.attribute_name]
- if attr_def.schema['type'] == "list":
- if not isinstance(self.args[2], int):
- ExceptionCollector.appendException(
- ValueError(_('Illegal arguments for function "{0}". '
- 'Third argument must be a positive'
- ' integer') .format(GET_ATTRIBUTE)))
- elif attr_def.schema['type'] != "map":
- ExceptionCollector.appendException(
- ValueError(_('Illegal arguments for function "{0}". '
- 'Expected arguments: "node-template-name", '
- '"attribute-name"').format(GET_ATTRIBUTE)))
+ 'arguments: "node-template-name", "req-or-cap"'
+ '(optional), "property name"'
+ ).format(GET_ATTRIBUTE)))
+ return
+ elif len(self.args) == 2:
+ self._find_node_template_containing_attribute()
+ else:
+ node_tpl = self._find_node_template(self.args[0])
+ index = 2
+ attrs = node_tpl.type_definition.get_attributes_def()
+ found = [attrs[self.args[1]]] if self.args[1] in attrs else []
+ if found:
+ attr = found[0]
+ else:
+ index = 3
+ # then check the req or caps
+ attr = self._find_req_or_cap_attribute(self.args[1],
+ self.args[2])
+
+ value_type = attr.schema['type']
+ if len(self.args) > index:
+ for elem in self.args[index:]:
+ if value_type == "list":
+ if not isinstance(elem, int):
+ ExceptionCollector.appendException(
+ ValueError(_('Illegal arguments for function'
+ ' "{0}". "{1}" Expected positive'
+ ' integer argument'
+ ).format(GET_ATTRIBUTE, elem)))
+ value_type = attr.schema['entry_schema']['type']
+ elif value_type == "map":
+ value_type = attr.schema['entry_schema']['type']
+ elif value_type in Schema.PROPERTY_TYPES:
+ ExceptionCollector.appendException(
+ ValueError(_('Illegal arguments for function'
+ ' "{0}". Unexpected attribute/'
+ 'index value "{1}"'
+ ).format(GET_ATTRIBUTE, elem)))
+ return
+ else: # It is a complex type
+ data_type = DataType(value_type)
+ props = data_type.get_all_properties()
+ found = [props[elem]] if elem in props else []
+ if found:
+ prop = found[0]
+ value_type = prop.schema['type']
+ else:
+ ExceptionCollector.appendException(
+ KeyError(_('Illegal arguments for function'
+ ' "{0}". Attribute name "{1}" not'
+ ' found in "{2}"'
+ ).format(GET_ATTRIBUTE,
+ elem,
+ value_type)))
def result(self):
return self
@@ -163,25 +203,7 @@ class GetAttribute(Function):
return self._find_node_template_containing_attribute()
def _find_node_template_containing_attribute(self):
- if self.node_template_name == HOST:
- # Currently this is the only way to tell whether the function
- # is used within the outputs section of the TOSCA template.
- if isinstance(self.context, list):
- ExceptionCollector.appendException(
- ValueError(_(
- '"get_attribute: [ HOST, ... ]" is not allowed in '
- '"outputs" section of the TOSCA template.')))
- return
- node_tpl = self._find_host_containing_attribute()
- if not node_tpl:
- ExceptionCollector.appendException(
- ValueError(_(
- '"get_attribute: [ HOST, ... ]" was used in node '
- 'template "{0}" but "{1}" was not found in '
- 'the relationship chain.').format(self.context.name,
- HOSTED_ON)))
- else:
- node_tpl = self._find_node_template(self.args[0])
+ node_tpl = self._find_node_template(self.args[0])
if node_tpl and \
not self._attribute_exists_in_type(node_tpl.type_definition):
ExceptionCollector.appendException(
@@ -214,6 +236,25 @@ class GetAttribute(Function):
target_name)
def _find_node_template(self, node_template_name):
+ if node_template_name == HOST:
+ # Currently this is the only way to tell whether the function
+ # is used within the outputs section of the TOSCA template.
+ if isinstance(self.context, list):
+ ExceptionCollector.appendException(
+ ValueError(_(
+ '"get_attribute: [ HOST, ... ]" is not allowed in '
+ '"outputs" section of the TOSCA template.')))
+ return
+ node_tpl = self._find_host_containing_attribute()
+ if not node_tpl:
+ ExceptionCollector.appendException(
+ ValueError(_(
+ '"get_attribute: [ HOST, ... ]" was used in node '
+ 'template "{0}" but "{1}" was not found in '
+ 'the relationship chain.').format(self.context.name,
+ HOSTED_ON)))
+ return
+ return node_tpl
if node_template_name == TARGET:
if not isinstance(self.context.type_definition, RelationshipType):
ExceptionCollector.appendException(
@@ -240,6 +281,51 @@ class GetAttribute(Function):
'Node template "{0}" was not found.'
).format(node_template_name)))
+ def _find_req_or_cap_attribute(self, req_or_cap, attr_name):
+ node_tpl = self._find_node_template(self.args[0])
+ # Find attribute in node template's requirements
+ for r in node_tpl.requirements:
+ for req, node_name in r.items():
+ if req == req_or_cap:
+ node_template = self._find_node_template(node_name)
+ return self._get_capability_attribute(
+ node_template,
+ req,
+ attr_name)
+ # If requirement was not found, look in node template's capabilities
+ return self._get_capability_attribute(node_tpl,
+ req_or_cap,
+ attr_name)
+
+ def _get_capability_attribute(self,
+ node_template,
+ capability_name,
+ attr_name):
+ """Gets a node template capability attribute."""
+ caps = node_template.get_capabilities()
+ if caps and capability_name in caps.keys():
+ cap = caps[capability_name]
+ attribute = None
+ attrs = cap.definition.get_attributes_def()
+ if attrs and attr_name in attrs.keys():
+ attribute = attrs[attr_name]
+ if not attribute:
+ ExceptionCollector.appendException(
+ KeyError(_('Attribute "%(attr)s" was not found in '
+ 'capability "%(cap)s" of node template '
+ '"%(ntpl1)s" referenced from node template '
+ '"%(ntpl2)s".') % {'attr': attr_name,
+ 'cap': capability_name,
+ 'ntpl1': node_template.name,
+ 'ntpl2': self.context.name}))
+ return attribute
+ msg = _('Requirement/Capability "{0}" referenced from node template '
+ '"{1}" was not found in node template "{2}".').format(
+ capability_name,
+ self.context.name,
+ node_template.name)
+ ExceptionCollector.appendException(KeyError(msg))
+
@property
def node_template_name(self):
return self.args[0]
@@ -551,11 +637,58 @@ class Concat(Function):
def result(self):
return self
+
+class Token(Function):
+ """Validate the function and provide an instance of the function
+
+ The token function is used within a TOSCA service template on a string to
+ parse out (tokenize) substrings separated by one or more token characters
+ within a larger string.
+
+
+ Arguments:
+
+ * The composite string that contains one or more substrings separated by
+ token characters.
+ * The string that contains one or more token characters that separate
+ substrings within the composite string.
+ * The integer indicates the index of the substring to return from the
+ composite string. Note that the first substring is denoted by using
+ the '0' (zero) integer value.
+
+ Example:
+
+ [ get_attribute: [ my_server, data_endpoint, ip_address ], ':', 1 ]
+
+ """
+
+ def validate(self):
+ if len(self.args) < 3:
+ ExceptionCollector.appendException(
+ ValueError(_('Invalid arguments for function "{0}". Expected '
+ 'at least three arguments.').format(TOKEN)))
+ else:
+ if not isinstance(self.args[1], str) or len(self.args[1]) != 1:
+ ExceptionCollector.appendException(
+ ValueError(_('Invalid arguments for function "{0}". '
+ 'Expected single char value as second '
+ 'argument.').format(TOKEN)))
+
+ if not isinstance(self.args[2], int):
+ ExceptionCollector.appendException(
+ ValueError(_('Invalid arguments for function "{0}". '
+ 'Expected integer value as third '
+ 'argument.').format(TOKEN)))
+
+ def result(self):
+ return self
+
function_mappings = {
GET_PROPERTY: GetProperty,
GET_INPUT: GetInput,
GET_ATTRIBUTE: GetAttribute,
- CONCAT: Concat
+ CONCAT: Concat,
+ TOKEN: Token
}
diff --git a/tosca2heat/tosca-parser/toscaparser/groups.py b/tosca2heat/tosca-parser/toscaparser/groups.py
index 5fd5dec..6a3e5c7 100644
--- a/tosca2heat/tosca-parser/toscaparser/groups.py
+++ b/tosca2heat/tosca-parser/toscaparser/groups.py
@@ -15,7 +15,7 @@ from toscaparser.common.exception import UnknownFieldError
from toscaparser.entity_template import EntityTemplate
from toscaparser.utils import validateutils
-SECTIONS = (TYPE, METADATA, DESCRIPTION, PROPERTIES, TARGETS, INTERFACES) = \
+SECTIONS = (TYPE, METADATA, DESCRIPTION, PROPERTIES, MEMBERS, INTERFACES) = \
('type', 'metadata', 'description',
'properties', 'members', 'interfaces')
diff --git a/tosca2heat/tosca-parser/toscaparser/imports.py b/tosca2heat/tosca-parser/toscaparser/imports.py
index 62748bd..58c9300 100644
--- a/tosca2heat/tosca-parser/toscaparser/imports.py
+++ b/tosca2heat/tosca-parser/toscaparser/imports.py
@@ -14,6 +14,7 @@ import logging
import os
from toscaparser.common.exception import ExceptionCollector
+from toscaparser.common.exception import InvalidPropertyValueError
from toscaparser.common.exception import MissingRequiredFieldError
from toscaparser.common.exception import UnknownFieldError
from toscaparser.common.exception import ValidationError
@@ -161,12 +162,17 @@ class ImportsLoader(object):
| URL | URL | OK |
+----------+--------+------------------------------+
"""
-
short_import_notation = False
if isinstance(import_uri_def, dict):
self._validate_import_keys(import_name, import_uri_def)
file_name = import_uri_def.get(self.FILE)
repository = import_uri_def.get(self.REPOSITORY)
+ repos = self.repositories.keys()
+ if repository is not None:
+ if repository not in repos:
+ ExceptionCollector.appendException(
+ InvalidPropertyValueError(
+ what=_('Repository is not found in "%s"') % repos))
else:
file_name = import_uri_def
repository = None
diff --git a/tosca2heat/tosca-parser/toscaparser/parameters.py b/tosca2heat/tosca-parser/toscaparser/parameters.py
index 983aee3..1d2cb29 100644
--- a/tosca2heat/tosca-parser/toscaparser/parameters.py
+++ b/tosca2heat/tosca-parser/toscaparser/parameters.py
@@ -27,8 +27,9 @@ log = logging.getLogger('tosca')
class Input(object):
- INPUTFIELD = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS) = \
- ('type', 'description', 'default', 'constraints')
+ INPUTFIELD = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS, REQUIRED,
+ STATUS) = ('type', 'description', 'default',
+ 'constraints', 'required', 'status')
def __init__(self, name, schema_dict):
self.name = name
@@ -53,7 +54,7 @@ class Input(object):
def validate(self, value=None):
self._validate_field()
self.validate_type(self.type)
- if value:
+ if value is not None:
self._validate_value(value)
def _validate_field(self):
@@ -68,13 +69,16 @@ class Input(object):
ExceptionCollector.appendException(
ValueError(_('Invalid type "%s".') % type))
+ # TODO(anyone) Need to test for any built-in datatype not just network
+ # that is, tosca.datatypes.* and not assume tosca.datatypes.network.*
+ # TODO(anyone) Add support for tosca.datatypes.Credential
def _validate_value(self, value):
tosca = EntityType.TOSCA_DEF
datatype = None
if self.type in tosca:
datatype = tosca[self.type]
- elif EntityType.DATATYPE_PREFIX + self.type in tosca:
- datatype = tosca[EntityType.DATATYPE_PREFIX + self.type]
+ elif EntityType.DATATYPE_NETWORK_PREFIX + self.type in tosca:
+ datatype = tosca[EntityType.DATATYPE_NETWORK_PREFIX + self.type]
DataEntity.validate_datatype(self.type, value, None, datatype)
diff --git a/tosca2heat/tosca-parser/toscaparser/properties.py b/tosca2heat/tosca-parser/toscaparser/properties.py
index 23c1db2..c69b151 100644
--- a/tosca2heat/tosca-parser/toscaparser/properties.py
+++ b/tosca2heat/tosca-parser/toscaparser/properties.py
@@ -67,7 +67,8 @@ class Property(object):
self.value = str(self.value)
self.value = DataEntity.validate_datatype(self.type, self.value,
self.entry_schema,
- self.custom_def)
+ self.custom_def,
+ self.name)
self._validate_constraints()
def _validate_constraints(self):
diff --git a/tosca2heat/tosca-parser/toscaparser/repositories.py b/tosca2heat/tosca-parser/toscaparser/repositories.py
new file mode 100644
index 0000000..184eba4
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/repositories.py
@@ -0,0 +1,52 @@
+# 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 MissingRequiredFieldError
+from toscaparser.common.exception import UnknownFieldError
+from toscaparser.common.exception import URLException
+from toscaparser.utils.gettextutils import _
+import toscaparser.utils.urlutils
+
+SECTIONS = (DESCRIPTION, URL, CREDENTIAL) = \
+ ('description', 'url', 'credential')
+
+
+class Repository(object):
+ def __init__(self, repositories, values):
+ self.name = repositories
+ self.reposit = values
+ if isinstance(self.reposit, dict):
+ if 'url' not in self.reposit.keys():
+ ExceptionCollector.appendException(
+ MissingRequiredFieldError(what=_('Repository "%s"')
+ % self.name, required='url'))
+ self.url = self.reposit['url']
+ self.load_and_validate(self.name, self.reposit)
+
+ def load_and_validate(self, val, reposit_def):
+ self.keyname = val
+ if isinstance(reposit_def, dict):
+ for key in reposit_def.keys():
+ if key not in SECTIONS:
+ ExceptionCollector.appendException(
+ UnknownFieldError(what=_('repositories "%s"')
+ % self.keyname, field=key))
+
+ if URL in reposit_def.keys():
+ reposit_url = reposit_def.get(URL)
+ url_val = toscaparser.utils.urlutils.UrlUtils.\
+ validate_url(reposit_url)
+ if url_val is not True:
+ ExceptionCollector.appendException(
+ URLException(what=_('repsositories "%s" Invalid Url')
+ % self.keyname))
diff --git a/tosca2heat/tosca-parser/toscaparser/shell.py b/tosca2heat/tosca-parser/toscaparser/shell.py
index 848726f..b41c024 100644
--- a/tosca2heat/tosca-parser/toscaparser/shell.py
+++ b/tosca2heat/tosca-parser/toscaparser/shell.py
@@ -67,33 +67,33 @@ class ParserShell(object):
version = tosca.version
if tosca.version:
- print ("\nversion: " + version)
+ print("\nversion: " + version)
if hasattr(tosca, 'description'):
description = tosca.description
if description:
- print ("\ndescription: " + description)
+ print("\ndescription: " + description)
if hasattr(tosca, 'inputs'):
inputs = tosca.inputs
if inputs:
- print ("\ninputs:")
+ print("\ninputs:")
for input in inputs:
- print ("\t" + input.name)
+ print("\t" + input.name)
if hasattr(tosca, 'nodetemplates'):
nodetemplates = tosca.nodetemplates
if nodetemplates:
- print ("\nnodetemplates:")
+ print("\nnodetemplates:")
for node in nodetemplates:
- print ("\t" + node.name)
+ print("\t" + node.name)
if hasattr(tosca, 'outputs'):
outputs = tosca.outputs
if outputs:
- print ("\noutputs:")
+ print("\noutputs:")
for output in outputs:
- print ("\t" + output.name)
+ print("\t" + output.name)
def main(args=None):
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/compute_with_nested_atributes.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/compute_with_nested_atributes.yaml
new file mode 100644
index 0000000..f23a8a1
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/compute_with_nested_atributes.yaml
@@ -0,0 +1,22 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Compute node type with capability with an atribute of type list
+
+capability_types:
+
+ tosca.capabilities.indigo.Endpoint:
+ derived_from: tosca.capabilities.Endpoint
+ attributes:
+ credential:
+ type: list
+ entry_schema:
+ type: tosca.datatypes.Credential
+
+node_types:
+
+ tosca.nodes.ComputeWithCapWithAttr:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ endpoint:
+ type: tosca.capabilities.indigo.Endpoint
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
index 11e1b51..b17513f 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
@@ -3,7 +3,8 @@ tosca_definitions_version: tosca_simple_yaml_1_0
description: >
Node type that has a requirement of a capability with a defined value
-node_types:
+capability_types:
+
tosca.capabilities.SomeCap:
derived_from: tosca.capabilities.Root
properties:
@@ -14,6 +15,8 @@ node_types:
constraints:
- equal: someval
+node_types:
+
tosca.nodes.SomeNode:
derived_from: tosca.nodes.Root
requirements:
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/datatypes/test_datatype_portspec_add_req.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/datatypes/test_datatype_portspec_add_req.yaml
new file mode 100644
index 0000000..f944927
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/datatypes/test_datatype_portspec_add_req.yaml
@@ -0,0 +1,41 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA test PortSpec Additional Requirement clauses
+
+node_types:
+
+ MyNodeType:
+ derived_from: Root
+ properties:
+ test_port:
+ type: PortSpec
+
+topology_template:
+
+ node_templates:
+
+ # Test invalid source value below (default) specified range constraint
+ test_node2:
+ type: MyNodeType
+ properties:
+ test_port:
+ protocol: tcp
+ source: 0
+
+ # Test invalid source value over specified range
+ test_node3:
+ type: MyNodeType
+ properties:
+ test_port:
+ protocol: tcp
+ source: 65535
+ source_range: [ 2, 65534 ]
+
+ # Test invalid source value under specified range
+ test_node4:
+ type: MyNodeType
+ properties:
+ test_port:
+ protocol: tcp
+ source: 1
+ source_range: [ 2, 65534 ]
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_index_error.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_index_error.yaml
index 88a2721..7511999 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_index_error.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_index_error.yaml
@@ -1,7 +1,7 @@
tosca_definitions_version: tosca_simple_yaml_1_0
description: >
- TOSCA template for testing get_attribute with a list attribute and an index
+ TOSCA template for testing get_attribute with an incorrect index
imports:
- ../custom_types/compute_with_attribute_list.yaml
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_nested_params.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_nested_params.yaml
new file mode 100644
index 0000000..79e632c
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_attribute_with_nested_params.yaml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ TOSCA template for testing get_attribute with nested attributes
+
+imports:
+ - ../custom_types/compute_with_nested_atributes.yaml
+
+topology_template:
+ node_templates:
+ server:
+ type: tosca.nodes.ComputeWithCapWithAttr
+ capabilities:
+ endpoint:
+ properties:
+ port: 80
+ interfaces:
+ Standard:
+ configure:
+ implementation: configure.sh
+ inputs:
+ ip_address: { get_attribute: [ SELF, endpoint, credential, 0, token ] }
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token.yaml
new file mode 100644
index 0000000..495a930
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token.yaml
@@ -0,0 +1,15 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Template for deploying a single server with token function.
+
+topology_template:
+ node_templates:
+ server:
+ type: tosca.nodes.Compute
+
+ outputs:
+ url:
+ description: Get the first part of the ip
+ value: { token: [ get_attribute: [ server, public_address ],
+ '.' ,
+ 0 ] }
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token_invalid.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token_invalid.yaml
new file mode 100644
index 0000000..35ae2ff
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_token_invalid.yaml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Template for deploying a single server with invalid token function.
+
+topology_template:
+ outputs:
+ invalid_token_syntax_1:
+ description: test token with only two paremeters.
+ value: { token: ["some_string", "_"]}
+
+ invalid_token_syntax_2:
+ description: test token with invalid string as third argument.
+ value: { token: ["some_string", "_", "1"]}
+
+ invalid_token_syntax_3:
+ description: test token with invalid string as second argument.
+ value: { token: ["some_string", "aa", "1"]}
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/test_containers.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/test_containers.yaml
new file mode 100644
index 0000000..ba1cc16
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/test_containers.yaml
@@ -0,0 +1,44 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ TOSCA simple profile with mysql docker container.
+
+# Repositories to retrieve code artifacts from
+repositories:
+ docker_hub: https://registry.hub.docker.com/
+
+topology_template:
+
+ inputs:
+ mysql_root_pwd:
+ type: string
+ description: Root password for MySQL.
+
+ node_templates:
+ # The MYSQL container based on official MySQL image in Docker hub
+ mysql_container:
+ type: tosca.nodes.Container.Application
+ requirements:
+ - host: mysql_runtime
+ artifacts:
+ my_image:
+ file: mysql
+ type: tosca.artifacts.Deployment.Image.Container.Docker
+ repository: docker_hub
+ interfaces:
+ Standard:
+ create:
+ implementation: my_image
+ inputs:
+ MYSQL_ROOT_PASSWORD: { get_input: mysql_root_pwd }
+
+ # The properties of the runtime to host the container
+ mysql_runtime:
+ type: tosca.nodes.Container.Runtime
+ capabilities:
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 2 MB
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/test_credential_datatype.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/test_credential_datatype.yaml
new file mode 100644
index 0000000..583ec82
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/test_credential_datatype.yaml
@@ -0,0 +1,77 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ TOSCA simple profile with to demonstrate the usage of the
+ TOSCA Credential Data Type.
+
+imports:
+ - custom_types/wordpress.yaml
+
+relationship_types:
+ my.types.WordpressDbConnection:
+ derived_from: tosca.relationships.ConnectsTo
+ properties:
+ credential:
+ user: db_user
+ token: db_pwd
+
+topology_template:
+ node_templates:
+ wordpress:
+ type: tosca.nodes.WebApplication.WordPress
+ requirements:
+ - host: webserver
+ - database_endpoint:
+ node: mysql_database
+ relationship: my.types.WordpressDbConnection
+
+ mysql_database:
+ type: tosca.nodes.Database
+ properties:
+ name: db_name
+ user: db_user
+ password: db_pwd
+ capabilities:
+ database_endpoint:
+ properties:
+ port: 3306
+ requirements:
+ - host:
+ node: mysql_dbms
+
+ mysql_dbms:
+ type: tosca.nodes.DBMS
+ properties:
+ root_password: db_root_pwd
+ port: 3306
+ requirements:
+ - host: server
+
+ webserver:
+ type: tosca.nodes.WebServer
+ properties:
+ admin_credential:
+ user: username
+ token: some_pass
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.Compute
+ capabilities:
+ host:
+ properties:
+ disk_size: 10 GB
+ num_cpus: 1
+ mem_size: 4096 MB
+ os:
+ properties:
+ architecture: x86_64
+ type: Linux
+ distribution: Ubuntu
+ version: 14.04
+
+ outputs:
+ website_url:
+ description: URL for Wordpress wiki.
+ value: { get_attribute: [server, private_address] }
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/test_invalid_input_defaults.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/test_invalid_input_defaults.yaml
new file mode 100644
index 0000000..f8f4ae7
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/test_invalid_input_defaults.yaml
@@ -0,0 +1,12 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Test template with default not matching required type.
+
+topology_template:
+ inputs:
+ invalid_default:
+ type: integer
+ default: two
+ valid_default:
+ type: integer
+ default: 2
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/test_repositories_definition.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/test_repositories_definition.yaml
index 2145d8f..c2856c8 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/test_repositories_definition.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/test_repositories_definition.yaml
@@ -4,9 +4,9 @@ repositories:
some_repository:
description: Some repo
url: https://raw.githubusercontent.com/openstack/tosca-parser/master/toscaparser/tests/data/custom_types/
- namespace_uri: http://docs.oasis-open.org/tosca/ns/simple/yaml/1.0a
- namespace_prefix: oasis_tosca
-
+ credential: #type: Credential
+ token_type: basic_auth
+ token: myusername:mypassword
imports:
- some_import:
file: compute_with_prop.yaml
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/subsystem.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/subsystem.yaml
index 99d645b..b27e698 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/subsystem.yaml
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/subsystem.yaml
@@ -12,15 +12,12 @@ topology_template:
inputs:
mq_server_ip:
type: string
- required: true
description: IP address of the message queuing server to receive messages from.
receiver_port:
type: string
- required: true
description: Port to be used for receiving messages.
my_cpus:
type: integer
- default: 2
description: Number of CPUs for the server.
constraints:
- valid_values: [ 1, 2, 4, 8 ]
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/tosca_repositories_test_definition.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/tosca_repositories_test_definition.yaml
new file mode 100644
index 0000000..0001d06
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/tosca_repositories_test_definition.yaml
@@ -0,0 +1,26 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA simple profile with repositories validation and imports.
+
+repositories:
+ repo_code0: https://raw.githubusercontent.com/nandinivemula/intern
+ repo_code1:
+ description: My project's code Repository in github usercontent.
+ url: https://raw.githubusercontent.com/nandinivemula/intern/master
+ credential: #type: Credential
+ token_type: basic_auth
+ token: myusername:mypassword
+
+ repo_code2:
+ description: My Project's code Repository in github.
+ url: https://github.com/nandinivemula/intern/master
+ credential: #type: Credential
+ token_type: basic_auth
+ token: myusername:mypassword
+
+imports:
+ - sample_import:
+ file: tosca_repository_import.yaml
+ repository: repo_code1
+ namespace_uri: https://github.com/nandinivemula/intern
+ namespace_prefix: intern
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py b/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py
index 0e613b2..0a6cfe0 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py
@@ -66,16 +66,21 @@ class DataTypeTest(TestCase):
tosca.my.datatypes.TestLab:
properties:
- temperature:
+ humidity:
+ type: range
+ required: false
+ constraints:
+ - in_range: [-256, INFINITY]
+ temperature1:
type: range
required: false
constraints:
- in_range: [-256, UNBOUNDED]
- humidity:
+ temperature2:
type: range
required: false
constraints:
- - in_range: [-256, INFINITY]
+ - in_range: [UNBOUNDED, 256]
'''
custom_type_def = yamlparser.simple_parse(custom_type_schema)
@@ -84,15 +89,6 @@ class DataTypeTest(TestCase):
value = yamlparser.simple_parse(value_snippet)
self.assertEqual(value, {})
- # TODO(Matt) - opened as bug 1555300
- # Need a test for PortSpec normative data type
- # that tests the spec. requirement: "A valid PortSpec
- # must have at least one of the following properties:
- # target, target_range, source or source_range."
- # TODO(Matt) - opened as bug 1555310
- # test PortSpec value for source and target
- # against the source_range and target_range
- # when specified.
def test_built_in_datatype(self):
value_snippet = '''
private_network:
@@ -140,6 +136,31 @@ class DataTypeTest(TestCase):
data = DataEntity('PortInfo', value.get('ethernet_port'))
self.assertIsNotNone(data.validate())
+ # Test normative PortSpec datatype's additional requirements
+ # TODO(Matt) - opened as bug 1555300
+ # Need a test for PortSpec normative data type
+ # that tests the spec. requirement: "A valid PortSpec
+ # must have at least one of the following properties:
+ # target, target_range, source or source_range."
+ # TODO(Matt) - opened as bug 1555310
+ # test PortSpec value for source and target
+ # against the source_range and target_range
+ # when specified.
+ def test_port_spec_addl_reqs(self):
+ value_snippet = '''
+ test_port:
+ protocol: tcp
+ target: 65535
+ target_range: [ 1, 65535 ]
+ source: 1
+ source_range: [ 1, 65535 ]
+
+ '''
+ value = yamlparser.simple_parse(value_snippet)
+ data = DataEntity('tosca.datatypes.network.PortSpec',
+ value.get('test_port'))
+ self.assertIsNotNone(data.validate())
+
def test_built_in_datatype_without_properties(self):
value_snippet = '''
2
@@ -365,6 +386,7 @@ class DataTypeTest(TestCase):
value_snippet = '''
user_port:
protocol: tcp
+ target: 1
target_range: [20000]
'''
value = yamlparser.simple_parse(value_snippet)
@@ -377,6 +399,7 @@ class DataTypeTest(TestCase):
value_snippet = '''
user_port:
protocol: tcp
+ target: 1
target_range: [20000, 3000]
'''
value = yamlparser.simple_parse(value_snippet)
@@ -400,9 +423,95 @@ class DataTypeTest(TestCase):
def test_range_unbounded(self):
value_snippet = '''
- temperature: [-100, 999999]
+ humidity: [-100, 100]
+ '''
+ value = yamlparser.simple_parse(value_snippet)
+ data = DataEntity('tosca.my.datatypes.TestLab',
+ value, DataTypeTest.custom_type_def)
+ err = self.assertRaises(exception.InvalidSchemaError,
+ lambda: data.validate())
+ self.assertEqual(_('The property "in_range" expects comparable values.'
+ ),
+ err.__str__())
+
+ def test_invalid_ranges_against_constraints(self):
+ # The TestLab range type has min=-256, max=UNBOUNDED
+ value_snippet = '''
+ temperature1: [-257, 999999]
+ '''
+ value = yamlparser.simple_parse(value_snippet)
+ data = DataEntity('tosca.my.datatypes.TestLab', value,
+ DataTypeTest.custom_type_def)
+ err = self.assertRaises(exception.ValidationError, data.validate)
+ self.assertEqual(_('The value "-257" of property "temperature1" is '
+ 'out of range "(min:-256, max:UNBOUNDED)".'),
+ err.__str__())
+
+ value_snippet = '''
+ temperature2: [-999999, 257]
+ '''
+ value = yamlparser.simple_parse(value_snippet)
+ data = DataEntity('tosca.my.datatypes.TestLab', value,
+ DataTypeTest.custom_type_def)
+ err = self.assertRaises(exception.ValidationError, data.validate)
+ self.assertEqual(_('The value "257" of property "temperature2" is '
+ 'out of range "(min:UNBOUNDED, max:256)".'),
+ err.__str__())
+
+ def test_valid_ranges_against_constraints(self):
+
+ # The TestLab range type has max=UNBOUNDED
+ value_snippet = '''
+ temperature1: [-255, 999999]
'''
value = yamlparser.simple_parse(value_snippet)
data = DataEntity('tosca.my.datatypes.TestLab', value,
DataTypeTest.custom_type_def)
self.assertIsNotNone(data.validate())
+
+ # The TestLab range type has min=UNBOUNDED
+ value_snippet = '''
+ temperature2: [-999999, 255]
+ '''
+ value = yamlparser.simple_parse(value_snippet)
+ data = DataEntity('tosca.my.datatypes.TestLab', value,
+ DataTypeTest.custom_type_def)
+ self.assertIsNotNone(data.validate())
+
+ def test_incorrect_field_in_datatype(self):
+ tpl_snippet = '''
+ tosca_definitions_version: tosca_simple_yaml_1_0
+ topology_template:
+ node_templates:
+ server:
+ type: tosca.nodes.Compute
+
+ webserver:
+ type: tosca.nodes.WebServer
+ properties:
+ admin_credential:
+ user: username
+ token: some_pass
+ some_field: value
+ requirements:
+ - host: server
+ '''
+ tpl = yamlparser.simple_parse(tpl_snippet)
+ err = self.assertRaises(exception.ValidationError, ToscaTemplate,
+ None, None, None, tpl)
+ self.assertIn(_('The pre-parsed input failed validation with the '
+ 'following error(s): \n\n\tUnknownFieldError: Data '
+ 'value of type "tosca.datatypes.Credential" contains'
+ ' unknown field "some_field". Refer to the definition'
+ ' to verify valid values'), err.__str__())
+
+ def test_functions_datatype(self):
+ value_snippet = '''
+ admin_credential:
+ user: username
+ token: { get_input: password }
+ '''
+ value = yamlparser.simple_parse(value_snippet)
+ data = DataEntity('tosca.datatypes.Credential',
+ value.get('admin_credential'))
+ self.assertIsNotNone(data.validate())
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py b/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
index 2a6225d..4d063e5 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
@@ -201,8 +201,9 @@ class GetAttributeTest(TestCase):
website_url_output.value.attribute_name)
def test_get_attribute_invalid_args(self):
- expected_msg = _('Expected arguments: "node-template-name", '
- '"attribute-name"')
+ expected_msg = _('Illegal arguments for function "get_attribute".'
+ ' Expected arguments: "node-template-name", '
+ '"req-or-cap"(optional), "property name"')
err = self.assertRaises(ValueError,
functions.get_function, None, None,
{'get_attribute': []})
@@ -211,10 +212,6 @@ class GetAttributeTest(TestCase):
functions.get_function, None, None,
{'get_attribute': ['x']})
self.assertIn(expected_msg, six.text_type(err))
- err = self.assertRaises(ValueError,
- functions.get_function, None, None,
- {'get_attribute': ['x', 'y', 'z', 'k']})
- self.assertIn(expected_msg, six.text_type(err))
def test_get_attribute_unknown_node_template_name(self):
self.assertRaises(
@@ -280,7 +277,7 @@ class GetAttributeTest(TestCase):
exception.ExceptionCollector.assertExceptionMessage(
ValueError,
_('Illegal arguments for function "get_attribute". '
- 'Expected arguments: "node-template-name", "attribute-name"'))
+ 'Unexpected attribute/index value "0"'))
def test_get_attribute_source_target_keywords(self):
tosca_tpl = os.path.join(
@@ -300,6 +297,10 @@ class GetAttributeTest(TestCase):
source_port = operation.inputs['source_port']
self.assertTrue(isinstance(source_port, functions.GetAttribute))
+ def test_get_attribute_with_nested_params(self):
+ self._load_template(
+ 'functions/test_get_attribute_with_nested_params.yaml')
+
class ConcatTest(TestCase):
@@ -322,3 +323,34 @@ class ConcatTest(TestCase):
ValueError,
_('Invalid arguments for function "concat". Expected at least '
'one arguments.'))
+
+
+class TokenTest(TestCase):
+
+ def _load_template(self, filename):
+ return ToscaTemplate(os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ filename))
+
+ def test_validate_token(self):
+ tosca = self._load_template("data/functions/test_token.yaml")
+ server_url_output = [
+ output for output in tosca.outputs if output.name == 'url'][0]
+ func = functions.get_function(self, tosca.outputs,
+ server_url_output.value)
+ self.assertIsInstance(func, functions.Token)
+
+ self.assertRaises(exception.ValidationError, self._load_template,
+ 'data/functions/test_token_invalid.yaml')
+ exception.ExceptionCollector.assertExceptionMessage(
+ ValueError,
+ _('Invalid arguments for function "token". Expected at least '
+ 'three arguments.'))
+ exception.ExceptionCollector.assertExceptionMessage(
+ ValueError,
+ _('Invalid arguments for function "token". Expected '
+ 'integer value as third argument.'))
+ exception.ExceptionCollector.assertExceptionMessage(
+ ValueError,
+ _('Invalid arguments for function "token". Expected '
+ 'single char value as second argument.'))
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_toscatpl.py b/tosca2heat/tosca-parser/toscaparser/tests/test_toscatpl.py
index ac55059..2488b65 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_toscatpl.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_toscatpl.py
@@ -12,10 +12,10 @@
import os
import six
-
from toscaparser.common import exception
import toscaparser.elements.interfaces as ifaces
from toscaparser.elements.nodetype import NodeType
+from toscaparser.elements.portspectype import PortSpec
from toscaparser.functions import GetInput
from toscaparser.functions import GetProperty
from toscaparser.nodetemplate import NodeTemplate
@@ -26,16 +26,17 @@ import toscaparser.utils.yamlparser
class ToscaTemplateTest(TestCase):
-
'''TOSCA template.'''
tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress.yaml")
tosca = ToscaTemplate(tosca_tpl)
-
tosca_elk_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"data/tosca_elk.yaml")
+ tosca_repo_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/tosca_repositories_test_definition.yaml")
def test_version(self):
self.assertEqual(self.tosca.version, "tosca_simple_yaml_1_0")
@@ -251,6 +252,18 @@ class ToscaTemplateTest(TestCase):
expected_hosts,
sorted([v.type for v in node_tpl.relationships.values()]))
+ def test_repositories(self):
+ template = ToscaTemplate(self.tosca_repo_tpl)
+ self.assertEqual(
+ ['repo_code0', 'repo_code1', 'repo_code2'],
+ sorted([input.name for input in template.repositories]))
+
+ input_name = "repo_code2"
+ expected_url = "https://github.com/nandinivemula/intern/master"
+ for input in template.repositories:
+ if input.name == input_name:
+ self.assertEqual(input.url, expected_url)
+
def test_template_macro(self):
template = ToscaTemplate(self.tosca_elk_tpl)
for node_tpl in template.nodetemplates:
@@ -747,3 +760,46 @@ class ToscaTemplateTest(TestCase):
self.assertTrue(rel.is_derived_from("tosca.relationships.Root"))
self.assertEqual(len(rel.interfaces), 1)
self.assertEqual(rel.interfaces[0].type, "Configure")
+
+ def test_various_portspec_errors(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/datatypes/test_datatype_portspec_add_req.yaml")
+ self.assertRaises(exception.ValidationError, ToscaTemplate, tosca_tpl,
+ None)
+
+ # TODO(TBD) find way to reuse error messages from constraints.py
+ msg = (_('The value "%(pvalue)s" of property "%(pname)s" is out of '
+ 'range "(min:%(vmin)s, max:%(vmax)s)".') %
+ dict(pname=PortSpec.SOURCE,
+ pvalue='0',
+ vmin='1',
+ vmax='65535'))
+ exception.ExceptionCollector.assertExceptionMessage(
+ exception.ValidationError, msg)
+
+ # Test value below range min.
+ msg = (_('The value "%(pvalue)s" of property "%(pname)s" is out of '
+ 'range "(min:%(vmin)s, max:%(vmax)s)".') %
+ dict(pname=PortSpec.SOURCE,
+ pvalue='1',
+ vmin='2',
+ vmax='65534'))
+ exception.ExceptionCollector.assertExceptionMessage(
+ exception.RangeValueError, msg)
+
+ # Test value above range max.
+ msg = (_('The value "%(pvalue)s" of property "%(pname)s" is out of '
+ 'range "(min:%(vmin)s, max:%(vmax)s)".') %
+ dict(pname=PortSpec.SOURCE,
+ pvalue='65535',
+ vmin='2',
+ vmax='65534'))
+ exception.ExceptionCollector.assertExceptionMessage(
+ exception.RangeValueError, msg)
+
+ def test_containers(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/test_containers.yaml")
+ ToscaTemplate(tosca_tpl)
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py b/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
index 81a1a6c..986d9e4 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
@@ -20,12 +20,12 @@ from toscaparser.parameters import Input
from toscaparser.parameters import Output
from toscaparser.policy import Policy
from toscaparser.relationship_template import RelationshipTemplate
+from toscaparser.repositories import Repository
from toscaparser.tests.base import TestCase
from toscaparser.topology_template import TopologyTemplate
from toscaparser.tosca_template import ToscaTemplate
from toscaparser.triggers import Triggers
from toscaparser.utils.gettextutils import _
-
import toscaparser.utils.yamlparser
@@ -105,7 +105,9 @@ class ToscaTemplateValidationTest(TestCase):
type: integer
description: Number of CPUs for the server.
constraint:
- - valid_values: [ 1, 2, 4, 8 ]
+ - valid_values: [ 1, 2, 4 ]
+ required: yes
+ status: supported
'''
inputs = (toscaparser.utils.yamlparser.
simple_parse(tpl_snippet)['inputs'])
@@ -335,6 +337,113 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
'to verify valid values.'),
err.__str__())
+ def _repo_content(self, path):
+ repositories = path['repositories']
+ reposit = []
+ for name, val in repositories.items():
+ reposits = Repository(name, val)
+ reposit.append(reposits)
+ return reposit
+
+ def test_repositories(self):
+ tpl_snippet = '''
+ repositories:
+ repo_code0: https://raw.githubusercontent.com/nandinivemula/intern
+ repo_code1:
+ description: My project's code Repository in github usercontent.
+ url: https://github.com/nandinivemula/intern
+ credential:
+ user: nandini
+ password: tcs@12345
+ repo_code2:
+ description: My Project's code Repository in github.
+ url: https://github.com/nandinivemula/intern
+ credential:
+ user: xyzw
+ password: xyz@123
+ '''
+ tpl = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet))
+ repoobject = self._repo_content(tpl)
+ actualrepo_names = []
+ for repo in repoobject:
+ repos = repo.name
+ actualrepo_names.append(repos)
+ reposname = list(tpl.values())
+ reposnames = reposname[0]
+ expected_reponames = list(reposnames.keys())
+ self.assertEqual(expected_reponames, actualrepo_names)
+
+ def test_repositories_with_missing_required_field(self):
+ tpl_snippet = '''
+ repositories:
+ repo_code0: https://raw.githubusercontent.com/nandinivemula/intern
+ repo_code1:
+ description: My project's code Repository in github usercontent.
+ credential:
+ user: nandini
+ password: tcs@12345
+ repo_code2:
+ description: My Project's code Repository in github.
+ url: https://github.com/nandinivemula/intern
+ credential:
+ user: xyzw
+ password: xyz@123
+ '''
+ tpl = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet))
+ err = self.assertRaises(exception.MissingRequiredFieldError,
+ self._repo_content, tpl)
+ expectedmessage = _('Repository "repo_code1" is missing '
+ 'required field "url".')
+ self.assertEqual(expectedmessage, err.__str__())
+
+ def test_repositories_with_unknown_field(self):
+ tpl_snippet = '''
+ repositories:
+ repo_code0: https://raw.githubusercontent.com/nandinivemula/intern
+ repo_code1:
+ description: My project's code Repository in github usercontent.
+ url: https://github.com/nandinivemula/intern
+ credential:
+ user: nandini
+ password: tcs@12345
+ repo_code2:
+ descripton: My Project's code Repository in github.
+ url: https://github.com/nandinivemula/intern
+ credential:
+ user: xyzw
+ password: xyz@123
+ '''
+ tpl = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet))
+ err = self.assertRaises(exception.UnknownFieldError,
+ self._repo_content, tpl)
+ expectedmessage = _('repositories "repo_code2" contains unknown field'
+ ' "descripton". Refer to the definition to verify'
+ ' valid values.')
+ self.assertEqual(expectedmessage, err.__str__())
+
+ def test_repositories_with_invalid_url(self):
+ tpl_snippet = '''
+ repositories:
+ repo_code0: https://raw.githubusercontent.com/nandinivemula/intern
+ repo_code1:
+ description: My project's code Repository in github usercontent.
+ url: h
+ credential:
+ user: nandini
+ password: tcs@12345
+ repo_code2:
+ description: My Project's code Repository in github.
+ url: https://github.com/nandinivemula/intern
+ credential:
+ user: xyzw
+ password: xyz@123
+ '''
+ tpl = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet))
+ err = self.assertRaises(exception.URLException,
+ self._repo_content, tpl)
+ expectedmessage = _('repsositories "repo_code1" Invalid Url')
+ self.assertEqual(expectedmessage, err.__str__())
+
def test_groups(self):
tpl_snippet = '''
node_templates:
@@ -1379,3 +1488,35 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
exception.MissingRequiredFieldError,
lambda: Policy(name, policies[name], None, None))
self.assertEqual(expectedmessage, err.__str__())
+
+ def test_credential_datatype(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/test_credential_datatype.yaml")
+ self.assertIsNotNone(ToscaTemplate(tosca_tpl))
+
+ def test_invalid_default_value(self):
+ tpl_path = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/test_invalid_input_defaults.yaml")
+ self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path)
+ exception.ExceptionCollector.assertExceptionMessage(
+ ValueError, _('"two" is not an integer.'))
+
+ def test_invalid_capability(self):
+ tpl_snippet = '''
+ node_templates:
+ server:
+ type: tosca.nodes.Compute
+ capabilities:
+ oss:
+ properties:
+ architecture: x86_64
+ '''
+ tpl = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet))
+ err = self.assertRaises(exception.UnknownFieldError,
+ TopologyTemplate, tpl, None)
+ expectedmessage = _('"capabilities" of template "server" contains '
+ 'unknown field "oss". Refer to the definition '
+ 'to verify valid values.')
+ self.assertEqual(expectedmessage, err.__str__())
diff --git a/tosca2heat/tosca-parser/toscaparser/topology_template.py b/tosca2heat/tosca-parser/toscaparser/topology_template.py
index d51512a..6cf4f31 100644
--- a/tosca2heat/tosca-parser/toscaparser/topology_template.py
+++ b/tosca2heat/tosca-parser/toscaparser/topology_template.py
@@ -66,6 +66,10 @@ class TopologyTemplate(object):
input = Input(name, attrs)
if self.parsed_params and name in self.parsed_params:
input.validate(self.parsed_params[name])
+ else:
+ default = input.default
+ if default:
+ input.validate(default)
inputs.append(input)
return inputs
diff --git a/tosca2heat/tosca-parser/toscaparser/tosca_template.py b/tosca2heat/tosca-parser/toscaparser/tosca_template.py
index 2ab2581..01e6c73 100644
--- a/tosca2heat/tosca-parser/toscaparser/tosca_template.py
+++ b/tosca2heat/tosca-parser/toscaparser/tosca_template.py
@@ -23,6 +23,7 @@ from toscaparser.elements.entity_type import update_definitions
from toscaparser.extensions.exttools import ExtTools
import toscaparser.imports
from toscaparser.prereq.csar import CSAR
+from toscaparser.repositories import Repository
from toscaparser.topology_template import TopologyTemplate
from toscaparser.tpl_relationship_graph import ToscaGraph
from toscaparser.utils.gettextutils import _
@@ -95,6 +96,7 @@ class ToscaTemplate(object):
self.relationship_types = self._tpl_relationship_types()
self.description = self._tpl_description()
self.topology_template = self._topology_template()
+ self.repositories = self._tpl_repositories()
if self.topology_template.tpl:
self.inputs = self._inputs()
self.relationship_templates = self._relationship_templates()
@@ -134,6 +136,15 @@ class ToscaTemplate(object):
def _tpl_imports(self):
return self.tpl.get(IMPORTS)
+ def _tpl_repositories(self):
+ repositories = self.tpl.get(REPOSITORIES)
+ reposit = []
+ if repositories:
+ for name, val in repositories.items():
+ reposits = Repository(name, val)
+ reposit.append(reposits)
+ return reposit
+
def _tpl_relationship_types(self):
return self._get_custom_types(RELATIONSHIP_TYPES)
diff --git a/tosca2heat/tosca-parser/toscaparser/utils/validateutils.py b/tosca2heat/tosca-parser/toscaparser/utils/validateutils.py
index f9b9fc5..43e14d6 100644
--- a/tosca2heat/tosca-parser/toscaparser/utils/validateutils.py
+++ b/tosca2heat/tosca-parser/toscaparser/utils/validateutils.py
@@ -17,14 +17,20 @@ import numbers
import re
import six
+# from toscaparser.elements import constraints
from toscaparser.common.exception import ExceptionCollector
from toscaparser.common.exception import InvalidTOSCAVersionPropertyException
+from toscaparser.common.exception import RangeValueError
from toscaparser.utils.gettextutils import _
+
log = logging.getLogger('tosca')
+RANGE_UNBOUNDED = 'UNBOUNDED'
+
def str_to_num(value):
'''Convert a string representation of a number into a numeric type.'''
+ # TODO(TBD) we should not allow numeric values in, input should be str
if isinstance(value, numbers.Number):
return value
try:
@@ -33,8 +39,11 @@ def str_to_num(value):
return float(value)
-def validate_number(value):
- return str_to_num(value)
+def validate_numeric(value):
+ if not isinstance(value, numbers.Number):
+ ExceptionCollector.appendException(
+ ValueError(_('"%s" is not a numeric.') % value))
+ return value
def validate_integer(value):
@@ -51,7 +60,7 @@ def validate_float(value):
if not isinstance(value, float):
ExceptionCollector.appendException(
ValueError(_('"%s" is not a float.') % value))
- return validate_number(value)
+ return value
def validate_string(value):
@@ -68,15 +77,53 @@ def validate_list(value):
return value
-def validate_range(value):
- validate_list(value)
- if isinstance(value, list):
- if len(value) != 2 or not (value[0] <= value[1]):
+def validate_range(range):
+ # list class check
+ validate_list(range)
+ # validate range list has a min and max
+ if len(range) != 2:
+ ExceptionCollector.appendException(
+ ValueError(_('"%s" is not a valid range.') % range))
+ # validate min and max are numerics or the keyword UNBOUNDED
+ min_test = max_test = False
+ if not range[0] == RANGE_UNBOUNDED:
+ min = validate_numeric(range[0])
+ else:
+ min_test = True
+ if not range[1] == RANGE_UNBOUNDED:
+ max = validate_numeric(range[1])
+ else:
+ max_test = True
+ # validate the max > min (account for UNBOUNDED)
+ if not min_test and not max_test:
+ # Note: min == max is allowed
+ if min > max:
+ ExceptionCollector.appendException(
+ ValueError(_('"%s" is not a valid range.') % range))
+
+ return range
+
+
+def validate_value_in_range(value, range, prop_name):
+ validate_numeric(value)
+ validate_range(range)
+
+ # Note: value is valid if equal to min
+ if range[0] != RANGE_UNBOUNDED:
+ if value < range[0]:
+ ExceptionCollector.appendException(
+ RangeValueError(pname=prop_name,
+ pvalue=value,
+ vmin=range[0],
+ vmax=range[1]))
+ # Note: value is valid if equal to max
+ if range[1] != RANGE_UNBOUNDED:
+ if value > range[1]:
ExceptionCollector.appendException(
- ValueError(_('"%s" is not a valid range.') % value))
- validate_integer(value[0])
- if not value[1] == "UNBOUNDED":
- validate_integer(value[1])
+ RangeValueError(pname=prop_name,
+ pvalue=value,
+ vmin=range[0],
+ vmax=range[1]))
return value