Fix translation issue with interface inputs and functions

Fix the issue with interface inputs described using intrinsic
functions, that produces an invalid HOT output. Also, include
necessary unit tests, and revert templates that were simplified
due to this issue..

This patch includes changes required in tosca-parser for
resolving the issue.

Change-Id: Iefde09d4633a1cadfe2c6b9ac28c58d2da79a37d
Partial-Bug: #1440247
This commit is contained in:
Vahid Hashemian 2015-09-24 10:23:24 -07:00
parent 6a9483180d
commit a80b6893c2
13 changed files with 92 additions and 112 deletions

View File

@ -14,10 +14,10 @@ import collections
import datetime
import re
import toscaparser
from toscaparser.common.exception import InvalidSchemaError
from toscaparser.common.exception import ValidationError
from toscaparser.elements import scalarunit
from toscaparser.functions import is_function
from toscaparser.utils.gettextutils import _
@ -262,7 +262,8 @@ class GreaterOrEqual(Constraint):
'be comparable.'))
def _is_valid(self, value):
if is_function(value) or value >= self.constraint_value:
if toscaparser.functions.is_function(value) or \
value >= self.constraint_value:
return True
return False
@ -417,8 +418,8 @@ class ValidValues(Constraint):
def _err_msg(self, value):
allowed = '[%s]' % ', '.join(str(a) for a in self.constraint_value)
return (_('%(pname)s: %(pvalue)s is not an valid '
'value "%(cvalue)s".') %
return (_('%(pname)s: %(pvalue)s is not a valid value. Expected a '
'value from "%(cvalue)s".') %
dict(pname=self.property_name,
pvalue=value,
cvalue=allowed))

View File

@ -16,6 +16,7 @@ import abc
import six
from toscaparser.common.exception import UnknownInputError
from toscaparser.dataentity import DataEntity
from toscaparser.utils.gettextutils import _
@ -80,9 +81,15 @@ class GetInput(Function):
raise UnknownInputError(input_name=self.args[0])
def result(self):
found_input = [input_def for input_def in self.tosca_tpl.inputs
if self.input_name == input_def.name][0]
return found_input.default
if self.tosca_tpl.parsed_params and \
self.input_name in self.tosca_tpl.parsed_params:
return DataEntity.validate_datatype(
self.tosca_tpl.tpl['inputs'][self.input_name]['type'],
self.tosca_tpl.parsed_params[self.input_name])
input = [input_def for input_def in self.tosca_tpl.inputs
if self.input_name == input_def.name][0]
return input.default
@property
def input_name(self):
@ -307,7 +314,7 @@ class GetProperty(Function):
else:
property_value = self._find_property(self.args[1]).value
if isinstance(property_value, Function):
return property_value
return property_value.result()
return get_function(self.tosca_tpl,
self.context,
property_value)

View File

@ -43,16 +43,14 @@ topology_template:
properties:
github_url: { get_input: github_url }
requirements:
- host:
node: nodejs
- database_connection:
node: mongo_db
- host: nodejs
- database_connection: mongo_db
interfaces:
Standard:
configure:
implementation: ../Scripts/nodejs/config.sh
inputs:
github_url: http://github.com/paypal/rest-api-sample-app-nodejs.git
github_url: { get_property: [ SELF, github_url ] }
mongodb_ip: { get_attribute: [mongo_server, private_address] }
start: ../Scripts/nodejs/start.sh
nodejs:
@ -66,16 +64,14 @@ topology_template:
mongo_db:
type: tosca.nodes.Database
requirements:
- host:
node: mongo_dbms
- host: mongo_dbms
interfaces:
Standard:
create: ../Scripts/mongodb/create_database.sh
mongo_dbms:
type: tosca.nodes.DBMS
requirements:
- host:
node: mongo_server
- host: mongo_server
interfaces:
Standard:
create: ../Scripts/mongodb/create.sh
@ -87,8 +83,7 @@ topology_template:
elasticsearch:
type: tosca.nodes.SoftwareComponent.Elasticsearch
requirements:
- host:
node: elasticsearch_server
- host: elasticsearch_server
interfaces:
Standard:
create: ../Scripts/elasticsearch/create.sh
@ -96,8 +91,7 @@ topology_template:
logstash:
type: tosca.nodes.SoftwareComponent.Logstash
requirements:
- host:
node: logstash_server
- host: logstash_server
- search_endpoint:
node: elasticsearch
capability: search_endpoint
@ -116,11 +110,8 @@ topology_template:
kibana:
type: tosca.nodes.SoftwareComponent.Kibana
requirements:
- host:
node: kibana_server
- search_endpoint:
node: elasticsearch
capability: search_endpoint
- host: kibana_server
- search_endpoint: elasticsearch
interfaces:
Standard:
create: ../Scripts/kibana/create.sh
@ -133,8 +124,7 @@ topology_template:
app_collectd:
type: tosca.nodes.SoftwareComponent.Collectd
requirements:
- host:
node: app_server
- host: app_server
- log_endpoint:
node: logstash
capability: log_endpoint
@ -155,8 +145,7 @@ topology_template:
app_rsyslog:
type: tosca.nodes.SoftwareComponent.Rsyslog
requirements:
- host:
node: app_server
- host: app_server
- log_endpoint:
node: logstash
capability: log_endpoint

View File

@ -33,10 +33,8 @@ topology_template:
wordpress:
type: tosca.nodes.WebApplication.WordPress
requirements:
- host:
node: webserver
- database_endpoint:
node: mysql_database
- host: webserver
- database_endpoint: mysql_database
interfaces:
Standard:
create: Scripts/WordPress/install.sh
@ -54,8 +52,7 @@ topology_template:
user: { get_input: db_user }
password: { get_input: db_pwd }
requirements:
- host:
node: mysql_dbms
- host: mysql_dbms
interfaces:
Standard:
configure:
@ -72,8 +69,7 @@ topology_template:
root_password: { get_input: db_root_pwd }
port: { get_input: db_port }
requirements:
- host:
node: server
- host: server
interfaces:
Standard:
create: Scripts/MYSQLDBMS/install.sh
@ -86,8 +82,7 @@ topology_template:
webserver:
type: tosca.nodes.WebServer
requirements:
- host:
node: server
- host: server
interfaces:
Standard:
create: Scripts/WebServer/install.sh

View File

@ -51,7 +51,7 @@ topology_template:
configure:
implementation: nodejs/config.sh
inputs:
github_url: http://github.com/paypal/rest-api-sample-app-nodejs.git
github_url: { get_property: [ SELF, github_url ] }
mongodb_ip: { get_attribute: [mongo_server, private_address] }
start: nodejs/start.sh
nodejs:
@ -111,9 +111,7 @@ topology_template:
type: tosca.nodes.SoftwareComponent.Kibana
requirements:
- host: kibana_server
- search_endpoint:
node: elasticsearch
capability: search_endpoint
- search_endpoint: elasticsearch
interfaces:
Standard:
create: kibana/create.sh

View File

@ -46,9 +46,9 @@ topology_template:
configure:
implementation: wordpress/wordpress_configure.sh
inputs:
wp_db_name: wordpress
wp_db_user: wp_user
wp_db_password: wp_pass
wp_db_name: { get_property: [ mysql_database, name ] }
wp_db_user: { get_property: [ mysql_database, user ] }
wp_db_password: { get_property: [ mysql_database, password ] }
mysql_database:
type: tosca.nodes.Database
@ -61,17 +61,17 @@ topology_template:
properties:
port: { get_input: db_port }
requirements:
- host:
node: mysql_dbms
- host: mysql_dbms
interfaces:
Standard:
configure:
implementation: mysql/mysql_database_configure.sh
inputs:
db_name: wordpress
db_user: wp_user
db_password: wp_pass
db_root_password: passw0rd
db_name: { get_property: [ SELF, name ] }
db_user: { get_property: [ SELF, user ] }
db_password: { get_property: [ SELF, password ] }
db_root_password: { get_property: [ mysql_dbms, root_password ] }
mysql_dbms:
type: tosca.nodes.DBMS
properties:
@ -84,12 +84,12 @@ topology_template:
create:
implementation: mysql/mysql_dbms_install.sh
inputs:
db_root_password: passw0rd
db_root_password: { get_property: [ mysql_dbms, root_password ] }
start: mysql/mysql_dbms_start.sh
configure:
implementation: mysql/mysql_dbms_configure.sh
inputs:
db_port: 3366
db_port: { get_property: [ mysql_dbms, port ] }
webserver:
type: tosca.nodes.WebServer
@ -99,6 +99,7 @@ topology_template:
Standard:
create: webserver/webserver_install.sh
start: webserver/webserver_start.sh
server:
type: tosca.nodes.Compute
capabilities:

View File

@ -100,8 +100,8 @@ class ConstraintTest(TestCase):
constraint = Constraint('prop', Schema.INTEGER, schema)
error = self.assertRaises(exception.ValidationError,
constraint.validate, 5)
self.assertEqual('prop: 5 is not an valid value "[2, 4, 6, 8]".',
str(error))
self.assertEqual('prop: 5 is not a valid value. Expected a value from '
'"[2, 4, 6, 8]".', str(error))
def test_invalid_in_range(self):
snippet = 'in_range: {2, 6}'

View File

@ -221,7 +221,7 @@ class DataTypeTest(TestCase):
data = DataEntity('tosca.my.datatypes.PeopleBase', value,
DataTypeTest.custom_type_def)
error = self.assertRaises(ValueError, data.validate)
self.assertEqual('"123" is not a string', error.__str__())
self.assertEqual('"123" is not a string.', error.__str__())
# the value of name doesn't meet the defined constraint
def test_value_error_in_dataentity(self):
@ -247,7 +247,7 @@ class DataTypeTest(TestCase):
data = DataEntity('tosca.my.datatypes.People', value,
DataTypeTest.custom_type_def)
error = self.assertRaises(ValueError, data.validate)
self.assertEqual('"1" is not a string', error.__str__())
self.assertEqual('"1" is not a string.', error.__str__())
# contact_pone is an invalid field name in nested datatype
def test_validation_in_nested_datatype(self):
@ -289,11 +289,11 @@ class DataTypeTest(TestCase):
"data/datatypes/test_custom_datatypes_value_error.yaml")
error = self.assertRaises(ValueError, ToscaTemplate, tpl_path)
self.assertEqual('"[\'1 foo street\', \'9 bar avenue\']" '
'is not a map', error.__str__())
'is not a map.', error.__str__())
def test_datatype_in_template_nested_datatype_error(self):
tpl_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"data/datatypes/test_custom_datatypes_nested_datatype_error.yaml")
error = self.assertRaises(ValueError, ToscaTemplate, tpl_path)
self.assertEqual('"123456789" is not a string', error.__str__())
self.assertEqual('"123456789" is not a string.', error.__str__())

