Merge "Add node attributes validator"
This commit is contained in:
commit
4f04cbade3
@ -28,7 +28,7 @@ from nailgun.api.v1.handlers.base import DeferredTaskHandler
|
|||||||
from nailgun.api.v1.handlers.base import DeploymentTasksHandler
|
from nailgun.api.v1.handlers.base import DeploymentTasksHandler
|
||||||
from nailgun.api.v1.handlers.base import SingleHandler
|
from nailgun.api.v1.handlers.base import SingleHandler
|
||||||
|
|
||||||
from nailgun.api.v1.validators.cluster import AttributesValidator
|
from nailgun.api.v1.validators.cluster import ClusterAttributesValidator
|
||||||
from nailgun.api.v1.validators.cluster import ClusterChangesValidator
|
from nailgun.api.v1.validators.cluster import ClusterChangesValidator
|
||||||
from nailgun.api.v1.validators.cluster import ClusterStopDeploymentValidator
|
from nailgun.api.v1.validators.cluster import ClusterStopDeploymentValidator
|
||||||
from nailgun.api.v1.validators.cluster import ClusterValidator
|
from nailgun.api.v1.validators.cluster import ClusterValidator
|
||||||
@ -121,7 +121,7 @@ class ClusterAttributesHandler(BaseHandler):
|
|||||||
"editable",
|
"editable",
|
||||||
)
|
)
|
||||||
|
|
||||||
validator = AttributesValidator
|
validator = ClusterAttributesValidator
|
||||||
|
|
||||||
@content
|
@content
|
||||||
def GET(self, cluster_id):
|
def GET(self, cluster_id):
|
||||||
|
@ -27,7 +27,7 @@ from nailgun.api.v1.handlers.base import CollectionHandler
|
|||||||
from nailgun.api.v1.handlers.base import content
|
from nailgun.api.v1.handlers.base import content
|
||||||
from nailgun.api.v1.handlers.base import SingleHandler
|
from nailgun.api.v1.handlers.base import SingleHandler
|
||||||
from nailgun.api.v1.validators.network import NetAssignmentValidator
|
from nailgun.api.v1.validators.network import NetAssignmentValidator
|
||||||
from nailgun.api.v1.validators.node import NodeValidator
|
from nailgun.api.v1.validators import node as node_validators
|
||||||
|
|
||||||
from nailgun import consts
|
from nailgun import consts
|
||||||
from nailgun.errors import errors
|
from nailgun.errors import errors
|
||||||
@ -48,7 +48,7 @@ from nailgun import notifier
|
|||||||
|
|
||||||
class NodeHandler(SingleHandler):
|
class NodeHandler(SingleHandler):
|
||||||
single = objects.Node
|
single = objects.Node
|
||||||
validator = NodeValidator
|
validator = node_validators.NodeValidator
|
||||||
|
|
||||||
@content
|
@content
|
||||||
def DELETE(self, obj_id):
|
def DELETE(self, obj_id):
|
||||||
@ -76,7 +76,7 @@ class NodeHandler(SingleHandler):
|
|||||||
class NodeCollectionHandler(CollectionHandler):
|
class NodeCollectionHandler(CollectionHandler):
|
||||||
"""Node collection handler"""
|
"""Node collection handler"""
|
||||||
|
|
||||||
validator = NodeValidator
|
validator = node_validators.NodeValidator
|
||||||
collection = objects.NodeCollection
|
collection = objects.NodeCollection
|
||||||
|
|
||||||
@content
|
@content
|
||||||
@ -159,7 +159,7 @@ class NodeCollectionHandler(CollectionHandler):
|
|||||||
class NodeAgentHandler(BaseHandler):
|
class NodeAgentHandler(BaseHandler):
|
||||||
|
|
||||||
collection = objects.NodeCollection
|
collection = objects.NodeCollection
|
||||||
validator = NodeValidator
|
validator = node_validators.NodeValidator
|
||||||
|
|
||||||
@content
|
@content
|
||||||
def PUT(self):
|
def PUT(self):
|
||||||
@ -328,7 +328,7 @@ class NodesAllocationStatsHandler(BaseHandler):
|
|||||||
class NodeAttributesHandler(BaseHandler):
|
class NodeAttributesHandler(BaseHandler):
|
||||||
"""Node attributes handler"""
|
"""Node attributes handler"""
|
||||||
|
|
||||||
# TODO(asvechnikov): Add validator
|
validator = node_validators.NodeAttributesValidator
|
||||||
|
|
||||||
@content
|
@content
|
||||||
def GET(self, node_id):
|
def GET(self, node_id):
|
||||||
|
@ -13,14 +13,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
from jsonschema.exceptions import ValidationError
|
from jsonschema import exceptions
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
from nailgun.api.v1.validators.json_schema import base_types
|
||||||
|
|
||||||
from nailgun.errors import errors
|
from nailgun.errors import errors
|
||||||
from nailgun import objects
|
from nailgun import objects
|
||||||
|
from nailgun.utils import restrictions
|
||||||
|
|
||||||
|
|
||||||
class BasicValidator(object):
|
class BasicValidator(object):
|
||||||
@ -60,7 +63,7 @@ class BasicValidator(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
jsonschema.validate(json_req, use_schema)
|
jsonschema.validate(json_req, use_schema)
|
||||||
except ValidationError as exc:
|
except exceptions.ValidationError as exc:
|
||||||
if len(exc.path) > 0:
|
if len(exc.path) > 0:
|
||||||
raise errors.InvalidData(
|
raise errors.InvalidData(
|
||||||
# NOTE(ikutukov): here was a exc.path.pop(). It was buggy
|
# NOTE(ikutukov): here was a exc.path.pop(). It was buggy
|
||||||
@ -121,3 +124,80 @@ class BaseDefferedTaskValidator(BasicValidator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, cluster):
|
def validate(cls, cluster):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BasicAttributesValidator(BasicValidator):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, data):
|
||||||
|
attrs = cls.validate_json(data)
|
||||||
|
|
||||||
|
cls.validate_attributes(attrs)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_attributes(cls, data):
|
||||||
|
"""Validate attributes."""
|
||||||
|
for attrs in six.itervalues(data):
|
||||||
|
if not isinstance(attrs, dict):
|
||||||
|
continue
|
||||||
|
for attr_name, attr in six.iteritems(attrs):
|
||||||
|
cls.validate_attribute(attr_name, attr)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_attribute(cls, attr_name, attr):
|
||||||
|
"""Validates a single attribute from settings.yaml.
|
||||||
|
|
||||||
|
Dict is of this form::
|
||||||
|
|
||||||
|
description: <description>
|
||||||
|
label: <label>
|
||||||
|
restrictions:
|
||||||
|
- <restriction>
|
||||||
|
- <restriction>
|
||||||
|
- ...
|
||||||
|
type: <type>
|
||||||
|
value: <value>
|
||||||
|
weight: <weight>
|
||||||
|
regex:
|
||||||
|
error: <error message>
|
||||||
|
source: <regexp source>
|
||||||
|
|
||||||
|
We validate that 'value' corresponds to 'type' according to
|
||||||
|
attribute_type_schemas mapping in json_schema/cluster.py.
|
||||||
|
If regex is present, we additionally check that the provided string
|
||||||
|
value matches the regexp.
|
||||||
|
|
||||||
|
:param attr_name: Name of the attribute being checked
|
||||||
|
:param attr: attribute value
|
||||||
|
:return: attribute or raise InvalidData exception
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(attr, dict):
|
||||||
|
return attr
|
||||||
|
|
||||||
|
if 'type' not in attr and 'value' not in attr:
|
||||||
|
return attr
|
||||||
|
|
||||||
|
schema = copy.deepcopy(base_types.ATTRIBUTE_SCHEMA)
|
||||||
|
type_ = attr.get('type')
|
||||||
|
if type_:
|
||||||
|
value_schema = base_types.ATTRIBUTE_TYPE_SCHEMAS.get(type_)
|
||||||
|
if value_schema:
|
||||||
|
schema['properties'].update(value_schema)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cls.validate_schema(attr, schema)
|
||||||
|
except errors.InvalidData as e:
|
||||||
|
raise errors.InvalidData('[{0}] {1}'.format(attr_name, e.message))
|
||||||
|
|
||||||
|
# Validate regexp only if some value is present
|
||||||
|
# Otherwise regexp might be invalid
|
||||||
|
if attr['value']:
|
||||||
|
regex_err = restrictions.AttributesRestriction.validate_regex(attr)
|
||||||
|
if regex_err is not None:
|
||||||
|
raise errors.InvalidData(
|
||||||
|
'[{0}] {1}'.format(attr_name, regex_err))
|
||||||
|
@ -13,28 +13,24 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
from distutils import version
|
||||||
from distutils.version import StrictVersion
|
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from nailgun.api.v1.validators.base import BaseDefferedTaskValidator
|
from nailgun.api.v1.validators import base
|
||||||
from nailgun.api.v1.validators.base import BasicValidator
|
|
||||||
from nailgun.api.v1.validators.json_schema import cluster as cluster_schema
|
from nailgun.api.v1.validators.json_schema import cluster as cluster_schema
|
||||||
from nailgun.api.v1.validators.node import ProvisionSelectedNodesValidator
|
from nailgun.api.v1.validators.node import ProvisionSelectedNodesValidator
|
||||||
|
|
||||||
from nailgun import consts
|
from nailgun import consts
|
||||||
from nailgun.db import db
|
from nailgun.db import db
|
||||||
from nailgun.db.sqlalchemy.models import Node
|
from nailgun.db.sqlalchemy.models import Node
|
||||||
from nailgun.errors import errors
|
from nailgun.errors import errors
|
||||||
from nailgun import objects
|
from nailgun import objects
|
||||||
from nailgun.plugins.manager import PluginManager
|
from nailgun.plugins.manager import PluginManager
|
||||||
from nailgun.utils import restrictions
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterValidator(BasicValidator):
|
class ClusterValidator(base.BasicValidator):
|
||||||
|
|
||||||
single_schema = cluster_schema.single_schema
|
single_schema = cluster_schema.single_schema
|
||||||
collection_schema = cluster_schema.collection_schema
|
collection_schema = cluster_schema.collection_schema
|
||||||
@ -219,7 +215,7 @@ class ClusterValidator(BasicValidator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AttributesValidator(BasicValidator):
|
class ClusterAttributesValidator(base.BasicAttributesValidator):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, data, cluster=None, force=False):
|
def validate(cls, data, cluster=None, force=False):
|
||||||
@ -240,15 +236,15 @@ class AttributesValidator(BasicValidator):
|
|||||||
attrs = objects.Cluster.get_updated_editable_attributes(cluster, d)
|
attrs = objects.Cluster.get_updated_editable_attributes(cluster, d)
|
||||||
cls.validate_provision(cluster, attrs)
|
cls.validate_provision(cluster, attrs)
|
||||||
cls.validate_allowed_attributes(cluster, d, force)
|
cls.validate_allowed_attributes(cluster, d, force)
|
||||||
cls.validate_editable_attributes(attrs)
|
cls.validate_attributes(attrs.get('editable', {}))
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_provision(cls, cluster, attrs):
|
def validate_provision(cls, cluster, attrs):
|
||||||
# NOTE(agordeev): disable classic provisioning for 7.0 or higher
|
# NOTE(agordeev): disable classic provisioning for 7.0 or higher
|
||||||
if StrictVersion(cluster.release.environment_version) >= \
|
if version.StrictVersion(cluster.release.environment_version) >= \
|
||||||
StrictVersion(consts.FUEL_IMAGE_BASED_ONLY):
|
version.StrictVersion(consts.FUEL_IMAGE_BASED_ONLY):
|
||||||
provision_data = attrs['editable'].get('provision')
|
provision_data = attrs['editable'].get('provision')
|
||||||
if provision_data:
|
if provision_data:
|
||||||
if provision_data['method']['value'] != \
|
if provision_data['method']['value'] != \
|
||||||
@ -262,72 +258,6 @@ class AttributesValidator(BasicValidator):
|
|||||||
u"Provisioning method is not set. Unable to continue",
|
u"Provisioning method is not set. Unable to continue",
|
||||||
log_message=True)
|
log_message=True)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate_editable_attributes(cls, data):
|
|
||||||
"""Validate 'editable' attributes."""
|
|
||||||
for attrs in data.get('editable', {}).values():
|
|
||||||
if not isinstance(attrs, dict):
|
|
||||||
continue
|
|
||||||
for attr_name, attr in six.iteritems(attrs):
|
|
||||||
cls.validate_attribute(attr_name, attr)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate_attribute(cls, attr_name, attr):
|
|
||||||
"""Validates a single attribute from settings.yaml.
|
|
||||||
|
|
||||||
Dict is of this form:
|
|
||||||
|
|
||||||
description: <description>
|
|
||||||
label: <label>
|
|
||||||
restrictions:
|
|
||||||
- <restriction>
|
|
||||||
- <restriction>
|
|
||||||
- ...
|
|
||||||
type: <type>
|
|
||||||
value: <value>
|
|
||||||
weight: <weight>
|
|
||||||
regex:
|
|
||||||
error: <error message>
|
|
||||||
source: <regexp source>
|
|
||||||
|
|
||||||
We validate that 'value' corresponds to 'type' according to
|
|
||||||
attribute_type_schemas mapping in json_schema/cluster.py.
|
|
||||||
If regex is present, we additionally check that the provided string
|
|
||||||
value matches the regexp.
|
|
||||||
|
|
||||||
:param attr_name: Name of the attribute being checked
|
|
||||||
:param attr: attribute value
|
|
||||||
:return: attribute or raise InvalidData exception
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not isinstance(attr, dict):
|
|
||||||
return attr
|
|
||||||
|
|
||||||
if 'type' not in attr and 'value' not in attr:
|
|
||||||
return attr
|
|
||||||
|
|
||||||
schema = copy.deepcopy(cluster_schema.attribute_schema)
|
|
||||||
type_ = attr.get('type')
|
|
||||||
if type_:
|
|
||||||
value_schema = cluster_schema.attribute_type_schemas.get(type_)
|
|
||||||
if value_schema:
|
|
||||||
schema['properties'].update(value_schema)
|
|
||||||
|
|
||||||
try:
|
|
||||||
cls.validate_schema(attr, schema)
|
|
||||||
except errors.InvalidData as e:
|
|
||||||
raise errors.InvalidData('[{0}] {1}'.format(attr_name, e.message))
|
|
||||||
|
|
||||||
# Validate regexp only if some value is present
|
|
||||||
# Otherwise regexp might be invalid
|
|
||||||
if attr['value']:
|
|
||||||
regex_err = restrictions.AttributesRestriction.validate_regex(attr)
|
|
||||||
if regex_err is not None:
|
|
||||||
raise errors.InvalidData(
|
|
||||||
'[{0}] {1}'.format(attr_name, regex_err))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_allowed_attributes(cls, cluster, data, force):
|
def validate_allowed_attributes(cls, cluster, data, force):
|
||||||
"""Validates if attributes are hot pluggable or not.
|
"""Validates if attributes are hot pluggable or not.
|
||||||
@ -391,7 +321,7 @@ class AttributesValidator(BasicValidator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterChangesValidator(BaseDefferedTaskValidator):
|
class ClusterChangesValidator(base.BaseDefferedTaskValidator):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, cluster):
|
def validate(cls, cluster):
|
||||||
@ -399,7 +329,7 @@ class ClusterChangesValidator(BaseDefferedTaskValidator):
|
|||||||
ProvisionSelectedNodesValidator.validate_provision(None, cluster)
|
ProvisionSelectedNodesValidator.validate_provision(None, cluster)
|
||||||
|
|
||||||
|
|
||||||
class ClusterStopDeploymentValidator(BaseDefferedTaskValidator):
|
class ClusterStopDeploymentValidator(base.BaseDefferedTaskValidator):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, cluster):
|
def validate(cls, cluster):
|
||||||
@ -412,7 +342,7 @@ class ClusterStopDeploymentValidator(BaseDefferedTaskValidator):
|
|||||||
raise errors.CannotBeStopped()
|
raise errors.CannotBeStopped()
|
||||||
|
|
||||||
|
|
||||||
class VmwareAttributesValidator(BasicValidator):
|
class VmwareAttributesValidator(base.BasicValidator):
|
||||||
|
|
||||||
single_schema = cluster_schema.vmware_attributes_schema
|
single_schema = cluster_schema.vmware_attributes_schema
|
||||||
|
|
||||||
|
@ -217,3 +217,107 @@ UI_SETTINGS = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ATTRIBUTE_SCHEMA = {
|
||||||
|
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title': 'Schema for single editable attribute',
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'type': {
|
||||||
|
'enum': [
|
||||||
|
'checkbox',
|
||||||
|
'custom_repo_configuration',
|
||||||
|
'hidden',
|
||||||
|
'password',
|
||||||
|
'radio',
|
||||||
|
'select',
|
||||||
|
'text',
|
||||||
|
'textarea',
|
||||||
|
'file',
|
||||||
|
'text_list',
|
||||||
|
'textarea_list',
|
||||||
|
'custom_hugepages'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
# 'value': None, # custom validation depending on type
|
||||||
|
'restrictions': RESTRICTIONS,
|
||||||
|
'weight': {
|
||||||
|
'type': 'integer',
|
||||||
|
'minimum': 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['type', 'value'],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Schema with allowed values for 'radio' and 'select' attribute types
|
||||||
|
ALLOWED_VALUES_SCHEMA = {
|
||||||
|
'value': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'values': {
|
||||||
|
'type': 'array',
|
||||||
|
'minItems': 1,
|
||||||
|
'items': [
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'data': {'type': 'string'},
|
||||||
|
'label': {'type': 'string'},
|
||||||
|
'description': {'type': 'string'},
|
||||||
|
'restrictions': RESTRICTIONS,
|
||||||
|
},
|
||||||
|
'required': ['data', 'label'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Schema with a structure of multiple text fields setting value
|
||||||
|
MULTIPLE_TEXT_FIELDS_SCHEMA = {
|
||||||
|
'value': {
|
||||||
|
'type': 'array',
|
||||||
|
'minItems': 1,
|
||||||
|
'items': {'type': 'string'},
|
||||||
|
},
|
||||||
|
'min': {
|
||||||
|
'type': 'integer',
|
||||||
|
'minimum': 1,
|
||||||
|
},
|
||||||
|
'max': {
|
||||||
|
'type': 'integer',
|
||||||
|
'minimum': 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional properties definitions for 'attirbute_schema'
|
||||||
|
# depending on 'type' property
|
||||||
|
ATTRIBUTE_TYPE_SCHEMAS = {
|
||||||
|
'checkbox': {'value': {'type': 'boolean'}},
|
||||||
|
'custom_repo_configuration': {
|
||||||
|
'value': {
|
||||||
|
'type': 'array',
|
||||||
|
'minItems': 1,
|
||||||
|
'items': [
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'name': {'type': 'string'},
|
||||||
|
'priority': {'type': ['integer', 'null']},
|
||||||
|
'section': {'type': 'string'},
|
||||||
|
'suite': {'type': 'string'},
|
||||||
|
'type': {'type': 'string'},
|
||||||
|
'uri': {'type': 'string'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'password': {'value': {'type': 'string'}},
|
||||||
|
'radio': ALLOWED_VALUES_SCHEMA,
|
||||||
|
'select': ALLOWED_VALUES_SCHEMA,
|
||||||
|
'text': {'value': {'type': 'string'}},
|
||||||
|
'textarea': {'value': {'type': 'string'}},
|
||||||
|
'text_list': MULTIPLE_TEXT_FIELDS_SCHEMA,
|
||||||
|
'textarea_list': MULTIPLE_TEXT_FIELDS_SCHEMA
|
||||||
|
}
|
||||||
|
@ -64,108 +64,6 @@ collection_schema = {
|
|||||||
"items": single_schema["properties"]
|
"items": single_schema["properties"]
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute_schema = {
|
|
||||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
|
||||||
'title': 'Schema for single editable attribute',
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'type': {
|
|
||||||
'enum': [
|
|
||||||
'checkbox',
|
|
||||||
'custom_repo_configuration',
|
|
||||||
'hidden',
|
|
||||||
'password',
|
|
||||||
'radio',
|
|
||||||
'select',
|
|
||||||
'text',
|
|
||||||
'textarea',
|
|
||||||
'file',
|
|
||||||
'text_list',
|
|
||||||
'textarea_list'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
# 'value': None, # custom validation depending on type
|
|
||||||
'restrictions': base_types.RESTRICTIONS,
|
|
||||||
'weight': {
|
|
||||||
'type': 'integer',
|
|
||||||
'minimum': 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'required': ['type', 'value'],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Schema with allowed values for 'radio' and 'select' attribute types
|
|
||||||
allowed_values_schema = {
|
|
||||||
'value': {
|
|
||||||
'type': 'string',
|
|
||||||
},
|
|
||||||
'values': {
|
|
||||||
'type': 'array',
|
|
||||||
'minItems': 1,
|
|
||||||
'items': [
|
|
||||||
{
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'data': {'type': 'string'},
|
|
||||||
'label': {'type': 'string'},
|
|
||||||
'description': {'type': 'string'},
|
|
||||||
'restrictions': base_types.RESTRICTIONS,
|
|
||||||
},
|
|
||||||
'required': ['data', 'label'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# Schema with a structure of multiple text fields setting value
|
|
||||||
multiple_text_fields_schema = {
|
|
||||||
'value': {
|
|
||||||
'type': 'array',
|
|
||||||
'minItems': 1,
|
|
||||||
'items': {'type': 'string'},
|
|
||||||
},
|
|
||||||
'min': {
|
|
||||||
'type': 'integer',
|
|
||||||
'minimum': 1,
|
|
||||||
},
|
|
||||||
'max': {
|
|
||||||
'type': 'integer',
|
|
||||||
'minimum': 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Additional properties definitions for 'attirbute_schema'
|
|
||||||
# depending on 'type' property
|
|
||||||
attribute_type_schemas = {
|
|
||||||
'checkbox': {'value': {'type': 'boolean'}},
|
|
||||||
'custom_repo_configuration': {
|
|
||||||
'value': {
|
|
||||||
'type': 'array',
|
|
||||||
'minItems': 1,
|
|
||||||
'items': [
|
|
||||||
{
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'name': {'type': 'string'},
|
|
||||||
'priority': {'type': ['integer', 'null']},
|
|
||||||
'section': {'type': 'string'},
|
|
||||||
'suite': {'type': 'string'},
|
|
||||||
'type': {'type': 'string'},
|
|
||||||
'uri': {'type': 'string'},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'password': {'value': {'type': 'string'}},
|
|
||||||
'radio': allowed_values_schema,
|
|
||||||
'select': allowed_values_schema,
|
|
||||||
'text': {'value': {'type': 'string'}},
|
|
||||||
'textarea': {'value': {'type': 'string'}},
|
|
||||||
'text_list': multiple_text_fields_schema,
|
|
||||||
'textarea_list': multiple_text_fields_schema
|
|
||||||
}
|
|
||||||
|
|
||||||
vmware_attributes_schema = {
|
vmware_attributes_schema = {
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"title": "Vmware attributes",
|
"title": "Vmware attributes",
|
||||||
|
@ -15,21 +15,19 @@
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nailgun.api.v1.validators.base import BasicValidator
|
from nailgun.api.v1.validators import base
|
||||||
from nailgun.api.v1.validators.graph import TaskDeploymentValidator
|
from nailgun.api.v1.validators.graph import TaskDeploymentValidator
|
||||||
from nailgun.api.v1.validators.json_schema import base_types
|
from nailgun.api.v1.validators.json_schema import base_types
|
||||||
from nailgun.api.v1.validators.json_schema import node_schema
|
from nailgun.api.v1.validators.json_schema import node_schema
|
||||||
|
|
||||||
from nailgun import consts
|
from nailgun import consts
|
||||||
from nailgun import objects
|
|
||||||
|
|
||||||
from nailgun.db import db
|
from nailgun.db import db
|
||||||
from nailgun.db.sqlalchemy.models import Node
|
from nailgun.db.sqlalchemy.models import Node
|
||||||
from nailgun.db.sqlalchemy.models import NodeNICInterface
|
from nailgun.db.sqlalchemy.models import NodeNICInterface
|
||||||
from nailgun.errors import errors
|
from nailgun.errors import errors
|
||||||
|
from nailgun import objects
|
||||||
|
|
||||||
|
|
||||||
class MetaInterfacesValidator(BasicValidator):
|
class MetaInterfacesValidator(base.BasicValidator):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_data(cls, interfaces):
|
def _validate_data(cls, interfaces):
|
||||||
if not isinstance(interfaces, list):
|
if not isinstance(interfaces, list):
|
||||||
@ -67,7 +65,7 @@ class MetaInterfacesValidator(BasicValidator):
|
|||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
class MetaValidator(BasicValidator):
|
class MetaValidator(base.BasicValidator):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_data(cls, meta):
|
def _validate_data(cls, meta):
|
||||||
if not isinstance(meta, dict):
|
if not isinstance(meta, dict):
|
||||||
@ -101,7 +99,7 @@ class MetaValidator(BasicValidator):
|
|||||||
return meta
|
return meta
|
||||||
|
|
||||||
|
|
||||||
class NodeValidator(BasicValidator):
|
class NodeValidator(base.BasicValidator):
|
||||||
|
|
||||||
single_schema = node_schema.single_schema
|
single_schema = node_schema.single_schema
|
||||||
|
|
||||||
@ -315,7 +313,7 @@ class NodeValidator(BasicValidator):
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class NodesFilterValidator(BasicValidator):
|
class NodesFilterValidator(base.BasicValidator):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, nodes):
|
def validate(cls, nodes):
|
||||||
@ -437,3 +435,10 @@ class NodeDeploymentValidator(TaskDeploymentValidator,
|
|||||||
raise errors.InvalidData('Tasks list must be specified.')
|
raise errors.InvalidData('Tasks list must be specified.')
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class NodeAttributesValidator(base.BasicAttributesValidator):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, data, node=None):
|
||||||
|
return super(NodeAttributesValidator, cls).validate(data)
|
||||||
|
@ -15,20 +15,18 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
|
|
||||||
from nailgun import consts
|
from nailgun import consts
|
||||||
from nailgun import objects
|
|
||||||
|
|
||||||
from nailgun.db.sqlalchemy.models import Release
|
from nailgun.db.sqlalchemy.models import Release
|
||||||
|
from nailgun import objects
|
||||||
from nailgun.settings import settings
|
from nailgun.settings import settings
|
||||||
from nailgun.test.base import BaseIntegrationTest
|
from nailgun.test.base import BaseIntegrationTest
|
||||||
from nailgun.utils import reverse
|
from nailgun.utils import reverse
|
||||||
|
|
||||||
|
|
||||||
class TestAttributes(BaseIntegrationTest):
|
class TestClusterAttributes(BaseIntegrationTest):
|
||||||
|
|
||||||
def test_attributes_creation(self):
|
def test_attributes_creation(self):
|
||||||
cluster = self.env.create_cluster(api=True)
|
cluster = self.env.create_cluster(api=True)
|
||||||
|
@ -431,11 +431,13 @@ class TestHandlers(BaseIntegrationTest):
|
|||||||
'group1': {
|
'group1': {
|
||||||
'metadata': {},
|
'metadata': {},
|
||||||
'comp1': {
|
'comp1': {
|
||||||
'value': 42
|
'type': 'text',
|
||||||
|
'value': '42'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'group2': {
|
'group2': {
|
||||||
'comp2': {
|
'comp2': {
|
||||||
|
'type': 'text',
|
||||||
'value': 'value1'
|
'value': 'value1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,7 +446,8 @@ class TestHandlers(BaseIntegrationTest):
|
|||||||
update_attributes = {
|
update_attributes = {
|
||||||
'group1': {
|
'group1': {
|
||||||
'comp1': {
|
'comp1': {
|
||||||
'value': 41
|
'type': 'text',
|
||||||
|
'value': '41'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -453,6 +456,6 @@ class TestHandlers(BaseIntegrationTest):
|
|||||||
jsonutils.dumps(update_attributes),
|
jsonutils.dumps(update_attributes),
|
||||||
headers=self.default_headers)
|
headers=self.default_headers)
|
||||||
|
|
||||||
fake_attributes['group1']['comp1']['value'] = 41
|
fake_attributes['group1']['comp1']['value'] = '41'
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
self.assertEqual(fake_attributes, resp.json_body)
|
self.assertEqual(fake_attributes, resp.json_body)
|
||||||
|
@ -12,31 +12,99 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
from mock import Mock
|
|
||||||
from mock import patch
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import mock
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from nailgun.api.v1.validators.cluster import AttributesValidator
|
from nailgun.api.v1.validators import base
|
||||||
|
from nailgun.api.v1.validators import cluster
|
||||||
from nailgun.errors import errors
|
from nailgun.errors import errors
|
||||||
from nailgun.test.base import BaseTestCase
|
from nailgun.test import base as base_test
|
||||||
|
|
||||||
|
|
||||||
class TestAttributesValidator(BaseTestCase):
|
class TestClusterAttributesValidator(base_test.BaseTestCase):
|
||||||
def test_generated_attributes_validation(self):
|
def test_generated_attributes_validation(self):
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate,
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
'{"generated": {"name": "test"}}')
|
'{"generated": {"name": "test"}}')
|
||||||
|
|
||||||
def test_editable_attributes_validation(self):
|
def test_editable_attributes_validation(self):
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate,
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
'{"editable": "name"}')
|
'{"editable": "name"}')
|
||||||
|
|
||||||
|
@mock.patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
||||||
|
def test_invalid_provisioning_method(self, mock_cluster_attrs):
|
||||||
|
attrs = {'editable': {'provision': {'method':
|
||||||
|
{'value': 'not_image', 'type': 'text'}}}}
|
||||||
|
mock_cluster_attrs.return_value = attrs
|
||||||
|
cluster_mock = mock.Mock(release=mock.Mock(environment_version='7.0'))
|
||||||
|
self.assertRaises(
|
||||||
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
|
json.dumps(attrs), cluster_mock)
|
||||||
|
|
||||||
|
@mock.patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
||||||
|
def test_provision_method_missing(self, mock_cluster_attrs):
|
||||||
|
attrs = {'editable': {'method':
|
||||||
|
{'value': 'not_image', 'type': 'text'}}}
|
||||||
|
mock_cluster_attrs.return_value = attrs
|
||||||
|
cluster_mock = mock.Mock(release=mock.Mock(environment_version='7.0'))
|
||||||
|
self.assertRaises(
|
||||||
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
|
json.dumps(attrs), cluster_mock)
|
||||||
|
|
||||||
|
@mock.patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
||||||
|
def test_provision_method_passed(self, mock_cluster_attrs):
|
||||||
|
attrs = {'editable': {'provision': {'method':
|
||||||
|
{'value': 'image', 'type': 'text'}}}}
|
||||||
|
mock_cluster_attrs.return_value = attrs
|
||||||
|
cluster_mock = mock.Mock(
|
||||||
|
is_locked=False, release=mock.Mock(environment_version='7.0')
|
||||||
|
)
|
||||||
|
self.assertNotRaises(
|
||||||
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
|
json.dumps(attrs), cluster_mock)
|
||||||
|
|
||||||
|
@mock.patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
||||||
|
def test_provision_method_passed_old(self, mock_cluster_attrs):
|
||||||
|
attrs = {'editable': {'provision': {'method':
|
||||||
|
{'value': 'image', 'type': 'text'}}}}
|
||||||
|
mock_cluster_attrs.return_value = attrs
|
||||||
|
cluster_mock = mock.Mock(
|
||||||
|
is_locked=False, release=mock.Mock(environment_version='6.0')
|
||||||
|
)
|
||||||
|
self.assertNotRaises(
|
||||||
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
|
json.dumps(attrs), cluster_mock)
|
||||||
|
|
||||||
|
def test_valid_attributes(self):
|
||||||
|
valid_attibutes = [
|
||||||
|
'{"group": {"name": "test"}}',
|
||||||
|
'{"name": "test"}',
|
||||||
|
]
|
||||||
|
|
||||||
|
for attributes in valid_attibutes:
|
||||||
|
self.assertNotRaises(
|
||||||
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate,
|
||||||
|
attributes)
|
||||||
|
self.assertNotRaises(
|
||||||
|
errors.InvalidData,
|
||||||
|
cluster.ClusterAttributesValidator.validate_attributes,
|
||||||
|
yaml.load(attributes))
|
||||||
|
|
||||||
|
|
||||||
|
class TestBasicAttributesValidator(base_test.BaseTestCase):
|
||||||
def test_missing_type(self):
|
def test_missing_type(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -45,13 +113,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_missing_value(self):
|
def test_missing_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -60,13 +128,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_invalid_regexp(self):
|
def test_invalid_regexp(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -79,13 +147,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_checkbox_value(self):
|
def test_checkbox_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -95,11 +163,11 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -109,13 +177,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_custom_repo_configuration_value(self):
|
def test_custom_repo_configuration_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
repos:
|
repos:
|
||||||
description: desc
|
description: desc
|
||||||
@ -135,13 +203,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
uri: http://archive.ubuntu.com/ubuntu/
|
uri: http://archive.ubuntu.com/ubuntu/
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_password_value(self):
|
def test_password_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -151,11 +219,11 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -165,13 +233,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_radio_value(self):
|
def test_radio_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
syslog_transport:
|
syslog_transport:
|
||||||
label: Syslog transport protocol
|
label: Syslog transport protocol
|
||||||
@ -189,13 +257,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 3
|
weight: 3
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_select_value(self):
|
def test_select_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
common:
|
common:
|
||||||
libvirt_type:
|
libvirt_type:
|
||||||
label: Hypervisor type
|
label: Hypervisor type
|
||||||
@ -210,13 +278,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
description: QEMU description
|
description: QEMU description
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_text_value(self):
|
def test_text_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -226,11 +294,11 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -240,13 +308,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_textarea_value(self):
|
def test_textarea_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -256,11 +324,11 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -270,13 +338,13 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
def test_text_list_value(self):
|
def test_text_list_value(self):
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -286,11 +354,11 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
# check that text_list value is a list
|
# check that text_list value is a list
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
attrs = '''
|
attrs = '''
|
||||||
editable:
|
|
||||||
storage:
|
storage:
|
||||||
osd_pool_size:
|
osd_pool_size:
|
||||||
description: desc
|
description: desc
|
||||||
@ -300,65 +368,23 @@ class TestAttributesValidator(BaseTestCase):
|
|||||||
weight: 80
|
weight: 80
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.assertRaises(errors.InvalidData,
|
self.assertRaises(
|
||||||
AttributesValidator.validate_editable_attributes,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attrs))
|
yaml.load(attrs))
|
||||||
|
|
||||||
@patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
|
||||||
def test_invalid_provisioning_method(self, mock_cluster_attrs):
|
|
||||||
attrs = {'editable': {'provision': {'method':
|
|
||||||
{'value': 'not_image', 'type': 'text'}}}}
|
|
||||||
mock_cluster_attrs.return_value = attrs
|
|
||||||
cluster_mock = Mock(release=Mock(environment_version='7.0'))
|
|
||||||
self.assertRaises(errors.InvalidData,
|
|
||||||
AttributesValidator.validate,
|
|
||||||
json.dumps(attrs), cluster_mock)
|
|
||||||
|
|
||||||
@patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
|
||||||
def test_provision_method_missing(self, mock_cluster_attrs):
|
|
||||||
attrs = {'editable': {'method':
|
|
||||||
{'value': 'not_image', 'type': 'text'}}}
|
|
||||||
mock_cluster_attrs.return_value = attrs
|
|
||||||
cluster_mock = Mock(release=Mock(environment_version='7.0'))
|
|
||||||
self.assertRaises(errors.InvalidData,
|
|
||||||
AttributesValidator.validate,
|
|
||||||
json.dumps(attrs), cluster_mock)
|
|
||||||
|
|
||||||
@patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
|
||||||
def test_provision_method_passed(self, mock_cluster_attrs):
|
|
||||||
attrs = {'editable': {'provision': {'method':
|
|
||||||
{'value': 'image', 'type': 'text'}}}}
|
|
||||||
mock_cluster_attrs.return_value = attrs
|
|
||||||
cluster_mock = Mock(
|
|
||||||
is_locked=False, release=Mock(environment_version='7.0')
|
|
||||||
)
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
|
||||||
AttributesValidator.validate,
|
|
||||||
json.dumps(attrs), cluster_mock)
|
|
||||||
|
|
||||||
@patch('nailgun.objects.Cluster.get_updated_editable_attributes')
|
|
||||||
def test_provision_method_passed_old(self, mock_cluster_attrs):
|
|
||||||
attrs = {'editable': {'provision': {'method':
|
|
||||||
{'value': 'image', 'type': 'text'}}}}
|
|
||||||
mock_cluster_attrs.return_value = attrs
|
|
||||||
cluster_mock = Mock(
|
|
||||||
is_locked=False, release=Mock(environment_version='6.0')
|
|
||||||
)
|
|
||||||
self.assertNotRaises(errors.InvalidData,
|
|
||||||
AttributesValidator.validate,
|
|
||||||
json.dumps(attrs), cluster_mock)
|
|
||||||
|
|
||||||
def test_valid_attributes(self):
|
def test_valid_attributes(self):
|
||||||
valid_attibutes = [
|
valid_attibutes = [
|
||||||
'{"editable": {"name": "test"}}',
|
'{"group": {"name": "test"}}',
|
||||||
'{"name": "test"}',
|
'{"name": "test"}',
|
||||||
]
|
]
|
||||||
|
|
||||||
for attributes in valid_attibutes:
|
for attributes in valid_attibutes:
|
||||||
self.assertNotRaises(errors.InvalidData,
|
self.assertNotRaises(
|
||||||
AttributesValidator.validate,
|
errors.InvalidData,
|
||||||
|
base.BasicAttributesValidator.validate,
|
||||||
attributes)
|
attributes)
|
||||||
self.assertNotRaises(
|
self.assertNotRaises(
|
||||||
errors.InvalidData,
|
errors.InvalidData,
|
||||||
AttributesValidator.validate_editable_attributes,
|
base.BasicAttributesValidator.validate_attributes,
|
||||||
yaml.load(attributes))
|
yaml.load(attributes))
|
||||||
|
Loading…
Reference in New Issue
Block a user