Merge "Add validation for parameters"

This commit is contained in:
Jenkins 2016-09-03 20:22:14 +00:00 committed by Gerrit Code Review
commit 8fe6af8eab
10 changed files with 119 additions and 34 deletions

View File

@ -114,6 +114,11 @@ class UnknownInputError(TOSCAException):
msg_fmt = _('Unknown input "%(input_name)s".') msg_fmt = _('Unknown input "%(input_name)s".')
class MissingRequiredParameterError(TOSCAException):
msg_fmt = _('%(what)s is missing required parameter for input: '
'"%(input_name)s".')
class InvalidPropertyValueError(TOSCAException): class InvalidPropertyValueError(TOSCAException):
msg_fmt = _('Value of property "%(what)s" is invalid.') msg_fmt = _('Value of property "%(what)s" is invalid.')

View File

@ -27,10 +27,10 @@ class Schema(collections.Mapping):
KEYS = ( KEYS = (
TYPE, REQUIRED, DESCRIPTION, TYPE, REQUIRED, DESCRIPTION,
DEFAULT, CONSTRAINTS, ENTRYSCHEMA DEFAULT, CONSTRAINTS, ENTRYSCHEMA, STATUS
) = ( ) = (
'type', 'required', 'description', 'type', 'required', 'description',
'default', 'constraints', 'entry_schema' 'default', 'constraints', 'entry_schema', 'status'
) )
PROPERTY_TYPES = ( PROPERTY_TYPES = (
@ -85,6 +85,10 @@ class Schema(collections.Mapping):
def default(self): def default(self):
return self.schema.get(self.DEFAULT) return self.schema.get(self.DEFAULT)
@property
def status(self):
return self.schema.get(self.STATUS, '')
@property @property
def constraints(self): def constraints(self):
if not self.constraints_list: if not self.constraints_list:

View File

@ -35,10 +35,17 @@ class Input(object):
self.name = name self.name = name
self.schema = Schema(name, schema_dict) self.schema = Schema(name, schema_dict)
self._validate_field()
self.validate_type(self.type)
@property @property
def type(self): def type(self):
return self.schema.type return self.schema.type
@property
def required(self):
return self.schema.required
@property @property
def description(self): def description(self):
return self.schema.description return self.schema.description
@ -51,9 +58,11 @@ class Input(object):
def constraints(self): def constraints(self):
return self.schema.constraints return self.schema.constraints
@property
def status(self):
return self.schema.status
def validate(self, value=None): def validate(self, value=None):
self._validate_field()
self.validate_type(self.type)
if value is not None: if value is not None:
self._validate_value(value) self._validate_value(value)

View File

@ -10,6 +10,7 @@ topology_template:
description: Number of CPUs for the server. description: Number of CPUs for the server.
constraints: constraints:
- valid_values: [ 1, 2, 4, 8 ] - valid_values: [ 1, 2, 4, 8 ]
default: 2
node_templates: node_templates:
server: server:

View File

@ -27,8 +27,10 @@ node_types:
properties: properties:
mq_server_ip: mq_server_ip:
type: string type: string
required: False
receiver_port: receiver_port:
type: integer type: integer
required: False
attributes: attributes:
receiver_ip: receiver_ip:
type: string type: string
@ -51,8 +53,10 @@ node_types:
properties: properties:
admin_user: admin_user:
type: string type: string
required: False
pool_size: pool_size:
type: integer type: integer
required: False
capabilities: capabilities:
message_receiver: message_receiver:
type: example.capabilities.Receiver type: example.capabilities.Receiver

View File

@ -15,7 +15,7 @@ topology_template:
description: IP address of the message queuing server to receive messages from. description: IP address of the message queuing server to receive messages from.
mq_server_port: mq_server_port:
type: integer type: integer
default1: 8080 default: 8080
description: Port to be used for receiving messages. description: Port to be used for receiving messages.
node_templates: node_templates:

View File

@ -24,7 +24,8 @@ class IntrinsicFunctionsTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress.yaml") "data/tosca_single_instance_wordpress.yaml")
params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user'} params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user',
'db_root_pwd': '12345678'}
tosca = ToscaTemplate(tosca_tpl, parsed_params=params) tosca = ToscaTemplate(tosca_tpl, parsed_params=params)
def _get_node(self, node_name, tosca=None): def _get_node(self, node_name, tosca=None):
@ -116,14 +117,17 @@ class IntrinsicFunctionsTest(TestCase):
self.assertEqual(3306, dbms_port.result()) self.assertEqual(3306, dbms_port.result())
dbms_root_password = self._get_property(mysql_dbms, dbms_root_password = self._get_property(mysql_dbms,
'root_password') 'root_password')
self.assertIsNone(dbms_root_password.result()) self.assertEqual(dbms_root_password.result(), '12345678')
def test_get_property_with_host(self): def test_get_property_with_host(self):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/functions/test_get_property_with_host.yaml") "data/functions/test_get_property_with_host.yaml")
mysql_database = self._get_node('mysql_database', mysql_database = self._get_node('mysql_database',
ToscaTemplate(tosca_tpl)) ToscaTemplate(tosca_tpl,
parsed_params={
'db_root_pwd': '123'
}))
operation = self._get_operation(mysql_database.interfaces, 'configure') operation = self._get_operation(mysql_database.interfaces, 'configure')
db_port = operation.inputs['db_port'] db_port = operation.inputs['db_port']
self.assertTrue(isinstance(db_port, functions.GetProperty)) self.assertTrue(isinstance(db_port, functions.GetProperty))
@ -138,7 +142,10 @@ class IntrinsicFunctionsTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/functions/tosca_nested_property_names_indexes.yaml") "data/functions/tosca_nested_property_names_indexes.yaml")
webserver = self._get_node('wordpress', ToscaTemplate(tosca_tpl)) webserver = self._get_node('wordpress',
ToscaTemplate(tosca_tpl,
parsed_params={
'db_root_pwd': '1234'}))
operation = self._get_operation(webserver.interfaces, 'configure') operation = self._get_operation(webserver.interfaces, 'configure')
wp_endpoint_prot = operation.inputs['wp_endpoint_protocol'] wp_endpoint_prot = operation.inputs['wp_endpoint_protocol']
self.assertTrue(isinstance(wp_endpoint_prot, functions.GetProperty)) self.assertTrue(isinstance(wp_endpoint_prot, functions.GetProperty))
@ -151,7 +158,10 @@ class IntrinsicFunctionsTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/functions/test_capabilties_inheritance.yaml") "data/functions/test_capabilties_inheritance.yaml")
some_node = self._get_node('some_node', ToscaTemplate(tosca_tpl)) some_node = self._get_node('some_node',
ToscaTemplate(tosca_tpl,
parsed_params={
'db_root_pwd': '1234'}))
operation = self._get_operation(some_node.interfaces, 'configure') operation = self._get_operation(some_node.interfaces, 'configure')
some_input = operation.inputs['some_input'] some_input = operation.inputs['some_input']
self.assertTrue(isinstance(some_input, functions.GetProperty)) self.assertTrue(isinstance(some_input, functions.GetProperty))
@ -161,7 +171,8 @@ class IntrinsicFunctionsTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/functions/test_get_property_source_target_keywords.yaml") "data/functions/test_get_property_source_target_keywords.yaml")
tosca = ToscaTemplate(tosca_tpl) tosca = ToscaTemplate(tosca_tpl,
parsed_params={'db_root_pwd': '1234'})
for node in tosca.nodetemplates: for node in tosca.nodetemplates:
for relationship, trgt in node.relationships.items(): for relationship, trgt in node.relationships.items():
@ -184,7 +195,8 @@ class GetAttributeTest(TestCase):
return ToscaTemplate(os.path.join( return ToscaTemplate(os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
'data', 'data',
filename)) filename),
parsed_params={'db_root_pwd': '1234'})
def _get_operation(self, interfaces, operation): def _get_operation(self, interfaces, operation):
return [ return [
@ -283,7 +295,8 @@ class GetAttributeTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/functions/test_get_attribute_source_target_keywords.yaml") "data/functions/test_get_attribute_source_target_keywords.yaml")
tosca = ToscaTemplate(tosca_tpl) tosca = ToscaTemplate(tosca_tpl,
parsed_params={'db_root_pwd': '12345678'})
for node in tosca.nodetemplates: for node in tosca.nodetemplates:
for relationship, trgt in node.relationships.items(): for relationship, trgt in node.relationships.items():

View File

@ -30,7 +30,9 @@ class ToscaTemplateTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress.yaml") "data/tosca_single_instance_wordpress.yaml")
tosca = ToscaTemplate(tosca_tpl) params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user',
'db_root_pwd': '12345678'}
tosca = ToscaTemplate(tosca_tpl, parsed_params=params)
tosca_elk_tpl = os.path.join( tosca_elk_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/tosca_elk.yaml") "data/tosca_elk.yaml")
@ -437,21 +439,30 @@ class ToscaTemplateTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress.yaml") "data/tosca_single_instance_wordpress.yaml")
tosca = ToscaTemplate(tosca_tpl) params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user',
'db_root_pwd': '12345678'}
tosca = ToscaTemplate(tosca_tpl, parsed_params=params)
self.assertTrue(tosca.topology_template.custom_defs) self.assertTrue(tosca.topology_template.custom_defs)
def test_local_template_with_url_import(self): def test_local_template_with_url_import(self):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress_with_url_import.yaml") "data/tosca_single_instance_wordpress_with_url_import.yaml")
tosca = ToscaTemplate(tosca_tpl) tosca = ToscaTemplate(tosca_tpl,
parsed_params={'db_root_pwd': '123456'})
self.assertTrue(tosca.topology_template.custom_defs) self.assertTrue(tosca.topology_template.custom_defs)
def test_url_template_with_local_relpath_import(self): def test_url_template_with_local_relpath_import(self):
tosca_tpl = ('https://raw.githubusercontent.com/openstack/' tosca_tpl = ('https://raw.githubusercontent.com/openstack/'
'tosca-parser/master/toscaparser/tests/data/' 'tosca-parser/master/toscaparser/tests/data/'
'tosca_single_instance_wordpress.yaml') 'tosca_single_instance_wordpress.yaml')
tosca = ToscaTemplate(tosca_tpl, None, False) tosca = ToscaTemplate(tosca_tpl, a_file=False,
parsed_params={"db_name": "mysql",
"db_user": "mysql",
"db_root_pwd": "1234",
"db_pwd": "5678",
"db_port": 3306,
"cpus": 4})
self.assertTrue(tosca.topology_template.custom_defs) self.assertTrue(tosca.topology_template.custom_defs)
def test_url_template_with_local_abspath_import(self): def test_url_template_with_local_abspath_import(self):
@ -472,19 +483,27 @@ class ToscaTemplateTest(TestCase):
tosca_tpl = ('https://raw.githubusercontent.com/openstack/' tosca_tpl = ('https://raw.githubusercontent.com/openstack/'
'tosca-parser/master/toscaparser/tests/data/' 'tosca-parser/master/toscaparser/tests/data/'
'tosca_single_instance_wordpress_with_url_import.yaml') 'tosca_single_instance_wordpress_with_url_import.yaml')
tosca = ToscaTemplate(tosca_tpl, None, False) tosca = ToscaTemplate(tosca_tpl, a_file=False,
parsed_params={"db_root_pwd": "1234"})
self.assertTrue(tosca.topology_template.custom_defs) self.assertTrue(tosca.topology_template.custom_defs)
def test_csar_parsing_wordpress(self): def test_csar_parsing_wordpress(self):
csar_archive = os.path.join( csar_archive = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
'data/CSAR/csar_wordpress.zip') 'data/CSAR/csar_wordpress.zip')
self.assertTrue(ToscaTemplate(csar_archive)) self.assertTrue(ToscaTemplate(csar_archive,
parsed_params={"db_name": "mysql",
"db_user": "mysql",
"db_root_pwd": "1234",
"db_pwd": "5678",
"db_port": 3306,
"cpus": 4}))
def test_csar_parsing_elk_url_based(self): def test_csar_parsing_elk_url_based(self):
csar_archive = ('https://github.com/openstack/tosca-parser/raw/master/' csar_archive = ('https://github.com/openstack/tosca-parser/raw/master/'
'toscaparser/tests/data/CSAR/csar_elk.zip') 'toscaparser/tests/data/CSAR/csar_elk.zip')
self.assertTrue(ToscaTemplate(csar_archive, None, False)) self.assertTrue(ToscaTemplate(csar_archive, a_file=False,
parsed_params={"my_cpus": 4}))
def test_nested_imports_in_templates(self): def test_nested_imports_in_templates(self):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
@ -592,7 +611,7 @@ class ToscaTemplateTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/CSAR/csar_elk.csar") "data/CSAR/csar_elk.csar")
tosca = ToscaTemplate(tosca_tpl) tosca = ToscaTemplate(tosca_tpl, parsed_params={"my_cpus": 2})
self.assertTrue(tosca.topology_template.custom_defs) self.assertTrue(tosca.topology_template.custom_defs)
def test_available_rel_tpls(self): def test_available_rel_tpls(self):
@ -658,9 +677,10 @@ class ToscaTemplateTest(TestCase):
"data/tosca_single_instance_wordpress.yaml") "data/tosca_single_instance_wordpress.yaml")
yaml_dict_tpl = toscaparser.utils.yamlparser.load_yaml(test_tpl) yaml_dict_tpl = toscaparser.utils.yamlparser.load_yaml(test_tpl)
params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user',
'db_root_pwd': '12345678'}
self.assertRaises(exception.ValidationError, ToscaTemplate, None, self.assertRaises(exception.ValidationError, ToscaTemplate, None,
None, False, yaml_dict_tpl) params, False, yaml_dict_tpl)
err_msg = (_('Relative file name "custom_types/wordpress.yaml" ' err_msg = (_('Relative file name "custom_types/wordpress.yaml" '
'cannot be used in a pre-parsed input template.')) 'cannot be used in a pre-parsed input template.'))
exception.ExceptionCollector.assertExceptionMessage(ImportError, exception.ExceptionCollector.assertExceptionMessage(ImportError,
@ -805,7 +825,7 @@ class ToscaTemplateTest(TestCase):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/test_containers.yaml") "data/test_containers.yaml")
ToscaTemplate(tosca_tpl) ToscaTemplate(tosca_tpl, parsed_params={"mysql_root_pwd": "12345678"})
def test_endpoint_on_compute(self): def test_endpoint_on_compute(self):
tosca_tpl = os.path.join( tosca_tpl = os.path.join(

View File

@ -35,7 +35,9 @@ class ToscaTemplateValidationTest(TestCase):
tpl_path = os.path.join( tpl_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"data/tosca_single_instance_wordpress.yaml") "data/tosca_single_instance_wordpress.yaml")
self.assertIsNotNone(ToscaTemplate(tpl_path)) params = {'db_name': 'my_wordpress', 'db_user': 'my_db_user',
'db_root_pwd': '12345678'}
self.assertIsNotNone(ToscaTemplate(tpl_path, params))
def test_first_level_sections(self): def test_first_level_sections(self):
tpl_path = os.path.join( tpl_path = os.path.join(
@ -120,7 +122,7 @@ class ToscaTemplateValidationTest(TestCase):
self.assertEqual(expectedmessage, err.__str__()) self.assertEqual(expectedmessage, err.__str__())
def test_inputs(self): def test_inputs(self):
tpl_snippet = ''' tpl_snippet1 = '''
inputs: inputs:
cpus: cpus:
type: integer type: integer
@ -130,14 +132,33 @@ class ToscaTemplateValidationTest(TestCase):
required: yes required: yes
status: supported status: supported
''' '''
inputs = (toscaparser.utils.yamlparser. tpl_snippet2 = '''
simple_parse(tpl_snippet)['inputs']) inputs:
name, attrs = list(inputs.items())[0] cpus:
input = Input(name, attrs) type: integer
err = self.assertRaises(exception.UnknownFieldError, input.validate) description: Number of CPUs for the server.
self.assertEqual(_('Input "cpus" contains unknown field "constraint". ' constraints:
'Refer to the definition to verify valid values.'), - valid_values: [ 1, 2, 4 ]
required: yes
status: supported
'''
inputs1 = (toscaparser.utils.yamlparser.
simple_parse(tpl_snippet1)['inputs'])
name1, attrs1 = list(inputs1.items())[0]
inputs2 = (toscaparser.utils.yamlparser.
simple_parse(tpl_snippet2)['inputs'])
name2, attrs2 = list(inputs2.items())[0]
try:
Input(name1, attrs1)
except Exception as err:
# err=self.assertRaises(exception.UnknownFieldError,
# input1.validate)
self.assertEqual(_('Input "cpus" contains unknown field '
'"constraint". Refer to the definition to '
'verify valid values.'),
err.__str__()) err.__str__())
input2 = Input(name2, attrs2)
self.assertTrue(input2.required)
def _imports_content_test(self, tpl_snippet, path, custom_type_def): def _imports_content_test(self, tpl_snippet, path, custom_type_def):
imports = (toscaparser.utils.yamlparser. imports = (toscaparser.utils.yamlparser.

View File

@ -73,6 +73,14 @@ class TopologyTemplate(object):
default = input.default default = input.default
if default: if default:
input.validate(default) input.validate(default)
if (self.parsed_params and input.name not in self.parsed_params
or self.parsed_params is None) and input.required \
and input.default is None:
exception.ExceptionCollector.appendException(
exception.MissingRequiredParameterError(
what='Template',
input_name=input.name))
inputs.append(input) inputs.append(input)
return inputs return inputs