deb-sahara/sahara/utils/api_validator.py
Michael McCune 0ca11cfc34 Refactoring datasource, job and job_binary name validations
This change adds the ability to have leading numerals in the names of
data sources, jobs, and job binaries. It also separates the name
validation of user keypairs to be inline with nova's validation rules.

Changes
* refactoring validate_name_format to allow leading numeral
* adding validate_keypair_name_format
* adding tests for keypair name validation
* fixing tests for new name rules
* removing outdated test from test_cluster_create_v_keypair_type

Change-Id: Ia500beb21412c535b153bfadc54cd0a4d1269262
Closes-Bug: 1376784
2015-01-27 00:22:43 -05:00

186 lines
5.7 KiB
Python

# Copyright (c) 2013 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
import jsonschema
from oslo_utils import uuidutils
import six
from sahara.swift import utils as su
@jsonschema.FormatChecker.cls_checks('valid_name_hostname')
def validate_name_hostname_format(entry):
if not isinstance(entry, six.string_types) or not entry:
# shoud fail type or length validation
return True
res = re.match(r"^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]"
r"*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z]"
r"[A-Za-z0-9\-]*[A-Za-z0-9])$", entry)
return res is not None
@jsonschema.FormatChecker.cls_checks('valid_name')
def validate_name_format(entry):
if not isinstance(entry, six.string_types):
# shoud fail type validation
return True
res = re.match(r"^[a-zA-Z0-9][a-zA-Z0-9\-_\.]*$", entry)
return res is not None
@jsonschema.FormatChecker.cls_checks('valid_keypair_name')
def validate_keypair_name_format(entry):
if not isinstance(entry, six.string_types):
# should fail type validation
return True
# this follows the validation put forth by nova for keypair names
res = re.match(r'^[a-zA-Z0-9\-_ ]+$', entry)
return res is not None
@jsonschema.FormatChecker.cls_checks('valid_job_location')
def validate_job_location_format(entry):
if not isinstance(entry, six.string_types):
# shoud fail type validation
return True
if entry.startswith('internal-db://'):
return uuidutils.is_uuid_like(entry[len("internal-db://"):])
if entry.startswith(su.SWIFT_INTERNAL_PREFIX):
# TODO(nprivalova):add hostname validation
return True
return False
@jsonschema.FormatChecker.cls_checks('valid_tag')
def validate_valid_tag_format(entry):
if not isinstance(entry, six.string_types):
# shoud fail type validation
return True
res = re.match(r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-_]"
r"*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9]"
r"[A-Za-z0-9\-_]*[A-Za-z0-9])$", entry)
return res is not None
@jsonschema.FormatChecker.cls_checks('uuid')
def validate_uuid_format(entry):
if not isinstance(entry, six.string_types):
# shoud fail type validation
return True
return uuidutils.is_uuid_like(entry)
@jsonschema.FormatChecker.cls_checks('posix_path')
def validate_posix_path(entry):
if not isinstance(entry, six.string_types):
# shoud fail type validation
return True
res = re.match("^(/([A-Z]|[a-z]|[0-9]|\-|_)+)+$", entry)
return res is not None
class ConfigTypeMeta(type):
def __instancecheck__(cls, instance):
# configs should be dict
if not isinstance(instance, dict):
return False
# check dict content
for applicable_target, configs in six.iteritems(instance):
# upper-level dict keys (applicable targets) should be strings
if not isinstance(applicable_target, six.string_types):
return False
# upper-level dict values should be dicts
if not isinstance(configs, dict):
return False
# check internal dict content
for config_name, config_value in six.iteritems(configs):
# internal dict keys should be strings
if not isinstance(config_name, six.string_types):
return False
# internal dict values should be strings or integers or bools
if not isinstance(config_value,
(six.string_types, six.integer_types)):
return False
return True
class SimpleConfigTypeMeta(type):
def __instancecheck__(cls, instance):
# configs should be dict
if not isinstance(instance, dict):
return False
# check dict content
for conf_name, conf_value in six.iteritems(instance):
# keys should be strings, values should be int, string or bool
if not isinstance(conf_name, six.string_types):
return False
if not isinstance(conf_value,
(six.string_types, six.integer_types)):
return False
return True
@six.add_metaclass(ConfigTypeMeta)
class ConfigsType(dict):
pass
@six.add_metaclass(SimpleConfigTypeMeta)
class SimpleConfigsType(dict):
pass
class FlavorTypeMeta(type):
def __instancecheck__(cls, instance):
try:
int(instance)
except (ValueError, TypeError):
return (isinstance(instance, six.string_types)
and uuidutils.is_uuid_like(instance))
return (isinstance(instance, six.integer_types + six.string_types)
and type(instance) != bool)
@six.add_metaclass(FlavorTypeMeta)
class FlavorType(object):
pass
class ApiValidator(jsonschema.Draft4Validator):
def __init__(self, schema):
format_checker = jsonschema.FormatChecker()
super(ApiValidator, self).__init__(
schema, format_checker=format_checker, types={
"configs": ConfigsType,
"flavor": FlavorType,
"simple_config": SimpleConfigsType,
})