From bfc96d9b50ae119fcbf39a9511bd9f662e7ad390 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 25 Feb 2010 14:59:33 -0800 Subject: [PATCH] ovsdb: Add support for "enum" constraints. Some of the uses for the formerly supported regular expression constraints were simply to limit values to those in a set of allowed values. This commit adds support for that kind of simple enumeration constraint. --- lib/ovsdb-data.c | 52 ++++++++++---- lib/ovsdb-data.h | 7 +- lib/ovsdb-parser.h | 2 + lib/ovsdb-types.c | 62 +++++++++++++++- lib/ovsdb-types.h | 6 ++ ovsdb/SPECS | 8 +++ ovsdb/mutation.c | 2 +- ovsdb/ovsdb-idlc.in | 140 ++++++++++++++++++++++++++++++++++++- tests/ovs-vsctl.at | 3 + tests/ovsdb-data.at | 75 +++++++++++++++++++- tests/ovsdb-types.at | 15 ++++ vswitchd/vswitch.ovsschema | 6 +- 12 files changed, 356 insertions(+), 22 deletions(-) diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c index 9d8eab85..42fdf1ea 100644 --- a/lib/ovsdb-data.c +++ b/lib/ovsdb-data.c @@ -590,6 +590,25 @@ struct ovsdb_error * ovsdb_atom_check_constraints(const union ovsdb_atom *atom, const struct ovsdb_base_type *base) { + if (base->enum_ + && ovsdb_datum_find_key(base->enum_, atom, base->type) == UINT_MAX) { + struct ovsdb_error *error; + struct ds actual = DS_EMPTY_INITIALIZER; + struct ds valid = DS_EMPTY_INITIALIZER; + + ovsdb_atom_to_string(atom, base->type, &actual); + ovsdb_datum_to_string(base->enum_, + ovsdb_base_type_get_enum_type(base->type), + &valid); + error = ovsdb_error("constraint violation", + "%s is not one of the allowed values (%s)", + ds_cstr(&actual), ds_cstr(&valid)); + ds_destroy(&actual); + ds_destroy(&valid); + + return error; + } + switch (base->type) { case OVSDB_TYPE_VOID: NOT_REACHED(); @@ -777,7 +796,7 @@ ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b) } struct ovsdb_datum_sort_cbdata { - const struct ovsdb_type *type; + enum ovsdb_atomic_type key_type; struct ovsdb_datum *datum; }; @@ -788,7 +807,7 @@ ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_) return ovsdb_atom_compare_3way(&cbdata->datum->keys[a], &cbdata->datum->keys[b], - cbdata->type->key.type); + cbdata->key_type); } static void @@ -797,13 +816,13 @@ ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_) struct ovsdb_datum_sort_cbdata *cbdata = cbdata_; ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]); - if (cbdata->type->value.type != OVSDB_TYPE_VOID) { + if (cbdata->datum->values) { ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]); } } struct ovsdb_error * -ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type) +ovsdb_datum_sort(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type) { if (datum->n < 2) { return NULL; @@ -811,15 +830,15 @@ ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type) struct ovsdb_datum_sort_cbdata cbdata; size_t i; - cbdata.type = type; + cbdata.key_type = key_type; cbdata.datum = datum; sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb, &cbdata); for (i = 0; i < datum->n - 1; i++) { if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1], - type->key.type)) { - if (ovsdb_type_is_map(type)) { + key_type)) { + if (datum->values) { return ovsdb_error(NULL, "map contains duplicate key"); } else { return ovsdb_error(NULL, "set contains duplicate"); @@ -831,6 +850,16 @@ ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type) } } +void +ovsdb_datum_sort_assert(struct ovsdb_datum *datum, + enum ovsdb_atomic_type key_type) +{ + struct ovsdb_error *error = ovsdb_datum_sort(datum, key_type); + if (error) { + NOT_REACHED(); + } +} + /* Checks that each of the atoms in 'datum' conforms to the constraints * specified by its 'type'. Returns an error if a constraint is violated, * otherwise a null pointer. @@ -931,7 +960,7 @@ ovsdb_datum_from_json(struct ovsdb_datum *datum, datum->n++; } - error = ovsdb_datum_sort(datum, type); + error = ovsdb_datum_sort(datum, type->key.type); if (error) { goto error; } @@ -1128,7 +1157,7 @@ ovsdb_datum_from_string(struct ovsdb_datum *datum, goto error; } - dberror = ovsdb_datum_sort(datum, type); + dberror = ovsdb_datum_sort(datum, type->key.type); if (dberror) { ovsdb_error_destroy(dberror); if (ovsdb_type_is_map(type)) { @@ -1424,7 +1453,7 @@ ovsdb_datum_union(struct ovsdb_datum *a, const struct ovsdb_datum *b, if (n != a->n) { struct ovsdb_error *error; a->n = n; - error = ovsdb_datum_sort(a, type); + error = ovsdb_datum_sort(a, type->key.type); assert(!error); } } @@ -1452,8 +1481,7 @@ ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type, } } if (changed) { - struct ovsdb_error *error = ovsdb_datum_sort(a, a_type); - assert(!error); + ovsdb_datum_sort_assert(a, a_type->key.type); } } diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h index 063536b2..a5c49f97 100644 --- a/lib/ovsdb-data.h +++ b/lib/ovsdb-data.h @@ -122,7 +122,12 @@ void ovsdb_datum_swap(struct ovsdb_datum *, struct ovsdb_datum *); /* Checking and maintaining invariants. */ struct ovsdb_error *ovsdb_datum_sort(struct ovsdb_datum *, - const struct ovsdb_type *); + enum ovsdb_atomic_type key_type) + WARN_UNUSED_RESULT; + +void ovsdb_datum_sort_assert(struct ovsdb_datum *, + enum ovsdb_atomic_type key_type); + struct ovsdb_error *ovsdb_datum_check_constraints( const struct ovsdb_datum *, const struct ovsdb_type *) WARN_UNUSED_RESULT; diff --git a/lib/ovsdb-parser.h b/lib/ovsdb-parser.h index a27563a3..d6270bb1 100644 --- a/lib/ovsdb-parser.h +++ b/lib/ovsdb-parser.h @@ -49,6 +49,8 @@ enum ovsdb_parser_types { OP_INTEGER = 1 << JSON_INTEGER, /* 123. */ OP_NONINTEGER = 1 << JSON_REAL, /* 123.456. */ OP_STRING = 1 << JSON_STRING, /* "..." */ + OP_ANY = (OP_NULL | OP_FALSE | OP_TRUE | OP_OBJECT | OP_ARRAY + | OP_INTEGER | OP_NONINTEGER | OP_STRING), OP_BOOLEAN = OP_FALSE | OP_TRUE, OP_NUMBER = OP_INTEGER | OP_NONINTEGER, diff --git a/lib/ovsdb-types.c b/lib/ovsdb-types.c index 5da71456..df18ee54 100644 --- a/lib/ovsdb-types.c +++ b/lib/ovsdb-types.c @@ -22,6 +22,7 @@ #include "dynamic-string.h" #include "json.h" +#include "ovsdb-data.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" @@ -115,6 +116,7 @@ void ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type) { base->type = type; + base->enum_ = NULL; switch (base->type) { case OVSDB_TYPE_VOID: @@ -151,12 +153,37 @@ ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type) } } +/* Returns the type of the 'enum_' member for an ovsdb_base_type whose 'type' + * is 'atomic_type'. */ +const struct ovsdb_type * +ovsdb_base_type_get_enum_type(enum ovsdb_atomic_type atomic_type) +{ + static struct ovsdb_type *types[OVSDB_N_TYPES]; + + if (!types[atomic_type]) { + struct ovsdb_type *type; + + types[atomic_type] = type = xmalloc(sizeof *type); + ovsdb_base_type_init(&type->key, atomic_type); + ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID); + type->n_min = 1; + type->n_max = UINT_MAX; + } + return types[atomic_type]; +} + void ovsdb_base_type_clone(struct ovsdb_base_type *dst, const struct ovsdb_base_type *src) { *dst = *src; + if (src->enum_) { + dst->enum_ = xmalloc(sizeof *dst->enum_); + ovsdb_datum_clone(dst->enum_, src->enum_, + ovsdb_base_type_get_enum_type(dst->type)); + } + switch (dst->type) { case OVSDB_TYPE_VOID: case OVSDB_TYPE_INTEGER: @@ -183,6 +210,12 @@ void ovsdb_base_type_destroy(struct ovsdb_base_type *base) { if (base) { + if (base->enum_) { + ovsdb_datum_destroy(base->enum_, + ovsdb_base_type_get_enum_type(base->type)); + free(base->enum_); + } + switch (base->type) { case OVSDB_TYPE_VOID: case OVSDB_TYPE_INTEGER: @@ -237,6 +270,10 @@ ovsdb_base_type_is_valid(const struct ovsdb_base_type *base) bool ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base) { + if (base->enum_) { + return true; + } + switch (base->type) { case OVSDB_TYPE_VOID: NOT_REACHED(); @@ -298,7 +335,7 @@ ovsdb_base_type_from_json(struct ovsdb_base_type *base, { struct ovsdb_parser parser; struct ovsdb_error *error; - const struct json *type; + const struct json *type, *enum_; if (json->type == JSON_STRING) { error = ovsdb_atomic_type_from_json(&base->type, json); @@ -322,7 +359,18 @@ ovsdb_base_type_from_json(struct ovsdb_base_type *base, } ovsdb_base_type_init(base, base->type); - if (base->type == OVSDB_TYPE_INTEGER) { + + enum_ = ovsdb_parser_member(&parser, "enum", OP_ANY | OP_OPTIONAL); + if (enum_) { + base->enum_ = xmalloc(sizeof *base->enum_); + error = ovsdb_datum_from_json( + base->enum_, ovsdb_base_type_get_enum_type(base->type), + enum_, NULL); + if (error) { + free(base->enum_); + base->enum_ = NULL; + } + } else if (base->type == OVSDB_TYPE_INTEGER) { const struct json *min, *max; min = ovsdb_parser_member(&parser, "minInteger", @@ -395,6 +443,14 @@ ovsdb_base_type_to_json(const struct ovsdb_base_type *base) json = json_object_create(); json_object_put_string(json, "type", ovsdb_atomic_type_to_string(base->type)); + + if (base->enum_) { + const struct ovsdb_type *type; + + type = ovsdb_base_type_get_enum_type(base->type); + json_object_put(json, "enum", ovsdb_datum_to_json(base->enum_, type)); + } + switch (base->type) { case OVSDB_TYPE_VOID: NOT_REACHED(); @@ -528,7 +584,7 @@ ovsdb_type_to_english(const struct ovsdb_type *type) struct ovsdb_error * ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json) { - type->value.type = OVSDB_TYPE_VOID; + ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID); type->n_min = 1; type->n_max = 1; diff --git a/lib/ovsdb-types.h b/lib/ovsdb-types.h index 0ef596ab..6f1727ed 100644 --- a/lib/ovsdb-types.h +++ b/lib/ovsdb-types.h @@ -46,6 +46,11 @@ struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type); struct ovsdb_base_type { enum ovsdb_atomic_type type; + + /* If nonnull, a datum with keys of type 'type' that expresses all the + * valid values for this base_type. */ + struct ovsdb_datum *enum_; + union { struct ovsdb_integer_constraints { int64_t min; /* minInteger or INT64_MIN. */ @@ -90,6 +95,7 @@ void ovsdb_base_type_destroy(struct ovsdb_base_type *); bool ovsdb_base_type_is_valid(const struct ovsdb_base_type *); bool ovsdb_base_type_has_constraints(const struct ovsdb_base_type *); void ovsdb_base_type_clear_constraints(struct ovsdb_base_type *); +const struct ovsdb_type *ovsdb_base_type_get_enum_type(enum ovsdb_atomic_type); struct ovsdb_error *ovsdb_base_type_from_json(struct ovsdb_base_type *, const struct json *) diff --git a/ovsdb/SPECS b/ovsdb/SPECS index adc6dd22..e5cc21b9 100644 --- a/ovsdb/SPECS +++ b/ovsdb/SPECS @@ -173,6 +173,7 @@ is represented by , as described below. or a JSON object with the following members: "type": required + "enum": optional "minInteger": optional, integers only "maxInteger": optional, integers only "minReal": optional, reals only @@ -184,6 +185,13 @@ is represented by , as described below. An by itself is equivalent to a JSON object with a single member "type" whose value is the . + "enum" may be specified as a whose type is a set of one + or more values specified for the member "type". If "enum" is + specified, then the valid values of the are limited to + those in the . + + "enum" is mutually exclusive with the following constraints. + If "type" is "integer", then "minInteger" or "maxInteger" or both may also be specified, restricting the valid integer range. If both are specified, then the maxInteger must be greater than or diff --git a/ovsdb/mutation.c b/ovsdb/mutation.c index 72d7d0ca..9f09d594 100644 --- a/ovsdb/mutation.c +++ b/ovsdb/mutation.c @@ -310,7 +310,7 @@ mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst, } } - error = ovsdb_datum_sort(dst, dst_type); + error = ovsdb_datum_sort(dst, dst_type->key.type); if (error) { ovsdb_error_destroy(error); return ovsdb_error("constraint violation", diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in index c314d615..2426e2da 100755 --- a/ovsdb/ovsdb-idlc.in +++ b/ovsdb/ovsdb-idlc.in @@ -18,7 +18,7 @@ class Error(Exception): def getMember(json, name, validTypes, description, default=None): if name in json: member = json[name] - if type(member) not in validTypes: + if len(validTypes) and type(member) not in validTypes: raise Error("%s: type mismatch for '%s' member" % (description, name)) return member @@ -108,13 +108,89 @@ def escapeCString(src): dst += c return dst +class UUID: + x = "[0-9a-fA-f]" + uuidRE = re.compile("^(%s{8})-(%s{4})-(%s{4})-(%s{4})-(%s{4})(%s{8})$" + % (x, x, x, x, x, x)) + + def __init__(self, value): + self.value = value + + @staticmethod + def fromString(s): + if not uuidRE.match(s): + raise Error("%s is not a valid UUID" % s) + return UUID(s) + + @staticmethod + def fromJson(json): + if UUID.isValidJson(json): + return UUID(json[1]) + else: + raise Error("%s is not valid JSON for a UUID" % json) + + @staticmethod + def isValidJson(json): + return len(json) == 2 and json[0] == "uuid" and uuidRE.match(json[1]) + + def toJson(self): + return ["uuid", self.value] + + def cInitUUID(self, var): + m = re.match(self.value) + return ["%s.parts[0] = 0x%s;" % (var, m.group(1)), + "%s.parts[1] = 0x%s%s;" % (var, m.group(2), m.group(3)), + "%s.parts[2] = 0x%s%s;" % (var, m.group(4), m.group(5)), + "%s.parts[3] = 0x%s;" % (var, m.group(6))] + +class Atom: + def __init__(self, type, value): + self.type = type + self.value = value + + @staticmethod + def fromJson(type_, json): + if ((type_ == 'integer' and type(json) in [int, long]) + or (type_ == 'real' and type(json) in [int, long, float]) + or (type_ == 'boolean' and json in [True, False]) + or (type_ == 'string' and type(json) in [str, unicode])): + return Atom(type_, json) + elif type_ == 'uuid': + return UUID.fromJson(json) + else: + raise Error("%s is not valid JSON for type %s" % (json, type_)) + + def toJson(self): + if self.type == 'uuid': + return self.value.toString() + else: + return self.value + + def cInitAtom(self, var): + if self.type == 'integer': + return ['%s.integer = %d;' % (var, self.value)] + elif self.type == 'real': + return ['%s.real = %.15g;' % (var, self.value)] + elif self.type == 'boolean': + if self.value: + return ['%s.boolean = true;'] + else: + return ['%s.boolean = false;'] + elif self.type == 'string': + return ['%s.string = xstrdup("%s");' + % (var, escapeCString(self.value))] + elif self.type == 'uuid': + return self.value.cInitUUID(var) + class BaseType: def __init__(self, type, + enum=None, refTable=None, minInteger=None, maxInteger=None, minReal=None, maxReal=None, minLength=None, maxLength=None): self.type = type + self.enum = enum self.refTable = refTable self.minInteger = minInteger self.maxInteger = maxInteger @@ -129,6 +205,10 @@ class BaseType: return BaseType(json) else: atomicType = mustGetMember(json, 'type', [unicode], description) + enum = getMember(json, 'enum', [], description) + if enum: + enumType = Type(atomicType, None, 0, 'unlimited') + enum = Datum.fromJson(enumType, enum) refTable = getMember(json, 'refTable', [unicode], description) minInteger = getMember(json, 'minInteger', [int, long], description) maxInteger = getMember(json, 'maxInteger', [int, long], description) @@ -136,7 +216,7 @@ class BaseType: maxReal = getMember(json, 'maxReal', [int, long, float], description) minLength = getMember(json, 'minLength', [int], description) maxLength = getMember(json, 'minLength', [int], description) - return BaseType(atomicType, refTable, minInteger, maxInteger, minReal, maxReal, minLength, maxLength) + return BaseType(atomicType, enum, refTable, minInteger, maxInteger, minReal, maxReal, minLength, maxLength) def toEnglish(self): if self.type == 'uuid' and self.refTable: @@ -179,6 +259,10 @@ class BaseType: stmts = [] stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % ( var, self.type.upper()),) + if self.enum: + stmts.append("%s.enum_ = xmalloc(sizeof *%s.enum_);" + % (var, var)) + stmts += self.enum.cInitDatum("%s.enum_" % var) if self.type == 'integer': if self.minInteger != None: stmts.append('%s.u.integer.min = %d;' % (var, self.minInteger)) @@ -284,6 +368,58 @@ class Type: initMax = "%s%s.n_max = %s;" % (indent, var, max) return "\n".join((initKey, initValue, initMin, initMax)) +class Datum: + def __init__(self, type, values): + self.type = type + self.values = values + + @staticmethod + def fromJson(type_, json): + if not type_.value: + if len(json) == 2 and json[0] == "set": + values = [] + for atomJson in json[1]: + values += [Atom.fromJson(type_.key, atomJson)] + else: + values = [Atom.fromJson(type_.key, json)] + else: + if len(json) != 2 or json[0] != "map": + raise Error("%s is not valid JSON for a map" % json) + values = [] + for pairJson in json[1]: + values += [(Atom.fromJson(type_.key, pairJson[0]), + Atom.fromJson(type_.value, pairJson[1]))] + return Datum(type_, values) + + def cInitDatum(self, var): + if len(self.values) == 0: + return ["ovsdb_datum_init_empty(%s);" % var] + + s = ["%s->n = %d;" % (var, len(self.values))] + s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);" + % (var, len(self.values), var)] + + for i in range(len(self.values)): + key = self.values[i] + if self.type.value: + key = key[0] + s += key.cInitAtom("%s->keys[%d]" % (var, i)) + + if self.type.value: + s += ["%s->values = xmalloc(%d * sizeof *%s->values);" + % (var, len(self.values), var)] + for i in range(len(self.values)): + value = self.values[i][1] + s += key.cInitAtom("%s->values[%d]" % (var, i)) + else: + s += ["%s->values = NULL;" % var] + + if len(self.values) > 1: + s += ["ovsdb_datum_sort_assert(%s, OVSDB_TYPE_%s);" + % (var, self.type.key.upper())] + + return s + def parseSchema(filename): return DbSchema.fromJson(json.load(open(filename, "r"))) diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at index 07244a82..045b1c30 100644 --- a/tests/ovs-vsctl.at +++ b/tests/ovs-vsctl.at @@ -590,6 +590,9 @@ AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=-1])], AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=4096])], [1], [], [ovs-vsctl: constraint violation: 4096 is not in the valid range 0 to 4095 (inclusive) ], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([set c br1 'connection-mode=xyz'])], + [1], [], [[ovs-vsctl: constraint violation: xyz is not one of the allowed values ([in-band, out-of-band]) +]], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([set c br1 connection-mode:x=y])], [1], [], [ovs-vsctl: cannot specify key to set for non-map column connection_mode ], [OVS_VSCTL_CLEANUP]) diff --git a/tests/ovsdb-data.at b/tests/ovsdb-data.at index 031eee04..4bfd9099 100644 --- a/tests/ovsdb-data.at +++ b/tests/ovsdb-data.at @@ -198,7 +198,80 @@ OVSDB_CHECK_NEGATIVE([uuids must be valid], [parse-atom-strings '[["uuid"]]' '1234-5678'], ["1234-5678" is not a valid UUID]) -AT_BANNER([OVSDB -- atoms with constraints]) +AT_BANNER([OVSDB -- atoms with enum constraints]) + +OVSDB_CHECK_POSITIVE([integer atom enum], + [[parse-atoms '[{"type": "integer", "enum": ["set", [1, 6, 8, 10]]}]' \ + '[0]' \ + '[1]' \ + '[2]' \ + '[3]' \ + '[6]' \ + '[7]' \ + '[8]' \ + '[9]' \ + '[10]' \ + '[11]']], + [[constraint violation: 0 is not one of the allowed values ([1, 6, 8, 10]) +1 +constraint violation: 2 is not one of the allowed values ([1, 6, 8, 10]) +constraint violation: 3 is not one of the allowed values ([1, 6, 8, 10]) +6 +constraint violation: 7 is not one of the allowed values ([1, 6, 8, 10]) +8 +constraint violation: 9 is not one of the allowed values ([1, 6, 8, 10]) +10 +constraint violation: 11 is not one of the allowed values ([1, 6, 8, 10])]]) + +OVSDB_CHECK_POSITIVE([real atom enum], + [[parse-atoms '[{"type": "real", "enum": ["set", [-1.5, 1.5]]}]' \ + '[-2]' \ + '[-1]' \ + '[-1.5]' \ + '[0]' \ + '[1]' \ + '[1.5]' \ + '[2]']], + [[constraint violation: -2 is not one of the allowed values ([-1.5, 1.5]) +constraint violation: -1 is not one of the allowed values ([-1.5, 1.5]) +-1.5 +constraint violation: 0 is not one of the allowed values ([-1.5, 1.5]) +constraint violation: 1 is not one of the allowed values ([-1.5, 1.5]) +1.5 +constraint violation: 2 is not one of the allowed values ([-1.5, 1.5])]]) + +OVSDB_CHECK_POSITIVE([boolean atom enum], + [[parse-atoms '[{"type": "boolean", "enum": false}]' \ + '[false]' \ + '[true]']], + [[false +constraint violation: true is not one of the allowed values ([false])]]) + +OVSDB_CHECK_POSITIVE([string atom enum], + [[parse-atoms '[{"type": "string", "enum": ["set", ["abc", "def"]]}]' \ + '[""]' \ + '["ab"]' \ + '["abc"]' \ + '["def"]' \ + '["defg"]' \ + '["DEF"]']], + [[constraint violation: "" is not one of the allowed values ([abc, def]) +constraint violation: ab is not one of the allowed values ([abc, def]) +"abc" +"def" +constraint violation: defg is not one of the allowed values ([abc, def]) +constraint violation: DEF is not one of the allowed values ([abc, def])]]) + +OVSDB_CHECK_POSITIVE([uuid atom enum], + [[parse-atoms '[{"type": "uuid", "enum": ["set", [["uuid", "6d53a6dd-2da7-4924-9927-97f613812382"], ["uuid", "52cbc842-137a-4db5-804f-9f34106a0ba3"]]]}]' \ + '["uuid", "6d53a6dd-2da7-4924-9927-97f613812382"]' \ + '["uuid", "52cbc842-137a-4db5-804f-9f34106a0ba3"]' \ + '["uuid", "dab2a6b2-6094-4f43-a7ef-4c0f0608f176"]']], + [[["uuid","6d53a6dd-2da7-4924-9927-97f613812382"] +["uuid","52cbc842-137a-4db5-804f-9f34106a0ba3"] +constraint violation: dab2a6b2-6094-4f43-a7ef-4c0f0608f176 is not one of the allowed values ([52cbc842-137a-4db5-804f-9f34106a0ba3, 6d53a6dd-2da7-4924-9927-97f613812382])]]) + +AT_BANNER([OVSDB -- atoms with other constraints]) OVSDB_CHECK_POSITIVE([integers >= 5], [[parse-atoms '[{"type": "integer", "minInteger": 5}]' \ diff --git a/tests/ovsdb-types.at b/tests/ovsdb-types.at index 2d981833..7122e9d2 100644 --- a/tests/ovsdb-types.at +++ b/tests/ovsdb-types.at @@ -15,6 +15,9 @@ OVSDB_CHECK_NEGATIVE([void is not a valid atomic-type], AT_BANNER([OVSDB -- base types]) +OVSDB_CHECK_POSITIVE([integer enum], + [[parse-base-type '{"type": "integer", "enum": ["set", [-1, 4, 5]]}' ]], + [[{"enum":["set",[-1,4,5]],"type":"integer"}]]) OVSDB_CHECK_POSITIVE([integer >= 5], [[parse-base-type '{"type": "integer", "minInteger": 5}' ]], [{"minInteger":5,"type":"integer"}]) @@ -28,6 +31,9 @@ OVSDB_CHECK_NEGATIVE([integer max may not be less than min], [[parse-base-type '{"type": "integer", "minInteger": 5, "maxInteger": 3}']], [minInteger exceeds maxInteger]) +OVSDB_CHECK_POSITIVE([real enum], + [[parse-base-type '{"type": "real", "enum": ["set", [1.5, 0, 2.75]]}' ]], + [[{"enum":["set",[0,1.5,2.75]],"type":"real"}]]) OVSDB_CHECK_POSITIVE([real >= -1.5], [[parse-base-type '{"type": "real", "minReal": -1.5}']], [{"minReal":-1.5,"type":"real"}]) @@ -43,7 +49,13 @@ OVSDB_CHECK_NEGATIVE([real max may not be less than min], OVSDB_CHECK_POSITIVE([boolean], [[parse-base-type '[{"type": "boolean"}]' ]], ["boolean"]) +OVSDB_CHECK_POSITIVE([boolean enum], + [[parse-base-type '{"type": "boolean", "enum": true}' ]], + [[{"enum":true,"type":"boolean"}]]) +OVSDB_CHECK_POSITIVE([string enum], + [[parse-base-type '{"type": "string", "enum": ["set", ["def", "abc"]]}']], + [[{"enum":["set",["abc","def"]],"type":"string"}]]) OVSDB_CHECK_POSITIVE([string minLength], [[parse-base-type '{"type": "string", "minLength": 1}']], [{"minLength":1,"type":"string"}]) @@ -60,6 +72,9 @@ OVSDB_CHECK_NEGATIVE([maxLength must not be negative], [[parse-base-type '{"type": "string", "maxLength": -1}']], [maxLength out of valid range 0 to 4294967295]) +OVSDB_CHECK_POSITIVE([uuid enum], + [[parse-base-type '{"type": "uuid", "enum": ["uuid", "36bf19c0-ad9d-4232-bb85-b3d73dfe2123"]}' ]], + [[{"enum":["uuid","36bf19c0-ad9d-4232-bb85-b3d73dfe2123"],"type":"uuid"}]]) OVSDB_CHECK_POSITIVE([uuid refTable], [[parse-base-type '{"type": "uuid", "refTable": "myTable"}' ]], [{"refTable":"myTable","type":"uuid"}]) diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index b75c100c..ac42edfc 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -245,7 +245,8 @@ "type": {"key": "integer", "min": 0, "max": 1}}, "fail_mode": { "comment": "Either \"standalone\" or \"secure\", or empty to use the implementation's default.", - "type": {"key": {"type": "string"}, + "type": {"key": {"type": "string", + "enum": ["set", ["standalone", "secure"]]}, "min": 0, "max": 1}}, "discover_accept_regex": { "comment": "If \"target\" is \"discover\", a POSIX extended regular expression against which the discovered controller location is validated. If not specified, the default is implementation-specific.", @@ -255,7 +256,8 @@ "type": {"key": "boolean", "min": 0, "max": 1}}, "connection_mode": { "comment": "Either \"in-band\" or \"out-of-band\". If not specified, the default is implementation-specific.", - "type": {"key": {"type": "string"}, + "type": {"key": {"type": "string", + "enum": ["set", ["in-band", "out-of-band"]]}, "min": 0, "max": 1}}, "local_ip": { "comment": "If \"target\" is not \"discover\", the IP address to configure on the local port.", -- 2.30.2