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
|
||||
|
||||
|
||||
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:
|
||||
|
||||
-----------------
|
||||
|
|
|
@ -22,10 +22,13 @@ from heat.openstack.common import log as logging
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PARAM_CONSTRAINTS = (CONSTRAINTS, DESCRIPTION, LENGTH, RANGE,
|
||||
MIN, MAX, ALLOWED_VALUES, ALLOWED_PATTERN) = \
|
||||
('constraints', 'description', 'length', 'range',
|
||||
'min', 'max', 'allowed_values', 'allowed_pattern')
|
||||
PARAM_CONSTRAINTS = (
|
||||
CONSTRAINTS, DESCRIPTION, LENGTH, RANGE, MIN, MAX,
|
||||
ALLOWED_VALUES, ALLOWED_PATTERN, CUSTOM_CONSTRAINT,
|
||||
) = (
|
||||
'constraints', 'description', 'length', 'range', 'min', 'max',
|
||||
'allowed_values', 'allowed_pattern', 'custom_constraint',
|
||||
)
|
||||
|
||||
|
||||
def snake_to_camel(name):
|
||||
|
@ -319,9 +322,10 @@ class HOTemplate(template.Template):
|
|||
return dict((name, HOTParamSchema.from_dict(schema))
|
||||
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,
|
||||
validate_value=validate_value)
|
||||
validate_value=validate_value, context=context)
|
||||
|
||||
|
||||
class HOTParamSchema(parameters.Schema):
|
||||
|
@ -372,6 +376,9 @@ class HOTParamSchema(parameters.Schema):
|
|||
if ALLOWED_PATTERN in constraint:
|
||||
cdef = constraint.get(ALLOWED_PATTERN)
|
||||
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
|
||||
# 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,
|
||||
constraints=[], hidden=False):
|
||||
constraints=[], hidden=False, context=None):
|
||||
super(Schema, self).__init__(data_type=data_type,
|
||||
description=description,
|
||||
default=default,
|
||||
|
@ -59,6 +59,7 @@ class Schema(constr.Schema):
|
|||
required=default is None,
|
||||
constraints=constraints)
|
||||
self.hidden = hidden
|
||||
self.context = context
|
||||
|
||||
# Schema class validates default value for lists assuming list type. For
|
||||
# comma delimited list string supported in paramaters Schema class, the
|
||||
|
@ -125,7 +126,7 @@ class Schema(constr.Schema):
|
|||
'false')).lower() == 'true')
|
||||
|
||||
def validate(self, name, value):
|
||||
super(Schema, self).validate_constraints(value)
|
||||
super(Schema, self).validate_constraints(value, self.context)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == self.TYPE:
|
||||
|
@ -333,13 +334,14 @@ class Parameters(collections.Mapping):
|
|||
)
|
||||
|
||||
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
|
||||
template, optionally setting the user-supplied parameter values.
|
||||
'''
|
||||
def user_parameter(schema_item):
|
||||
name, schema = schema_item
|
||||
schema.context = context
|
||||
return Parameter(name, schema,
|
||||
user_params.get(name),
|
||||
validate_value)
|
||||
|
|
|
@ -103,7 +103,8 @@ class Stack(collections.Mapping):
|
|||
|
||||
self.env = env or environment.Environment({})
|
||||
self.parameters = self.t.parameters(self.identifier(),
|
||||
user_params=self.env.params)
|
||||
user_params=self.env.params,
|
||||
context=context)
|
||||
|
||||
self._set_param_stackid()
|
||||
|
||||
|
|
|
@ -493,10 +493,12 @@ class Template(collections.Mapping):
|
|||
return dict((name, parameters.Schema.from_dict(schema))
|
||||
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,
|
||||
user_params=user_params,
|
||||
validate_value=validate_value)
|
||||
validate_value=validate_value,
|
||||
context=context)
|
||||
|
||||
|
||||
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 resource
|
||||
from heat.engine import hot
|
||||
from heat.engine import resources
|
||||
from heat.engine import template
|
||||
from heat.engine import constraints
|
||||
|
||||
|
@ -764,6 +765,41 @@ class HOTParamValidatorTest(HeatTestCase):
|
|||
value = 50000
|
||||
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):
|
||||
range_desc = 'Value must be between 30000 and 50000'
|
||||
param = {
|
||||
|
|
Loading…
Reference in New Issue