Enable usage of custom constraint in parameters
Add a new custom_constraint keyword type in HOT parameters to allow validation of custom parameters. blueprint param-constraints Co-Authored-By: cedric.soulas@cloudwatt.com Change-Id: Ic52475ea8a39541f7b1643038a2bcba15f0d7471
This commit is contained in:
parent
7100cfa147
commit
84c93280ed
|
@ -350,6 +350,35 @@ For example:
|
||||||
description: User name must start with an uppercase character
|
description: User name must start with an uppercase character
|
||||||
|
|
||||||
|
|
||||||
|
custom_constraint
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
The *custom_constraint* constraint adds an extra step of validation, generally
|
||||||
|
to check that the specified resource exists in the backend. Custom constraints
|
||||||
|
get implemented by plug-ins and can provide any kind of advanced constraint
|
||||||
|
validation logic.
|
||||||
|
|
||||||
|
The syntax of the custom_constraint constraint is:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
custom_constraint: <name>
|
||||||
|
|
||||||
|
The *name* specifies the concrete type of custom constraint. It corresponds to
|
||||||
|
the name under which the respective validation plugin has been registered with
|
||||||
|
the Heat engine.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
key_name
|
||||||
|
type: string
|
||||||
|
description: SSH key pair
|
||||||
|
constraints:
|
||||||
|
- custom_constraint: nova.keypair
|
||||||
|
|
||||||
|
|
||||||
.. _hot_spec_resources:
|
.. _hot_spec_resources:
|
||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
@ -22,10 +22,13 @@ from heat.openstack.common import log as logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
PARAM_CONSTRAINTS = (CONSTRAINTS, DESCRIPTION, LENGTH, RANGE,
|
PARAM_CONSTRAINTS = (
|
||||||
MIN, MAX, ALLOWED_VALUES, ALLOWED_PATTERN) = \
|
CONSTRAINTS, DESCRIPTION, LENGTH, RANGE, MIN, MAX,
|
||||||
('constraints', 'description', 'length', 'range',
|
ALLOWED_VALUES, ALLOWED_PATTERN, CUSTOM_CONSTRAINT,
|
||||||
'min', 'max', 'allowed_values', 'allowed_pattern')
|
) = (
|
||||||
|
'constraints', 'description', 'length', 'range', 'min', 'max',
|
||||||
|
'allowed_values', 'allowed_pattern', 'custom_constraint',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def snake_to_camel(name):
|
def snake_to_camel(name):
|
||||||
|
@ -319,9 +322,10 @@ class HOTemplate(template.Template):
|
||||||
return dict((name, HOTParamSchema.from_dict(schema))
|
return dict((name, HOTParamSchema.from_dict(schema))
|
||||||
for name, schema in params)
|
for name, schema in params)
|
||||||
|
|
||||||
def parameters(self, stack_identifier, user_params, validate_value=True):
|
def parameters(self, stack_identifier, user_params, validate_value=True,
|
||||||
|
context=None):
|
||||||
return HOTParameters(stack_identifier, self, user_params=user_params,
|
return HOTParameters(stack_identifier, self, user_params=user_params,
|
||||||
validate_value=validate_value)
|
validate_value=validate_value, context=context)
|
||||||
|
|
||||||
|
|
||||||
class HOTParamSchema(parameters.Schema):
|
class HOTParamSchema(parameters.Schema):
|
||||||
|
@ -372,6 +376,9 @@ class HOTParamSchema(parameters.Schema):
|
||||||
if ALLOWED_PATTERN in constraint:
|
if ALLOWED_PATTERN in constraint:
|
||||||
cdef = constraint.get(ALLOWED_PATTERN)
|
cdef = constraint.get(ALLOWED_PATTERN)
|
||||||
yield constr.AllowedPattern(cdef, desc)
|
yield constr.AllowedPattern(cdef, desc)
|
||||||
|
if CUSTOM_CONSTRAINT in constraint:
|
||||||
|
cdef = constraint.get(CUSTOM_CONSTRAINT)
|
||||||
|
yield constr.CustomConstraint(cdef, desc)
|
||||||
|
|
||||||
# make update_allowed true by default on TemplateResources
|
# make update_allowed true by default on TemplateResources
|
||||||
# as the template should deal with this.
|
# as the template should deal with this.
|
||||||
|
|
|
@ -51,7 +51,7 @@ class Schema(constr.Schema):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, data_type, description=None, default=None, schema=None,
|
def __init__(self, data_type, description=None, default=None, schema=None,
|
||||||
constraints=[], hidden=False):
|
constraints=[], hidden=False, context=None):
|
||||||
super(Schema, self).__init__(data_type=data_type,
|
super(Schema, self).__init__(data_type=data_type,
|
||||||
description=description,
|
description=description,
|
||||||
default=default,
|
default=default,
|
||||||
|
@ -59,6 +59,7 @@ class Schema(constr.Schema):
|
||||||
required=default is None,
|
required=default is None,
|
||||||
constraints=constraints)
|
constraints=constraints)
|
||||||
self.hidden = hidden
|
self.hidden = hidden
|
||||||
|
self.context = context
|
||||||
|
|
||||||
# Schema class validates default value for lists assuming list type. For
|
# Schema class validates default value for lists assuming list type. For
|
||||||
# comma delimited list string supported in paramaters Schema class, the
|
# comma delimited list string supported in paramaters Schema class, the
|
||||||
|
@ -125,7 +126,7 @@ class Schema(constr.Schema):
|
||||||
'false')).lower() == 'true')
|
'false')).lower() == 'true')
|
||||||
|
|
||||||
def validate(self, name, value):
|
def validate(self, name, value):
|
||||||
super(Schema, self).validate_constraints(value)
|
super(Schema, self).validate_constraints(value, self.context)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if key == self.TYPE:
|
if key == self.TYPE:
|
||||||
|
@ -333,13 +334,14 @@ class Parameters(collections.Mapping):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, stack_identifier, tmpl, user_params={},
|
def __init__(self, stack_identifier, tmpl, user_params={},
|
||||||
validate_value=True):
|
validate_value=True, context=None):
|
||||||
'''
|
'''
|
||||||
Create the parameter container for a stack from the stack name and
|
Create the parameter container for a stack from the stack name and
|
||||||
template, optionally setting the user-supplied parameter values.
|
template, optionally setting the user-supplied parameter values.
|
||||||
'''
|
'''
|
||||||
def user_parameter(schema_item):
|
def user_parameter(schema_item):
|
||||||
name, schema = schema_item
|
name, schema = schema_item
|
||||||
|
schema.context = context
|
||||||
return Parameter(name, schema,
|
return Parameter(name, schema,
|
||||||
user_params.get(name),
|
user_params.get(name),
|
||||||
validate_value)
|
validate_value)
|
||||||
|
|
|
@ -103,7 +103,8 @@ class Stack(collections.Mapping):
|
||||||
|
|
||||||
self.env = env or environment.Environment({})
|
self.env = env or environment.Environment({})
|
||||||
self.parameters = self.t.parameters(self.identifier(),
|
self.parameters = self.t.parameters(self.identifier(),
|
||||||
user_params=self.env.params)
|
user_params=self.env.params,
|
||||||
|
context=context)
|
||||||
|
|
||||||
self._set_param_stackid()
|
self._set_param_stackid()
|
||||||
|
|
||||||
|
|
|
@ -493,10 +493,12 @@ class Template(collections.Mapping):
|
||||||
return dict((name, parameters.Schema.from_dict(schema))
|
return dict((name, parameters.Schema.from_dict(schema))
|
||||||
for name, schema in params)
|
for name, schema in params)
|
||||||
|
|
||||||
def parameters(self, stack_identifier, user_params, validate_value=True):
|
def parameters(self, stack_identifier, user_params, validate_value=True,
|
||||||
|
context=None):
|
||||||
return parameters.Parameters(stack_identifier, self,
|
return parameters.Parameters(stack_identifier, self,
|
||||||
user_params=user_params,
|
user_params=user_params,
|
||||||
validate_value=validate_value)
|
validate_value=validate_value,
|
||||||
|
context=context)
|
||||||
|
|
||||||
|
|
||||||
def _resolve(match, handle, snippet, transform=None):
|
def _resolve(match, handle, snippet, transform=None):
|
||||||
|
|
|
@ -17,6 +17,7 @@ from heat.common import identifier
|
||||||
from heat.engine import parser
|
from heat.engine import parser
|
||||||
from heat.engine import resource
|
from heat.engine import resource
|
||||||
from heat.engine import hot
|
from heat.engine import hot
|
||||||
|
from heat.engine import resources
|
||||||
from heat.engine import template
|
from heat.engine import template
|
||||||
from heat.engine import constraints
|
from heat.engine import constraints
|
||||||
|
|
||||||
|
@ -764,6 +765,41 @@ class HOTParamValidatorTest(HeatTestCase):
|
||||||
value = 50000
|
value = 50000
|
||||||
self.assertTrue(v(value))
|
self.assertTrue(v(value))
|
||||||
|
|
||||||
|
def test_custom_constraint(self):
|
||||||
|
class ZeroConstraint(object):
|
||||||
|
def validate(self, value, context):
|
||||||
|
return value == "0"
|
||||||
|
|
||||||
|
env = resources.global_env()
|
||||||
|
env.register_constraint("zero", ZeroConstraint)
|
||||||
|
self.addCleanup(env.constraints.pop, "zero")
|
||||||
|
|
||||||
|
desc = 'Value must be zero'
|
||||||
|
param = {
|
||||||
|
'param1': {
|
||||||
|
'type': 'string',
|
||||||
|
'constraints': [
|
||||||
|
{'custom_constraint': 'zero',
|
||||||
|
'description': desc}]}}
|
||||||
|
|
||||||
|
name = 'param1'
|
||||||
|
schema = param['param1']
|
||||||
|
|
||||||
|
def v(value):
|
||||||
|
hot.HOTParamSchema.from_dict(schema).validate(name, value)
|
||||||
|
return True
|
||||||
|
|
||||||
|
value = "1"
|
||||||
|
err = self.assertRaises(ValueError, v, value)
|
||||||
|
self.assertEqual(desc, str(err))
|
||||||
|
|
||||||
|
value = "2"
|
||||||
|
err = self.assertRaises(ValueError, v, value)
|
||||||
|
self.assertEqual(desc, str(err))
|
||||||
|
|
||||||
|
value = "0"
|
||||||
|
self.assertTrue(v(value))
|
||||||
|
|
||||||
def test_range_constraint_invalid_default(self):
|
def test_range_constraint_invalid_default(self):
|
||||||
range_desc = 'Value must be between 30000 and 50000'
|
range_desc = 'Value must be between 30000 and 50000'
|
||||||
param = {
|
param = {
|
||||||
|
|
Loading…
Reference in New Issue