View File

@ -23,7 +23,8 @@ class IntrinsicFunctionsTest(TestCase):
tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress.yaml")
tosca = ToscaTemplate(tosca_tpl)
params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user'}
tosca = ToscaTemplate(tosca_tpl, parsed_params=params)
def _get_node(self, node_name):
return [
@ -39,34 +40,30 @@ class IntrinsicFunctionsTest(TestCase):
return [prop.value for prop in node_template.get_properties_objects()
if prop.name == property_name][0]
def test_get_property(self):
TestCase.skip(self, 'bug #1440247')
mysql_dbms = self._get_node('mysql_dbms')
operation = self._get_operation(mysql_dbms.interfaces, 'configure')
db_root_password = operation.inputs['db_root_password']
self.assertTrue(isinstance(db_root_password, functions.GetProperty))
result = db_root_password.result()
self.assertTrue(isinstance(result, functions.GetInput))
def _get_inputs_dict(self):
inputs = {}
for input in self.tosca.inputs:
inputs[input.name] = input.default
return inputs
def test_get_requirement_property(self):
TestCase.skip(self, 'bug #1440247')
def _get_input(self, name):
self._get_inputs_dict()[name]
def test_get_property(self):
wordpress = self._get_node('wordpress')
operation = self._get_operation(wordpress.interfaces, 'configure')
wp_db_port = operation.inputs['wp_db_port']
self.assertTrue(isinstance(wp_db_port, functions.GetProperty))
result = wp_db_port.result()
self.assertTrue(isinstance(result, functions.GetInput))
self.assertEqual('db_port', result.input_name)
wp_db_password = operation.inputs['wp_db_password']
self.assertTrue(isinstance(wp_db_password, functions.GetProperty))
result = wp_db_password.result()
self.assertEqual('wp_pass', result)
def test_get_capability_property(self):
TestCase.skip(self, 'bug #1440247')
mysql_database = self._get_node('mysql_database')
operation = self._get_operation(mysql_database.interfaces, 'configure')
db_port = operation.inputs['db_port']
self.assertTrue(isinstance(db_port, functions.GetProperty))
result = db_port.result()
self.assertTrue(isinstance(result, functions.GetInput))
self.assertEqual('db_port', result.input_name)
def test_get_property_with_input_param(self):
wordpress = self._get_node('wordpress')
operation = self._get_operation(wordpress.interfaces, 'configure')
wp_db_user = operation.inputs['wp_db_user']
self.assertTrue(isinstance(wp_db_user, functions.GetProperty))
result = wp_db_user.result()
self.assertEqual('my_db_user', result)
def test_unknown_capability_property(self):
err = self.assertRaises(
@ -87,13 +84,6 @@ class IntrinsicFunctionsTest(TestCase):
expected_inputs.remove(prop.value.input_name)
self.assertListEqual(expected_inputs, [])
def test_get_input_in_interface(self):
TestCase.skip(self, 'bug #1440247')
mysql_dbms = self._get_node('mysql_dbms')
operation = self._get_operation(mysql_dbms.interfaces, 'configure')
db_user = operation.inputs['db_user']
self.assertTrue(isinstance(db_user, functions.GetInput))
def test_get_input_validation(self):
self.assertRaises(exception.UnknownInputError,
self._load_template,

View File

@ -44,7 +44,7 @@ class PropertyTest(TestCase):
propertyInstance = Property('test_property', 'a',
test_property_schema)
error = self.assertRaises(ValueError, propertyInstance.validate)
self.assertEqual('"a" is not a list', str(error))
self.assertEqual('"a" is not a list.', str(error))
def test_list_entry_schema(self):
test_property_schema = {'type': 'list',
@ -73,8 +73,7 @@ class PropertyTest(TestCase):
propertyInstance = Property('test_property', [1, 'b'],
test_property_schema)
error = self.assertRaises(ValueError, propertyInstance.validate)
self.assertEqual('"b" is not an integer',
str(error))
self.assertEqual('"b" is not an integer.', str(error))
def test_map(self):
test_property_schema = {'type': 'map'}
@ -88,7 +87,7 @@ class PropertyTest(TestCase):
propertyInstance = Property('test_property', 12,
test_property_schema)
error = self.assertRaises(ValueError, propertyInstance.validate)
self.assertEqual('"12" is not a map', str(error))
self.assertEqual('"12" is not a map.', str(error))
def test_map_entry_schema(self):
test_property_schema = {'type': 'map',
@ -107,7 +106,7 @@ class PropertyTest(TestCase):
{'valid': True, 'contact_name': 123},
test_property_schema)
error = self.assertRaises(ValueError, propertyInstance.validate)
self.assertEqual('"123" is not a boolean', str(error))
self.assertEqual('"123" is not a boolean.', str(error))
def test_boolean(self):
test_property_schema = {'type': 'boolean'}
@ -124,7 +123,7 @@ class PropertyTest(TestCase):
propertyInstance = Property('test_property', 12,
test_property_schema)
error = self.assertRaises(ValueError, propertyInstance.validate)
self.assertEqual('"12" is not a boolean', str(error))
self.assertEqual('"12" is not a boolean.', str(error))
def test_float(self):
test_property_schema = {'type': 'float'}
@ -138,7 +137,7 @@ class PropertyTest(TestCase):
propertyInstance = Property('test_property', 12,
test_property_schema)
error = self.assertRaises(ValueError, propertyInstance.validate)
self.assertEqual('"12" is not a float', str(error))
self.assertEqual('"12" is not a float.', str(error))
def test_timestamp(self):
test_property_schema = {'type': 'timestamp'}

View File

@ -68,7 +68,7 @@ class ToscaTemplateTest(TestCase):
expected_type = "tosca.nodes.Database"
expected_properties = ['name', 'password', 'user']
expected_capabilities = ['database_endpoint']
expected_requirements = [{'host': {'node': 'mysql_dbms'}}]
expected_requirements = [{'host': 'mysql_dbms'}]
''' TODO: needs enhancement in tosca_elk.yaml..
expected_relationshp = ['tosca.relationships.HostedOn']
expected_host = ['mysql_dbms']

View File

@ -730,7 +730,7 @@ custom_types/wordpress.yaml
capability: log_endpoint
occurrences: [a, w]
'''
expectedmessage = ('"a" is not an integer')
expectedmessage = ('"a" is not an integer.')
err = self.assertRaises(
ValueError,
lambda: self._single_node_template_content_test(tpl_snippet))
@ -745,7 +745,7 @@ custom_types/wordpress.yaml
capability: log_endpoint
occurrences: -1
'''
expectedmessage = ('"-1" is not a list')
expectedmessage = ('"-1" is not a list.')
err = self.assertRaises(
ValueError,
lambda: self._single_node_template_content_test(tpl_snippet))
@ -1000,7 +1000,7 @@ custom_types/wordpress.yaml
lambda: self._single_node_template_content_test(tpl_snippet))
self.assertEqual(expectedmessage, err.__str__())
# validatating capability property values
# validating capability property values
tpl_snippet = '''
node_templates:
server:
@ -1010,8 +1010,8 @@ custom_types/wordpress.yaml
properties:
initiator: test
'''
expectedmessage = ('initiator: test is not an valid value '
'"[source, target, peer]".')
expectedmessage = ('initiator: test is not a valid value. Expected a '
'value from "[source, target, peer]".')
err = self.assertRaises(
exception.ValidationError,

View File

@ -42,31 +42,31 @@ def validate_integer(value):
try:
value = int(value)
except Exception:
raise ValueError(_('"%s" is not an integer') % value)
raise ValueError(_('"%s" is not an integer.') % value)
return value
def validate_float(value):
if not isinstance(value, float):
raise ValueError(_('"%s" is not a float') % value)
raise ValueError(_('"%s" is not a float.') % value)
return validate_number(value)
def validate_string(value):
if not isinstance(value, six.string_types):
raise ValueError(_('"%s" is not a string') % value)
raise ValueError(_('"%s" is not a string.') % value)
return value
def validate_list(value):
if not isinstance(value, list):
raise ValueError(_('"%s" is not a list') % value)
raise ValueError(_('"%s" is not a list.') % value)
return value
def validate_map(value):
if not isinstance(value, collections.Mapping):
raise ValueError(_('"%s" is not a map') % value)
raise ValueError(_('"%s" is not a map.') % value)
return value
@ -78,7 +78,7 @@ def validate_boolean(value):
normalised = value.lower()
if normalised in ['true', 'false']:
return normalised == 'true'
raise ValueError(_('"%s" is not a boolean') % value)
raise ValueError(_('"%s" is not a boolean.') % value)
def validate_timestamp(value):