mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-06 21:52:54 +08:00
849 lines
19 KiB
Go
849 lines
19 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// 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.
|
|
|
|
package validators
|
|
|
|
// TODO
|
|
//
|
|
// email
|
|
// uuid
|
|
// uri
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"math"
|
|
"net"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/gotypes"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/util/regutils"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/util/choices"
|
|
)
|
|
|
|
type ValidatorFunc func(context.Context, *jsonutils.JSONDict) error
|
|
|
|
type IValidatorBase interface {
|
|
Validate(ctx context.Context, data *jsonutils.JSONDict) error
|
|
}
|
|
|
|
type IValidator interface {
|
|
IValidatorBase
|
|
Optional(bool) IValidator
|
|
getValue() interface{}
|
|
setDefault(data *jsonutils.JSONDict) bool
|
|
}
|
|
|
|
type Validator struct {
|
|
parent IValidator
|
|
Key string
|
|
optional bool
|
|
defaultVal interface{}
|
|
value jsonutils.JSONObject
|
|
}
|
|
|
|
func (v *Validator) SetParent(parent IValidator) IValidator {
|
|
v.parent = parent
|
|
return parent
|
|
}
|
|
|
|
func (v *Validator) Optional(optional bool) IValidator {
|
|
v.optional = optional
|
|
return v.parent
|
|
}
|
|
|
|
func (v *Validator) Default(defaultVal interface{}) IValidator {
|
|
v.defaultVal = defaultVal
|
|
return v.parent
|
|
}
|
|
|
|
func (v *Validator) getValue() interface{} {
|
|
return nil
|
|
}
|
|
|
|
func (v *Validator) setDefault(data *jsonutils.JSONDict) bool {
|
|
switch v.defaultVal.(type) {
|
|
case string:
|
|
s := v.defaultVal.(string)
|
|
v.value = jsonutils.NewString(s)
|
|
data.Set(v.Key, v.value)
|
|
return true
|
|
case bool:
|
|
b := v.defaultVal.(bool)
|
|
v.value = jsonutils.NewBool(b)
|
|
data.Set(v.Key, v.value)
|
|
return true
|
|
case int, int32, int64, uint, uint32, uint64:
|
|
value := reflect.ValueOf(v.defaultVal)
|
|
value64 := value.Convert(gotypes.Int64Type)
|
|
defaultVal64 := value64.Interface().(int64)
|
|
v.value = jsonutils.NewInt(defaultVal64)
|
|
data.Set(v.Key, v.value)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (v *Validator) Validate(data *jsonutils.JSONDict) error {
|
|
err, _ := v.validateEx(data)
|
|
return err
|
|
}
|
|
|
|
func (v *Validator) validateEx(data *jsonutils.JSONDict) (err error, isSet bool) {
|
|
if !data.Contains(v.Key) {
|
|
if v.defaultVal != nil {
|
|
isSet = v.parent.setDefault(data)
|
|
return nil, isSet
|
|
}
|
|
if !v.optional {
|
|
err = newMissingKeyError(v.Key)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
value, err := data.Get(v.Key)
|
|
if err != nil {
|
|
return
|
|
}
|
|
v.value = value
|
|
isSet = true
|
|
return
|
|
}
|
|
|
|
type ValidatorIPv4Prefix struct {
|
|
Validator
|
|
Value netutils.IPV4Prefix
|
|
}
|
|
|
|
func NewIPv4PrefixValidator(key string) *ValidatorIPv4Prefix {
|
|
v := &ValidatorIPv4Prefix{
|
|
Validator: Validator{Key: key},
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorIPv4Prefix) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
s, err := v.value.GetString()
|
|
if err != nil {
|
|
return newGeneralError(v.Key, err)
|
|
}
|
|
v.Value, err = netutils.NewIPV4Prefix(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data.Set(v.Key, jsonutils.NewString(s))
|
|
return nil
|
|
}
|
|
|
|
type ValidatorIntChoices struct {
|
|
Validator
|
|
choices []int64
|
|
|
|
Value int64
|
|
}
|
|
|
|
func NewIntChoicesValidator(key string, choices []int64) *ValidatorIntChoices {
|
|
v := &ValidatorIntChoices{
|
|
Validator: Validator{Key: key},
|
|
choices: choices,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorIntChoices) has(i int64) bool {
|
|
for _, c := range v.choices {
|
|
if c == i {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (v *ValidatorIntChoices) Default(i int64) IValidator {
|
|
if v.has(i) {
|
|
v.Validator.Default(i)
|
|
return v
|
|
}
|
|
panic("invalid default for " + v.Key)
|
|
}
|
|
|
|
func (v *ValidatorIntChoices) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorIntChoices) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
i, err := v.value.Int()
|
|
if err != nil {
|
|
return newGeneralError(v.Key, err)
|
|
}
|
|
if !v.has(i) {
|
|
return newInvalidIntChoiceError(v.Key, v.choices, i)
|
|
}
|
|
// in case it's stringified from v.value
|
|
data.Set(v.Key, jsonutils.NewInt(i))
|
|
v.Value = i
|
|
return nil
|
|
}
|
|
|
|
type ValidatorStringChoices struct {
|
|
Validator
|
|
Choices choices.Choices
|
|
Value string
|
|
}
|
|
|
|
func NewStringChoicesValidator(key string, choices choices.Choices) *ValidatorStringChoices {
|
|
v := &ValidatorStringChoices{
|
|
Validator: Validator{Key: key},
|
|
Choices: choices,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorStringChoices) Default(s string) IValidator {
|
|
if v.Choices.Has(s) {
|
|
v.Validator.Default(s)
|
|
return v
|
|
}
|
|
panic("invalid default for " + v.Key)
|
|
}
|
|
|
|
func (v *ValidatorStringChoices) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorStringChoices) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
s, err := v.value.GetString()
|
|
if err != nil {
|
|
return newGeneralError(v.Key, err)
|
|
}
|
|
if !v.Choices.Has(s) {
|
|
return newInvalidChoiceError(v.Key, v.Choices, s)
|
|
}
|
|
// in case it's stringified from v.value
|
|
data.Set(v.Key, jsonutils.NewString(s))
|
|
v.Value = s
|
|
return nil
|
|
}
|
|
|
|
type ValidatorStringMultiChoices struct {
|
|
Validator
|
|
Choices choices.Choices
|
|
Value string
|
|
sep string
|
|
keepDup bool
|
|
}
|
|
|
|
func NewStringMultiChoicesValidator(key string, choices choices.Choices) *ValidatorStringMultiChoices {
|
|
v := &ValidatorStringMultiChoices{
|
|
Validator: Validator{Key: key},
|
|
Choices: choices,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorStringMultiChoices) Sep(s string) *ValidatorStringMultiChoices {
|
|
v.sep = s
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorStringMultiChoices) KeepDup(b bool) *ValidatorStringMultiChoices {
|
|
v.keepDup = b
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorStringMultiChoices) validateString(s string) (string, bool) {
|
|
choices := strings.Split(s, v.sep)
|
|
j := 0
|
|
for i, choice := range choices {
|
|
if !v.Choices.Has(choice) {
|
|
return "", false
|
|
}
|
|
if !v.keepDup {
|
|
isDup := false
|
|
for k := 0; k < j; k++ {
|
|
if choices[k] == choices[i] {
|
|
isDup = true
|
|
}
|
|
}
|
|
if !isDup {
|
|
choices[j] = choices[i]
|
|
j += 1
|
|
}
|
|
}
|
|
}
|
|
if !v.keepDup {
|
|
choices = choices[:j]
|
|
}
|
|
s = strings.Join(choices, v.sep)
|
|
return s, true
|
|
}
|
|
|
|
func (v *ValidatorStringMultiChoices) Default(s string) IValidator {
|
|
s, ok := v.validateString(s)
|
|
if !ok {
|
|
panic("invalid default for " + v.Key)
|
|
}
|
|
v.Validator.Default(s)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorStringMultiChoices) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorStringMultiChoices) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
s, err := v.value.GetString()
|
|
if err != nil {
|
|
return newGeneralError(v.Key, err)
|
|
}
|
|
s, ok := v.validateString(s)
|
|
if !ok {
|
|
return newInvalidChoiceError(v.Key, v.Choices, s)
|
|
}
|
|
// in case it's stringified from v.value
|
|
data.Set(v.Key, jsonutils.NewString(s))
|
|
v.Value = s
|
|
return nil
|
|
}
|
|
|
|
type ValidatorBool struct {
|
|
Validator
|
|
Value bool
|
|
}
|
|
|
|
func (v *ValidatorBool) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorBool) Default(i bool) IValidator {
|
|
return v.Validator.Default(i)
|
|
}
|
|
func (v *ValidatorBool) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
i, err := v.value.Bool()
|
|
if err != nil {
|
|
return newInvalidTypeError(v.Key, "bool", err)
|
|
}
|
|
data.Set(v.Key, jsonutils.NewBool(i))
|
|
v.Value = i
|
|
return nil
|
|
}
|
|
|
|
func NewBoolValidator(key string) *ValidatorBool {
|
|
v := &ValidatorBool{
|
|
Validator: Validator{Key: key},
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
type ValidatorRange struct {
|
|
Validator
|
|
Lower int64
|
|
Upper int64
|
|
Value int64
|
|
}
|
|
|
|
func (v *ValidatorRange) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorRange) Default(i int64) IValidator {
|
|
if i >= v.Lower && i <= v.Upper {
|
|
return v.Validator.Default(i)
|
|
}
|
|
panic("invalid default for " + v.Key)
|
|
}
|
|
func (v *ValidatorRange) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
i, err := v.value.Int()
|
|
if err != nil {
|
|
return newInvalidTypeError(v.Key, "integer", err)
|
|
}
|
|
if i < v.Lower || i > v.Upper {
|
|
return newNotInRangeError(v.Key, i, v.Lower, v.Upper)
|
|
}
|
|
data.Set(v.Key, jsonutils.NewInt(i))
|
|
v.Value = i
|
|
return nil
|
|
}
|
|
|
|
func NewRangeValidator(key string, lower int64, upper int64) *ValidatorRange {
|
|
v := &ValidatorRange{
|
|
Validator: Validator{Key: key},
|
|
Lower: lower,
|
|
Upper: upper,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func NewPortValidator(key string) *ValidatorRange {
|
|
return NewRangeValidator(key, 1, 65535)
|
|
}
|
|
|
|
func NewVlanIdValidator(key string) *ValidatorRange {
|
|
// The convention is vendor specific
|
|
//
|
|
// 0, 4095: reserved
|
|
// 1: no vlan tagging
|
|
return NewRangeValidator(key, 1, 4094)
|
|
}
|
|
|
|
func NewNonNegativeValidator(key string) *ValidatorRange {
|
|
return NewRangeValidator(key, 0, math.MaxInt64)
|
|
}
|
|
|
|
type ValidatorModelIdOrName struct {
|
|
Validator
|
|
ModelKeyword string
|
|
OwnerId mcclient.IIdentityProvider
|
|
ModelManager db.IModelManager
|
|
Model db.IModel
|
|
|
|
modelIdKey string
|
|
noPendingDeleted bool
|
|
allowEmpty bool
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetProjectId() string {
|
|
return v.OwnerId.GetProjectId()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetUserId() string {
|
|
return v.OwnerId.GetUserId()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetTenantId() string {
|
|
return v.OwnerId.GetTenantId()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetProjectDomainId() string {
|
|
return v.OwnerId.GetProjectDomainId()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetUserName() string {
|
|
return v.OwnerId.GetUserName()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetProjectName() string {
|
|
return v.OwnerId.GetProjectName()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetTenantName() string {
|
|
return v.OwnerId.GetTenantName()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetProjectDomain() string {
|
|
return v.OwnerId.GetProjectDomain()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetDomainId() string {
|
|
return v.OwnerId.GetDomainId()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) GetDomainName() string {
|
|
return v.OwnerId.GetDomainName()
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) getValue() interface{} {
|
|
return v.Model
|
|
}
|
|
|
|
func NewModelIdOrNameValidator(key string, modelKeyword string, ownerId mcclient.IIdentityProvider) *ValidatorModelIdOrName {
|
|
v := &ValidatorModelIdOrName{
|
|
Validator: Validator{Key: key},
|
|
OwnerId: ownerId,
|
|
ModelKeyword: modelKeyword,
|
|
modelIdKey: key + "_id",
|
|
noPendingDeleted: true,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) ModelIdKey(modelIdKey string) *ValidatorModelIdOrName {
|
|
v.modelIdKey = modelIdKey
|
|
return v
|
|
}
|
|
|
|
// AllowPendingDeleted allows the to-be-validated id or name to be of a pending deleted model
|
|
func (v *ValidatorModelIdOrName) AllowPendingDeleted(b bool) *ValidatorModelIdOrName {
|
|
v.noPendingDeleted = !b
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) AllowEmpty(b bool) *ValidatorModelIdOrName {
|
|
v.allowEmpty = b
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if !data.Contains(v.Key) && data.Contains(v.modelIdKey) {
|
|
// a hack when validator is used solely for fetching model
|
|
// object. This can happen when input json data was validated
|
|
// more than once in different places
|
|
j, err := data.Get(v.modelIdKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data.Set(v.Key, j)
|
|
defer func() {
|
|
// restore
|
|
data.Remove(v.Key)
|
|
}()
|
|
}
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
modelIdOrName, err := v.value.GetString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if modelIdOrName == "" && v.allowEmpty {
|
|
return nil
|
|
}
|
|
|
|
modelManager := db.GetModelManager(v.ModelKeyword)
|
|
if modelManager == nil {
|
|
return newModelManagerError(v.ModelKeyword)
|
|
}
|
|
v.ModelManager = modelManager
|
|
model, err := modelManager.FetchByIdOrName(ctx, v, modelIdOrName)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return newModelNotFoundError(v.ModelKeyword, modelIdOrName, err)
|
|
} else {
|
|
return httperrors.NewGeneralError(err)
|
|
}
|
|
}
|
|
if v.noPendingDeleted {
|
|
if pd, ok := model.(db.IPendingDeletable); ok && pd.GetPendingDeleted() {
|
|
return newModelNotFoundError(v.ModelKeyword, modelIdOrName, nil)
|
|
}
|
|
}
|
|
v.Model = model
|
|
return nil
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
err := v.validate(ctx, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var val string
|
|
if v.Model != nil {
|
|
val = v.Model.GetId()
|
|
}
|
|
if v.modelIdKey != "" {
|
|
if data.Contains(v.Key) {
|
|
data.Remove(v.Key)
|
|
}
|
|
if val != "" {
|
|
data.Set(v.modelIdKey, jsonutils.NewString(val))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (v *ValidatorModelIdOrName) QueryFilter(ctx context.Context, q *sqlchemy.SQuery, data *jsonutils.JSONDict) (*sqlchemy.SQuery, error) {
|
|
err := v.validate(ctx, data)
|
|
if err != nil {
|
|
if IsModelNotFoundError(err) {
|
|
// hack
|
|
q = q.Equals(v.modelIdKey, "0")
|
|
q = q.Equals(v.modelIdKey, "1")
|
|
return q, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
if v.Model != nil {
|
|
q = q.Equals(v.modelIdKey, v.Model.GetId())
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
type ValidatorRegexp struct {
|
|
Validator
|
|
Regexp *regexp.Regexp
|
|
Value string
|
|
allowEmpty bool
|
|
}
|
|
|
|
func (v *ValidatorRegexp) AllowEmpty(allowEmpty bool) *ValidatorRegexp {
|
|
v.allowEmpty = allowEmpty
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorRegexp) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorRegexp) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
value, err := v.value.GetString()
|
|
if err != nil {
|
|
return newInvalidTypeError(v.Key, "string", err)
|
|
}
|
|
if v.allowEmpty && len(value) == 0 {
|
|
return nil
|
|
}
|
|
if !v.Regexp.MatchString(value) {
|
|
return newInvalidValueError(v.Key, value)
|
|
}
|
|
v.Value = value
|
|
return nil
|
|
}
|
|
|
|
func NewRegexpValidator(key string, regexp *regexp.Regexp) *ValidatorRegexp {
|
|
v := &ValidatorRegexp{
|
|
Validator: Validator{Key: key},
|
|
Regexp: regexp,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
type ValidatorDomainName struct {
|
|
ValidatorRegexp
|
|
}
|
|
|
|
func NewDomainNameValidator(key string) *ValidatorDomainName {
|
|
v := &ValidatorDomainName{
|
|
ValidatorRegexp: *NewRegexpValidator(key, regutils.DOMAINNAME_REG),
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
type ValidatorHostPort struct {
|
|
ValidatorRegexp
|
|
|
|
optionalPort bool
|
|
Domain string
|
|
Port int
|
|
Value string
|
|
}
|
|
|
|
var regHostPort *regexp.Regexp
|
|
|
|
func init() {
|
|
// guard against surprise
|
|
exp := regutils.DOMAINNAME_REG.String()
|
|
if exp != "" && exp[len(exp)-1] == '$' {
|
|
exp = exp[:len(exp)-1]
|
|
}
|
|
exp += "(?::[0-9]{1,5})?"
|
|
regHostPort = regexp.MustCompile(exp)
|
|
}
|
|
|
|
func NewHostPortValidator(key string) *ValidatorHostPort {
|
|
v := &ValidatorHostPort{
|
|
ValidatorRegexp: *NewRegexpValidator(key, regHostPort),
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorHostPort) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorHostPort) OptionalPort(optionalPort bool) *ValidatorHostPort {
|
|
v.optionalPort = optionalPort
|
|
return v
|
|
}
|
|
|
|
func (v *ValidatorHostPort) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
err := v.ValidatorRegexp.Validate(ctx, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hostPort := v.ValidatorRegexp.Value
|
|
if hostPort == "" && (v.optional || v.allowEmpty) {
|
|
return nil
|
|
}
|
|
i := strings.IndexRune(hostPort, ':')
|
|
if i < 0 {
|
|
if v.optionalPort {
|
|
v.Value = hostPort
|
|
v.Domain = hostPort
|
|
return nil
|
|
}
|
|
return newInvalidValueError(v.Key, "port missing")
|
|
}
|
|
portStr := hostPort[i+1:]
|
|
port, err := strconv.ParseUint(portStr, 10, 16)
|
|
if err != nil {
|
|
return newInvalidValueError(v.Key, "bad port integer: "+err.Error())
|
|
}
|
|
if port <= 0 {
|
|
return newInvalidValueError(v.Key, "negative port")
|
|
}
|
|
v.Value = hostPort
|
|
v.Domain = hostPort[:i]
|
|
v.Port = int(port)
|
|
return nil
|
|
}
|
|
|
|
type ValidatorURLPath struct {
|
|
ValidatorRegexp
|
|
}
|
|
|
|
// URI Path as defined in https://tools.ietf.org/html/rfc3986#section-3.3
|
|
var regexpURLPath = regexp.MustCompile(`^(?:/[a-zA-Z0-9.%$&'()*+,;=!~_-]*)*$`)
|
|
|
|
func NewURLPathValidator(key string) *ValidatorURLPath {
|
|
v := &ValidatorURLPath{
|
|
ValidatorRegexp: *NewRegexpValidator(key, regexpURLPath),
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
type ValidatorStruct struct {
|
|
Validator
|
|
Value interface{}
|
|
}
|
|
|
|
func (v *ValidatorStruct) getValue() interface{} {
|
|
return v.Value
|
|
}
|
|
|
|
func (v *ValidatorStruct) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
err := v.value.Unmarshal(v.Value)
|
|
if err != nil {
|
|
return newGeneralError(v.Key, err)
|
|
}
|
|
if valueValidator, ok := v.Value.(IValidatorBase); ok {
|
|
err = valueValidator.Validate(ctx, data)
|
|
if err != nil {
|
|
return newInvalidStructError(v.Key, err)
|
|
}
|
|
}
|
|
data.Set(v.Key, jsonutils.Marshal(v.Value))
|
|
return nil
|
|
}
|
|
|
|
func NewStructValidator(key string, value interface{}) *ValidatorStruct {
|
|
v := &ValidatorStruct{
|
|
Validator: Validator{Key: key},
|
|
Value: value,
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
type ValidatorIPv4Addr struct {
|
|
Validator
|
|
IP net.IP
|
|
}
|
|
|
|
func (v *ValidatorIPv4Addr) getValue() interface{} {
|
|
return v.IP
|
|
}
|
|
|
|
func (v *ValidatorIPv4Addr) setDefault(data *jsonutils.JSONDict) bool {
|
|
if v.defaultVal == nil {
|
|
return false
|
|
}
|
|
defaultIP, ok := v.defaultVal.(net.IP)
|
|
if !ok {
|
|
return false
|
|
}
|
|
value := jsonutils.NewString(defaultIP.String())
|
|
v.value = value
|
|
data.Set(v.Key, value)
|
|
return true
|
|
}
|
|
|
|
func (v *ValidatorIPv4Addr) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
|
|
if err, isSet := v.Validator.validateEx(data); err != nil || !isSet {
|
|
return err
|
|
}
|
|
s, err := v.value.GetString()
|
|
if err != nil {
|
|
return newInvalidTypeError(v.Key, "string", err)
|
|
}
|
|
ip := net.ParseIP(s).To4()
|
|
if ip == nil {
|
|
return newInvalidValueError(v.Key, s)
|
|
}
|
|
v.IP = ip
|
|
return nil
|
|
}
|
|
|
|
func NewIPv4AddrValidator(key string) *ValidatorIPv4Addr {
|
|
v := &ValidatorIPv4Addr{
|
|
Validator: Validator{Key: key},
|
|
}
|
|
v.SetParent(v)
|
|
return v
|
|
}
|
|
|
|
var ValidateModel = func(ctx context.Context, userCred mcclient.TokenCredential, manager db.IStandaloneModelManager, id *string) (db.IModel, error) {
|
|
if len(*id) == 0 {
|
|
return nil, httperrors.NewMissingParameterError(manager.Keyword() + "_id")
|
|
}
|
|
|
|
model, err := manager.FetchByIdOrName(ctx, userCred, *id)
|
|
if err != nil {
|
|
if errors.Cause(err) == sql.ErrNoRows {
|
|
return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), *id)
|
|
}
|
|
if errors.Cause(err) == sqlchemy.ErrDuplicateEntry {
|
|
return nil, httperrors.NewDuplicateResourceError(manager.Keyword(), *id)
|
|
}
|
|
return nil, httperrors.NewGeneralError(err)
|
|
}
|
|
*id = model.GetId()
|
|
return model, nil
|
|
}
|