1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
|
package ovsdb
import (
"fmt"
"reflect"
)
var (
intType = reflect.TypeOf(0)
realType = reflect.TypeOf(0.0)
boolType = reflect.TypeOf(true)
strType = reflect.TypeOf("")
)
// ErrWrongType describes typing error
type ErrWrongType struct {
from string
expected string
got interface{}
}
func (e *ErrWrongType) Error() string {
return fmt.Sprintf("Wrong Type (%s): expected %s but got %+v (%s)",
e.from, e.expected, e.got, reflect.TypeOf(e.got))
}
// NewErrWrongType creates a new ErrWrongType
func NewErrWrongType(from, expected string, got interface{}) error {
return &ErrWrongType{
from: from,
expected: expected,
got: got,
}
}
// NativeTypeFromAtomic returns the native type that can hold a value of an
// AtomicType
func NativeTypeFromAtomic(basicType string) reflect.Type {
switch basicType {
case TypeInteger:
return intType
case TypeReal:
return realType
case TypeBoolean:
return boolType
case TypeString:
return strType
case TypeUUID:
return strType
default:
panic("Unknown basic type %s basicType")
}
}
// NativeType returns the reflect.Type that can hold the value of a column
// OVS Type to Native Type convertions:
//
// OVS sets -> go slices or a go native type depending on the key
// OVS uuid -> go strings
// OVS map -> go map
// OVS enum -> go native type depending on the type of the enum key
func NativeType(column *ColumnSchema) reflect.Type {
switch column.Type {
case TypeInteger, TypeReal, TypeBoolean, TypeUUID, TypeString:
return NativeTypeFromAtomic(column.Type)
case TypeEnum:
return NativeTypeFromAtomic(column.TypeObj.Key.Type)
case TypeMap:
keyType := NativeTypeFromAtomic(column.TypeObj.Key.Type)
valueType := NativeTypeFromAtomic(column.TypeObj.Value.Type)
return reflect.MapOf(keyType, valueType)
case TypeSet:
keyType := NativeTypeFromAtomic(column.TypeObj.Key.Type)
// optional type
if column.TypeObj.Min() == 0 && column.TypeObj.Max() == 1 {
return reflect.PtrTo(keyType)
}
// non-optional type with max 1
if column.TypeObj.Min() == 1 && column.TypeObj.Max() == 1 {
return keyType
}
return reflect.SliceOf(keyType)
default:
panic(fmt.Errorf("unknown extended type %s", column.Type))
}
}
// OvsToNativeAtomic returns the native type of the basic ovs type
func OvsToNativeAtomic(basicType string, ovsElem interface{}) (interface{}, error) {
switch basicType {
case TypeReal, TypeString, TypeBoolean:
naType := NativeTypeFromAtomic(basicType)
if reflect.TypeOf(ovsElem) != naType {
return nil, NewErrWrongType("OvsToNativeAtomic", naType.String(), ovsElem)
}
return ovsElem, nil
case TypeInteger:
naType := NativeTypeFromAtomic(basicType)
// Default decoding of numbers is float64, convert them to int
if !reflect.TypeOf(ovsElem).ConvertibleTo(naType) {
return nil, NewErrWrongType("OvsToNativeAtomic", fmt.Sprintf("Convertible to %s", naType), ovsElem)
}
return reflect.ValueOf(ovsElem).Convert(naType).Interface(), nil
case TypeUUID:
uuid, ok := ovsElem.(UUID)
if !ok {
return nil, NewErrWrongType("OvsToNativeAtomic", "UUID", ovsElem)
}
return uuid.GoUUID, nil
default:
panic(fmt.Errorf("unknown atomic type %s", basicType))
}
}
func OvsToNativeSlice(baseType string, ovsElem interface{}) (interface{}, error) {
naType := NativeTypeFromAtomic(baseType)
var nativeSet reflect.Value
switch ovsSet := ovsElem.(type) {
case OvsSet:
nativeSet = reflect.MakeSlice(reflect.SliceOf(naType), 0, len(ovsSet.GoSet))
for _, v := range ovsSet.GoSet {
nv, err := OvsToNativeAtomic(baseType, v)
if err != nil {
return nil, err
}
nativeSet = reflect.Append(nativeSet, reflect.ValueOf(nv))
}
default:
nativeSet = reflect.MakeSlice(reflect.SliceOf(naType), 0, 1)
nv, err := OvsToNativeAtomic(baseType, ovsElem)
if err != nil {
return nil, err
}
nativeSet = reflect.Append(nativeSet, reflect.ValueOf(nv))
}
return nativeSet.Interface(), nil
}
// OvsToNative transforms an ovs type to native one based on the column type information
func OvsToNative(column *ColumnSchema, ovsElem interface{}) (interface{}, error) {
switch column.Type {
case TypeReal, TypeString, TypeBoolean, TypeInteger, TypeUUID:
return OvsToNativeAtomic(column.Type, ovsElem)
case TypeEnum:
return OvsToNativeAtomic(column.TypeObj.Key.Type, ovsElem)
case TypeSet:
naType := NativeType(column)
// The inner slice is []interface{}
// We need to convert it to the real type os slice
switch naType.Kind() {
case reflect.Ptr:
switch ovsSet := ovsElem.(type) {
case OvsSet:
if len(ovsSet.GoSet) > 1 {
return nil, fmt.Errorf("expected a slice of len =< 1, but got a slice with %d elements", len(ovsSet.GoSet))
}
if len(ovsSet.GoSet) == 0 {
return reflect.Zero(naType).Interface(), nil
}
native, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsSet.GoSet[0])
if err != nil {
return nil, err
}
pv := reflect.New(naType.Elem())
pv.Elem().Set(reflect.ValueOf(native))
return pv.Interface(), nil
default:
native, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsElem)
if err != nil {
return nil, err
}
pv := reflect.New(naType.Elem())
pv.Elem().Set(reflect.ValueOf(native))
return pv.Interface(), nil
}
case reflect.Slice:
return OvsToNativeSlice(column.TypeObj.Key.Type, ovsElem)
default:
return nil, fmt.Errorf("native type was not slice or pointer. got %d", naType.Kind())
}
case TypeMap:
naType := NativeType(column)
ovsMap, ok := ovsElem.(OvsMap)
if !ok {
return nil, NewErrWrongType("OvsToNative", "OvsMap", ovsElem)
}
// The inner slice is map[interface]interface{}
// We need to convert it to the real type os slice
nativeMap := reflect.MakeMapWithSize(naType, len(ovsMap.GoMap))
for k, v := range ovsMap.GoMap {
nk, err := OvsToNativeAtomic(column.TypeObj.Key.Type, k)
if err != nil {
return nil, err
}
nv, err := OvsToNativeAtomic(column.TypeObj.Value.Type, v)
if err != nil {
return nil, err
}
nativeMap.SetMapIndex(reflect.ValueOf(nk), reflect.ValueOf(nv))
}
return nativeMap.Interface(), nil
default:
panic(fmt.Sprintf("Unknown Type: %v", column.Type))
}
}
// NativeToOvsAtomic returns the OVS type of the atomic native value
func NativeToOvsAtomic(basicType string, nativeElem interface{}) (interface{}, error) {
naType := NativeTypeFromAtomic(basicType)
if reflect.TypeOf(nativeElem) != naType {
return nil, NewErrWrongType("NativeToOvsAtomic", naType.String(), nativeElem)
}
switch basicType {
case TypeUUID:
return UUID{GoUUID: nativeElem.(string)}, nil
default:
return nativeElem, nil
}
}
// NativeToOvs transforms an native type to a ovs type based on the column type information
func NativeToOvs(column *ColumnSchema, rawElem interface{}) (interface{}, error) {
naType := NativeType(column)
if t := reflect.TypeOf(rawElem); t != naType {
return nil, NewErrWrongType("NativeToOvs", naType.String(), rawElem)
}
switch column.Type {
case TypeInteger, TypeReal, TypeString, TypeBoolean, TypeEnum:
return rawElem, nil
case TypeUUID:
return UUID{GoUUID: rawElem.(string)}, nil
case TypeSet:
var ovsSet OvsSet
if column.TypeObj.Key.Type == TypeUUID {
ovsSlice := []interface{}{}
if _, ok := rawElem.([]string); ok {
for _, v := range rawElem.([]string) {
uuid := UUID{GoUUID: v}
ovsSlice = append(ovsSlice, uuid)
}
} else if _, ok := rawElem.(*string); ok {
v := rawElem.(*string)
if v != nil {
uuid := UUID{GoUUID: *v}
ovsSlice = append(ovsSlice, uuid)
}
} else {
return nil, fmt.Errorf("uuid slice was neither []string or *string")
}
ovsSet = OvsSet{GoSet: ovsSlice}
} else {
var err error
ovsSet, err = NewOvsSet(rawElem)
if err != nil {
return nil, err
}
}
return ovsSet, nil
case TypeMap:
nativeMapVal := reflect.ValueOf(rawElem)
ovsMap := make(map[interface{}]interface{}, nativeMapVal.Len())
for _, key := range nativeMapVal.MapKeys() {
ovsKey, err := NativeToOvsAtomic(column.TypeObj.Key.Type, key.Interface())
if err != nil {
return nil, err
}
ovsVal, err := NativeToOvsAtomic(column.TypeObj.Value.Type, nativeMapVal.MapIndex(key).Interface())
if err != nil {
return nil, err
}
ovsMap[ovsKey] = ovsVal
}
return OvsMap{GoMap: ovsMap}, nil
default:
panic(fmt.Sprintf("Unknown Type: %v", column.Type))
}
}
// IsDefaultValue checks if a provided native element corresponds to the default value of its
// designated column type
func IsDefaultValue(column *ColumnSchema, nativeElem interface{}) bool {
switch column.Type {
case TypeEnum:
return isDefaultBaseValue(nativeElem, column.TypeObj.Key.Type)
default:
return isDefaultBaseValue(nativeElem, column.Type)
}
}
// ValidateMutationAtomic checks if the mutation is valid for a specific AtomicType
func validateMutationAtomic(atype string, mutator Mutator, value interface{}) error {
nType := NativeTypeFromAtomic(atype)
if reflect.TypeOf(value) != nType {
return NewErrWrongType(fmt.Sprintf("Mutation of atomic type %s", atype), nType.String(), value)
}
switch atype {
case TypeUUID, TypeString, TypeBoolean:
return fmt.Errorf("atomictype %s does not support mutation", atype)
case TypeReal:
switch mutator {
case MutateOperationAdd, MutateOperationSubtract, MutateOperationMultiply, MutateOperationDivide:
return nil
default:
return fmt.Errorf("wrong mutator for real type %s", mutator)
}
case TypeInteger:
switch mutator {
case MutateOperationAdd, MutateOperationSubtract, MutateOperationMultiply, MutateOperationDivide, MutateOperationModulo:
return nil
default:
return fmt.Errorf("wrong mutator for integer type: %s", mutator)
}
default:
panic("Unsupported Atomic Type")
}
}
// ValidateMutation checks if the mutation value and mutator string area appropriate
// for a given column based on the rules specified RFC7047
func ValidateMutation(column *ColumnSchema, mutator Mutator, value interface{}) error {
if !column.Mutable() {
return fmt.Errorf("column is not mutable")
}
switch column.Type {
case TypeSet:
switch mutator {
case MutateOperationInsert, MutateOperationDelete:
// RFC7047 says a <set> may be an <atom> with a single
// element. Check if we can store this value in our column
if reflect.TypeOf(value).Kind() != reflect.Slice {
if NativeType(column) != reflect.SliceOf(reflect.TypeOf(value)) {
return NewErrWrongType(fmt.Sprintf("Mutation %s of single value in to column %s", mutator, column),
NativeType(column).String(), reflect.SliceOf(reflect.TypeOf(value)).String())
}
return nil
}
if NativeType(column) != reflect.TypeOf(value) {
return NewErrWrongType(fmt.Sprintf("Mutation %s of column %s", mutator, column),
NativeType(column).String(), value)
}
return nil
default:
return validateMutationAtomic(column.TypeObj.Key.Type, mutator, value)
}
case TypeMap:
switch mutator {
case MutateOperationInsert:
// Value must be a map of the same kind
if reflect.TypeOf(value) != NativeType(column) {
return NewErrWrongType(fmt.Sprintf("Mutation %s of column %s", mutator, column),
NativeType(column).String(), value)
}
return nil
case MutateOperationDelete:
// Value must be a map of the same kind or a set of keys to delete
if reflect.TypeOf(value) != NativeType(column) &&
reflect.TypeOf(value) != reflect.SliceOf(NativeTypeFromAtomic(column.TypeObj.Key.Type)) {
return NewErrWrongType(fmt.Sprintf("Mutation %s of column %s", mutator, column),
"compatible map type", value)
}
return nil
default:
return fmt.Errorf("wrong mutator for map type: %s", mutator)
}
case TypeEnum:
// RFC does not clarify what to do with enums.
return fmt.Errorf("enums do not support mutation")
default:
return validateMutationAtomic(column.Type, mutator, value)
}
}
func ValidateCondition(column *ColumnSchema, function ConditionFunction, nativeValue interface{}) error {
if NativeType(column) != reflect.TypeOf(nativeValue) {
return NewErrWrongType(fmt.Sprintf("Condition for column %s", column),
NativeType(column).String(), nativeValue)
}
switch column.Type {
case TypeSet, TypeMap, TypeBoolean, TypeString, TypeUUID:
switch function {
case ConditionEqual, ConditionNotEqual, ConditionIncludes, ConditionExcludes:
return nil
default:
return fmt.Errorf("wrong condition function %s for type: %s", function, column.Type)
}
case TypeInteger, TypeReal:
// All functions are valid
return nil
default:
panic("Unsupported Type")
}
}
func isDefaultBaseValue(elem interface{}, etype ExtendedType) bool {
value := reflect.ValueOf(elem)
if !value.IsValid() {
return true
}
if reflect.TypeOf(elem).Kind() == reflect.Ptr {
return reflect.ValueOf(elem).IsZero()
}
switch etype {
case TypeUUID:
return elem.(string) == "00000000-0000-0000-0000-000000000000" || elem.(string) == ""
case TypeMap, TypeSet:
if value.Kind() == reflect.Array {
return value.Len() == 0
}
return value.IsNil() || value.Len() == 0
case TypeString:
return elem.(string) == ""
case TypeInteger:
return elem.(int) == 0
case TypeReal:
return elem.(float64) == 0
default:
return false
}
}
|