ovsdb: Add support for "enum" constraints.
authorBen Pfaff <blp@nicira.com>
Thu, 25 Feb 2010 22:59:33 +0000 (14:59 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 25 Feb 2010 22:59:41 +0000 (14:59 -0800)
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.

12 files changed:
lib/ovsdb-data.c
lib/ovsdb-data.h
lib/ovsdb-parser.h
lib/ovsdb-types.c
lib/ovsdb-types.h
ovsdb/SPECS
ovsdb/mutation.c
ovsdb/ovsdb-idlc.in
tests/ovs-vsctl.at
tests/ovsdb-data.at
tests/ovsdb-types.at
vswitchd/vswitch.ovsschema

index 9d8eab85c8d6ddd8bc243d1553419f35f569db14..42fdf1ea4f305a1e043b83f03157b14c24cea2c1 100644 (file)
@@ -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);
     }
 }
 \f
index 063536b220e0a079161bc8bde5c5ce429d7da826..a5c49f974e48df6d2e4cbd722fc42f70c3f48106 100644 (file)
@@ -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;
index a27563a354a6d61eb92b3225fae7f44103f574bf..d6270bb12cb72cbec0452b7d6644f2d204ac0be4 100644 (file)
@@ -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,
index 5da71456bb05f17e6bdecd4611023b6e7fafc651..df18ee54c1341a8793272e6eefcccc9dc11c6135 100644 (file)
@@ -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;
 
index 0ef596ab0ae7a93ec818ea6b7f4b7092075032e1..6f1727ed158d5ab2bd0069bf832dcbc641b6fc33 100644 (file)
@@ -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 *)
index adc6dd2278b4ad341695341996bfcf0813825900..e5cc21b92411f40b6c3316863a20d2ead301c3c9 100644 (file)
@@ -173,6 +173,7 @@ is represented by <database-schema>, as described below.
     <atomic-type> or a JSON object with the following members:
 
         "type": <atomic-type>              required
+        "enum": <value>                    optional
         "minInteger": <integer>            optional, integers only
         "maxInteger": <integer>            optional, integers only
         "minReal": <real>                  optional, reals only
@@ -184,6 +185,13 @@ is represented by <database-schema>, as described below.
     An <atomic-type> by itself is equivalent to a JSON object with a
     single member "type" whose value is the <atomic-type>.
 
+    "enum" may be specified as a <value> 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 <base-type> are limited to
+    those in the <value>.
+
+    "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
index 72d7d0caf29781a52c2949cf87c0a95f8d1f84c6..9f09d594197712b71755aaf565daa2310e5d83cb 100644 (file)
@@ -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",
index c314d6151e1e5a6c4546e02c386ea86578a69760..2426e2dacc9d966052e4c3075d0eca6d60dedcb7 100755 (executable)
@@ -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")))
 
index 07244a82a5ea3eb022c3ddbebc240984d21a2151..045b1c301467bb6e496be8285a8d664b0b91ee5d 100644 (file)
@@ -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])
index 031eee04006bbc651d37fdcffb962a0f41d1e773..4bfd90991a92eaf647211d881f22a382d3ac6cb2 100644 (file)
@@ -198,7 +198,80 @@ OVSDB_CHECK_NEGATIVE([uuids must be valid],
   [parse-atom-strings '[["uuid"]]' '1234-5678'],
   ["1234-5678" is not a valid UUID])
 \f
-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])]])
+\f
+AT_BANNER([OVSDB -- atoms with other constraints])
 
 OVSDB_CHECK_POSITIVE([integers >= 5], 
   [[parse-atoms '[{"type": "integer", "minInteger": 5}]' \
index 2d981833c2e86df6183f24db1f3c79fcc94c770b..7122e9d2dbac133c612d1a500348aae4f49e17e7 100644 (file)
@@ -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"}])
index b75c100cd4d9ee3838d5137bc790f737412c816a..ac42edfcb16850fecdff0dadc6cc3d83805b0968 100644 (file)
          "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.",
          "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.",