-# Copyright (C) 2008, 2009 Nicira Networks, Inc.
+# Copyright (C) 2008, 2009, 2010 Nicira Networks, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
EXTRA_DIST += extras/ezio/ezio3.ti
if HAVE_CURSES
-if HAVE_PCRE
+if HAVE_PCRE_PARTIAL
install-data-hook:
@echo tic -x $(srcdir)/extras/ezio/ezio3.ti
@if ! tic -x $(srcdir)/extras/ezio/ezio3.ti; then \
$(PCRE_LIBS) \
$(SSL_LIBS) \
-lm
-endif # HAVE_PCRE
+endif # HAVE_PCRE_PARTIAL
endif # HAVE_CURSES
lib/packets.h \
lib/pcap.c \
lib/pcap.h \
+ lib/pcre.h \
lib/poll-loop.c \
lib/poll-loop.h \
lib/port-array.c \
#include "json.h"
#include "shash.h"
#include "sort.h"
+#include "unicode.h"
static struct json *
wrap_json(const char *name, struct json *wrapped)
return error0;
}
-struct ovsdb_error *
-ovsdb_atom_from_json(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
- const struct json *json,
- const struct ovsdb_symbol_table *symtab)
+static struct ovsdb_error * WARN_UNUSED_RESULT
+ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
{
switch (type) {
case OVSDB_TYPE_VOID:
ovsdb_atomic_type_to_string(type));
}
+struct ovsdb_error *
+ovsdb_atom_from_json(union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
+{
+ struct ovsdb_error *error;
+
+ error = ovsdb_atom_from_json__(atom, base->type, json, symtab);
+ if (error) {
+ return error;
+ }
+
+ error = ovsdb_atom_check_constraints(atom, base);
+ if (error) {
+ ovsdb_atom_destroy(atom, base->type);
+ }
+ return error;
+}
+
struct json *
ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
{
}
}
-/* Initializes 'atom' to a value of the given 'type' parsed from 's', which
- * takes one of the following forms:
- *
- * - OVSDB_TYPE_INTEGER: A decimal integer optionally preceded by a sign.
- *
- * - OVSDB_TYPE_REAL: A floating-point number in the format accepted by
- * strtod().
- *
- * - OVSDB_TYPE_BOOLEAN: "true", "yes", "on", "1" for true, or "false",
- * "no", "off", or "0" for false.
- *
- * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise
- * an arbitrary string.
- *
- * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format.
- *
- * Returns a null pointer if successful, otherwise an error message describing
- * the problem. The caller is responsible for freeing the error.
- */
-char *
-ovsdb_atom_from_string(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
- const char *s)
+static char *
+ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ const char *s)
{
switch (type) {
case OVSDB_TYPE_VOID:
return NULL;
}
+/* Initializes 'atom' to a value of type 'base' parsed from 's', which takes
+ * one of the following forms:
+ *
+ * - OVSDB_TYPE_INTEGER: A decimal integer optionally preceded by a sign.
+ *
+ * - OVSDB_TYPE_REAL: A floating-point number in the format accepted by
+ * strtod().
+ *
+ * - OVSDB_TYPE_BOOLEAN: "true", "yes", "on", "1" for true, or "false",
+ * "no", "off", or "0" for false.
+ *
+ * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise
+ * an arbitrary string.
+ *
+ * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format.
+ *
+ * Returns a null pointer if successful, otherwise an error message describing
+ * the problem. The caller is responsible for freeing the error.
+ */
+char *
+ovsdb_atom_from_string(union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base, const char *s)
+{
+ struct ovsdb_error *error;
+ char *msg;
+
+ msg = ovsdb_atom_from_string__(atom, base->type, s);
+ if (msg) {
+ return msg;
+ }
+
+ error = ovsdb_atom_check_constraints(atom, base);
+ if (error) {
+ msg = ovsdb_error_to_string(error);
+ ovsdb_error_destroy(error);
+ }
+ return msg;
+}
+
static bool
string_needs_quotes(const char *s)
{
NOT_REACHED();
}
}
+
+static struct ovsdb_error *
+check_string_constraints(const char *s,
+ const struct ovsdb_string_constraints *c)
+{
+ size_t n_chars;
+ char *msg;
+
+ msg = utf8_validate(s, &n_chars);
+ if (msg) {
+ struct ovsdb_error *error;
+
+ error = ovsdb_error("constraint violation",
+ "\"%s\" is not a valid UTF-8 string: %s",
+ s, msg);
+ free(msg);
+ return error;
+ }
+
+ if (n_chars < c->minLen) {
+ return ovsdb_error(
+ "constraint violation",
+ "\"%s\" length %zu is less than minimum allowed "
+ "length %u", s, n_chars, c->minLen);
+ } else if (n_chars > c->maxLen) {
+ return ovsdb_error(
+ "constraint violation",
+ "\"%s\" length %zu is greater than maximum allowed "
+ "length %u", s, n_chars, c->maxLen);
+ }
+
+#if HAVE_PCRE
+ if (c->re) {
+ int retval;
+
+ retval = pcre_exec(c->re, NULL, s, strlen(s), 0,
+ PCRE_ANCHORED | PCRE_NO_UTF8_CHECK, NULL, 0);
+ if (retval == PCRE_ERROR_NOMATCH) {
+ if (c->reComment) {
+ return ovsdb_error("constraint violation",
+ "\"%s\" is not a %s", s, c->reComment);
+ } else {
+ return ovsdb_error("constraint violation",
+ "\"%s\" does not match regular expression "
+ "/%s/", s, c->reMatch);
+ }
+ } else if (retval < 0) {
+ /* PCRE doesn't have a function to translate an error code to a
+ * description. Bizarre. See pcreapi(3) for error details. */
+ return ovsdb_error("internal error", "PCRE returned error %d",
+ retval);
+ }
+ }
+#endif /* HAVE_PCRE */
+
+ return NULL;
+}
+
+/* Checks whether 'atom' meets the constraints (if any) defined in 'base'.
+ * (base->type must specify 'atom''s type.) Returns a null pointer if the
+ * constraints are met, otherwise an error that explains the violation. */
+struct ovsdb_error *
+ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base)
+{
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ if (atom->integer >= base->u.integer.min
+ && atom->integer <= base->u.integer.max) {
+ return NULL;
+ } else if (base->u.integer.min != INT64_MIN) {
+ if (base->u.integer.max != INT64_MAX) {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is not in the valid range "
+ "%"PRId64" to %"PRId64" (inclusive)",
+ atom->integer,
+ base->u.integer.min, base->u.integer.max);
+ } else {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is less than minimum allowed "
+ "value %"PRId64,
+ atom->integer, base->u.integer.min);
+ }
+ } else {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is greater than maximum allowed "
+ "value %"PRId64,
+ atom->integer, base->u.integer.max);
+ }
+ NOT_REACHED();
+
+ case OVSDB_TYPE_REAL:
+ if (atom->real >= base->u.real.min && atom->real <= base->u.real.max) {
+ return NULL;
+ } else if (base->u.real.min != -DBL_MAX) {
+ if (base->u.real.max != DBL_MAX) {
+ return ovsdb_error("constraint violation",
+ "%.*g is not in the valid range "
+ "%.*g to %.*g (inclusive)",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.min,
+ DBL_DIG, base->u.real.max);
+ } else {
+ return ovsdb_error("constraint violation",
+ "%.*g is less than minimum allowed "
+ "value %.*g",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.min);
+ }
+ } else {
+ return ovsdb_error("constraint violation",
+ "%.*g is greater than maximum allowed "
+ "value %.*g",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.max);
+ }
+ NOT_REACHED();
+
+ case OVSDB_TYPE_BOOLEAN:
+ return NULL;
+
+ case OVSDB_TYPE_STRING:
+ return check_string_constraints(atom->string, &base->u.string);
+
+ case OVSDB_TYPE_UUID:
+ return NULL;
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
\f
static union ovsdb_atom *
alloc_default_atoms(enum ovsdb_atomic_type type, size_t n)
const struct ovsdb_type *type)
{
datum->n = type->n_min;
- datum->keys = alloc_default_atoms(type->key_type, datum->n);
- datum->values = alloc_default_atoms(type->value_type, datum->n);
+ datum->keys = alloc_default_atoms(type->key.type, datum->n);
+ datum->values = alloc_default_atoms(type->value.type, datum->n);
}
bool
return false;
}
for (i = 0; i < datum->n; i++) {
- if (!ovsdb_atom_is_default(&datum->keys[i], type->key_type)) {
+ if (!ovsdb_atom_is_default(&datum->keys[i], type->key.type)) {
return false;
}
- if (type->value_type != OVSDB_TYPE_VOID
- && !ovsdb_atom_is_default(&datum->values[i], type->value_type)) {
+ if (type->value.type != OVSDB_TYPE_VOID
+ && !ovsdb_atom_is_default(&datum->values[i], type->value.type)) {
return false;
}
}
{
unsigned int n = old->n;
new->n = n;
- new->keys = clone_atoms(old->keys, type->key_type, n);
- new->values = clone_atoms(old->values, type->value_type, n);
+ new->keys = clone_atoms(old->keys, type->key.type, n);
+ new->values = clone_atoms(old->values, type->value.type, n);
}
static void
void
ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type)
{
- free_data(type->key_type, datum->keys, datum->n);
- free_data(type->value_type, datum->values, datum->n);
+ free_data(type->key.type, datum->keys, datum->n);
+ free_data(type->value.type, datum->values, datum->n);
}
void
return ovsdb_atom_compare_3way(&cbdata->datum->keys[a],
&cbdata->datum->keys[b],
- cbdata->type->key_type);
+ cbdata->type->key.type);
}
static void
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->type->value.type != OVSDB_TYPE_VOID) {
ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]);
}
}
for (i = 0; i < datum->n - 1; i++) {
if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1],
- type->key_type)) {
+ type->key.type)) {
if (ovsdb_type_is_map(type)) {
return ovsdb_error(NULL, "map contains duplicate key");
} else {
}
}
+/* 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.
+ *
+ * This function is not commonly useful because the most ordinary way to obtain
+ * a datum is ultimately via ovsdb_atom_from_string() or
+ * ovsdb_atom_from_json(), which check constraints themselves. */
+struct ovsdb_error *
+ovsdb_datum_check_constraints(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ struct ovsdb_error *error;
+ unsigned int i;
+
+ for (i = 0; i < datum->n; i++) {
+ error = ovsdb_atom_check_constraints(&datum->keys[i], &type->key);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (type->value.type != OVSDB_TYPE_VOID) {
+ for (i = 0; i < datum->n; i++) {
+ error = ovsdb_atom_check_constraints(&datum->values[i],
+ &type->value);
+ if (error) {
+ return error;
+ }
+ }
+ }
+
+ return NULL;
+}
+
struct ovsdb_error *
ovsdb_datum_from_json(struct ovsdb_datum *datum,
const struct ovsdb_type *type,
datum->keys = xmalloc(sizeof *datum->keys);
datum->values = NULL;
- error = ovsdb_atom_from_json(&datum->keys[0], type->key_type,
+ error = ovsdb_atom_from_json(&datum->keys[0], &type->key,
json, symtab);
if (error) {
free(datum->keys);
}
}
- error = ovsdb_atom_from_json(&datum->keys[i], type->key_type,
+ error = ovsdb_atom_from_json(&datum->keys[i], &type->key,
key, symtab);
if (error) {
goto error;
if (is_map) {
error = ovsdb_atom_from_json(&datum->values[i],
- type->value_type, value, symtab);
+ &type->value, value, symtab);
if (error) {
- ovsdb_atom_destroy(&datum->keys[i], type->key_type);
+ ovsdb_atom_destroy(&datum->keys[i], type->key.type);
goto error;
}
}
/* These tests somewhat tolerate a 'datum' that does not exactly match
* 'type', in particular a datum with 'n' not in the allowed range. */
if (datum->n == 1 && ovsdb_type_is_scalar(type)) {
- return ovsdb_atom_to_json(&datum->keys[0], type->key_type);
- } else if (type->value_type == OVSDB_TYPE_VOID) {
+ return ovsdb_atom_to_json(&datum->keys[0], type->key.type);
+ } else if (type->value.type == OVSDB_TYPE_VOID) {
struct json **elems;
size_t i;
elems = xmalloc(datum->n * sizeof *elems);
for (i = 0; i < datum->n; i++) {
- elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key_type);
+ elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key.type);
}
return wrap_json("set", json_array_create(elems, datum->n));
elems = xmalloc(datum->n * sizeof *elems);
for (i = 0; i < datum->n; i++) {
elems[i] = json_array_create_2(
- ovsdb_atom_to_json(&datum->keys[i], type->key_type),
- ovsdb_atom_to_json(&datum->values[i], type->value_type));
+ ovsdb_atom_to_json(&datum->keys[i], type->key.type),
+ ovsdb_atom_to_json(&datum->values[i], type->value.type));
}
return wrap_json("map", json_array_create(elems, datum->n));
}
static char *
-parse_atom_token(const char **s, enum ovsdb_atomic_type type,
+parse_atom_token(const char **s, const struct ovsdb_base_type *base,
union ovsdb_atom *atom)
{
char *token, *error;
error = ovsdb_token_parse(s, &token);
if (!error) {
- error = ovsdb_atom_from_string(atom, type, token);
+ error = ovsdb_atom_from_string(atom, base, token);
free(token);
}
return error;
}
-
static char *
parse_key_value(const char **s, const struct ovsdb_type *type,
union ovsdb_atom *key, union ovsdb_atom *value)
const char *start = *s;
char *error;
- error = parse_atom_token(s, type->key_type, key);
- if (!error && type->value_type != OVSDB_TYPE_VOID) {
+ error = parse_atom_token(s, &type->key, key);
+ if (!error && type->value.type != OVSDB_TYPE_VOID) {
*s = skip_spaces(*s);
if (**s == '=') {
(*s)++;
*s = skip_spaces(*s);
- error = parse_atom_token(s, type->value_type, value);
+ error = parse_atom_token(s, &type->value, value);
} else {
error = xasprintf("%s: syntax error at \"%c\" expecting \"=\"",
start, **s);
}
if (error) {
- ovsdb_atom_destroy(key, type->key_type);
+ ovsdb_atom_destroy(key, type->key.type);
}
}
return error;
free_key_value(const struct ovsdb_type *type,
union ovsdb_atom *key, union ovsdb_atom *value)
{
- ovsdb_atom_destroy(key, type->key_type);
- if (type->value_type != OVSDB_TYPE_VOID) {
- ovsdb_atom_destroy(value, type->value_type);
+ ovsdb_atom_destroy(key, type->key.type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(value, type->value.type);
}
}
ds_put_cstr(out, ", ");
}
- ovsdb_atom_to_string(&datum->keys[i], type->key_type, out);
+ ovsdb_atom_to_string(&datum->keys[i], type->key.type, out);
if (is_map) {
ds_put_char(out, '=');
- ovsdb_atom_to_string(&datum->values[i], type->value_type, out);
+ ovsdb_atom_to_string(&datum->values[i], type->value.type, out);
}
}
if (type->n_max > 1 || !datum->n) {
ovsdb_datum_hash(const struct ovsdb_datum *datum,
const struct ovsdb_type *type, uint32_t basis)
{
- basis = hash_atoms(type->key_type, datum->keys, datum->n, basis);
- basis ^= (type->key_type << 24) | (type->value_type << 16) | datum->n;
- basis = hash_atoms(type->value_type, datum->values, datum->n, basis);
+ basis = hash_atoms(type->key.type, datum->keys, datum->n, basis);
+ basis ^= (type->key.type << 24) | (type->value.type << 16) | datum->n;
+ basis = hash_atoms(type->value.type, datum->values, datum->n, basis);
return basis;
}
return a->n < b->n ? -1 : 1;
}
- cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key_type, a->n);
+ cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key.type, a->n);
if (cmp) {
return cmp;
}
- return (type->value_type == OVSDB_TYPE_VOID ? 0
- : atom_arrays_compare_3way(a->values, b->values, type->value_type,
+ return (type->value.type == OVSDB_TYPE_VOID ? 0
+ : atom_arrays_compare_3way(a->values, b->values, type->value.type,
a->n));
}
/* If 'key' is one of the keys in 'datum', returns its index within 'datum',
- * otherwise UINT_MAX. 'key_type' must be the type of the atoms stored in the
+ * otherwise UINT_MAX. 'key.type' must be the type of the atoms stored in the
* 'keys' array in 'datum'.
*/
unsigned int
}
/* If 'key' and 'value' is one of the key-value pairs in 'datum', returns its
- * index within 'datum', otherwise UINT_MAX. 'key_type' must be the type of
+ * index within 'datum', otherwise UINT_MAX. 'key.type' must be the type of
* the atoms stored in the 'keys' array in 'datum'. 'value_type' may be the
* type of the 'values' atoms or OVSDB_TYPE_VOID to compare only keys.
*/
/* If atom 'i' in 'a' is also in 'b', returns its index in 'b', otherwise
* UINT_MAX. 'type' must be the type of 'a' and 'b', except that
- * type->value_type may be set to OVSDB_TYPE_VOID to compare keys but not
+ * type->value.type may be set to OVSDB_TYPE_VOID to compare keys but not
* values. */
static unsigned int
ovsdb_datum_find(const struct ovsdb_datum *a, int i,
const struct ovsdb_type *type)
{
return ovsdb_datum_find_key_value(b,
- &a->keys[i], type->key_type,
+ &a->keys[i], type->key.type,
a->values ? &a->values[i] : NULL,
- type->value_type);
+ type->value.type);
}
/* Returns true if every element in 'a' is also in 'b', false otherwise. */
unsigned int capacity)
{
a->keys = xrealloc(a->keys, capacity * sizeof *a->keys);
- if (type->value_type != OVSDB_TYPE_VOID) {
+ if (type->value.type != OVSDB_TYPE_VOID) {
a->values = xrealloc(a->values, capacity * sizeof *a->values);
}
}
ovsdb_datum_remove_unsafe(struct ovsdb_datum *datum, size_t idx,
const struct ovsdb_type *type)
{
- ovsdb_atom_destroy(&datum->keys[idx], type->key_type);
+ ovsdb_atom_destroy(&datum->keys[idx], type->key.type);
datum->keys[idx] = datum->keys[datum->n - 1];
- if (type->value_type != OVSDB_TYPE_VOID) {
- ovsdb_atom_destroy(&datum->values[idx], type->value_type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(&datum->values[idx], type->value.type);
datum->values[idx] = datum->values[datum->n - 1];
}
datum->n--;
{
size_t idx = datum->n++;
datum->keys = xrealloc(datum->keys, datum->n * sizeof *datum->keys);
- ovsdb_atom_clone(&datum->keys[idx], key, type->key_type);
- if (type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_clone(&datum->keys[idx], key, type->key.type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
datum->values = xrealloc(datum->values,
datum->n * sizeof *datum->values);
- ovsdb_atom_clone(&datum->values[idx], value, type->value_type);
+ ovsdb_atom_clone(&datum->values[idx], value, type->value.type);
}
}
for (bi = 0; bi < b->n; bi++) {
unsigned int ai;
- ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key_type);
+ ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key.type);
if (ai == UINT_MAX) {
if (n == a->n) {
ovsdb_datum_reallocate(a, type, a->n + (b->n - bi));
}
- ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key_type);
- if (type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key.type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
ovsdb_atom_clone(&a->values[n], &b->values[bi],
- type->value_type);
+ type->value.type);
}
n++;
- } else if (replace && type->value_type != OVSDB_TYPE_VOID) {
- ovsdb_atom_destroy(&a->values[ai], type->value_type);
+ } else if (replace && type->value.type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(&a->values[ai], type->value.type);
ovsdb_atom_clone(&a->values[ai], &b->values[bi],
- type->value_type);
+ type->value.type);
}
}
if (n != a->n) {
bool changed = false;
size_t i;
- assert(a_type->key_type == b_type->key_type);
- assert(a_type->value_type == b_type->value_type
- || b_type->value_type == OVSDB_TYPE_VOID);
+ assert(a_type->key.type == b_type->key.type);
+ assert(a_type->value.type == b_type->value.type
+ || b_type->value.type == OVSDB_TYPE_VOID);
/* XXX The big-O of this could easily be improved. */
for (i = 0; i < a->n; ) {
}
struct ovsdb_error *ovsdb_atom_from_json(union ovsdb_atom *,
- enum ovsdb_atomic_type,
+ const struct ovsdb_base_type *,
const struct json *,
const struct ovsdb_symbol_table *)
WARN_UNUSED_RESULT;
struct json *ovsdb_atom_to_json(const union ovsdb_atom *,
enum ovsdb_atomic_type);
-char *ovsdb_atom_from_string(union ovsdb_atom *, enum ovsdb_atomic_type,
- const char *)
+char *ovsdb_atom_from_string(union ovsdb_atom *,
+ const struct ovsdb_base_type *, const char *)
WARN_UNUSED_RESULT;
void ovsdb_atom_to_string(const union ovsdb_atom *, enum ovsdb_atomic_type,
struct ds *);
+
+struct ovsdb_error *ovsdb_atom_check_constraints(
+ const union ovsdb_atom *, const struct ovsdb_base_type *)
+ WARN_UNUSED_RESULT;
\f
/* An instance of an OVSDB type (given by struct ovsdb_type).
*
/* Checking and maintaining invariants. */
struct ovsdb_error *ovsdb_datum_sort(struct ovsdb_datum *,
const struct ovsdb_type *);
+struct ovsdb_error *ovsdb_datum_check_constraints(
+ const struct ovsdb_datum *, const struct ovsdb_type *)
+ WARN_UNUSED_RESULT;
/* Type conversion. */
struct ovsdb_error *ovsdb_datum_from_json(struct ovsdb_datum *,
size_t column_idx = column - class->columns;
assert(row->new != NULL);
+ assert(column_idx < class->n_columns);
if (hmap_node_is_null(&row->txn_node)) {
hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
uuid_hash(&row->uuid));
ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
const struct json_array *results)
{
+ static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT;
struct ovsdb_error *error;
struct json *json_uuid;
union ovsdb_atom uuid;
return false;
}
- error = ovsdb_atom_from_json(&uuid, OVSDB_TYPE_UUID, json_uuid, NULL);
+ error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL);
if (error) {
char *s = ovsdb_error_to_string(error);
VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON "
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ovsdb_error *ovsdb_parser_get_error(const struct ovsdb_parser *);
struct ovsdb_error *ovsdb_parser_finish(struct ovsdb_parser *)
WARN_UNUSED_RESULT;
+void ovsdb_parser_destroy(struct ovsdb_parser *);
bool ovsdb_parser_is_id(const char *string);
#include "ovsdb-types.h"
+#include <float.h>
#include <limits.h>
#include "dynamic-string.h"
#include "ovsdb-parser.h"
const struct ovsdb_type ovsdb_type_integer =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_INTEGER);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_INTEGER_INIT);
const struct ovsdb_type ovsdb_type_real =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_REAL);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_REAL_INIT);
const struct ovsdb_type ovsdb_type_boolean =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_BOOLEAN);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_BOOLEAN_INIT);
const struct ovsdb_type ovsdb_type_string =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_STRING);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_STRING_INIT);
const struct ovsdb_type ovsdb_type_uuid =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_UUID);
-
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_UUID_INIT);
+\f
+/* ovsdb_atomic_type */
const char *
ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type)
{
return json_string_create(ovsdb_atomic_type_to_string(type));
}
-bool
-ovsdb_type_is_valid(const struct ovsdb_type *type)
-{
- return (type->key_type != OVSDB_TYPE_VOID
- && ovsdb_atomic_type_is_valid(type->key_type)
- && ovsdb_atomic_type_is_valid(type->value_type)
- && type->n_min <= 1
- && type->n_min <= type->n_max
- && (type->value_type == OVSDB_TYPE_VOID
- || ovsdb_atomic_type_is_valid_key(type->key_type)));
-}
-
bool
ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type)
{
return ovsdb_syntax_error(json, NULL, "atomic-type expected");
}
}
+\f
+/* ovsdb_base_type */
+
+void
+ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type)
+{
+ base->type = type;
+
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ break;
+
+ case OVSDB_TYPE_INTEGER:
+ base->u.integer.min = INT64_MIN;
+ base->u.integer.max = INT64_MAX;
+ break;
+
+ case OVSDB_TYPE_REAL:
+ base->u.real.min = -DBL_MAX;
+ base->u.real.max = DBL_MAX;
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ base->u.string.re = NULL;
+ base->u.string.reMatch = NULL;
+ base->u.string.reComment = NULL;
+ base->u.string.minLen = 0;
+ base->u.string.maxLen = UINT_MAX;
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_base_type_clone(struct ovsdb_base_type *dst,
+ const struct ovsdb_base_type *src)
+{
+ *dst = *src;
+
+ switch (dst->type) {
+ case OVSDB_TYPE_VOID:
+ case OVSDB_TYPE_INTEGER:
+ case OVSDB_TYPE_REAL:
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (dst->u.string.re) {
+ pcre_refcount(dst->u.string.re, 1);
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_base_type_destroy(struct ovsdb_base_type *base)
+{
+ if (base) {
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ case OVSDB_TYPE_INTEGER:
+ case OVSDB_TYPE_REAL:
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (base->u.string.re && !pcre_refcount(base->u.string.re, -1)) {
+ pcre_free(base->u.string.re);
+ free(base->u.string.reMatch);
+ free(base->u.string.reComment);
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+ }
+}
+
+bool
+ovsdb_base_type_is_valid(const struct ovsdb_base_type *base)
+{
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ return true;
+
+ case OVSDB_TYPE_INTEGER:
+ return base->u.integer.min <= base->u.integer.max;
+
+ case OVSDB_TYPE_REAL:
+ return base->u.real.min <= base->u.real.max;
+
+ case OVSDB_TYPE_BOOLEAN:
+ return true;
+
+ case OVSDB_TYPE_STRING:
+ return base->u.string.minLen <= base->u.string.maxLen;
+
+ case OVSDB_TYPE_UUID:
+ return true;
+
+ case OVSDB_N_TYPES:
+ default:
+ return false;
+ }
+}
+
+bool
+ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base)
+{
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ return (base->u.integer.min != INT64_MIN
+ || base->u.integer.max != INT64_MAX);
+
+ case OVSDB_TYPE_REAL:
+ return (base->u.real.min != -DBL_MAX
+ || base->u.real.max != DBL_MAX);
+
+ case OVSDB_TYPE_BOOLEAN:
+ return false;
+
+ case OVSDB_TYPE_STRING:
+ return (base->u.string.reMatch != NULL
+ || base->u.string.minLen != 0
+ || base->u.string.maxLen != UINT_MAX);
+
+ case OVSDB_TYPE_UUID:
+ return false;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_base_type_clear_constraints(struct ovsdb_base_type *base)
+{
+ enum ovsdb_atomic_type type = base->type;
+ ovsdb_base_type_destroy(base);
+ ovsdb_base_type_init(base, type);
+}
+
+struct ovsdb_error *
+ovsdb_base_type_set_regex(struct ovsdb_base_type *base,
+ const char *reMatch, const char *reComment)
+{
+ const char *errorString;
+ const char *pattern;
+ int errorOffset;
+
+ /* Compile pattern, anchoring it at both ends. */
+ pattern = reMatch;
+ if (pattern[0] == '\0' || strchr(pattern, '\0')[-1] != '$') {
+ pattern = xasprintf("%s$", pattern);
+ }
+ base->u.string.re = pcre_compile(pattern, (PCRE_ANCHORED | PCRE_UTF8
+ | PCRE_JAVASCRIPT_COMPAT),
+ &errorString, &errorOffset, NULL);
+ if (pattern != reMatch) {
+ free((char *) pattern);
+ }
+ if (!base->u.string.re) {
+ return ovsdb_syntax_error(NULL, "invalid regular expression",
+ "\"%s\" is not a valid regular "
+ "expression: %s", reMatch, errorString);
+ }
+
+ /* Save regular expression. */
+ pcre_refcount(base->u.string.re, 1);
+ base->u.string.reMatch = xstrdup(reMatch);
+ base->u.string.reComment = reComment ? xstrdup(reComment) : NULL;
+ return NULL;
+}
+
+static struct ovsdb_error *
+parse_optional_uint(struct ovsdb_parser *parser, const char *member,
+ unsigned int *uint)
+{
+ const struct json *json;
+
+ json = ovsdb_parser_member(parser, member, OP_INTEGER | OP_OPTIONAL);
+ if (json) {
+ if (json->u.integer < 0 || json->u.integer > UINT_MAX) {
+ return ovsdb_syntax_error(json, NULL,
+ "%s out of valid range 0 to %u",
+ member, UINT_MAX);
+ }
+ *uint = json->u.integer;
+ }
+ return NULL;
+}
+
+struct ovsdb_error *
+ovsdb_base_type_from_json(struct ovsdb_base_type *base,
+ const struct json *json)
+{
+ struct ovsdb_parser parser;
+ struct ovsdb_error *error;
+ const struct json *type;
+
+ if (json->type == JSON_STRING) {
+ error = ovsdb_atomic_type_from_json(&base->type, json);
+ if (error) {
+ return error;
+ }
+ ovsdb_base_type_init(base, base->type);
+ return NULL;
+ }
+
+ ovsdb_parser_init(&parser, json, "ovsdb type");
+ type = ovsdb_parser_member(&parser, "type", OP_STRING);
+ if (ovsdb_parser_has_error(&parser)) {
+ base->type = OVSDB_TYPE_VOID;
+ return ovsdb_parser_finish(&parser);
+ }
+
+ error = ovsdb_atomic_type_from_json(&base->type, type);
+ if (error) {
+ return error;
+ }
+
+ ovsdb_base_type_init(base, base->type);
+ if (base->type == OVSDB_TYPE_INTEGER) {
+ const struct json *min, *max;
+
+ min = ovsdb_parser_member(&parser, "minInteger",
+ OP_INTEGER | OP_OPTIONAL);
+ max = ovsdb_parser_member(&parser, "maxInteger",
+ OP_INTEGER | OP_OPTIONAL);
+ base->u.integer.min = min ? min->u.integer : INT64_MIN;
+ base->u.integer.max = max ? max->u.integer : INT64_MAX;
+ if (base->u.integer.min > base->u.integer.max) {
+ error = ovsdb_syntax_error(json, NULL,
+ "minInteger exceeds maxInteger");
+ }
+ } else if (base->type == OVSDB_TYPE_REAL) {
+ const struct json *min, *max;
+
+ min = ovsdb_parser_member(&parser, "minReal", OP_NUMBER | OP_OPTIONAL);
+ max = ovsdb_parser_member(&parser, "maxReal", OP_NUMBER | OP_OPTIONAL);
+ base->u.real.min = min ? json_real(min) : -DBL_MAX;
+ base->u.real.max = max ? json_real(max) : DBL_MAX;
+ if (base->u.real.min > base->u.real.max) {
+ error = ovsdb_syntax_error(json, NULL, "minReal exceeds maxReal");
+ }
+ } else if (base->type == OVSDB_TYPE_STRING) {
+ const struct json *reMatch;
+
+ reMatch = ovsdb_parser_member(&parser, "reMatch",
+ OP_STRING | OP_OPTIONAL);
+ if (reMatch) {
+ const struct json *reComment;
+
+ reComment = ovsdb_parser_member(&parser, "reComment",
+ OP_STRING | OP_OPTIONAL);
+ error = ovsdb_base_type_set_regex(
+ base, json_string(reMatch),
+ reComment ? json_string(reComment) : NULL);
+ }
+
+ if (!error) {
+ error = parse_optional_uint(&parser, "minLength",
+ &base->u.string.minLen);
+ }
+ if (!error) {
+ error = parse_optional_uint(&parser, "maxLength",
+ &base->u.string.maxLen);
+ }
+ if (!error && base->u.string.minLen > base->u.string.maxLen) {
+ error = ovsdb_syntax_error(json, NULL,
+ "minLength exceeds maxLength");
+ }
+ }
+
+ if (error) {
+ ovsdb_error_destroy(ovsdb_parser_finish(&parser));
+ } else {
+ error = ovsdb_parser_finish(&parser);
+ }
+ if (error) {
+ ovsdb_base_type_destroy(base);
+ base->type = OVSDB_TYPE_VOID;
+ }
+ return error;
+}
+
+struct json *
+ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
+{
+ struct json *json;
+
+ if (!ovsdb_base_type_has_constraints(base)) {
+ return json_string_create(ovsdb_atomic_type_to_string(base->type));
+ }
+
+ json = json_object_create();
+ json_object_put_string(json, "type",
+ ovsdb_atomic_type_to_string(base->type));
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ if (base->u.integer.min != INT64_MIN) {
+ json_object_put(json, "minInteger",
+ json_integer_create(base->u.integer.min));
+ }
+ if (base->u.integer.max != INT64_MAX) {
+ json_object_put(json, "maxInteger",
+ json_integer_create(base->u.integer.max));
+ }
+ break;
+
+ case OVSDB_TYPE_REAL:
+ if (base->u.real.min != -DBL_MAX) {
+ json_object_put(json, "minReal",
+ json_real_create(base->u.real.min));
+ }
+ if (base->u.real.max != DBL_MAX) {
+ json_object_put(json, "maxReal",
+ json_real_create(base->u.real.max));
+ }
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (base->u.string.reMatch) {
+ json_object_put_string(json, "reMatch", base->u.string.reMatch);
+ if (base->u.string.reComment) {
+ json_object_put_string(json, "reComment",
+ base->u.string.reComment);
+ }
+ }
+ if (base->u.string.minLen != 0) {
+ json_object_put(json, "minLength",
+ json_integer_create(base->u.string.minLen));
+ }
+ if (base->u.string.maxLen != UINT_MAX) {
+ json_object_put(json, "maxLength",
+ json_integer_create(base->u.string.maxLen));
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+
+ return json;
+}
+\f
+/* ovsdb_type */
+
+void
+ovsdb_type_clone(struct ovsdb_type *dst, const struct ovsdb_type *src)
+{
+ ovsdb_base_type_clone(&dst->key, &src->key);
+ ovsdb_base_type_clone(&dst->value, &src->value);
+ dst->n_min = src->n_min;
+ dst->n_max = src->n_max;
+}
+
+void
+ovsdb_type_destroy(struct ovsdb_type *type)
+{
+ ovsdb_base_type_destroy(&type->key);
+ ovsdb_base_type_destroy(&type->value);
+}
+
+bool
+ovsdb_type_is_valid(const struct ovsdb_type *type)
+{
+ return (type->key.type != OVSDB_TYPE_VOID
+ && ovsdb_base_type_is_valid(&type->key)
+ && ovsdb_base_type_is_valid(&type->value)
+ && type->n_min <= 1
+ && type->n_min <= type->n_max);
+}
static struct ovsdb_error *
n_from_json(const struct json *json, unsigned int *n)
char *
ovsdb_type_to_english(const struct ovsdb_type *type)
{
- const char *key = ovsdb_atomic_type_to_string(type->key_type);
- const char *value = ovsdb_atomic_type_to_string(type->value_type);
+ const char *key = ovsdb_atomic_type_to_string(type->key.type);
+ const char *value = ovsdb_atomic_type_to_string(type->value.type);
if (ovsdb_type_is_scalar(type)) {
return xstrdup(key);
} else {
struct ovsdb_error *
ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
{
- type->value_type = OVSDB_TYPE_VOID;
+ type->value.type = OVSDB_TYPE_VOID;
type->n_min = 1;
type->n_max = 1;
if (json->type == JSON_STRING) {
- return ovsdb_atomic_type_from_json(&type->key_type, json);
+ return ovsdb_base_type_from_json(&type->key, json);
} else if (json->type == JSON_OBJECT) {
const struct json *key, *value, *min, *max;
struct ovsdb_error *error;
struct ovsdb_parser parser;
ovsdb_parser_init(&parser, json, "ovsdb type");
- key = ovsdb_parser_member(&parser, "key", OP_STRING);
- value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OPTIONAL);
+ key = ovsdb_parser_member(&parser, "key", OP_STRING | OP_OBJECT);
+ value = ovsdb_parser_member(&parser, "value",
+ OP_STRING | OP_OBJECT | OP_OPTIONAL);
min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL);
max = ovsdb_parser_member(&parser, "max",
OP_INTEGER | OP_STRING | OP_OPTIONAL);
return error;
}
- error = ovsdb_atomic_type_from_json(&type->key_type, key);
+ error = ovsdb_base_type_from_json(&type->key, key);
if (error) {
return error;
}
if (value) {
- error = ovsdb_atomic_type_from_json(&type->value_type, value);
+ error = ovsdb_base_type_from_json(&type->value, value);
if (error) {
return error;
}
struct json *
ovsdb_type_to_json(const struct ovsdb_type *type)
{
- if (ovsdb_type_is_scalar(type)) {
- return ovsdb_atomic_type_to_json(type->key_type);
+ if (ovsdb_type_is_scalar(type)
+ && !ovsdb_base_type_has_constraints(&type->key)) {
+ return ovsdb_base_type_to_json(&type->key);
} else {
struct json *json = json_object_create();
- json_object_put(json, "key",
- ovsdb_atomic_type_to_json(type->key_type));
- if (type->value_type != OVSDB_TYPE_VOID) {
+ json_object_put(json, "key", ovsdb_base_type_to_json(&type->key));
+ if (type->value.type != OVSDB_TYPE_VOID) {
json_object_put(json, "value",
- ovsdb_atomic_type_to_json(type->value_type));
+ ovsdb_base_type_to_json(&type->value));
}
if (type->n_min != 1) {
json_object_put(json, "min", json_integer_create(type->n_min));
#ifndef OVSDB_TYPES_H
#define OVSDB_TYPES_H 1
+#include <float.h>
+#include <pcre.h>
#include <stdbool.h>
#include <stdint.h>
#include "compiler.h"
};
static inline bool ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type);
-static inline bool ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type);
bool ovsdb_atomic_type_from_string(const char *, enum ovsdb_atomic_type *);
struct ovsdb_error *ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *,
const struct json *);
const char *ovsdb_atomic_type_to_string(enum ovsdb_atomic_type);
struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
\f
+/* An atomic type plus optional constraints. */
+
+struct ovsdb_base_type {
+ enum ovsdb_atomic_type type;
+ union {
+ struct ovsdb_integer_constraints {
+ int64_t min; /* minInteger or INT64_MIN. */
+ int64_t max; /* maxInteger or INT64_MAX. */
+ } integer;
+
+ struct ovsdb_real_constraints {
+ double min; /* minReal or -DBL_MAX. */
+ double max; /* minReal or DBL_MAX. */
+ } real;
+
+ /* No constraints for Boolean types. */
+
+ struct ovsdb_string_constraints {
+ pcre *re; /* Compiled regular expression. */
+ char *reMatch; /* reMatch or NULL. */
+ char *reComment; /* reComment or NULL. */
+ unsigned int minLen; /* minLength or 0. */
+ unsigned int maxLen; /* maxLength or UINT_MAX. */
+ } string;
+ } u;
+};
+
+#define OVSDB_BASE_VOID_INIT { .type = OVSDB_TYPE_VOID }
+#define OVSDB_BASE_INTEGER_INIT { .type = OVSDB_TYPE_INTEGER, \
+ .u.integer = { INT64_MIN, INT64_MAX } }
+#define OVSDB_BASE_REAL_INIT { .type = OVSDB_TYPE_REAL, \
+ .u.real = { -DBL_MAX, DBL_MAX } }
+#define OVSDB_BASE_BOOLEAN_INIT { .type = OVSDB_TYPE_BOOLEAN }
+#define OVSDB_BASE_STRING_INIT { .type = OVSDB_TYPE_STRING, \
+ .u.string = { NULL, NULL, NULL, \
+ 0, UINT_MAX } }
+#define OVSDB_BASE_UUID_INIT { .type = OVSDB_TYPE_UUID }
+
+void ovsdb_base_type_init(struct ovsdb_base_type *, enum ovsdb_atomic_type);
+void ovsdb_base_type_clone(struct ovsdb_base_type *,
+ const struct ovsdb_base_type *);
+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 *);
+struct ovsdb_error *ovsdb_base_type_set_regex(struct ovsdb_base_type *,
+ const char *reMatch,
+ const char *reComment)
+ WARN_UNUSED_RESULT;
+
+struct ovsdb_error *ovsdb_base_type_from_json(struct ovsdb_base_type *,
+ const struct json *)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_base_type_to_json(const struct ovsdb_base_type *);
+\f
/* An OVSDB type.
*
* Several rules constrain the valid types. See ovsdb_type_is_valid() (in
* can also be considered an optional pair of 'key_type' and 'value_type'.
*/
struct ovsdb_type {
- enum ovsdb_atomic_type key_type;
- enum ovsdb_atomic_type value_type;
+ struct ovsdb_base_type key;
+ struct ovsdb_base_type value;
unsigned int n_min;
unsigned int n_max; /* UINT_MAX stands in for "unlimited". */
};
-#define OVSDB_TYPE_SCALAR_INITIALIZER(KEY_TYPE) \
- { KEY_TYPE, OVSDB_TYPE_VOID, 1, 1 }
+#define OVSDB_TYPE_SCALAR_INITIALIZER(KEY) { KEY, OVSDB_BASE_VOID_INIT, 1, 1 }
extern const struct ovsdb_type ovsdb_type_integer;
extern const struct ovsdb_type ovsdb_type_real;
extern const struct ovsdb_type ovsdb_type_string;
extern const struct ovsdb_type ovsdb_type_uuid;
+void ovsdb_type_clone(struct ovsdb_type *, const struct ovsdb_type *);
+void ovsdb_type_destroy(struct ovsdb_type *);
+
bool ovsdb_type_is_valid(const struct ovsdb_type *);
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *);
return atomic_type >= 0 && atomic_type < OVSDB_N_TYPES;
}
-static inline bool
-ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type atomic_type)
-{
- /* XXX should we disallow reals or booleans as keys? */
- return ovsdb_atomic_type_is_valid(atomic_type);
-}
-
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type)
{
- return (type->value_type == OVSDB_TYPE_VOID
+ return (type->value.type == OVSDB_TYPE_VOID
&& type->n_min == 1 && type->n_max == 1);
}
static inline bool ovsdb_type_is_set(const struct ovsdb_type *type)
{
- return (type->value_type == OVSDB_TYPE_VOID
+ return (type->value.type == OVSDB_TYPE_VOID
&& (type->n_min != 1 || type->n_max != 1));
}
static inline bool ovsdb_type_is_map(const struct ovsdb_type *type)
{
- return type->value_type != OVSDB_TYPE_VOID;
+ return type->value.type != OVSDB_TYPE_VOID;
}
#endif /* ovsdb-types.h */
--- /dev/null
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * 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.
+ */
+
+#ifndef PCRE_H
+#define PCRE_H 1
+
+#ifdef HAVE_PCRE
+#include_next <pcre.h>
+#else
+typedef void pcre;
+#endif
+
+#endif /* pcre.h */
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "unicode.h"
+#include <inttypes.h>
+
+#include "dynamic-string.h"
+#include "util.h"
+
/* Returns the unicode code point corresponding to leading surrogate 'leading'
* and trailing surrogate 'trailing'. The return value will not make any
* sense if 'leading' or 'trailing' are not in the correct ranges for leading
int x1 = trailing & 0x3ff;
return (u << 16) | (x0 << 10) | x1;
}
+
+/* Returns the number of Unicode characters in UTF-8 string 's'. */
+size_t
+utf8_length(const char *s_)
+{
+ const uint8_t *s;
+ size_t length;
+
+ length = 0;
+ for (s = (const uint8_t *) s_; *s != '\0'; s++) {
+ /* The most-significant bits of the first byte in a character are one
+ * of 2#01, 2#00, or 2#11. 2#10 is a continuation byte. */
+ length += (*s & 0xc0) != 0x80;
+ }
+ return length;
+}
+
+static char *
+invalid_utf8_sequence(const uint8_t *s, int n, size_t *lengthp)
+{
+ struct ds msg;
+ int i;
+
+ if (lengthp) {
+ *lengthp = 0;
+ }
+
+ ds_init(&msg);
+ ds_put_cstr(&msg, "invalid UTF-8 sequence");
+ for (i = 0; i < n; i++) {
+ ds_put_format(&msg, " 0x%02"PRIx8, s[i]);
+ }
+ return ds_steal_cstr(&msg);
+}
+
+struct utf8_sequence {
+ uint8_t octets[5][2];
+};
+
+static const struct utf8_sequence *
+lookup_utf8_sequence(uint8_t c)
+{
+ static const struct utf8_sequence seqs[] = {
+ { { { 0x01, 0x7f },
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xc2, 0xdf }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xe0, 0xe0 }, { 0xa0, 0xbf }, { 0x80, 0xbf },
+ {0,0}, {0, 0 } } },
+
+ { { { 0xe1, 0xec }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xed, 0xed }, { 0x80, 0x9f }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xee, 0xef }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xf0, 0xf0 }, { 0x90, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 } } },
+
+ { { { 0xf1, 0xf3 }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 } } },
+
+ { { { 0xf4, 0xf4 }, { 0x80, 0x8f }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 } } },
+ };
+
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(seqs); i++) {
+ const uint8_t *o = seqs[i].octets[0];
+ if (c >= o[0] && c <= o[1]) {
+ return &seqs[i];
+ }
+ }
+ return NULL;
+}
+
+/* Checks that 's' is a valid, null-terminated UTF-8 string. If so, returns a
+ * null pointer and sets '*lengthp' to the number of Unicode characters in
+ * 's'. If not, returns an error message that the caller must free and sets
+ * '*lengthp' to 0.
+ *
+ * 'lengthp' may be NULL if the length is not needed. */
+char *
+utf8_validate(const char *s_, size_t *lengthp)
+{
+ size_t length = 0;
+ const uint8_t *s;
+
+ for (s = (const uint8_t *) s_; *s != '\0'; ) {
+ length++;
+ if (s[0] < 0x80) {
+ s++;
+ } else {
+ const struct utf8_sequence *seq;
+ int i;
+
+ seq = lookup_utf8_sequence(s[0]);
+ if (!seq) {
+ return invalid_utf8_sequence(s, 1, lengthp);
+ }
+
+ for (i = 1; seq->octets[i][0]; i++) {
+ const uint8_t *o = seq->octets[i];
+ if (s[i] < o[0] || s[i] > o[1]) {
+ return invalid_utf8_sequence(s, i + 1, lengthp);
+ }
+ }
+ s += i;
+ }
+ }
+ if (lengthp) {
+ *lengthp = length;
+ }
+ return NULL;
+}
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define UNICODE_H 1
#include <stdbool.h>
+#include <stddef.h>
+#include "compiler.h"
/* Returns true if 'c' is a Unicode code point, otherwise false. */
static inline bool
int utf16_decode_surrogate_pair(int leading, int trailing);
+size_t utf8_length(const char *);
+char *utf8_validate(const char *, size_t *lengthp) WARN_UNUSED_RESULT;
+
#endif /* unicode.h */
fi])
dnl Checks for libpcre.
+dnl
+dnl ovsdb wants any reasonable version of libpcre (6.6 is what
+dnl XenServer 5.5 has).
+dnl
+dnl ezio-term wants libpcre that supports the PCRE_PARTIAL feature,
+dnl which is libpcre 7.2 or later.
AC_DEFUN([OVS_CHECK_PCRE],
[dnl Make sure that pkg-config is installed.
m4_pattern_forbid([PKG_CHECK_MODULES])
- PKG_CHECK_MODULES([PCRE], [libpcre >= 7.2], [HAVE_PCRE=yes], [HAVE_PCRE=no])
+ HAVE_PCRE=no
+ HAVE_PCRE_PARTIAL=no
+ PKG_CHECK_MODULES([PCRE], [libpcre >= 6.6],
+ [HAVE_PCRE=yes
+ PKG_CHECK_EXISTS([libpcre >= 7.2], [HAVE_PCRE_PARTIAL=yes])])
AM_CONDITIONAL([HAVE_PCRE], [test "$HAVE_PCRE" = yes])
+ AM_CONDITIONAL([HAVE_PCRE_PARTIAL], [test "$HAVE_PCRE_PARTIAL" = yes])
if test "$HAVE_PCRE" = yes; then
AC_DEFINE([HAVE_PCRE], [1], [Define to 1 if libpcre is installed.])
fi])
<string>
A JSON string. Any Unicode string is allowed, as specified by RFC
- 4627. Implementations may disallowed null bytes.
+ 4627. Implementations may disallow null bytes.
<id>
Any JSON value.
+<error>
+
+ A JSON object with the following members:
+
+ "error": <string> required
+ "details": <string> optional
+
+ The value of the "error" member is a short string, specified in
+ this document, that broadly indicates the class of the error.
+ Most "error" strings are specific to contexts described elsewhere
+ in this document, but the following "error" strings may appear in
+ any context where an <error> is permitted:
+
+ "error": "resources exhausted"
+
+ The operation requires more resources (memory, disk, CPU,
+ etc.) than are currently available to the database server.
+
+ "error": "I/O error"
+
+ Problems accessing the disk, network, or other required
+ resources prevented the operation from completing.
+
+ Database implementations may use "error" strings not specified
+ in this document to indicate errors that do not fit into any of
+ the specified categories.
+
+ Optionally, an <error> may include a "details" member, whose value
+ is a string that describes the error in more detail for the
+ benefit of a human user or administrator. This document does not
+ specify the format or content of the "details" string.
+
+ An <error> may also have other members that describe the error in
+ more detail. This document does not specify the names or values
+ of these members.
+
Schema Format
-------------
object that describes the type of a database column, with the
following members:
- "key": <atomic-type> required
- "value": <atomic-type> optional
+ "key": <base-type> required
+ "value": <base-type> optional
"min": <integer> optional
"max": <integer> or "unlimited" optional
If "value" is specified, the type is a map from type "key" to type
"value".
+<base-type>
+
+ The type of a key or value in a database column. Either an
+ <atomic-type> or a JSON object with the following members:
+
+ "type": <atomic-type> required
+ "minInteger": <integer> optional, integers only
+ "maxInteger": <integer> optional, integers only
+ "minReal": <real> optional, reals only
+ "maxReal": <real> optional, reals only
+ "reMatch": <string> optional, strings only
+ "reComment": <string> optional, strings only
+ "minLength": <integer> optional, strings only
+ "maxLength": <integer> optional, strings only
+ "refTable": <id> optional, uuids only
+
+ An <atomic-type> by itself is equivalent to a JSON object with a
+ single member "type" whose value is the <atomic-type>.
+
+ 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
+ equal to minInteger.
+
+ If "type" is "real", then "minReal" or "maxReal" or both may also
+ be specified, restricting the valid real range. If both are
+ specified, then the maxReal must be greater than or equal to
+ minReal.
+
+ If "type" is "string", then:
+
+ "reMatch" may be a JavaScript (Perl 5-like) regular expression
+ that restricts the allowed values. The regular expression
+ must match the entire string value, that is, it is treated as
+ if it begins with ^ and ends with $, regardless of whether it
+ really does.
+
+ If "reMatch" is specified, then "reComment" may be a string
+ that describes the allowed values, phrased so that it fits
+ into a sentence such as "This value must be...".
+
+ "minLength" and "maxLength" or both may be specified,
+ restricting the valid length of value strings. If both are
+ specified, then maxLength must be greater than or equal to
+ minLength. String length is measured in characters (not bytes
+ or UTF-16 code units).
+
+ The contraints on <base-type> are "immediate", enforced
+ immediately by each operation.
+
<atomic-type>
One of the strings "integer", "real", "boolean", "string", or
individual operations. Some operations do not produce any
results, in which case the object will have no members.
- - A JSON object that contains an "error" member indicates that the
- operation completed with an error. The value of the "error"
- member is a short string, specified in this document, that
- broadly indicates the class of the error. Besides the ones
- listed for a specific operation, any operation may result in one
- the following "error"s:
-
- "error": "resources exhausted"
-
- The operation or the transaction requires more resources
- (memory, disk, CPU, etc.) than are currently available to
- the database server.
-
- "error": "syntax error"
-
- The operation is not specified correctly: a required request
- object member is missing, an unknown or unsupported request
- object member is present, the operation attempts to act on a
- table that does not exist, the operation modifies a
- read-only table column, etc.
-
- Database implementations may use "error" strings not specified
- in this document to indicate errors that do not fit into any of
- the specified categories.
-
- Optionally, the object may include a "details" member, whose
- value is a string that describes the error in more detail for
- the benefit of a human user or administrator. The object may
- also have other members that describe the error in more detail.
- This document does not specify the names or values of these
- members.
+ - An <error>, which indicates that the operation completed with an
+ error.
- A JSON null value indicates that the operation was not attempted
because a prior operation failed.
difference, product, quotient, or remainder, respectively,
of <column> and <value>.
+ Constraints on <column> are ignored when parsing <value>.
+
boolean
string
uuid
Any <mutator> valid for the set's element type may be
applied to the set, in which case the mutation is applied
to each member of the set individually. <value> must be a
- scalar value of the same type as the set's element type.
+ scalar value of the same type as the set's element type,
+ except that contraints are ignored.
If <mutator> is "insert", then each of the values in the
set in <value> is added to <column> if it is not already
Semantics:
- Inserts "row" into "table". If "row" does not specify values
- for all the columns in "table", those columns receive default
- values.
+ Inserts "row" into "table".
+
+ If "row" does not specify values for all the columns in "table",
+ those columns receive default values. The default value for a
+ column depends on its type. The default for a column whose <type>
+ specifies a "min" of 0 is an empty set or empty map. Otherwise,
+ the default is a single value or a single key-value pair, whose
+ value(s) depend on its <atomic-type>:
+
+ - "integer" or "real": 0
+
+ - "boolean": false
+
+ - "string": "" (the empty string)
+
+ - "uuid": 00000000-0000-0000-0000-000000000000
If "uuid-name" is not supplied, the new row receives a new,
randomly generated UUID.
The same "uuid-name" appeared on an earlier "insert" operation
within this transaction.
+ "error": "constraint violation"
+
+ One of the values in "row" does not satisfy the immediate
+ constraints for its column's <base-type>. This error will
+ occur for columns that are not explicitly set by "row" if the
+ default value does not satisfy the column's constraints.
+
select
......
The "count" member of the result specifies the number of rows
that matched.
+Errors:
+
+ "error": "constraint violation"
+
+ One of the values in "row" does not satisfy the immediate
+ constraints for its column's <base-type>.
mutate
......
The mutation caused the column's value to violate a
constraint, e.g. it caused a column to have more or fewer
- values than are allowed or an arithmetic operation caused a
- set or map to have duplicate elements.
+ values than are allowed, an arithmetic operation caused a set
+ or map to have duplicate elements, or it violated a constraint
+ specified by a column's <base-type>.
delete
......
# ovsdb-tool
bin_PROGRAMS += ovsdb/ovsdb-tool
ovsdb_ovsdb_tool_SOURCES = ovsdb/ovsdb-tool.c
-ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
+ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(PCRE_LIBS)
# ovsdb-tool.1
man_MANS += ovsdb/ovsdb-tool.1
DISTCLEANFILES += ovsdb/ovsdb-tool.1
# ovsdb-client
bin_PROGRAMS += ovsdb/ovsdb-client
ovsdb_ovsdb_client_SOURCES = ovsdb/ovsdb-client.c
-ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS) $(PCRE_LIBS)
# ovsdb-client.1
man_MANS += ovsdb/ovsdb-client.1
DISTCLEANFILES += ovsdb/ovsdb-client.1
# ovsdb-server
sbin_PROGRAMS += ovsdb/ovsdb-server
ovsdb_ovsdb_server_SOURCES = ovsdb/ovsdb-server.c
-ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS) $(PCRE_LIBS)
# ovsdb-server.1
man_MANS += ovsdb/ovsdb-server.1
DISTCLEANFILES += ovsdb/ovsdb-server.1
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
column->comment = comment ? xstrdup(comment) : NULL;
column->mutable = mutable;
column->persistent = persistent;
- column->type = *type;
+ ovsdb_type_clone(&column->type, type);
return column;
}
void
ovsdb_column_destroy(struct ovsdb_column *column)
{
+ ovsdb_type_destroy(&column->type);
free(column->name);
free(column->comment);
free(column);
comment ? json_string(comment) : NULL,
mutable ? json_boolean(mutable) : true,
persistent, &type);
+
+ ovsdb_type_destroy(&type);
+
return NULL;
}
/* XXX this is O(n**2) */
for (i = 0; i < json->u.array.n; i++) {
- struct ovsdb_column *column;
+ const struct ovsdb_column *column;
const char *s;
if (json->u.array.elems[i]->type != JSON_STRING) {
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* n_max == 1? (They would always be "false" if the value was
* missing.) */
if (!ovsdb_type_is_scalar(&type)
- || (type.key_type != OVSDB_TYPE_INTEGER
- && type.key_type != OVSDB_TYPE_REAL)) {
+ || (type.key.type != OVSDB_TYPE_INTEGER
+ && type.key.type != OVSDB_TYPE_REAL)) {
char *s = ovsdb_type_to_english(&type);
error = ovsdb_syntax_error(
json, NULL, "Type mismatch: \"%s\" operator may not be "
if (ovsdb_type_is_scalar(type)) {
int cmp = ovsdb_atom_compare_3way(&field->keys[0], &arg->keys[0],
- type->key_type);
+ type->key.type);
switch (c->function) {
case OVSDB_F_LT:
return cmp < 0;
if (!error) {
error = parse_row(parser, "row", table, x->symtab, &row, NULL);
}
+ if (!error) {
+ /* Check constraints for columns not included in "row", in case the
+ * default values do not satisfy the constraints. We could check only
+ * the columns that have their default values by supplying an
+ * ovsdb_column_set to parse_row() above, but I suspect that this is
+ * cheaper. */
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ const struct ovsdb_datum *datum = &row->fields[column->index];
+
+ /* If there are 0 keys or pairs, there's nothing to check.
+ * If there is 1, it might be a default value.
+ * If there are more, it can't be a default value, so the value has
+ * already been checked. */
+ if (datum->n == 1) {
+ error = ovsdb_datum_check_constraints(datum, &column->type);
+ if (error) {
+ ovsdb_row_destroy(row);
+ break;
+ }
+ }
+ }
+ }
if (!error) {
*ovsdb_row_get_uuid_rw(row) = row_uuid;
ovsdb_txn_row_insert(x->txn, row);
json_object_put(result, "uuid",
ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID],
&ovsdb_type_uuid));
- row = NULL;
}
return error;
}
#include "row.h"
#include "table.h"
-enum mutate_error {
- ME_OK,
- ME_DOM,
- ME_RANGE,
- ME_COUNT,
- ME_DUP
-};
-
struct ovsdb_error *
ovsdb_mutator_from_string(const char *name, enum ovsdb_mutator *mutator)
{
"No column %s in table %s.",
column_name, ts->name);
}
- m->type = m->column->type;
+ ovsdb_type_clone(&m->type, &m->column->type);
mutator_name = json_string(array->elems[1]);
error = ovsdb_mutator_from_string(mutator_name, &m->mutator);
if (error) {
- return error;
+ goto exit;
}
/* Type-check and relax restrictions on 'type' if appropriate. */
case OVSDB_M_DIV:
case OVSDB_M_MOD:
if ((!ovsdb_type_is_scalar(&m->type) && !ovsdb_type_is_set(&m->type))
- || (m->type.key_type != OVSDB_TYPE_INTEGER
- && m->type.key_type != OVSDB_TYPE_REAL)
+ || (m->type.key.type != OVSDB_TYPE_INTEGER
+ && m->type.key.type != OVSDB_TYPE_REAL)
|| (m->mutator == OVSDB_M_MOD
- && m->type.key_type == OVSDB_TYPE_REAL)) {
+ && m->type.key.type == OVSDB_TYPE_REAL)) {
return type_mismatch(m, json);
}
+ ovsdb_base_type_clear_constraints(&m->type.key);
m->type.n_min = m->type.n_max = 1;
- return ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
- symtab);
+ error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
+ symtab);
+ break;
case OVSDB_M_INSERT:
case OVSDB_M_DELETE:
if (error && ovsdb_type_is_map(&m->type)
&& m->mutator == OVSDB_M_DELETE) {
ovsdb_error_destroy(error);
- m->type.value_type = OVSDB_TYPE_VOID;
+ m->type.value.type = OVSDB_TYPE_VOID;
error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
symtab);
}
- return error;
+ break;
+
+ default:
+ NOT_REACHED();
}
- NOT_REACHED();
+exit:
+ if (error) {
+ ovsdb_type_destroy(&m->type);
+ }
+ return error;
}
static void
ovsdb_mutation_free(struct ovsdb_mutation *m)
{
ovsdb_datum_destroy(&m->arg, &m->type);
+ ovsdb_type_destroy(&m->type);
}
struct ovsdb_error *
}
free(set->mutations);
}
+\f
+enum ovsdb_mutation_scalar_error {
+ ME_OK,
+ ME_DOM,
+ ME_RANGE
+};
+
+struct ovsdb_scalar_mutation {
+ int (*mutate_integer)(int64_t *x, int64_t y);
+ int (*mutate_real)(double *x, double y);
+ enum ovsdb_mutator mutator;
+};
+
+static const struct ovsdb_scalar_mutation add_mutation;
+static const struct ovsdb_scalar_mutation sub_mutation;
+static const struct ovsdb_scalar_mutation mul_mutation;
+static const struct ovsdb_scalar_mutation div_mutation;
+static const struct ovsdb_scalar_mutation mod_mutation;
+
+static struct ovsdb_error *
+ovsdb_mutation_scalar_error(enum ovsdb_mutation_scalar_error error,
+ enum ovsdb_mutator mutator)
+{
+ switch (error) {
+ case ME_OK:
+ return OVSDB_BUG("unexpected success");
+
+ case ME_DOM:
+ return ovsdb_error("domain error", "Division by zero.");
+
+ case ME_RANGE:
+ return ovsdb_error("range error",
+ "Result of \"%s\" operation is out of range.",
+ ovsdb_mutator_to_string(mutator));
+
+ default:
+ return OVSDB_BUG("unexpected error");
+ }
+}
+
+static int
+check_real_range(double x)
+{
+ return x >= -DBL_MAX && x <= DBL_MAX ? 0 : ME_RANGE;
+}
+
+static struct ovsdb_error *
+mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst,
+ const union ovsdb_atom *arg,
+ const struct ovsdb_scalar_mutation *mutation)
+{
+ const struct ovsdb_base_type *base = &dst_type->key;
+ struct ovsdb_error *error;
+ unsigned int i;
+
+ if (base->type == OVSDB_TYPE_INTEGER) {
+ int64_t y = arg->integer;
+ for (i = 0; i < dst->n; i++) {
+ enum ovsdb_mutation_scalar_error me;
+
+ me = (mutation->mutate_integer)(&dst->keys[i].integer, y);
+ if (me != ME_OK) {
+ return ovsdb_mutation_scalar_error(me, mutation->mutator);
+ }
+ }
+ } else if (base->type == OVSDB_TYPE_REAL) {
+ double y = arg->real;
+ for (i = 0; i < dst->n; i++) {
+ double *x = &dst->keys[i].real;
+ enum ovsdb_mutation_scalar_error me;
+
+ me = (mutation->mutate_real)(x, y);
+ if (me == ME_OK) {
+ me = check_real_range(*x);
+ }
+ if (me != ME_OK) {
+ return ovsdb_mutation_scalar_error(me, mutation->mutator);
+ }
+ }
+ } else {
+ NOT_REACHED();
+ }
+
+ for (i = 0; i < dst->n; i++) {
+ error = ovsdb_atom_check_constraints(&dst->keys[i], base);
+ if (error) {
+ return error;
+ }
+ }
+
+ error = ovsdb_datum_sort(dst, dst_type);
+ if (error) {
+ ovsdb_error_destroy(error);
+ return ovsdb_error("constraint violation",
+ "Result of \"%s\" operation contains duplicates.",
+ ovsdb_mutator_to_string(mutation->mutator));
+ }
+ return NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_mutation_check_count(struct ovsdb_datum *dst,
+ const struct ovsdb_type *dst_type)
+{
+ if (!ovsdb_datum_conforms_to_type(dst, dst_type)) {
+ char *s = ovsdb_type_to_english(dst_type);
+ struct ovsdb_error *e = ovsdb_error(
+ "constaint violation",
+ "Attempted to store %u elements in %s.", dst->n, s);
+ free(s);
+ return e;
+ }
+ return NULL;
+}
+
+struct ovsdb_error *
+ovsdb_mutation_set_execute(struct ovsdb_row *row,
+ const struct ovsdb_mutation_set *set)
+{
+ size_t i;
+
+ for (i = 0; i < set->n_mutations; i++) {
+ const struct ovsdb_mutation *m = &set->mutations[i];
+ struct ovsdb_datum *dst = &row->fields[m->column->index];
+ const struct ovsdb_type *dst_type = &m->column->type;
+ const struct ovsdb_datum *arg = &set->mutations[i].arg;
+ const struct ovsdb_type *arg_type = &m->type;
+ struct ovsdb_error *error;
+
+ switch (m->mutator) {
+ case OVSDB_M_ADD:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &add_mutation);
+ break;
+
+ case OVSDB_M_SUB:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &sub_mutation);
+ break;
+
+ case OVSDB_M_MUL:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &mul_mutation);
+ break;
+
+ case OVSDB_M_DIV:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &div_mutation);
+ break;
+
+ case OVSDB_M_MOD:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &mod_mutation);
+ break;
+ case OVSDB_M_INSERT:
+ ovsdb_datum_union(dst, arg, dst_type, false);
+ error = ovsdb_mutation_check_count(dst, dst_type);
+ break;
+
+ case OVSDB_M_DELETE:
+ ovsdb_datum_subtract(dst, dst_type, arg, arg_type);
+ error = ovsdb_mutation_check_count(dst, dst_type);
+ break;
+ }
+ if (error) {
+ return error;
+ }
+ }
+
+ return NULL;
+}
+\f
static int
add_int(int64_t *x, int64_t y)
{
return error;
}
-static int
-check_real_range(double x)
-{
- return x >= -DBL_MAX && x <= DBL_MAX ? 0 : ME_RANGE;
-}
-
static int
add_double(double *x, double y)
{
}
}
-static int
-mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst,
- const union ovsdb_atom *arg,
- int (*mutate_integer)(int64_t *x, int64_t y),
- int (*mutate_real)(double *x, double y))
-{
- struct ovsdb_error *error;
- unsigned int i;
-
- if (dst_type->key_type == OVSDB_TYPE_INTEGER) {
- int64_t y = arg->integer;
- for (i = 0; i < dst->n; i++) {
- int error = mutate_integer(&dst->keys[i].integer, y);
- if (error) {
- return error;
- }
- }
- } else if (dst_type->key_type == OVSDB_TYPE_REAL) {
- double y = arg->real;
- for (i = 0; i < dst->n; i++) {
- double *x = &dst->keys[i].real;
- int error = mutate_real(x, y);
- if (!error) {
- error = check_real_range(*x);
- }
- if (error) {
- return error;
- }
- }
- } else {
- NOT_REACHED();
- }
-
- error = ovsdb_datum_sort(dst, dst_type);
- if (error) {
- ovsdb_error_destroy(error);
- return ME_DUP;
- }
- return 0;
-}
-
-struct ovsdb_error *
-ovsdb_mutation_set_execute(struct ovsdb_row *row,
- const struct ovsdb_mutation_set *set)
-{
- size_t i;
-
- for (i = 0; i < set->n_mutations; i++) {
- const struct ovsdb_mutation *m = &set->mutations[i];
- struct ovsdb_datum *dst = &row->fields[m->column->index];
- const struct ovsdb_type *dst_type = &m->column->type;
- const struct ovsdb_datum *arg = &set->mutations[i].arg;
- const struct ovsdb_type *arg_type = &m->type;
- int error;
-
- switch (m->mutator) {
- case OVSDB_M_ADD:
- error = mutate_scalar(dst_type, dst, &arg->keys[0],
- add_int, add_double);
- break;
-
- case OVSDB_M_SUB:
- error = mutate_scalar(dst_type, dst, &arg->keys[0],
- sub_int, sub_double);
- break;
-
- case OVSDB_M_MUL:
- error = mutate_scalar(dst_type, dst, &arg->keys[0],
- mul_int, mul_double);
- break;
-
- case OVSDB_M_DIV:
- error = mutate_scalar(dst_type, dst, &arg->keys[0],
- div_int, div_double);
- break;
-
- case OVSDB_M_MOD:
- error = mutate_scalar(dst_type, dst, &arg->keys[0],
- mod_int, NULL);
- break;
-
- case OVSDB_M_INSERT:
- ovsdb_datum_union(dst, arg, dst_type, false);
- error = ovsdb_datum_conforms_to_type(dst, dst_type) ? 0 : ME_COUNT;
- break;
-
- case OVSDB_M_DELETE:
- ovsdb_datum_subtract(dst, dst_type, arg, arg_type);
- error = ovsdb_datum_conforms_to_type(dst, dst_type) ? 0 : ME_COUNT;
- break;
- }
+static const struct ovsdb_scalar_mutation add_mutation = {
+ add_int, add_double, OVSDB_M_ADD
+};
- switch (error) {
- case 0:
- break;
+static const struct ovsdb_scalar_mutation sub_mutation = {
+ sub_int, sub_double, OVSDB_M_SUB
+};
- case ME_DOM:
- return ovsdb_error("domain error", "Division by zero.");
-
- case ME_RANGE:
- return ovsdb_error("range error",
- "Result of \"%s\" operation is out of range.",
- ovsdb_mutator_to_string(m->mutator));
-
- case ME_DUP:
- return ovsdb_error("constraint violation",
- "Result of \"%s\" operation contains "
- "duplicates.",
- ovsdb_mutator_to_string(m->mutator));
-
- case ME_COUNT: {
- char *s = ovsdb_type_to_english(dst_type);
- struct ovsdb_error *e = ovsdb_error(
- "constaint violation",
- "Attempted to store %u elements in %s.", dst->n, s);
- free(s);
- return e;
- }
+static const struct ovsdb_scalar_mutation mul_mutation = {
+ mul_int, mul_double, OVSDB_M_MUL
+};
- default:
- return OVSDB_BUG("unexpected errno");
- }
- }
+static const struct ovsdb_scalar_mutation div_mutation = {
+ div_int, div_double, OVSDB_M_DIV
+};
- return NULL;
-}
+static const struct ovsdb_scalar_mutation mod_mutation = {
+ mod_int, NULL, OVSDB_M_MOD
+};
struct shash_node *column_node;
SHASH_FOR_EACH (column_node, &ts->columns) {
- struct ovsdb_column *column = column_node->data;
+ const struct ovsdb_column *column = column_node->data;
struct json *type = ovsdb_type_to_json(&column->type);
table_add_row(&t);
idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database')
return DbSchema(name, comment, tables, idlPrefix, idlHeader)
- def toJson(self):
- d = {"name": self.name,
- "tables": {}}
- for name, table in self.tables.iteritems():
- d["tables"][name] = table.toJson()
- if self.comment != None:
- d["comment"] = self.comment
- return d
-
class TableSchema:
def __init__(self, comment, columns):
self.comment = comment
json, "column %s in %s" % (name, description))
return TableSchema(comment, columns)
- def toJson(self):
- d = {"columns": {}}
- for name, column in self.columns.iteritems():
- d["columns"][name] = column.toJson()
- if self.comment != None:
- d["comment"] = self.comment
- return d
-
class ColumnSchema:
def __init__(self, comment, type, persistent):
self.comment = comment
persistent = ephemeral != True
return ColumnSchema(comment, type, persistent)
- def toJson(self):
- d = {"type": self.type.toJson()}
- if self.persistent == False:
- d["ephemeral"] = True
- if self.comment != None:
- d["comment"] = self.comment
- return d
+def escapeCString(src):
+ dst = ""
+ for c in src:
+ if c in "\\\"":
+ dst += "\\" + c
+ elif ord(c) < 32:
+ if c == '\n':
+ dst += '\\n'
+ elif c == '\r':
+ dst += '\\r'
+ elif c == '\a':
+ dst += '\\a'
+ elif c == '\b':
+ dst += '\\b'
+ elif c == '\f':
+ dst += '\\f'
+ elif c == '\t':
+ dst += '\\t'
+ elif c == '\v':
+ dst += '\\v'
+ else:
+ dst += '\\%03o' % ord(c)
+ else:
+ dst += c
+ return dst
+
+class BaseType:
+ def __init__(self, type, refTable=None, minInteger=None, maxInteger=None,
+ minReal=None, maxReal=None, reMatch=None, reComment=None,
+ minLength=None, maxLength=None):
+ self.type = type
+ self.refTable = refTable
+ self.minInteger = minInteger
+ self.maxInteger = maxInteger
+ self.minReal = minReal
+ self.maxReal = maxReal
+ self.reMatch = reMatch
+ self.reComment = reComment
+ self.minLength = minLength
+ self.maxLength = maxLength
+
+ @staticmethod
+ def fromJson(json, description):
+ if type(json) == unicode:
+ return BaseType(json)
+ else:
+ atomicType = mustGetMember(json, 'type', [unicode], description)
+ refTable = getMember(json, 'refTable', [unicode], description)
+ minInteger = getMember(json, 'minInteger', [int, long], description)
+ maxInteger = getMember(json, 'maxInteger', [int, long], description)
+ minReal = getMember(json, 'minReal', [int, long, float], description)
+ maxReal = getMember(json, 'maxReal', [int, long, float], description)
+ reMatch = getMember(json, 'reMatch', [unicode], description)
+ reComment = getMember(json, 'reComment', [unicode], description)
+ minLength = getMember(json, 'minLength', [int], description)
+ maxLength = getMember(json, 'minLength', [int], description)
+ return BaseType(atomicType, refTable, minInteger, maxInteger, minReal, maxReal, reMatch, reComment, minLength, maxLength)
+
+ def toEnglish(self):
+ if self.type == 'uuid' and self.refTable:
+ return self.refTable
+ else:
+ return self.type
+
+ def toCType(self, prefix):
+ if self.refTable:
+ return "struct %s%s *" % (prefix, self.refTable.lower())
+ else:
+ return {'integer': 'int64_t ',
+ 'real': 'double ',
+ 'uuid': 'struct uuid ',
+ 'boolean': 'bool ',
+ 'string': 'char *'}[self.type]
+
+ def copyCValue(self, dst, src):
+ args = {'dst': dst, 'src': src}
+ if self.refTable:
+ return ("%(dst)s = %(src)s->header_.uuid;") % args
+ elif self.type == 'string':
+ return "%(dst)s = xstrdup(%(src)s);" % args
+ else:
+ return "%(dst)s = %(src)s;" % args
+
+ def initCDefault(self, var, isOptional):
+ if self.refTable:
+ return "%s = NULL;" % var
+ elif self.type == 'string' and not isOptional:
+ return "%s = \"\";" % var
+ else:
+ return {'integer': '%s = 0;',
+ 'real': '%s = 0.0;',
+ 'uuid': 'uuid_zero(&%s);',
+ 'boolean': '%s = false;',
+ 'string': '%s = NULL;'}[self.type] % var
+
+ def cInitBaseType(self, indent, var):
+ stmts = []
+ stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % (
+ var, self.type.upper()),)
+ if self.type == 'integer':
+ if self.minInteger != None:
+ stmts.append('%s.u.integer.min = %d;' % (var, self.minInteger))
+ if self.maxInteger != None:
+ stmts.append('%s.u.integer.max = %d;' % (var, self.maxInteger))
+ elif self.type == 'real':
+ if self.minReal != None:
+ stmts.append('%s.u.real.min = %d;' % (var, self.minReal))
+ if self.maxReal != None:
+ stmts.append('%s.u.real.max = %d;' % (var, self.maxReal))
+ elif self.type == 'string':
+ if self.reMatch != None:
+ if self.reComment != None:
+ reComment = '"%s"' % escapeCString(self.reComment)
+ else:
+ reComment = NULL
+ stmts.append('do_set_regex(&%s, "%s", %s);' % (
+ var, escapeCString(self.reMatch), reComment))
+ if self.minLength != None:
+ stmts.append('%s.u.string.minLen = %d;' % (var, self.minLength))
+ if self.maxLength != None:
+ stmts.append('%s.u.string.maxLen = %d;' % (var, self.maxLength))
+ return '\n'.join([indent + stmt for stmt in stmts])
class Type:
- def __init__(self, key, keyRefTable=None, value=None, valueRefTable=None,
- min=1, max=1):
+ def __init__(self, key, value=None, min=1, max=1):
self.key = key
- self.keyRefTable = keyRefTable
self.value = value
- self.valueRefTable = valueRefTable
self.min = min
self.max = max
@staticmethod
def fromJson(json, description):
if type(json) == unicode:
- return Type(json)
+ return Type(BaseType(json))
else:
- key = mustGetMember(json, 'key', [unicode], description)
+ keyJson = mustGetMember(json, 'key', [dict, unicode], description)
+ key = BaseType.fromJson(keyJson, 'key in %s' % description)
keyRefTable = getMember(json, 'keyRefTable', [unicode], description)
- value = getMember(json, 'value', [unicode], description)
- valueRefTable = getMember(json, 'valueRefTable', [unicode], description)
+ if keyRefTable:
+ key.refTable = keyRefTable
+
+ valueJson = getMember(json, 'value', [dict, unicode], description)
+ if valueJson:
+ value = BaseType.fromJson(valueJson,
+ 'value in %s' % description)
+ valueRefTable = getMember(json, 'valueRefTable', [unicode], description)
+ if valueRefTable:
+ value.refTable = valueRefTable
+ else:
+ value = None
+
min = getMember(json, 'min', [int], description, 1)
max = getMember(json, 'max', [int, unicode], description, 1)
- return Type(key, keyRefTable, value, valueRefTable, min, max)
-
- def toJson(self):
- if self.value == None and self.min == 1 and self.max == 1:
- return self.key
- else:
- d = {"key": self.key}
- if self.value != None:
- d["value"] = self.value
- if self.min != 1:
- d["min"] = self.min
- if self.max != 1:
- d["max"] = self.max
- return d
+ return Type(key, value, min, max)
def isScalar(self):
return self.min == 1 and self.max == 1 and not self.value
def isOptional(self):
return self.min == 0 and self.max == 1
+ def isOptionalPointer(self):
+ return (self.min == 0 and self.max == 1 and not self.value
+ and (self.key.type == 'string' or self.key.refTable))
+
def toEnglish(self):
- keyName = atomicTypeToEnglish(self.key, self.keyRefTable)
+ keyName = self.key.toEnglish()
if self.value:
- valueName = atomicTypeToEnglish(self.value, self.valueRefTable)
+ valueName = self.value.toEnglish()
if self.isScalar():
- return atomicTypeToEnglish(self.key, self.keyRefTable)
+ return keyName
elif self.isOptional():
if self.value:
return "optional %s-%s pair" % (keyName, valueName)
else:
return "set of %s%s" % (quantity, keyName)
+ def cDeclComment(self):
+ if self.min == 1 and self.max == 1 and self.key.type == "string":
+ return "\t/* Always nonnull. */"
+ else:
+ return ""
-def atomicTypeToEnglish(base, refTable):
- if base == 'uuid' and refTable:
- return refTable
- else:
- return base
+ def cInitType(self, indent, var):
+ initKey = self.key.cInitBaseType(indent, "%s.key" % var)
+ if self.value:
+ initValue = self.value.cInitBaseType(indent, "%s.value" % var)
+ else:
+ initValue = ('%sovsdb_base_type_init(&%s.value, '
+ 'OVSDB_TYPE_VOID);' % (indent, var))
+ initMin = "%s%s.n_min = %s;" % (indent, var, self.min)
+ if self.max == "unlimited":
+ max = "UINT_MAX"
+ else:
+ max = self.max
+ initMax = "%s%s.n_max = %s;" % (indent, var, max)
+ return "\n".join((initKey, initValue, initMin, initMax))
def parseSchema(filename):
return DbSchema.fromJson(json.load(open(filename, "r")))
execfile(annotationFile, globals(), {"s": schemaJson})
json.dump(schemaJson, sys.stdout)
-def cBaseType(prefix, type, refTable=None):
- if type == 'uuid' and refTable:
- return "struct %s%s *" % (prefix, refTable.lower())
- else:
- return {'integer': 'int64_t ',
- 'real': 'double ',
- 'uuid': 'struct uuid ',
- 'boolean': 'bool ',
- 'string': 'char *'}[type]
-
-def cCopyType(indent, dst, src, type, refTable=None):
- args = {'indent': indent,
- 'dst': dst,
- 'src': src}
- if type == 'uuid' and refTable:
- return ("%(indent)s%(dst)s = %(src)s->header_.uuid;") % args
- elif type == 'string':
- return "%(indent)s%(dst)s = xstrdup(%(src)s);" % args
- else:
- return "%(indent)s%(dst)s = %(src)s;" % args
-
-def typeIsOptionalPointer(type):
- return (type.min == 0 and type.max == 1 and not type.value
- and (type.key == 'string'
- or (type.key == 'uuid' and type.keyRefTable)))
-
-def cDeclComment(type):
- if type.min == 1 and type.max == 1 and type.key == "string":
- return "\t/* Always nonnull. */"
- else:
- return ""
-
-def cInitDefault(var, type, refTable, isOptional):
- if type == 'uuid' and refTable:
- return "%s = NULL;" % var
- elif type == 'string' and not isOptional:
- return "%s = \"\";" % var
- else:
- return {'integer': '%s = 0;',
- 'real': '%s = 0.0;',
- 'uuid': 'uuid_zero(&%s);',
- 'boolean': '%s = false;',
- 'string': '%s = NULL;'}[type] % var
-
def constify(cType, const):
if (const
and cType.endswith('*') and not cType.endswith('**')
pointer = ''
else:
singleton = False
- if typeIsOptionalPointer(type):
+ if type.isOptionalPointer():
pointer = ''
else:
pointer = '*'
if type.value:
key = {'name': "key_%s" % columnName,
- 'type': constify(cBaseType(prefix, type.key, type.keyRefTable) + pointer, const),
+ 'type': constify(type.key.toCType(prefix) + pointer, const),
'comment': ''}
value = {'name': "value_%s" % columnName,
- 'type': constify(cBaseType(prefix, type.value, type.valueRefTable) + pointer, const),
+ 'type': constify(type.value.toCType(prefix) + pointer, const),
'comment': ''}
members = [key, value]
else:
m = {'name': columnName,
- 'type': constify(cBaseType(prefix, type.key, type.keyRefTable) + pointer, const),
- 'comment': cDeclComment(type)}
+ 'type': constify(type.key.toCType(prefix) + pointer, const),
+ 'comment': type.cDeclComment()}
members = [m]
- if not singleton and not typeIsOptionalPointer(type):
+ if not singleton and not type.isOptionalPointer():
members.append({'name': 'n_%s' % columnName,
'type': 'size_t ',
'comment': ''})
#include "ovsdb-idl-provider.h"
#include "uuid.h"''' % {'prefix': prefix.upper()}
- for tableName, table in schema.tables.iteritems():
+ for tableName, table in sorted(schema.tables.iteritems()):
structName = "%s%s" % (prefix, tableName.lower())
print "\f"
print "/* %s table. */" % tableName
print "struct %s {" % structName
print "\tstruct ovsdb_idl_row header_;"
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
print "\n\t/* %s column. */" % columnName
for member in cMembers(prefix, columnName, column, False):
print "\t%(type)s%(name)s;%(comment)s" % member
# Column indexes.
printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper())
- for columnName in table.columns]
+ for columnName in sorted(table.columns)]
+ ["%s_N_COLUMNS" % structName.upper()])
print
struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
''' % {'s': structName, 'S': structName.upper()}
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
print 'void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': structName, 'c': columnName}
print
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName},
args = ['%(type)s%(name)s' % member for member
print '%s);' % ', '.join(args)
# Table indexes.
- printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in schema.tables] + ["%sN_TABLES" % prefix.upper()])
+ printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()])
print
for tableName in schema.tables:
print "#define %(p)stable_%(t)s (%(p)stable_classes[%(P)sTABLE_%(T)s])" % {
print "\nextern struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper())
print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix
+ print "\nvoid %sinit(void);" % prefix
print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
def printEnum(members):
#include <config.h>
#include %s
+#include <assert.h>
#include <limits.h>
-#include "ovsdb-data.h"''' % schema.idlHeader
+#include "ovsdb-data.h"
+#include "ovsdb-error.h"
+
+static bool inited;
+
+static void UNUSED
+do_set_regex(struct ovsdb_base_type *base, const char *reMatch,
+ const char *reComment)
+{
+ struct ovsdb_error *error;
+
+ error = ovsdb_base_type_set_regex(base, reMatch, reComment);
+ if (error) {
+ char *s = ovsdb_error_to_string(error);
+ ovs_error(0, "%%s", s);
+ free(s);
+ ovsdb_error_destroy(error);
+ }
+}''' % schema.idlHeader
# Cast functions.
- for tableName, table in schema.tables.iteritems():
+ for tableName, table in sorted(schema.tables.iteritems()):
structName = "%s%s" % (prefix, tableName.lower())
print '''
static struct %(s)s *
''' % {'s': structName}
- for tableName, table in schema.tables.iteritems():
+ for tableName, table in sorted(schema.tables.iteritems()):
structName = "%s%s" % (prefix, tableName.lower())
print "\f"
if table.comment != None:
print "/* %s table. */" % (tableName)
# Parse functions.
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
print '''
static void
%(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum)
'c': columnName}
type = column.type
- refKey = type.key == "uuid" and type.keyRefTable
- refValue = type.value == "uuid" and type.valueRefTable
if type.value:
keyVar = "row->key_%s" % columnName
valueVar = "row->value_%s" % columnName
keyVar = "row->%s" % columnName
valueVar = None
- if ((type.min == 1 and type.max == 1) or
- typeIsOptionalPointer(type)):
+ if (type.min == 1 and type.max == 1) or type.isOptionalPointer():
print
+ print " assert(inited);"
print " if (datum->n >= 1) {"
- if not refKey:
- print " %s = datum->keys[0].%s;" % (keyVar, type.key)
+ if not type.key.refTable:
+ print " %s = datum->keys[0].%s;" % (keyVar, type.key.type)
else:
- print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.keyRefTable.lower(), prefix, prefix.upper(), type.keyRefTable.upper())
+ print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
if valueVar:
- if refValue:
- print " %s = datum->values[0].%s;" % (valueVar, type.value)
+ if type.value.refTable:
+ print " %s = datum->values[0].%s;" % (valueVar, type.value.type)
else:
- print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.valueRefTable.lower(), prefix, prefix.upper(), type.valueRefTable.upper())
+ print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
print " } else {"
- print " %s" % cInitDefault(keyVar, type.key, type.keyRefTable, type.min == 0)
+ print " %s" % type.key.initCDefault(keyVar, type.min == 0)
if valueVar:
- print " %s" % cInitDefault(valueVar, type.value, type.valueRefTable, type.min == 0)
+ print " %s" % type.value.initCDefault(valueVar, type.min == 0)
print " }"
else:
if type.max != 'unlimited':
nMax = "datum->n"
print " size_t i;"
print
+ print " assert(inited);"
print " %s = NULL;" % keyVar
if valueVar:
print " %s = NULL;" % valueVar
print " row->n_%s = 0;" % columnName
print " for (i = 0; i < %s; i++) {" % nMax
refs = []
- if refKey:
- print " struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.keyRefTable.lower(), prefix, type.keyRefTable.lower(), prefix, prefix.upper(), type.keyRefTable.upper())
+ if type.key.refTable:
+ print " struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.key.refTable.lower(), prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
keySrc = "keyRow"
refs.append('keyRow')
else:
- keySrc = "datum->keys[i].%s" % type.key
- if refValue:
- print " struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.valueRefTable.lower(), prefix, type.valueRefTable.lower(), prefix, prefix.upper(), type.valueRefTable.upper())
+ keySrc = "datum->keys[i].%s" % type.key.type
+ if type.value and type.value.refTable:
+ print " struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.value.refTable.lower(), prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
valueSrc = "valueRow"
refs.append('valueRow')
elif valueVar:
- valueSrc = "datum->values[i].%s" % type.value
+ valueSrc = "datum->values[i].%s" % type.value.type
if refs:
print " if (%s) {" % ' && '.join(refs)
indent = " "
print "}"
# Unparse functions.
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
type = column.type
- if (type.min != 1 or type.max != 1) and not typeIsOptionalPointer(type):
+ if (type.min != 1 or type.max != 1) and not type.isOptionalPointer():
print '''
static void
%(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
{
struct %(s)s *row = %(s)s_cast(row_);
-''' % {'s': structName, 'c': columnName}
+
+ assert(inited);''' % {'s': structName, 'c': columnName}
if type.value:
keyVar = "row->key_%s" % columnName
valueVar = "row->value_%s" % columnName
'T': tableName.upper()}
# Verify functions.
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
print '''
void
%(s)s_verify_%(c)s(const struct %(s)s *row)
{
+ assert(inited);
ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]);
}''' % {'s': structName,
'S': structName.upper(),
'C': columnName.upper()}
# Set functions.
- for columnName, column in table.columns.iteritems():
+ for columnName, column in sorted(table.columns.iteritems()):
type = column.type
print '\nvoid'
members = cMembers(prefix, columnName, column, True)
print " struct ovsdb_datum datum;"
if type.min == 1 and type.max == 1:
print
+ print " assert(inited);"
print " datum.n = 1;"
print " datum.keys = xmalloc(sizeof *datum.keys);"
- print cCopyType(" ", "datum.keys[0].%s" % type.key, keyVar, type.key, type.keyRefTable)
+ print " " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
if type.value:
print " datum.values = xmalloc(sizeof *datum.values);"
- print cCopyType(" ", "datum.values[0].%s" % type.value, valueVar, type.value, type.valueRefTable)
+ print " "+ type.value.copyCValue("datum.values[0].%s" % type.value.type, valueVar)
else:
print " datum.values = NULL;"
- elif typeIsOptionalPointer(type):
+ elif type.isOptionalPointer():
print
+ print " assert(inited);"
print " if (%s) {" % keyVar
print " datum.n = 1;"
print " datum.keys = xmalloc(sizeof *datum.keys);"
- print cCopyType(" ", "datum.keys[0].%s" % type.key, keyVar, type.key, type.keyRefTable)
+ print " " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
print " } else {"
print " datum.n = 0;"
print " datum.keys = NULL;"
else:
print " size_t i;"
print
+ print " assert(inited);"
print " datum.n = %s;" % nVar
print " datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar
if type.value:
else:
print " datum.values = NULL;"
print " for (i = 0; i < %s; i++) {" % nVar
- print cCopyType(" ", "datum.keys[i].%s" % type.key, "%s[i]" % keyVar, type.key, type.keyRefTable)
+ print " " + type.key.copyCValue("datum.keys[i].%s" % type.key.type, "%s[i]" % keyVar)
if type.value:
- print cCopyType(" ", "datum.values[i].%s" % type.value, "%s[i]" % valueVar, type.value, type.valueRefTable)
+ print " " + type.value.copyCValue("datum.values[i].%s" % type.value.type, "%s[i]" % valueVar)
print " }"
print " ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
% {'s': structName,
print "}"
# Table columns.
- print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS] = {" % (
+ print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
structName, structName.upper())
- for columnName, column in table.columns.iteritems():
- type = column.type
-
- if type.value:
- valueTypeName = type.value.upper()
- else:
- valueTypeName = "VOID"
- if type.max == "unlimited":
- max = "UINT_MAX"
- else:
- max = type.max
- print """\
- {"%(c)s",
- {OVSDB_TYPE_%(kt)s, OVSDB_TYPE_%(vt)s, %(min)s, %(max)s},
- %(s)s_parse_%(c)s,
- %(s)s_unparse_%(c)s},""" % {'c': columnName,
- 's': structName,
- 'kt': type.key.upper(),
- 'vt': valueTypeName,
- 'min': type.min,
- 'max': max}
- print "};"
+ print """
+static void\n%s_columns_init(void)
+{
+ struct ovsdb_idl_column *c;\
+""" % structName
+ for columnName, column in sorted(table.columns.iteritems()):
+ cs = "%s_col_%s" % (structName, columnName)
+ d = {'cs': cs, 'c': columnName, 's': structName}
+ print
+ print " /* Initialize %(cs)s. */" % d
+ print " c = &%(cs)s;" % d
+ print " c->name = \"%(c)s\";" % d
+ print column.type.cInitType(" ", "c->type")
+ print " c->parse = %(s)s_parse_%(c)s;" % d
+ print " c->unparse = %(s)s_unparse_%(c)s;" % d
+ print "}"
# Table classes.
print "\f"
print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
- for tableName, table in schema.tables.iteritems():
+ for tableName, table in sorted(schema.tables.iteritems()):
structName = "%s%s" % (prefix, tableName.lower())
print " {\"%s\"," % tableName
print " %s_columns, ARRAY_SIZE(%s_columns)," % (
print " %stable_classes, ARRAY_SIZE(%stable_classes)" % (prefix, prefix)
print "};"
+ # global init function
+ print """
+void
+%sinit(void)
+{
+ if (inited) {
+ return;
+ }
+ inited = true;
+""" % prefix
+ for tableName, table in sorted(schema.tables.iteritems()):
+ structName = "%s%s" % (prefix, tableName.lower())
+ print " %s_columns_init();" % structName
+ print "}"
+
def ovsdb_escape(string):
def escape(match):
c = match.group(0)
name_, table_name, column_name);
}
- if (column->type.key_type != OVSDB_TYPE_STRING
- || column->type.value_type != OVSDB_TYPE_VOID) {
+ if (column->type.key.type != OVSDB_TYPE_STRING
+ || column->type.value.type != OVSDB_TYPE_VOID) {
ovs_fatal(0, "remote \"%s\": type of table \"%s\" column \"%s\" is "
"not string or set of strings",
name_, table_name, column_name);
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
columns = json_object_create();
SHASH_FOR_EACH (node, &ts->columns) {
- struct ovsdb_column *column = node->data;
+ const struct ovsdb_column *column = node->data;
if (node->name[0] != '_') {
json_object_put(columns, column->name,
ovsdb_column_to_json(column));
tests/idltest.c \
tests/idltest.h
EXTRA_DIST += tests/uuidfilt.pl
-tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS) $(PCRE_LIBS)
# idltest schema and IDL
OVSIDL_BUILT += tests/idltest.c tests/idltest.h tests/idltest.ovsidl
cp stdout out2
AT_CHECK([perl $srcdir/uuidfilt.pl out1 out2], [0],
[[<0>
-_uuid (RO): <0>
-controller (RO): []
-datapath_id (RO): []
-datapath_type (RO): ""
-external_ids (RW): {}
-flood_vlans (RW): []
-mirrors (RO): []
-name (RO): "br0"
-netflow (RO): []
-other_config (RW): {}
-ports (RO): []
+_uuid : <0>
+controller : []
+datapath_id : []
+datapath_type : ""
+external_ids : {}
+flood_vlans : []
+mirrors : []
+name : "br0"
+netflow : []
+other_config : {}
+ports : []
+sflow : []
]], [ignore], [test ! -e pid || kill `cat pid`])
AT_CHECK(
[RUN_OVS_VSCTL(
[0], [stdout], [], [OVS_VSCTL_CLEANUP])
AT_CHECK([perl $srcdir/uuidfilt.pl netflow-uuid stdout], [0],
[[<0>
-_uuid (RO): <0>
-active_timeout (RW): 0
-add_id_to_interface (RW): false
-engine_id (RW): []
-engine_type (RW): []
-targets (RW): ["1.2.3.4:567"]
+_uuid : <0>
+active_timeout : 0
+add_id_to_interface : false
+engine_id : []
+engine_type : []
+targets : ["1.2.3.4:567"]
]], [ignore], [test ! -e pid || kill `cat pid`])
AT_CHECK([RUN_OVS_VSCTL([list interx x])],
[1], [], [ovs-vsctl: unknown table "interx"
[1], [], [ovs-vsctl: no key "x" in Bridge record "br0" column external_ids
], [OVS_VSCTL_CLEANUP])
AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=-1])],
- [1], [], [ovs-vsctl: -1 is outside the valid range 1 to 4095 (inclusive)
+ [1], [], [ovs-vsctl: constraint violation: -1 is not in the valid range 0 to 4095 (inclusive)
], [OVS_VSCTL_CLEANUP])
AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=4096])],
- [1], [], [ovs-vsctl: 4096 is outside the valid range 1 to 4095 (inclusive)
-], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set b br0 datapath_id=4096])],
- [1], [], [ovs-vsctl: datapath_id=4096: cannot modify read-only column datapath_id in table Bridge
+ [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: xyz is not valid (it does not match ^in-band|out-of-band$)
+ [1], [], [ovs-vsctl: constraint violation: "xyz" is not a either "in-band" or "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])
-AT_CHECK([RUN_OVS_VSCTL([add b br1 datapath_id x=y])],
- [1], [], [ovs-vsctl: cannot modify read-only column datapath_id in table Bridge
-], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([--force add b br1 datapath_id x y])],
+AT_CHECK([RUN_OVS_VSCTL([add b br1 datapath_id x y])],
[1], [], [ovs-vsctl: "add" operation would put 2 values in column datapath_id of table Bridge but the maximum number is 1
], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([remove b br1 datapath_id x=y])],
- [1], [], [ovs-vsctl: cannot modify read-only column datapath_id in table Bridge
-], [OVS_VSCTL_CLEANUP])
AT_CHECK([RUN_OVS_VSCTL([remove n `cat netflow-uuid` targets '"1.2.3.4:567"'])],
[1], [], [ovs-vsctl: "remove" operation would put 0 values in column targets of table NetFlow but the minimun number is 1
], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([clear bri br0 netflow])],
- [1], [], [ovs-vsctl: cannot modify read-only column netflow in table Bridge
-], [OVS_VSCTL_CLEANUP])
AT_CHECK([RUN_OVS_VSCTL([clear n `cat netflow-uuid` targets])],
[1], [], [ovs-vsctl: "clear" operation cannot be applied to column targets of table NetFlow, which is not allowed to be empty
], [OVS_VSCTL_CLEANUP])
[0], [stdout], [], [OVS_VSCTL_CLEANUP])
AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0],
[[<0>
-_uuid (RO): <1>
-controller (RO): []
-datapath_id (RO): []
-datapath_type (RO): ""
-external_ids (RW): {}
-flood_vlans (RW): []
-mirrors (RO): []
-name (RO): "br0"
-netflow (RO): []
-other_config (RW): {}
-ports (RO): []
+_uuid : <1>
+controller : []
+datapath_id : []
+datapath_type : ""
+external_ids : {}
+flood_vlans : []
+mirrors : []
+name : "br0"
+netflow : []
+other_config : {}
+ports : []
+sflow : []
]], [ignore], [test ! -e pid || kill `cat pid`])
OVS_VSCTL_CLEANUP
AT_CLEANUP
-AT_BANNER([OVSDB -- atoms])
+AT_BANNER([OVSDB -- atoms without constraints])
OVSDB_CHECK_POSITIVE([integer atom from JSON],
[[parse-atoms '["integer"]' \
["uuid", "00001000-0000-0000-0000-000000000000"]]']],
[[[["uuid","00000000-0000-0000-0000-000000000000"],["uuid","00000000-0000-0000-0000-000000000001"],["uuid","00000000-0000-0000-0000-000000000010"],["uuid","00000000-0000-0000-0000-000000000100"],["uuid","00000000-0000-0000-0000-000000001000"],["uuid","00000000-0000-0000-0000-000000010000"],["uuid","00000000-0000-0000-0000-000000100000"],["uuid","00000000-0000-0000-0000-000001000000"],["uuid","00000000-0000-0000-0000-000010000000"],["uuid","00000000-0000-0000-0000-000100000000"],["uuid","00000000-0000-0000-0000-001000000000"],["uuid","00000000-0000-0000-0000-010000000000"],["uuid","00000000-0000-0000-0000-100000000000"],["uuid","00000000-0000-0000-0001-000000000000"],["uuid","00000000-0000-0000-0010-000000000000"],["uuid","00000000-0000-0000-0100-000000000000"],["uuid","00000000-0000-0000-1000-000000000000"],["uuid","00000000-0000-0001-0000-000000000000"],["uuid","00000000-0000-0010-0000-000000000000"],["uuid","00000000-0000-0100-0000-000000000000"],["uuid","00000000-0000-1000-0000-000000000000"],["uuid","00000000-0001-0000-0000-000000000000"],["uuid","00000000-0010-0000-0000-000000000000"],["uuid","00000000-0100-0000-0000-000000000000"],["uuid","00000000-1000-0000-0000-000000000000"],["uuid","00000001-0000-0000-0000-000000000000"],["uuid","00000010-0000-0000-0000-000000000000"],["uuid","00000100-0000-0000-0000-000000000000"],["uuid","00001000-0000-0000-0000-000000000000"],["uuid","00010000-0000-0000-0000-000000000000"],["uuid","00100000-0000-0000-0000-000000000000"],["uuid","01000000-0000-0000-0000-000000000000"],["uuid","10000000-0000-0000-0000-000000000000"]]]])
-OVSDB_CHECK_NEGATIVE([real not acceptable integer JSON atom],
+OVSDB_CHECK_POSITIVE([real not acceptable integer JSON atom],
[[parse-atoms '["integer"]' '[0.5]' ]],
- [expected integer])
+ [syntax "0.5": syntax error: expected integer])
+
+dnl <C0> is not allowed anywhere in a UTF-8 string.
+dnl <ED A0 80> is a surrogate and not allowed in UTF-8.
+OVSDB_CHECK_POSITIVE([no invalid UTF-8 sequences in strings],
+ [parse-atoms '[["string"]]' \
+ '@<:@"m4_esyscmd([printf "\xc0"])"@:>@' \
+ '@<:@"m4_esyscmd([printf "\xed\xa0\x80"])"@:>@' \
+],
+ [constraint violation: "m4_esyscmd([printf "\xc0"])" is not a valid UTF-8 string: invalid UTF-8 sequence 0xc0
+constraint violation: "m4_esyscmd([printf "\xed\xa0\x80"])" is not a valid UTF-8 string: invalid UTF-8 sequence 0xed 0xa0])
OVSDB_CHECK_NEGATIVE([real not acceptable integer string atom],
[[parse-atom-strings '["integer"]' '0.5' ]],
["0.5" is not a valid integer])
-OVSDB_CHECK_NEGATIVE([string "true" not acceptable boolean JSON atom],
+OVSDB_CHECK_POSITIVE([string "true" not acceptable boolean JSON atom],
[[parse-atoms '["boolean"]' '["true"]' ]],
- [expected boolean])
+ [syntax ""true"": syntax error: expected boolean])
OVSDB_CHECK_NEGATIVE([string "true" not acceptable boolean string atom],
[[parse-atom-strings '["boolean"]' '"true"' ]],
[""true"" is not a valid boolean (use "true" or "false")])
-OVSDB_CHECK_NEGATIVE([integer not acceptable string JSON atom],
+OVSDB_CHECK_POSITIVE([integer not acceptable string JSON atom],
[[parse-atoms '["string"]' '[1]']],
- [expected string])
+ [syntax "1": syntax error: expected string])
-OVSDB_CHECK_NEGATIVE([uuid atom must be expressed as JSON array],
+OVSDB_CHECK_POSITIVE([uuid atom must be expressed as JSON array],
[[parse-atoms '["uuid"]' '["550e8400-e29b-41d4-a716-446655440000"]']],
- [[expected ["uuid", <string>]]])
+ [[syntax ""550e8400-e29b-41d4-a716-446655440000"": syntax error: expected ["uuid", <string>]]])
OVSDB_CHECK_NEGATIVE([empty string atom must be quoted],
[[parse-atom-strings '["string"]' '']],
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])
+
+OVSDB_CHECK_POSITIVE([integers >= 5],
+ [[parse-atoms '[{"type": "integer", "minInteger": 5}]' \
+ '[0]' \
+ '[4]' \
+ '[5]' \
+ '[6]' \
+ '[12345]']],
+ [constraint violation: 0 is less than minimum allowed value 5
+constraint violation: 4 is less than minimum allowed value 5
+5
+6
+12345])
+
+OVSDB_CHECK_POSITIVE([integers <= -1],
+ [[parse-atoms '[{"type": "integer", "maxInteger": -1}]' \
+ '[0]' \
+ '[-1]' \
+ '[-2]' \
+ '[-123]']],
+ [constraint violation: 0 is greater than maximum allowed value -1
+-1
+-2
+-123])
+
+OVSDB_CHECK_POSITIVE([integers in range -10 to 10],
+ [[parse-atoms '[{"type": "integer", "minInteger": -10, "maxInteger": 10}]' \
+ '[-20]' \
+ '[-11]' \
+ '[-10]' \
+ '[-9]' \
+ '[1]' \
+ '[9]' \
+ '[10]' \
+ '[11]' \
+ '[123576]']],
+ [constraint violation: -20 is not in the valid range -10 to 10 (inclusive)
+constraint violation: -11 is not in the valid range -10 to 10 (inclusive)
+-10
+-9
+1
+9
+10
+constraint violation: 11 is not in the valid range -10 to 10 (inclusive)
+constraint violation: 123576 is not in the valid range -10 to 10 (inclusive)])
+
+OVSDB_CHECK_POSITIVE([reals >= 5],
+ [[parse-atoms '[{"type": "real", "minReal": 5}]' \
+ '[0]' \
+ '[4]' \
+ '[5]' \
+ '[6]' \
+ '[12345]']],
+ [constraint violation: 0 is less than minimum allowed value 5
+constraint violation: 4 is less than minimum allowed value 5
+5
+6
+12345])
+
+OVSDB_CHECK_POSITIVE([reals <= -1],
+ [[parse-atoms '[{"type": "real", "maxReal": -1}]' \
+ '[0]' \
+ '[-1]' \
+ '[-2]' \
+ '[-123]']],
+ [constraint violation: 0 is greater than maximum allowed value -1
+-1
+-2
+-123])
+
+OVSDB_CHECK_POSITIVE([reals in range -10 to 10],
+ [[parse-atoms '[{"type": "real", "minReal": -10, "maxReal": 10}]' \
+ '[-20]' \
+ '[-11]' \
+ '[-10]' \
+ '[-9]' \
+ '[1]' \
+ '[9]' \
+ '[10]' \
+ '[11]' \
+ '[123576]']],
+ [constraint violation: -20 is not in the valid range -10 to 10 (inclusive)
+constraint violation: -11 is not in the valid range -10 to 10 (inclusive)
+-10
+-9
+1
+9
+10
+constraint violation: 11 is not in the valid range -10 to 10 (inclusive)
+constraint violation: 123576 is not in the valid range -10 to 10 (inclusive)])
+
+OVSDB_CHECK_POSITIVE([strings matching /(a(b)?)c?/],
+ [[parse-atoms '{"type": "string", "reMatch": "(a(b)?)?c?"}' \
+ '[""]' \
+ '["a"]' \
+ '["ab"]' \
+ '["abc"]' \
+ '["ac"]' \
+ '["b"]' \
+ '["bc"]' \
+ '["c"]']],
+ [[""
+"a"
+"ab"
+"abc"
+"ac"
+constraint violation: "b" does not match regular expression /(a(b)?)?c?/
+constraint violation: "bc" does not match regular expression /(a(b)?)?c?/
+"c"]])
+
+OVSDB_CHECK_POSITIVE([strings at least 2 characters long],
+ [[parse-atoms '{"type": "string", "minLength": 2}' \
+ '[""]' \
+ '["a"]' \
+ '["ab"]' \
+ '["abc"]' \
+ '["\ud834\udd1e"]']],
+ [[constraint violation: "" length 0 is less than minimum allowed length 2
+constraint violation: "a" length 1 is less than minimum allowed length 2
+"ab"
+"abc"
+constraint violation: "𝄞" length 1 is less than minimum allowed length 2]])
+
+OVSDB_CHECK_POSITIVE([strings no more than 2 characters long],
+ [[parse-atoms '{"type": "string", "maxLength": 2}' \
+ '[""]' \
+ '["a"]' \
+ '["ab"]' \
+ '["abc"]' \
+ '["\ud834\udd1e"]']],
+ [[""
+"a"
+"ab"
+constraint violation: "abc" length 3 is greater than maximum allowed length 2
+"𝄞"]])
AT_BANNER([OSVDB -- simple data])
""
"true"
"\"\\/\b\f\n\r\t"])
-
+\f
AT_BANNER([OVSDB -- set data])
OVSDB_CHECK_POSITIVE([JSON optional boolean],
355ad037-f1da-40aa-b47c-ff9c7e8c6a38,
7ef21525-0088-4a28-a418-5518413e43ea']],
[set contains duplicate value])
-
+\f
AT_BANNER([OVSDB -- map data])
OVSDB_CHECK_POSITIVE([JSON map of 1 integer to boolean],
"number": {"type": "integer"},
"name": {"type": "string"}}}}}]])
+m4_define([CONSTRAINT_SCHEMA],
+ [[{"name": "constraints",
+ "tables": {
+ "constrained": {
+ "columns": {
+ "positive": {"type": {"key": {"type": "integer",
+ "minInteger": 1}}}}}}}]])
+
# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
#
# Runs "test-ovsdb execute" with the given SCHEMA and each of the
"rows": [{"name": "one", "number": 1}]}]]]],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
]])
-])
+
+OVSDB_CHECK_EXECUTION([insert and update constraints],
+ [CONSTRAINT_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "constrained",
+ "row": {}}]]],
+ [[[{"op": "insert",
+ "table": "constrained",
+ "row": {"positive": -1}}]]],
+ [[[{"op": "update",
+ "table": "constrained",
+ "where": [],
+ "row": {"positive": -2}}]]]],
+ [[[{"details":"0 is less than minimum allowed value 1","error":"constraint violation"}]
+[{"details":"-1 is less than minimum allowed value 1","error":"constraint violation"}]
+[{"details":"-2 is less than minimum allowed value 1","error":"constraint violation"}]
+]])])
EXECUTION_EXAMPLES
row 6: domain error: Division by zero.
], [mutation])
+OVSDB_CHECK_POSITIVE([executing mutations on integers with constraints],
+ [[execute-mutations \
+ '{"columns": {"i": {"type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 2}}}}}' \
+ '[[["i", "+=", 1]],
+ [["i", "-=", 2]],
+ [["i", "*=", 3]],
+ [["i", "/=", 4]],
+ [["i", "%=", 2]]]' \
+ '[{"i": 0},
+ {"i": 1},
+ {"i": 2}']]],
+ [mutation 0:
+row 0: {"i":1}
+row 1: {"i":2}
+row 2: constraint violation: 3 is not in the valid range 0 to 2 (inclusive)
+
+mutation 1:
+row 0: constraint violation: -2 is not in the valid range 0 to 2 (inclusive)
+row 1: constraint violation: -1 is not in the valid range 0 to 2 (inclusive)
+row 2: {"i":0}
+
+mutation 2:
+row 0: no change
+row 1: constraint violation: 3 is not in the valid range 0 to 2 (inclusive)
+row 2: constraint violation: 6 is not in the valid range 0 to 2 (inclusive)
+
+mutation 3:
+row 0: no change
+row 1: {"i":0}
+row 2: {"i":0}
+
+mutation 4:
+row 0: no change
+row 1: no change
+row 2: {"i":0}
+], [mutation])
+
OVSDB_CHECK_POSITIVE([executing mutations on reals],
[[execute-mutations \
'{"columns": {"r": {"type": "real"}}}' \
row 2: domain error: Division by zero.
], [mutation])
+OVSDB_CHECK_POSITIVE([executing mutations on reals with constraints],
+ [[execute-mutations \
+ '{"columns": {"r": {"type": {"key": {"type": "real",
+ "minReal": -2.5,
+ "maxReal": 1.75}}}}}' \
+ '[[["r", "+=", 0.5]],
+ [["r", "-=", 1.5]],
+ [["r", "*=", 2.5]],
+ [["r", "/=", 4]]]' \
+ '[{"r": 0},
+ {"r": -2.5},
+ {"r": 1.25}']]],
+ [mutation 0:
+row 0: {"r":0.5}
+row 1: {"r":-2}
+row 2: {"r":1.75}
+
+mutation 1:
+row 0: {"r":-1.5}
+row 1: constraint violation: -4 is not in the valid range -2.5 to 1.75 (inclusive)
+row 2: {"r":-0.25}
+
+mutation 2:
+row 0: no change
+row 1: constraint violation: -6.25 is not in the valid range -2.5 to 1.75 (inclusive)
+row 2: constraint violation: 3.125 is not in the valid range -2.5 to 1.75 (inclusive)
+
+mutation 3:
+row 0: no change
+row 1: {"r":-0.625}
+row 2: {"r":0.3125}
+], [mutation])
+
OVSDB_CHECK_POSITIVE([executing mutations on integer sets],
[[execute-mutations \
- '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \
+ '{"columns": {"i": {"type": {"key": {"type": "integer",
+ "maxInteger": 5},
+ "min": 0,
+ "max": "unlimited"}}}}' \
'[[["i", "+=", 1]],
[["i", "-=", 2]],
[["i", "*=", 3]],
row 0: no change
row 1: no change
row 2: {"i":["set",[0,3]]}
-row 3: {"i":["set",[0,3,6]]}
+row 3: constraint violation: 6 is greater than maximum allowed value 5
mutation 3:
row 0: no change
OVSDB_CHECK_POSITIVE([executing mutations on real sets],
[[execute-mutations \
- '{"columns": {"r": {"type": {"key": "real", "min": 0, "max": "unlimited"}}}}' \
+ '{"columns": {"r": {"type": {"key": {"type": "real",
+ "maxReal": 6},
+ "min": 0, "max": "unlimited"}}}}' \
'[[["r", "+=", 0.5]],
[["r", "-=", 1.5]],
[["r", "*=", 2.5]],
row 0: no change
row 1: {"r":["set",[1.25]]}
row 2: {"r":["set",[1.25,3.75]]}
-row 3: {"r":["set",[1.25,3.75,6.25]]}
+row 3: constraint violation: 6.25 is greater than maximum allowed value 6
mutation 3:
row 0: no change
row 3: {"u":["set",[]]}
]], [mutation])
-
OVSDB_CHECK_POSITIVE([executing mutations on integer maps],
[[execute-mutations \
'{"columns": {"i": {"type": {"key": "integer", "value": "integer", "min": 0, "max": "unlimited"}}}}' \
OVSDB_CHECK_NEGATIVE([void is not a valid atomic-type],
[[parse-atomic-type '["void"]' ]], ["void" is not an atomic-type])
+AT_BANNER([OVSDB -- base types])
+
+OVSDB_CHECK_POSITIVE([integer >= 5],
+ [[parse-base-type '{"type": "integer", "minInteger": 5}' ]],
+ [{"minInteger":5,"type":"integer"}])
+OVSDB_CHECK_POSITIVE([integer <= 7],
+ [[parse-base-type '{"type": "integer", "maxInteger": 7}' ]],
+ [{"maxInteger":7,"type":"integer"}])
+OVSDB_CHECK_POSITIVE([integer between -5 and 10],
+ [[parse-base-type '{"type": "integer", "minInteger": -5, "maxInteger": 10}']],
+ [{"maxInteger":10,"minInteger":-5,"type":"integer"}])
+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 >= -1.5],
+ [[parse-base-type '{"type": "real", "minReal": -1.5}']],
+ [{"minReal":-1.5,"type":"real"}])
+OVSDB_CHECK_POSITIVE([real <= 1e5],
+ [[parse-base-type '{"type": "real", "maxReal": 1e5}']],
+ [{"maxReal":100000,"type":"real"}])
+OVSDB_CHECK_POSITIVE([real between -2.5 and 3.75],
+ [[parse-base-type '{"type": "real", "minReal": -2.5, "maxReal": 3.75}']],
+ [{"maxReal":3.75,"minReal":-2.5,"type":"real"}])
+OVSDB_CHECK_NEGATIVE([real max may not be less than min],
+ [[parse-base-type '{"type": "real", "minReal": 555, "maxReal": 444}']],
+ [minReal exceeds maxReal])
+
+OVSDB_CHECK_POSITIVE([boolean],
+ [[parse-base-type '[{"type": "boolean"}]' ]], ["boolean"])
+
+OVSDB_CHECK_POSITIVE([string reMatch],
+ [[parse-base-type '{"type": "string", "reMatch": "\\d{3}-\\d{3}-\\d{4}"}']],
+ [{"reMatch":"\\d{3}-\\d{3}-\\d{4}","type":"string"}])
+OVSDB_CHECK_POSITIVE([string reMatch + reComment],
+ [[parse-base-type '{"type": "string", "reMatch": "\\d{3}-\\d{3}-\\d{4}", "reComment": "US-style telephone number"}']],
+ [{"reComment":"US-style telephone number","reMatch":"\\d{3}-\\d{3}-\\d{4}","type":"string"}])
+OVSDB_CHECK_NEGATIVE([reMatch must be a valid JavaScript regexp],
+ [[parse-base-type '{"type": "string", "reMatch": "ab@:>@cd"}']],
+ [[test-ovsdb: invalid regular expression: "ab@:>@cd" is not a valid regular expression: @:>@ is an invalid data character in JavaScript compatibility mode]])
+
+OVSDB_CHECK_POSITIVE([string minLength],
+ [[parse-base-type '{"type": "string", "minLength": 1}']],
+ [{"minLength":1,"type":"string"}])
+OVSDB_CHECK_POSITIVE([string maxLength],
+ [[parse-base-type '{"type": "string", "maxLength": 5}']],
+ [{"maxLength":5,"type":"string"}])
+OVSDB_CHECK_POSITIVE([string minLength and maxLength],
+ [[parse-base-type '{"type": "string", "minLength": 1, "maxLength": 5}']],
+ [{"maxLength":5,"minLength":1,"type":"string"}])
+OVSDB_CHECK_NEGATIVE([maxLength must not be less than minLength],
+ [[parse-base-type '{"type": "string", "minLength": 5, "maxLength": 3}']],
+ [minLength exceeds maxLength])
+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_NEGATIVE([void is not a valid base-type],
+ [[parse-base-type '["void"]' ]], ["void" is not an atomic-type])
+OVSDB_CHECK_NEGATIVE(["type" member must be present],
+ [[parse-base-type '{}']], [Parsing ovsdb type failed: Required 'type' member is missing.])
+
AT_BANNER([OVSDB -- simple types])
OVSDB_CHECK_POSITIVE([simple integer],
" open FILE with FLAGS, run COMMANDs\n"
" parse-atomic-type TYPE\n"
" parse TYPE as OVSDB atomic type, and re-serialize\n"
+ " parse-base-type TYPE\n"
+ " parse TYPE as OVSDB base type, and re-serialize\n"
" parse-type JSON\n"
" parse JSON as OVSDB type, and re-serialize\n"
" parse-atoms TYPE ATOM...\n"
print_and_free_json(ovsdb_atomic_type_to_json(type));
}
+static void
+do_parse_base_type(int argc UNUSED, char *argv[])
+{
+ struct ovsdb_base_type base;
+ struct json *json;
+
+ json = unbox_json(parse_json(argv[1]));
+ check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
+ json_destroy(json);
+ print_and_free_json(ovsdb_base_type_to_json(&base));
+ ovsdb_base_type_destroy(&base);
+}
+
static void
do_parse_type(int argc UNUSED, char *argv[])
{
check_ovsdb_error(ovsdb_type_from_json(&type, json));
json_destroy(json);
print_and_free_json(ovsdb_type_to_json(&type));
+ ovsdb_type_destroy(&type);
}
static void
do_parse_atoms(int argc, char *argv[])
{
- enum ovsdb_atomic_type type;
+ struct ovsdb_base_type base;
struct json *json;
int i;
json = unbox_json(parse_json(argv[1]));
- check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
+ check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
json_destroy(json);
for (i = 2; i < argc; i++) {
+ struct ovsdb_error *error;
union ovsdb_atom atom;
json = unbox_json(parse_json(argv[i]));
- check_ovsdb_error(ovsdb_atom_from_json(&atom, type, json, NULL));
+ error = ovsdb_atom_from_json(&atom, &base, json, NULL);
json_destroy(json);
- print_and_free_json(ovsdb_atom_to_json(&atom, type));
-
- ovsdb_atom_destroy(&atom, type);
+ if (error) {
+ print_and_free_ovsdb_error(error);
+ } else {
+ print_and_free_json(ovsdb_atom_to_json(&atom, base.type));
+ ovsdb_atom_destroy(&atom, base.type);
+ }
}
+ ovsdb_base_type_destroy(&base);
}
static void
do_parse_atom_strings(int argc, char *argv[])
{
- enum ovsdb_atomic_type type;
+ struct ovsdb_base_type base;
struct json *json;
int i;
json = unbox_json(parse_json(argv[1]));
- check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
+ check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
json_destroy(json);
for (i = 2; i < argc; i++) {
union ovsdb_atom atom;
struct ds out;
- die_if_error(ovsdb_atom_from_string(&atom, type, argv[i]));
+ die_if_error(ovsdb_atom_from_string(&atom, &base, argv[i]));
ds_init(&out);
- ovsdb_atom_to_string(&atom, type, &out);
+ ovsdb_atom_to_string(&atom, base.type, &out);
puts(ds_cstr(&out));
ds_destroy(&out);
- ovsdb_atom_destroy(&atom, type);
+ ovsdb_atom_destroy(&atom, base.type);
}
+ ovsdb_base_type_destroy(&base);
}
static void
ovsdb_datum_destroy(&datum, &type);
}
+ ovsdb_type_destroy(&type);
}
static void
ovsdb_datum_destroy(&datum, &type);
}
+ ovsdb_type_destroy(&type);
}
static enum ovsdb_atomic_type compare_atoms_atomic_type;
static void
do_sort_atoms(int argc UNUSED, char *argv[])
{
- enum ovsdb_atomic_type type;
+ struct ovsdb_base_type base;
union ovsdb_atom *atoms;
struct json *json, **json_atoms;
size_t n_atoms;
int i;
json = unbox_json(parse_json(argv[1]));
- check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
+ check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
json_destroy(json);
json = unbox_json(parse_json(argv[2]));
n_atoms = json->u.array.n;
atoms = xmalloc(n_atoms * sizeof *atoms);
for (i = 0; i < n_atoms; i++) {
- check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], type,
+ check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], &base,
json->u.array.elems[i], NULL));
}
json_destroy(json);
/* Sort atoms. */
- compare_atoms_atomic_type = type;
+ compare_atoms_atomic_type = base.type;
qsort(atoms, n_atoms, sizeof *atoms, compare_atoms);
/* Convert internal representation back to JSON. */
json_atoms = xmalloc(n_atoms * sizeof *json_atoms);
for (i = 0; i < n_atoms; i++) {
- json_atoms[i] = ovsdb_atom_to_json(&atoms[i], type);
- ovsdb_atom_destroy(&atoms[i], type);
+ json_atoms[i] = ovsdb_atom_to_json(&atoms[i], base.type);
+ ovsdb_atom_destroy(&atoms[i], base.type);
}
print_and_free_json(json_array_create(json_atoms, n_atoms));
free(atoms);
+ ovsdb_base_type_destroy(&base);
}
static void
int error;
int i;
+ idltest_init();
+
idl = ovsdb_idl_create(argv[1], &idltest_idl_class);
if (argc > 2) {
struct stream *stream;
static struct command all_commands[] = {
{ "log-io", 2, INT_MAX, do_log_io },
{ "parse-atomic-type", 1, 1, do_parse_atomic_type },
+ { "parse-base-type", 1, 1, do_parse_base_type },
{ "parse-type", 1, 1, do_parse_type },
{ "parse-atoms", 2, INT_MAX, do_parse_atoms },
{ "parse-atom-strings", 2, INT_MAX, do_parse_atom_strings },
$(SSL_LIBS)
utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c vswitchd/vswitch-idl.c
-utilities_ovs_vsctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_vsctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS) $(PCRE_LIBS)
utilities_ovs_wdt_SOURCES = utilities/ovs-wdt.c
\fIcolumn\fR is not a map column or if \fIkey\fR is not specified,
\fB\-\-if\-exists\fR has no effect.
.
-.IP "[\fB\-\-force\fR] \fBset \fItable record column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
+.IP "\fBset \fItable record column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
Sets the value of each specified \fIcolumn\fR in the given
\fIrecord\fR in \fItable\fR to \fIvalue\fR. For map columns, a
\fIkey\fR may optionally be specified, in which case the value
associated with \fIkey\fR in that column is changed (or added, if none
exists), instead of the entire map.
.
-.IP "[\fB\-\-force\fR] \fBadd \fItable record column \fR[\fIkey\fB=\fR]\fIvalue\fR..."
+.IP "\fBadd \fItable record column \fR[\fIkey\fB=\fR]\fIvalue\fR..."
Adds the specified value or key-value pair to \fIcolumn\fR in
\fIrecord\fR in \fItable\fR. If \fIcolumn\fR is a map, then \fIkey\fR
is required, otherwise it is prohibited. If \fIkey\fR already exists
in a map column, then the current \fIvalue\fR is not replaced (use the
\fBset\fR command to replace an existing value).
.
-.IP "[\fB\-\-force\fR] \fBremove \fItable record column \fR\fIvalue\fR..."
-.IQ "[\fB\-\-force\fR] \fBremove \fItable record column \fR\fIkey\fR..."
-.IQ "[\fB\-\-force\fR] \fBremove \fItable record column \fR\fIkey\fB=\fR\fIvalue\fR..."
+.IP "\fBremove \fItable record column \fR\fIvalue\fR..."
+.IQ "\fBremove \fItable record column \fR\fIkey\fR..."
+.IQ "\fBremove \fItable record column \fR\fIkey\fB=\fR\fIvalue\fR..."
Removes the specified values or key-value pairs from \fIcolumn\fR in
\fIrecord\fR in \fItable\fR. The first form applies to columns that
are not maps: each specified \fIvalue\fR is removed from the column.
It is not an error if the column does not contain the specified key or
value or pair.
.
-.IP "\fB[\fB\-\-force\fR] \fBclear\fR \fItable record column\fR..."
+.IP "\fBclear\fR \fItable record column\fR..."
Sets each \fIcolumn\fR in \fIrecord\fR in \fItable\fR to the empty set
or empty map, as appropriate. This command applies only to columns
that are allowed to be empty.
#include <float.h>
#include <getopt.h>
#include <inttypes.h>
-#include <regex.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
vlog_init();
vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_WARN);
vlog_set_levels(VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN);
+ ovsrec_init();
/* Log our arguments. This is often valuable for debugging systems. */
args = process_escape_args(argv);
time_alarm(timeout);
}
- /* Do basic command syntax checking. */
-
/* Now execute the commands. */
idl = the_idl = ovsdb_idl_create(db, &ovsrec_idl_class);
seqno = ovsdb_idl_get_seqno(idl);
\f
/* Parameter commands. */
-/* POSIX extended regular expression for an 8-bit unsigned decimal integer. */
-#define OCTET_RE "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
-
-/* POSIX extended regular expression for an IP address. */
-#define IP_RE "("OCTET_RE"\\."OCTET_RE"\\."OCTET_RE"\\."OCTET_RE")"
-
-/* POSIX extended regular expression for a netmask. */
-#define NETMASK_RE \
- "255.255.255."NETMASK_END_RE"|" \
- "255.255."NETMASK_END_RE".0|" \
- "255."NETMASK_END_RE".0.0|" \
- NETMASK_END_RE".0.0.0"
-#define NETMASK_END_RE "(255|254|252|248|240|224|192|128|0)"
-
-/* POSIX extended regular expression for an Ethernet address. */
-#define XX_RE "[0-9a-fA-F][0-9a-fA-F]"
-#define MAC_RE XX_RE":"XX_RE":"XX_RE":"XX_RE":"XX_RE":"XX_RE
-
-/* POSIX extended regular expression for a TCP or UDP port number. */
-#define PORT_RE \
- "([0-9]|" \
- "[1-9][0-9]|" \
- "[1-9][0-9][0-9]|" \
- "[1-9][0-9][0-9][0-9]|" \
- "[1-5][0-9][0-9][0-9][0-9]|" \
- "6[1-4][0-9][0-9][0-9]|" \
- "65[1-4][0-9][0-9]|" \
- "655[1-2][0-9]|" \
- "6553[1-5])"
-
-enum {
- VSCF_READONLY = 1 << 0,
- VSCF_HIDDEN = 1 << 1
-};
-
-struct vsctl_column {
- struct ovsdb_idl_column *idl;
- int flags;
- const char *constraint;
-};
-
-static const struct vsctl_column bridge_columns[] = {
- {&ovsrec_bridge_col_controller, VSCF_READONLY, NULL},
- {&ovsrec_bridge_col_datapath_id, VSCF_READONLY, NULL},
- {&ovsrec_bridge_col_datapath_type, VSCF_READONLY, NULL},
- {&ovsrec_bridge_col_external_ids, 0, NULL},
- {&ovsrec_bridge_col_flood_vlans, 0, "[1,4095]"},
- {&ovsrec_bridge_col_mirrors, VSCF_READONLY, NULL},
- {&ovsrec_bridge_col_name, VSCF_READONLY, NULL},
- {&ovsrec_bridge_col_netflow, VSCF_READONLY, NULL},
- {&ovsrec_bridge_col_other_config, 0, NULL},
- {&ovsrec_bridge_col_ports, VSCF_READONLY, NULL},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column controller_columns[] = {
- {&ovsrec_controller_col_connection_mode, 0, "in-band|out-of-band"},
- {&ovsrec_controller_col_controller_burst_limit, 0, "[25,]"},
- {&ovsrec_controller_col_controller_rate_limit, 0, "[100,]"},
- {&ovsrec_controller_col_discover_accept_regex, 0, NULL},
- {&ovsrec_controller_col_discover_update_resolv_conf, 0, NULL},
- {&ovsrec_controller_col_fail_mode, 0, "standalone|secure"},
- {&ovsrec_controller_col_inactivity_probe, 0, "[5000,]"},
- {&ovsrec_controller_col_local_gateway, 0, IP_RE},
- {&ovsrec_controller_col_local_ip, 0, IP_RE},
- {&ovsrec_controller_col_local_netmask, 0, NETMASK_RE},
- {&ovsrec_controller_col_max_backoff, 0, "[1000,]"},
- {&ovsrec_controller_col_target, 0, NULL},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column interface_columns[] = {
- {&ovsrec_interface_col_external_ids, 0, NULL},
- {&ovsrec_interface_col_ingress_policing_burst, 0, "[10,]"},
- {&ovsrec_interface_col_ingress_policing_rate, 0, "[100,]"},
- {&ovsrec_interface_col_mac, 0, MAC_RE},
- {&ovsrec_interface_col_name, VSCF_READONLY, NULL},
- {&ovsrec_interface_col_ofport, VSCF_READONLY, NULL},
- {&ovsrec_interface_col_options, 0, NULL},
- {&ovsrec_interface_col_type, VSCF_READONLY, NULL},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column mirror_columns[] = {
- {&ovsrec_mirror_col_name, VSCF_READONLY, NULL},
- {&ovsrec_mirror_col_output_port, 0, "Port"},
- {&ovsrec_mirror_col_output_vlan, 0, "[1,4095]"},
- {&ovsrec_mirror_col_select_dst_port, 0, "Port"},
- {&ovsrec_mirror_col_select_src_port, 0, "Port"},
- {&ovsrec_mirror_col_select_vlan, 0, "[1,4095]"},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column netflow_columns[] = {
- {&ovsrec_netflow_col_active_timeout, 0, "[-1,]"},
- {&ovsrec_netflow_col_add_id_to_interface, 0, NULL},
- {&ovsrec_netflow_col_engine_id, 0, "[0,255]"},
- {&ovsrec_netflow_col_engine_type, 0, "[0,255]"},
- {&ovsrec_netflow_col_targets, 0, IP_RE":"PORT_RE},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column open_vswitch_columns[] = {
- {&ovsrec_open_vswitch_col_bridges, VSCF_READONLY, NULL},
- {&ovsrec_open_vswitch_col_controller, VSCF_READONLY, NULL},
- {&ovsrec_open_vswitch_col_cur_cfg, VSCF_HIDDEN, NULL},
- {&ovsrec_open_vswitch_col_managers, 0, "p?(ssl|tcp|unix):.*"},
- {&ovsrec_open_vswitch_col_next_cfg, VSCF_HIDDEN, NULL},
- {&ovsrec_open_vswitch_col_ssl, VSCF_READONLY, NULL},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column port_columns[] = {
- {&ovsrec_port_col_bond_downdelay, 0, "[0,]"},
- {&ovsrec_port_col_bond_fake_iface, VSCF_READONLY, NULL},
- {&ovsrec_port_col_bond_updelay, 0, "[0,]"},
- {&ovsrec_port_col_external_ids, 0, NULL},
- {&ovsrec_port_col_fake_bridge, VSCF_READONLY, NULL},
- {&ovsrec_port_col_interfaces, VSCF_READONLY, NULL},
- {&ovsrec_port_col_mac, 0, MAC_RE},
- {&ovsrec_port_col_name, VSCF_READONLY, NULL},
- {&ovsrec_port_col_other_config, 0, NULL},
- {&ovsrec_port_col_tag, 0, "[0,4095]"},
- {&ovsrec_port_col_trunks, 0, "[0,4095]"},
- {NULL, 0, NULL},
-};
-
-static const struct vsctl_column ssl_columns[] = {
- {&ovsrec_ssl_col_bootstrap_ca_cert, 0, NULL},
- {&ovsrec_ssl_col_ca_cert, 0, NULL},
- {&ovsrec_ssl_col_certificate, 0, NULL},
- {&ovsrec_ssl_col_private_key, 0, NULL},
- {NULL, 0, NULL},
-};
-
struct vsctl_row_id {
const struct ovsdb_idl_table_class *table;
const struct ovsdb_idl_column *name_column;
struct vsctl_table_class {
struct ovsdb_idl_table_class *class;
- const struct vsctl_column *columns;
struct vsctl_row_id row_ids[2];
};
static const struct vsctl_table_class tables[] = {
- {&ovsrec_table_bridge, bridge_columns,
+ {&ovsrec_table_bridge,
{{&ovsrec_table_bridge, &ovsrec_bridge_col_name, NULL},
{NULL, NULL, NULL}}},
- {&ovsrec_table_controller, controller_columns,
+ {&ovsrec_table_controller,
{{&ovsrec_table_bridge,
&ovsrec_bridge_col_name,
&ovsrec_bridge_col_controller},
NULL,
&ovsrec_open_vswitch_col_controller}}},
- {&ovsrec_table_interface, interface_columns,
+ {&ovsrec_table_interface,
{{&ovsrec_table_interface, &ovsrec_interface_col_name, NULL},
{NULL, NULL, NULL}}},
- {&ovsrec_table_mirror, mirror_columns,
+ {&ovsrec_table_mirror,
{{&ovsrec_table_mirror, &ovsrec_mirror_col_name, NULL},
{NULL, NULL, NULL}}},
- {&ovsrec_table_netflow, netflow_columns,
+ {&ovsrec_table_netflow,
{{&ovsrec_table_bridge,
&ovsrec_bridge_col_name,
&ovsrec_bridge_col_netflow},
{NULL, NULL, NULL}}},
- {&ovsrec_table_open_vswitch, open_vswitch_columns,
+ {&ovsrec_table_open_vswitch,
{{&ovsrec_table_open_vswitch, NULL, NULL},
{NULL, NULL, NULL}}},
- {&ovsrec_table_port, port_columns,
+ {&ovsrec_table_port,
{{&ovsrec_table_port, &ovsrec_port_col_name, NULL},
{NULL, NULL, NULL}}},
- {&ovsrec_table_ssl, ssl_columns,
+ {&ovsrec_table_ssl,
{{&ovsrec_table_open_vswitch, NULL, &ovsrec_open_vswitch_col_ssl}}},
- {NULL, NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
+ {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
};
static void
unsigned int best_score = 0;
/* It might make sense to relax this assertion. */
- assert(id->name_column->type.key_type == OVSDB_TYPE_STRING);
+ assert(id->name_column->type.key.type == OVSDB_TYPE_STRING);
referrer = NULL;
for (row = ovsdb_idl_first_row(ctx->idl, id->table);
if (id->uuid_column) {
struct ovsdb_datum uuid;
- assert(id->uuid_column->type.key_type == OVSDB_TYPE_UUID);
- assert(id->uuid_column->type.value_type == OVSDB_TYPE_VOID);
+ assert(id->uuid_column->type.key.type == OVSDB_TYPE_UUID);
+ assert(id->uuid_column->type.value.type == OVSDB_TYPE_VOID);
ovsdb_idl_txn_read(referrer, id->uuid_column, &uuid);
if (uuid.n == 1) {
static char *
get_column(const struct vsctl_table_class *table, const char *column_name,
- const struct vsctl_column **columnp)
+ const struct ovsdb_idl_column **columnp)
{
- const struct vsctl_column *column;
- const struct vsctl_column *best_match = NULL;
+ const struct ovsdb_idl_column *best_match = NULL;
unsigned int best_score = 0;
+ size_t i;
- for (column = table->columns; column->idl; column++) {
- if (!(column->flags & VSCF_HIDDEN)) {
- unsigned int score = score_partial_match(column->idl->name,
- column_name);
- if (score > best_score) {
- best_match = column;
- best_score = score;
- } else if (score == best_score) {
- best_match = NULL;
- }
+ for (i = 0; i < table->class->n_columns; i++) {
+ const struct ovsdb_idl_column *column = &table->class->columns[i];
+ unsigned int score = score_partial_match(column->name, column_name);
+ if (score > best_score) {
+ best_match = column;
+ best_score = score;
+ } else if (score == best_score) {
+ best_match = NULL;
}
}
static char * WARN_UNUSED_RESULT
parse_column_key_value(const char *arg, const struct vsctl_table_class *table,
- const struct vsctl_column **columnp,
+ const struct ovsdb_idl_column **columnp,
char **keyp, char **valuep)
{
const char *p = arg;
table = get_table(table_name);
row = must_get_row(ctx, table, record_id);
for (i = 3; i < ctx->argc; i++) {
- const struct vsctl_column *column;
+ const struct ovsdb_idl_column *column;
struct ovsdb_datum datum;
char *key_string;
die_if_error(parse_column_key_value(ctx->argv[i], table,
&column, &key_string, NULL));
- ovsdb_idl_txn_read(row, column->idl, &datum);
+ ovsdb_idl_txn_read(row, column, &datum);
if (key_string) {
union ovsdb_atom key;
unsigned int idx;
- if (column->idl->type.value_type == OVSDB_TYPE_VOID) {
+ if (column->type.value.type == OVSDB_TYPE_VOID) {
vsctl_fatal("cannot specify key to get for non-map column %s",
- column->idl->name);
+ column->name);
}
die_if_error(ovsdb_atom_from_string(&key,
- column->idl->type.key_type,
+ &column->type.key,
key_string));
idx = ovsdb_datum_find_key(&datum, &key,
- column->idl->type.key_type);
+ column->type.key.type);
if (idx == UINT_MAX) {
if (!if_exists) {
vsctl_fatal("no key \"%s\" in %s record \"%s\" column %s",
key_string, table->class->name, record_id,
- column->idl->name);
+ column->name);
}
} else {
ovsdb_atom_to_string(&datum.values[idx],
- column->idl->type.value_type, out);
+ column->type.value.type, out);
}
- ovsdb_atom_destroy(&key, column->idl->type.key_type);
+ ovsdb_atom_destroy(&key, column->type.key.type);
} else {
- ovsdb_datum_to_string(&datum, &column->idl->type, out);
+ ovsdb_datum_to_string(&datum, &column->type, out);
}
ds_put_char(out, '\n');
- ovsdb_datum_destroy(&datum, &column->idl->type);
+ ovsdb_datum_destroy(&datum, &column->type);
free(key_string);
}
list_record(const struct vsctl_table_class *table,
const struct ovsdb_idl_row *row, struct ds *out)
{
- const struct vsctl_column *column;
+ size_t i;
- ds_put_format(out, "%-20s (RO): "UUID_FMT"\n", "_uuid",
+ ds_put_format(out, "%-20s: "UUID_FMT"\n", "_uuid",
UUID_ARGS(&row->uuid));
- for (column = table->columns; column->idl; column++) {
+ for (i = 0; i < table->class->n_columns; i++) {
+ const struct ovsdb_idl_column *column = &table->class->columns[i];
struct ovsdb_datum datum;
- if (column->flags & VSCF_HIDDEN) {
- continue;
- }
-
- ovsdb_idl_txn_read(row, column->idl, &datum);
+ ovsdb_idl_txn_read(row, column, &datum);
- ds_put_format(out, "%-20s (%s): ", column->idl->name,
- column->flags & VSCF_READONLY ? "RO" : "RW");
- ovsdb_datum_to_string(&datum, &column->idl->type, out);
+ ds_put_format(out, "%-20s: ", column->name);
+ ovsdb_datum_to_string(&datum, &column->type, out);
ds_put_char(out, '\n');
- ovsdb_datum_destroy(&datum, &column->idl->type);
+ ovsdb_datum_destroy(&datum, &column->type);
}
}
}
}
-static void
-check_string_constraint(const struct ovsdb_datum *datum,
- const char *constraint)
-{
- unsigned int i;
- char *regex;
- regex_t re;
- int retval;
-
- regex = xasprintf("^%s$", constraint);
- retval = regcomp(&re, regex, REG_NOSUB | REG_EXTENDED);
- if (retval) {
- size_t length = regerror(retval, &re, NULL, 0);
- char *buffer = xmalloc(length);
- regerror(retval, &re, buffer, length);
- vsctl_fatal("internal error compiling regular expression %s: %s",
- regex, buffer);
- }
-
- for (i = 0; i < datum->n; i++) {
- const char *key = datum->keys[i].string;
- if (regexec(&re, key, 0, NULL, 0)) {
- vsctl_fatal("%s is not valid (it does not match %s)", key, regex);
- }
- }
- free(regex);
- regfree(&re);
-}
-
-static void
-check_integer_constraint(const struct ovsdb_datum *datum,
- const char *constraint)
-{
- int64_t min, max;
- unsigned int i;
- int n = -1;
-
- sscanf(constraint, "[%"SCNd64",%"SCNd64"]%n", &min, &max, &n);
- if (n == -1) {
- sscanf(constraint, "[%"SCNd64",]%n", &min, &n);
- if (n == -1) {
- sscanf(constraint, "[,%"SCNd64"]%n", &max, &n);
- if (n == -1) {
- VLOG_DBG("internal error: bad integer contraint \"%s\"",
- constraint);
- return;
- } else {
- min = INT64_MIN;
- }
- } else {
- max = INT64_MAX;
- }
- }
-
- for (i = 0; i < datum->n; i++) {
- int64_t value = datum->keys[i].integer;
- if (value < min || value > max) {
- if (max == INT64_MAX) {
- vsctl_fatal("%"PRId64" is less than the minimum "
- "allowed value %"PRId64, value, min);
- } else if (min == INT64_MIN) {
- vsctl_fatal("%"PRId64" is greater than the maximum "
- "allowed value %"PRId64, value, max);
- } else {
- vsctl_fatal("%"PRId64" is outside the valid range %"PRId64" "
- "to %"PRId64" (inclusive)", value, min, max);
- }
- }
- }
-}
-
-static void
-check_constraint(const struct ovsdb_datum *datum,
- const struct ovsdb_type *type, const char *constraint)
-{
- if (constraint && datum->n) {
- if (type->key_type == OVSDB_TYPE_STRING) {
- check_string_constraint(datum, constraint);
- } else if (type->key_type == OVSDB_TYPE_INTEGER) {
- check_integer_constraint(datum, constraint);
- }
- }
-}
-
static void
set_column(const struct vsctl_table_class *table,
- const struct ovsdb_idl_row *row,
- const char *arg, bool force)
+ const struct ovsdb_idl_row *row, const char *arg)
{
- const struct vsctl_column *column;
+ const struct ovsdb_idl_column *column;
char *key_string, *value_string;
char *error;
error = parse_column_key_value(arg, table, &column, &key_string,
&value_string);
die_if_error(error);
- if (column->flags & VSCF_READONLY && !force) {
- vsctl_fatal("%s: cannot modify read-only column %s in table %s",
- arg, column->idl->name, table->class->name);
- }
if (!value_string) {
vsctl_fatal("%s: missing value", arg);
}
union ovsdb_atom key, value;
struct ovsdb_datum old, new;
- if (column->idl->type.value_type == OVSDB_TYPE_VOID) {
+ if (column->type.value.type == OVSDB_TYPE_VOID) {
vsctl_fatal("cannot specify key to set for non-map column %s",
- column->idl->name);
+ column->name);
}
- die_if_error(ovsdb_atom_from_string(&key,
- column->idl->type.key_type,
+ die_if_error(ovsdb_atom_from_string(&key, &column->type.key,
key_string));
- die_if_error(ovsdb_atom_from_string(&value,
- column->idl->type.value_type,
+ die_if_error(ovsdb_atom_from_string(&value, &column->type.value,
value_string));
ovsdb_datum_init_empty(&new);
- ovsdb_datum_add_unsafe(&new, &key, &value, &column->idl->type);
+ ovsdb_datum_add_unsafe(&new, &key, &value, &column->type);
- ovsdb_atom_destroy(&key, column->idl->type.key_type);
- ovsdb_atom_destroy(&value, column->idl->type.value_type);
+ ovsdb_atom_destroy(&key, column->type.key.type);
+ ovsdb_atom_destroy(&value, column->type.value.type);
- ovsdb_idl_txn_read(row, column->idl, &old);
- ovsdb_datum_union(&old, &new, &column->idl->type, true);
- ovsdb_idl_txn_write(row, column->idl, &old);
+ ovsdb_idl_txn_read(row, column, &old);
+ ovsdb_datum_union(&old, &new, &column->type, true);
+ ovsdb_idl_txn_write(row, column, &old);
- ovsdb_datum_destroy(&new, &column->idl->type);
+ ovsdb_datum_destroy(&new, &column->type);
} else {
struct ovsdb_datum datum;
- die_if_error(ovsdb_datum_from_string(&datum, &column->idl->type,
+ die_if_error(ovsdb_datum_from_string(&datum, &column->type,
value_string));
- if (!force) {
- check_constraint(&datum, &column->idl->type,
- column->constraint);
- }
- ovsdb_idl_txn_write(row, column->idl, &datum);
+ ovsdb_idl_txn_write(row, column, &datum);
}
free(key_string);
static void
cmd_set(struct vsctl_context *ctx)
{
- bool force = shash_find(&ctx->options, "--force");
const char *table_name = ctx->argv[1];
const char *record_id = ctx->argv[2];
const struct vsctl_table_class *table;
table = get_table(table_name);
row = must_get_row(ctx, table, record_id);
for (i = 3; i < ctx->argc; i++) {
- set_column(table, row, ctx->argv[i], force);
+ set_column(table, row, ctx->argv[i]);
}
}
static void
cmd_add(struct vsctl_context *ctx)
{
- bool force = shash_find(&ctx->options, "--force");
const char *table_name = ctx->argv[1];
const char *record_id = ctx->argv[2];
const char *column_name = ctx->argv[3];
const struct vsctl_table_class *table;
- const struct vsctl_column *column;
+ const struct ovsdb_idl_column *column;
const struct ovsdb_idl_row *row;
const struct ovsdb_type *type;
struct ovsdb_datum old;
table = get_table(table_name);
row = must_get_row(ctx, table, record_id);
die_if_error(get_column(table, column_name, &column));
- if (column->flags & VSCF_READONLY && !force) {
- vsctl_fatal("cannot modify read-only column %s in table %s",
- column->idl->name, table->class->name);
- }
- type = &column->idl->type;
- ovsdb_idl_txn_read(row, column->idl, &old);
+ type = &column->type;
+ ovsdb_idl_txn_read(row, column, &old);
for (i = 4; i < ctx->argc; i++) {
struct ovsdb_type add_type;
struct ovsdb_datum add;
vsctl_fatal("\"add\" operation would put %u %s in column %s of "
"table %s but the maximum number is %u",
old.n,
- type->value_type == OVSDB_TYPE_VOID ? "values" : "pairs",
- column->idl->name, table->class->name, type->n_max);
+ type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
+ column->name, table->class->name, type->n_max);
}
- ovsdb_idl_txn_write(row, column->idl, &old);
+ ovsdb_idl_txn_write(row, column, &old);
}
static void
cmd_remove(struct vsctl_context *ctx)
{
- bool force = shash_find(&ctx->options, "--force");
const char *table_name = ctx->argv[1];
const char *record_id = ctx->argv[2];
const char *column_name = ctx->argv[3];
const struct vsctl_table_class *table;
- const struct vsctl_column *column;
+ const struct ovsdb_idl_column *column;
const struct ovsdb_idl_row *row;
const struct ovsdb_type *type;
struct ovsdb_datum old;
table = get_table(table_name);
row = must_get_row(ctx, table, record_id);
die_if_error(get_column(table, column_name, &column));
- if (column->flags & VSCF_READONLY && !force) {
- vsctl_fatal("cannot modify read-only column %s in table %s",
- column->idl->name, table->class->name);
- }
- type = &column->idl->type;
- ovsdb_idl_txn_read(row, column->idl, &old);
+ type = &column->type;
+ ovsdb_idl_txn_read(row, column, &old);
for (i = 4; i < ctx->argc; i++) {
struct ovsdb_type rm_type;
struct ovsdb_datum rm;
error = ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i]);
if (error && ovsdb_type_is_map(&rm_type)) {
free(error);
- rm_type.value_type = OVSDB_TYPE_VOID;
+ rm_type.value.type = OVSDB_TYPE_VOID;
die_if_error(ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i]));
}
ovsdb_datum_subtract(&old, type, &rm, &rm_type);
vsctl_fatal("\"remove\" operation would put %u %s in column %s of "
"table %s but the minimun number is %u",
old.n,
- type->value_type == OVSDB_TYPE_VOID ? "values" : "pairs",
- column->idl->name, table->class->name, type->n_min);
+ type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
+ column->name, table->class->name, type->n_min);
}
- ovsdb_idl_txn_write(row, column->idl, &old);
+ ovsdb_idl_txn_write(row, column, &old);
}
static void
cmd_clear(struct vsctl_context *ctx)
{
- bool force = shash_find(&ctx->options, "--force");
const char *table_name = ctx->argv[1];
const char *record_id = ctx->argv[2];
const struct vsctl_table_class *table;
table = get_table(table_name);
row = must_get_row(ctx, table, record_id);
for (i = 3; i < ctx->argc; i++) {
- const struct vsctl_column *column;
+ const struct ovsdb_idl_column *column;
const struct ovsdb_type *type;
struct ovsdb_datum datum;
die_if_error(get_column(table, ctx->argv[i], &column));
- type = &column->idl->type;
- if (column->flags & VSCF_READONLY && !force) {
- vsctl_fatal("cannot modify read-only column %s in table %s",
- column->idl->name, table->class->name);
- } else if (type->n_min > 0) {
+ type = &column->type;
+ if (type->n_min > 0) {
vsctl_fatal("\"clear\" operation cannot be applied to column %s "
"of table %s, which is not allowed to be empty",
- column->idl->name, table->class->name);
+ column->name, table->class->name);
}
ovsdb_datum_init_empty(&datum);
- ovsdb_idl_txn_write(row, column->idl, &datum);
+ ovsdb_idl_txn_write(row, column, &datum);
}
}
table = get_table(table_name);
row = ovsdb_idl_txn_insert(ctx->txn, table->class);
for (i = 2; i < ctx->argc; i++) {
- set_column(table, row, ctx->argv[i], force);
+ set_column(table, row, ctx->argv[i]);
}
ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid));
}
/* Parameter commands. */
{"get", 3, INT_MAX, cmd_get, NULL, "--if-exists"},
{"list", 1, INT_MAX, cmd_list, NULL, ""},
- {"set", 3, INT_MAX, cmd_set, NULL, "--force"},
- {"add", 4, INT_MAX, cmd_add, NULL, "--force"},
- {"remove", 4, INT_MAX, cmd_remove, NULL, "--force"},
- {"clear", 3, INT_MAX, cmd_clear, NULL, "--force"},
{"create", 2, INT_MAX, cmd_create, post_create, "--force"},
{"destroy", 1, INT_MAX, cmd_destroy, NULL, "--force,--if-exists"},
+ {"set", 3, INT_MAX, cmd_set, NULL, ""},
+ {"add", 4, INT_MAX, cmd_add, NULL, ""},
+ {"remove", 4, INT_MAX, cmd_remove, NULL, ""},
+ {"clear", 3, INT_MAX, cmd_clear, NULL, ""},
{NULL, 0, 0, NULL, NULL, NULL},
};
ofproto/libofproto.a \
lib/libsflow.a \
lib/libopenvswitch.a \
- $(SSL_LIBS)
+ $(SSL_LIBS) \
+ $(PCRE_LIBS)
vswitchd_ovs_brcompatd_SOURCES = \
vswitchd/ovs-brcompatd.c \
vswitchd/vswitch-idl.c \
vswitchd/vswitch-idl.h
-vswitchd_ovs_brcompatd_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+vswitchd_ovs_brcompatd_LDADD = lib/libopenvswitch.a $(SSL_LIBS) $(PCRE_LIBS)
EXTRA_DIST += \
vswitchd/ovs-vswitchd.8.in \
remote = parse_options(argc, argv);
signal(SIGPIPE, SIG_IGN);
process_init();
+ ovsrec_init();
die_if_already_running();
daemonize_start();
signal(SIGPIPE, SIG_IGN);
sighup = signal_register(SIGHUP);
process_init();
+ ovsrec_init();
die_if_already_running();
daemonize_start();
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"flood_vlans": {
"comment": "VLAN IDs of VLANs on which MAC address learning should be disabled, so that packets are flooded instead of being sent to specific ports that are believed to contain packets' destination MACs. This should ordinarily be used to disable MAC learning on VLANs used for mirroring (RSPAN VLANs). It may also be useful for debugging.",
- "type": {"key": "integer", "min": 0, "max": 4096}
-}}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": 4096}}}},
"Port": {
"comment": "A port within a Bridge. May contain a single Interface or multiple (bonded) Interfaces.",
"columns": {
"type": {"key": "uuid", "min": 1, "max": "unlimited"}},
"trunks": {
"comment": "The 802.1Q VLAN(s) that this port trunks. Should be empty if this port trunks all VLAN(s) or if this is not a trunk port.",
- "type": {"key": "integer", "min": 0, "max": 4096}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": 4096}},
"tag": {
"comment": "This port's implicitly tagged VLAN. Should be empty if this is a trunk port.",
- "type": {"key": "integer", "min": 0, "max": 1}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": 1}},
"mac": {
- "comment": "The MAC address to use for this port for the purpose of choosing the bridge's MAC address. This column does not necessarily reflect the port's actual MAC address, nor will setting it change the port's actual MAC address. Exactly 12 hex digits in the form XX:XX:XX:XX:XX:XX.",
- "type": {"key": "string", "min": 0, "max": 1}},
+ "comment": "The MAC address to use for this port for the purpose of choosing the bridge's MAC address. This column does not necessarily reflect the port's actual MAC address, nor will setting it change the port's actual MAC address.",
+ "type": {"key": {"type": "string",
+ "reMatch": "[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}",
+ "reComment": "an Ethernet address in the form XX:XX:XX:XX:XX:XX, where each X is a hex digit"},
+ "min": 0, "max": 1}},
"bond_updelay": {
"comment": "For a bonded port, the number of milliseconds for which carrier must stay up on an interface before the interface is considered to be up. Ignored for non-bonded ports.",
"type": "integer"},
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"ingress_policing_rate": {
"comment": "Maximum rate for data received on this interface, in kbps. Set to 0 to disable policing.",
- "type": "integer"},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0}}},
"ingress_policing_burst": {
"comment": "Maximum burst size for data received on this interface, in kb. The default burst size if set to 0 is 1000 kb.",
- "type": "integer"},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0}}},
"mac": {
- "comment": "Ethernet address to set for this interface. If unset then the default MAC address is used. May not be supported on all interfaces. Exactly 12 hex digits in the form XX:XX:XX:XX:XX:XX.",
- "type": {"key": "string", "min": 0, "max": 1}},
+ "comment": "Ethernet address to set for this interface. If unset then the default MAC address is used. May not be supported on all interfaces.",
+ "type": {"key": {"type": "string",
+ "reMatch": "[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}",
+ "reComment": "an Ethernet address in the form XX:XX:XX:XX:XX:XX, where each X is a hex digit"},
+ "min": 0, "max": 1}},
"external_ids": {
"comment": "Key-value pairs that identify this interface's role in external systems. The currently defined key-value pairs are: \"xs-vif-uuid\", the UUID of the Citrix XenServer VIF associated with this interface; \"xs-network-uuid\", the UUID of the Citrix XenServer network to which this interface is attached; \"xs-vif-vm-uuid\", the UUID of the Citrix XenServer VM to which this interface belongs; \"xs-vif-mac\", the value of the \"MAC\" field in the Citrix XenServer VIF record for this interface.",
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"comment": "Arbitrary identifier for the Mirror.",
"type": "string"},
"select_src_port": {
- "comment": "Ports on which arriving packets are selected for mirroring.",
+ "comment": "Ports on which arriving packets are selected for mirroring. If this column and select_dst_port are both empty, then all packets on all ports are selected for mirroring.",
"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
"select_dst_port": {
"comment": "Ports on which departing packets are selected for mirroring.",
"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
"select_vlan": {
- "comment": "VLANs on which packets are selected for mirroring.",
- "type": {"key": "integer", "min": 0, "max": 4096}},
+ "comment": "VLANs on which packets are selected for mirroring. An empty set selects packets on all VLANs.",
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": 4096}},
"output_port": {
"comment": "Output port for selected packets. Mutually exclusive with output_vlan.",
"type": {"key": "uuid", "min": 0, "max": 1}},
"output_vlan": {
"comment": "Output VLAN for selected packets. Mutually exclusive with output_port.",
- "type": {"key": "integer", "min": 0, "max": 1}}}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 1,
+ "maxInteger": 4095},
+ "min": 0, "max": 1}}}},
"NetFlow": {
"comment": "A NetFlow target.",
"columns": {
"targets": {
"comment": "NetFlow targets in the form \"IP:PORT\".",
- "type": {"key": "string", "min": 1, "max": "unlimited"}},
+ "type": {"key": {"type": "string",
+ "reMatch": "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){3}:[0-9]{1,5}",
+ "reComment": "an IP address followed by a UDP port number, separated by \":\""},
+ "min": 1, "max": "unlimited"}},
"engine_type": {
"comment": "Engine type to use in NetFlow messages. Defaults to datapath index if not specified.",
- "type": {"key": "integer", "min": 0, "max": 1}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 255},
+ "min": 0, "max": 1}},
"engine_id": {
"comment": "Engine ID to use in NetFlow messages. Defaults to datapath index if not specified.",
- "type": {"key": "integer", "min": 0, "max": 1}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 255},
+ "min": 0, "max": 1}},
"add_id_to_interface": {
"comment": "Place least-significant 7 bits of engine ID into most significant bits of ingress and egress interface fields of NetFlow records?",
"type": "boolean"},
"active_timeout": {
"comment": "Active timeout interval, in seconds. A value of 0 requests the default timeout; a negative value disables active timeouts.",
- "type": "integer"}}},
+ "type": {"key": {"type": "integer",
+ "minInteger": -1}}}}},
"sFlow": {
"comment": "A sFlow target.",
"columns": {
"type": "string"},
"max_backoff": {
"comment": "Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 1000},
+ "min": 0, "max": 1}},
"inactivity_probe": {
"comment": "Maximum number of milliseconds of idle time on connection to controller before sending an inactivity probe message. Default is implementation-specific.",
"type": {"key": "integer", "min": 0, "max": 1}},
"fail_mode": {
"comment": "Either \"standalone\" or \"secure\", or empty to use the implementation's default.",
- "type": {"key": "string", "min": 0, "max": 1}},
+ "type": {"key": {"type": "string",
+ "reMatch": "standalone|secure",
+ "reComment": "either \"standalone\" or \"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": "string", "min": 0, "max": 1}},
"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": "string", "min": 0, "max": 1}},
+ "type": {"key": {"type": "string",
+ "reMatch": "in-band|out-of-band",
+ "reComment": "either \"in-band\" or \"out-of-band\""},
+ "min": 0, "max": 1}},
"local_ip": {
"comment": "If \"target\" is not \"discover\", the IP address to configure on the local port.",
- "type": {"key": "string", "min": 0, "max": 1}},
+ "type": {"key": {"type": "string",
+ "reMatch": "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){3}",
+ "reComment": "an IP address"},
+ "min": 0, "max": 1}},
"local_netmask": {
"comment": "If \"target\" is not \"discover\", the IP netmask to configure on the local port.",
- "type": {"key": "string", "min": 0, "max": 1}},
+ "type": {"key": {"type": "string",
+ "reMatch": "255\\.255\\.255\\.(255|254|252|248|240|224|192|128|0)|255\\.255\\.(255|254|252|248|240|224|192|128|0)\\.0|255\\.(255|254|252|248|240|224|192|128|0)\\.0\\.0|(255|254|252|248|240|224|192|128|0)\\.0\\.0\\.0",
+ "reComment": "a netmask"},
+ "min": 0, "max": 1}},
"local_gateway": {
"comment": "If \"target\" is not \"discover\", the IP gateway to configure on the local port.",
- "type": {"key": "string", "min": 0, "max": 1}},
+ "type": {"key": {"type": "string",
+ "reMatch": "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){3}",
+ "reComment": "an IP address"},
+ "min": 0, "max": 1}},
"controller_rate_limit": {
"comment": "The maximum rate at which packets will be forwarded to the OpenFlow controller, in packets per second. If not specified, the default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 100},
+ "min": 0, "max": 1}},
"controller_burst_limit": {
"comment": "The maximum number of unused packet credits that the bridge will allow to accumulate, in packets. If not specified, the default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}}}},
+ "type": {"key": {"type": "integer",
+ "minInteger": 25},
+ "min": 0, "max": 1}}}},
"SSL": {
"comment": "SSL configuration for an Open_vSwitch.",
"columns": {