2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

ovsdb: Add support for weak references.

This commit is contained in:
Ben Pfaff 2010-03-15 15:41:54 -07:00
parent 17d18afbfd
commit 7360012bdf
13 changed files with 561 additions and 36 deletions

View File

@ -412,10 +412,30 @@ ovsdb_base_type_from_json(struct ovsdb_base_type *base,
refTable = ovsdb_parser_member(&parser, "refTable",
OP_ID | OP_OPTIONAL);
if (refTable) {
const struct json *refType;
base->u.uuid.refTableName = xstrdup(refTable->u.string);
/* We can't set base->u.uuid.refTable here because we don't have
* enough context (we might not even be running in ovsdb-server).
* ovsdb_create() will set refTable later. */
refType = ovsdb_parser_member(&parser, "refType",
OP_ID | OP_OPTIONAL);
if (refType) {
const char *refType_s = json_string(refType);
if (!strcmp(refType_s, "strong")) {
base->u.uuid.refType = OVSDB_REF_STRONG;
} else if (!strcmp(refType_s, "weak")) {
base->u.uuid.refType = OVSDB_REF_WEAK;
} else {
error = ovsdb_syntax_error(json, NULL, "refType must be "
"\"strong\" or \"weak\" (not "
"\"%s\")", refType_s);
}
} else {
base->u.uuid.refType = OVSDB_REF_STRONG;
}
}
}
@ -495,6 +515,9 @@ ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
if (base->u.uuid.refTableName) {
json_object_put_string(json, "refTable",
base->u.uuid.refTableName);
if (base->u.uuid.refType == OVSDB_REF_WEAK) {
json_object_put_string(json, "refType", "weak");
}
}
break;

View File

@ -44,6 +44,11 @@ struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
/* An atomic type plus optional constraints. */
enum ovsdb_ref_type {
OVSDB_REF_STRONG, /* Target must exist. */
OVSDB_REF_WEAK /* Delete reference if target disappears. */
};
struct ovsdb_base_type {
enum ovsdb_atomic_type type;
@ -72,6 +77,7 @@ struct ovsdb_base_type {
struct ovsdb_uuid_constraints {
char *refTableName; /* Name of referenced table, or NULL. */
struct ovsdb_table *refTable; /* Referenced table, if available. */
enum ovsdb_ref_type refType; /* Reference type. */
} uuid;
} u;
};
@ -85,7 +91,7 @@ struct ovsdb_base_type {
#define OVSDB_BASE_STRING_INIT { .type = OVSDB_TYPE_STRING, \
.u.string = { 0, UINT_MAX } }
#define OVSDB_BASE_UUID_INIT { .type = OVSDB_TYPE_UUID, \
.u.uuid = { NULL, NULL } }
.u.uuid = { NULL, NULL, 0 } }
void ovsdb_base_type_init(struct ovsdb_base_type *, enum ovsdb_atomic_type);
void ovsdb_base_type_clone(struct ovsdb_base_type *,
@ -101,6 +107,11 @@ 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 *);
static inline bool ovsdb_base_type_is_ref(const struct ovsdb_base_type *);
static inline bool ovsdb_base_type_is_strong_ref(
const struct ovsdb_base_type *);
static inline bool ovsdb_base_type_is_weak_ref(const struct ovsdb_base_type *);
/* An OVSDB type.
*
@ -160,6 +171,26 @@ ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type atomic_type)
return atomic_type >= 0 && atomic_type < OVSDB_N_TYPES;
}
static inline bool
ovsdb_base_type_is_ref(const struct ovsdb_base_type *base)
{
return base->type == OVSDB_TYPE_UUID && base->u.uuid.refTable;
}
static inline bool
ovsdb_base_type_is_strong_ref(const struct ovsdb_base_type *base)
{
return (ovsdb_base_type_is_ref(base)
&& base->u.uuid.refType == OVSDB_REF_STRONG);
}
static inline bool
ovsdb_base_type_is_weak_ref(const struct ovsdb_base_type *base)
{
return (ovsdb_base_type_is_ref(base)
&& base->u.uuid.refType == OVSDB_REF_WEAK);
}
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type)
{
return (type->value.type == OVSDB_TYPE_VOID

View File

@ -196,13 +196,14 @@ class Atom:
class BaseType:
def __init__(self, type,
enum=None,
refTable=None,
refTable=None, refType="strong",
minInteger=None, maxInteger=None,
minReal=None, maxReal=None,
minLength=None, maxLength=None):
self.type = type
self.enum = enum
self.refTable = refTable
self.refType = refType
self.minInteger = minInteger
self.maxInteger = maxInteger
self.minReal = minReal
@ -221,17 +222,23 @@ class BaseType:
enumType = Type(atomicType, None, 0, 'unlimited')
enum = Datum.fromJson(enumType, enum)
refTable = getMember(json, 'refTable', [unicode], description)
refType = getMember(json, 'refType', [unicode], description)
if refType == None:
refType = "strong"
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)
minLength = getMember(json, 'minLength', [int], description)
maxLength = getMember(json, 'minLength', [int], description)
return BaseType(atomicType, enum, refTable, minInteger, maxInteger, minReal, maxReal, minLength, maxLength)
return BaseType(atomicType, enum, refTable, refType, minInteger, maxInteger, minReal, maxReal, minLength, maxLength)
def toEnglish(self, escapeLiteral=returnUnchanged):
if self.type == 'uuid' and self.refTable:
return escapeLiteral(self.refTable)
s = escapeLiteral(self.refTable)
if self.refType == 'weak':
s = "weak reference to " + s
return s
else:
return self.type

View File

@ -175,6 +175,7 @@ is represented by <database-schema>, as described below.
"minLength": <integer> optional, strings only
"maxLength": <integer> optional, strings only
"refTable": <id> optional, uuids only
"refType": "strong" or "weak" optional, only with "refTable"
An <atomic-type> by itself is equivalent to a JSON object with a
single member "type" whose value is the <atomic-type>.
@ -203,8 +204,17 @@ is represented by <database-schema>, as described below.
bytes or UTF-16 code units).
If "type" is "uuid", then "refTable", if present, must be the name
of a table within this database. If "refTable" is set, the
allowed UUIDs are limited to UUIDs for rows in the named table.
of a table within this database. If "refTable" is specified, then
"refType" may also be specified. If "refTable" is set, the effect
depends on "refType":
- If "refType" is "strong" or if "refType" is omitted, the
allowed UUIDs are limited to UUIDs for rows in the named
table.
- If "refType" is "weak", then any UUIDs are allowed, but
UUIDs that do not correspond to rows in the named table will
be automatically deleted.
"refTable" constraints are "deferred" constraints: they are
enforced only at transaction commit time (see the "transact"
@ -337,11 +347,20 @@ include at least the following:
When the commit was attempted, a column's value referenced the
UUID for a row that did not exist in the table named by the
column's <base-type> key or value "refTable". (This can be
caused by inserting a row that references a nonexistent row,
by deleting a row that is still referenced by another row, by
specifying the UUID for a row in the wrong table, and other
ways.)
column's <base-type> key or value "refTable" that has a
"refType" of "strong". (This can be caused by inserting a row
that references a nonexistent row, by deleting a row that is
still referenced by another row, by specifying the UUID for a
row in the wrong table, and other ways.)
"error": "constraint violation"
A column with a <base-type> key or value "refTable" whose
"refType" is "weak" became empty due to deletion(s) caused
because the rows that it referenced were deleted (or never
existed, if the column's row was inserted within the
transaction), and this column is not allowed to be empty
because its <type> has a "min" of 1.
If "params" contains one or more "wait" operations, then the
transaction may take an arbitrary amount of time to complete. The

View File

@ -35,6 +35,8 @@ allocate_row(const struct ovsdb_table *table)
struct ovsdb_row *row = xmalloc(row_size);
row->table = (struct ovsdb_table *) table;
row->txn_row = NULL;
list_init(&row->src_refs);
list_init(&row->dst_refs);
row->n_refs = 0;
return row;
}
@ -77,8 +79,23 @@ ovsdb_row_destroy(struct ovsdb_row *row)
{
if (row) {
const struct ovsdb_table *table = row->table;
struct ovsdb_weak_ref *weak, *next;
const struct shash_node *node;
LIST_FOR_EACH_SAFE (weak, next, struct ovsdb_weak_ref, dst_node,
&row->dst_refs) {
list_remove(&weak->src_node);
list_remove(&weak->dst_node);
free(weak);
}
LIST_FOR_EACH_SAFE (weak, next, struct ovsdb_weak_ref, src_node,
&row->src_refs) {
list_remove(&weak->src_node);
list_remove(&weak->dst_node);
free(weak);
}
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_datum_destroy(&row->fields[column->index], &column->type);

View File

@ -20,20 +20,42 @@
#include <stdint.h>
#include "column.h"
#include "hmap.h"
#include "list.h"
#include "ovsdb-data.h"
struct ovsdb_column_set;
/* A weak reference.
*
* When a column in row A contains a weak reference to UUID of a row B this
* constitutes a weak reference from A (the source) to B (the destination).
*
* Rows A and B may be in the same table or different tables.
*
* Weak references from a row to itself are allowed, but no "struct
* ovsdb_weak_ref" structures are created for them.
*/
struct ovsdb_weak_ref {
struct list src_node; /* In src->src_refs list. */
struct list dst_node; /* In destination row's dst_refs list. */
struct ovsdb_row *src; /* Source row. */
};
/* A row in a database table. */
struct ovsdb_row {
struct ovsdb_table *table; /* Table to which this belongs. */
struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */
struct ovsdb_table *table; /* Table to which this belongs. */
struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */
struct ovsdb_txn_row *txn_row; /* Transaction that row is in, if any. */
/* Number of refs to this row from other rows, in this table or other
* tables, through 'uuid' columns that have a 'refTable' constraint
* pointing to this table. A row with nonzero 'n_refs' cannot be deleted.
* Updated and checked only at transaction commit. */
/* Weak references. */
struct list src_refs; /* Weak references from this row. */
struct list dst_refs; /* Weak references to this row. */
/* Number of strong refs to this row from other rows, in this table or
* other tables, through 'uuid' columns that have a 'refTable' constraint
* pointing to this table and a 'refType' of "strong". A row with nonzero
* 'n_refs' cannot be deleted. Updated and checked only at transaction
* commit. */
size_t n_refs;
struct ovsdb_datum fields[];

View File

@ -160,7 +160,7 @@ ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn,
const struct ovsdb_table *table;
unsigned int i;
if (base->type != OVSDB_TYPE_UUID || !base->u.uuid.refTable) {
if (!ovsdb_base_type_is_strong_ref(base)) {
return NULL;
}
@ -270,6 +270,142 @@ ovsdb_txn_row_commit(struct ovsdb_txn *txn OVS_UNUSED,
return NULL;
}
static void
add_weak_ref(struct ovsdb_txn *txn,
const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
{
struct ovsdb_row *src = (struct ovsdb_row *) src_;
struct ovsdb_row *dst = (struct ovsdb_row *) dst_;
struct ovsdb_weak_ref *weak;
if (src == dst) {
return;
}
dst = ovsdb_txn_row_modify(txn, dst);
if (!list_is_empty(&dst->dst_refs)) {
/* Omit duplicates. */
weak = CONTAINER_OF(list_back(&dst->dst_refs),
struct ovsdb_weak_ref, dst_node);
if (weak->src == src) {
return;
}
}
weak = xmalloc(sizeof *weak);
weak->src = src;
list_push_back(&dst->dst_refs, &weak->dst_node);
list_push_back(&src->src_refs, &weak->src_node);
}
static struct ovsdb_error * WARN_UNUSED_RESULT
assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
{
struct ovsdb_table *table;
struct shash_node *node;
if (txn_row->old) {
/* Mark rows that have weak references to 'txn_row' as modified, so
* that their weak references will get reassessed. */
struct ovsdb_weak_ref *weak, *next;
LIST_FOR_EACH_SAFE (weak, next, struct ovsdb_weak_ref, dst_node,
&txn_row->old->dst_refs) {
if (!weak->src->txn_row) {
ovsdb_txn_row_modify(txn, weak->src);
}
}
}
if (!txn_row->new) {
/* We don't have to do anything about references that originate at
* 'txn_row', because ovsdb_row_destroy() will remove those weak
* references. */
return NULL;
}
table = txn_row->new->table;
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
struct ovsdb_datum *datum = &txn_row->new->fields[column->index];
unsigned int orig_n, i;
bool zero = false;
orig_n = datum->n;
if (ovsdb_base_type_is_weak_ref(&column->type.key)) {
for (i = 0; i < datum->n; ) {
const struct ovsdb_row *row;
row = ovsdb_table_get_row(column->type.key.u.uuid.refTable,
&datum->keys[i].uuid);
if (row) {
add_weak_ref(txn, txn_row->new, row);
i++;
} else {
if (uuid_is_zero(&datum->keys[i].uuid)) {
zero = true;
}
ovsdb_datum_remove_unsafe(datum, i, &column->type);
}
}
}
if (ovsdb_base_type_is_weak_ref(&column->type.value)) {
for (i = 0; i < datum->n; ) {
const struct ovsdb_row *row;
row = ovsdb_table_get_row(column->type.value.u.uuid.refTable,
&datum->values[i].uuid);
if (row) {
add_weak_ref(txn, txn_row->new, row);
i++;
} else {
if (uuid_is_zero(&datum->values[i].uuid)) {
zero = true;
}
ovsdb_datum_remove_unsafe(datum, i, &column->type);
}
}
}
if (datum->n != orig_n) {
bitmap_set1(txn_row->changed, column->index);
ovsdb_datum_sort_assert(datum, column->type.key.type);
if (datum->n < column->type.n_min) {
const struct uuid *row_uuid = ovsdb_row_get_uuid(txn_row->new);
if (zero && !txn_row->old) {
return ovsdb_error(
"constraint violation",
"Weak reference column \"%s\" in \"%s\" row "UUID_FMT
" (inserted within this transaction) contained "
"all-zeros UUID (probably as the default value for "
"this column) but deleting this value caused a "
"constraint volation because this column is not "
"allowed to be empty.", column->name,
table->schema->name, UUID_ARGS(row_uuid));
} else {
return ovsdb_error(
"constraint violation",
"Deletion of %u weak reference(s) to deleted (or "
"never-existing) rows from column \"%s\" in \"%s\" "
"row "UUID_FMT" %scaused this column to become empty, "
"but constraints on this column disallow an "
"empty column.",
orig_n - datum->n, column->name, table->schema->name,
UUID_ARGS(row_uuid),
(txn_row->old
? ""
: "(inserted within this transaction) "));
}
}
}
}
return NULL;
}
static struct ovsdb_error * WARN_UNUSED_RESULT
determine_changes(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
{
@ -330,6 +466,14 @@ ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable)
return error;
}
/* Check reference counts and remove bad reference for "weak" referential
* integrity. */
error = for_each_txn_row(txn, assess_weak_refs);
if (error) {
ovsdb_txn_abort(txn);
return error;
}
/* Send the commit to each replica. */
LIST_FOR_EACH (replica, struct ovsdb_replica, node, &txn->db->replicas) {
error = (replica->class->commit)(replica, txn, durable);

View File

@ -215,7 +215,7 @@ tests_test_ovsdb_SOURCES = \
tests/test-ovsdb.c \
tests/idltest.c \
tests/idltest.h
EXTRA_DIST += tests/uuidfilt.pl
EXTRA_DIST += tests/uuidfilt.pl tests/ovsdb-monitor-sort.pl
tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
# idltest schema and IDL

View File

@ -30,6 +30,30 @@ m4_define([CONSTRAINT_SCHEMA],
"positive": {"type": {"key": {"type": "integer",
"minInteger": 1}}}}}}}]])
m4_define([WEAK_SCHEMA],
[[{"name": "weak",
"tables": {
"a": {
"columns": {
"a": {"type": "integer"},
"a2a": {"type": {"key": {"type": "uuid",
"refTable": "a",
"refType": "weak"},
"min": 0, "max": "unlimited"}},
"a2a1": {"type": {"key": {"type": "uuid",
"refTable": "a",
"refType": "weak"}}},
"a2b": {"type": {"key": {"type": "uuid",
"refTable": "b",
"refType": "weak"}}}}},
"b": {
"columns": {
"b": {"type": "integer"},
"b2a": {"type": {"key": {"type": "uuid",
"refTable": "a",
"refType": "weak"},
"min": 0, "max": "unlimited"}}}}}}]])
# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
#
# Runs "test-ovsdb execute" with the given SCHEMA and each of the
@ -551,6 +575,144 @@ OVSDB_CHECK_EXECUTION([referential integrity -- mutual references],
[{"count":1},{"details":"cannot delete a row <0> because of 1 remaining reference(s)","error":"referential integrity violation"}]
[{"count":1},{"details":"cannot delete b row <1> because of 1 remaining reference(s)","error":"referential integrity violation"}]
[{"count":1},{"count":1}]
]])
OVSDB_CHECK_EXECUTION([weak references],
[WEAK_SCHEMA],
[[[["weak",
{"op": "insert",
"table": "a",
"row": {"a": 0,
"a2a": ["set", [["named-uuid", "row1"],
["named-uuid", "row2"],
["uuid", "0e767b36-6822-4044-8307-d58467e04669"]]],
"a2a1": ["named-uuid", "row1"],
"a2b": ["named-uuid", "row3"]},
"uuid-name": "row1"},
{"op": "insert",
"table": "a",
"row": {"a": 1,
"a2a": ["set", [["named-uuid", "row1"],
["named-uuid", "row2"]]],
"a2a1": ["named-uuid", "row2"],
"a2b": ["named-uuid", "row3"]},
"uuid-name": "row2"},
{"op": "insert",
"table": "a",
"row": {"a": 2,
"a2a": ["set", [["named-uuid", "row1"],
["named-uuid", "row2"]]],
"a2a1": ["named-uuid", "row2"],
"a2b": ["named-uuid", "row4"]}},
{"op": "insert",
"table": "b",
"row": {"b": 2,
"b2a": ["named-uuid", "row1"]},
"uuid-name": "row3"},
{"op": "insert",
"table": "b",
"row": {"b": 3,
"b2a": ["named-uuid", "row2"]},
"uuid-name": "row4"}]]],
dnl Check that the nonexistent row UUID we added to row a0 was deleted,
dnl and that other rows were inserted as requested.
[[["weak",
{"op": "select",
"table": "a",
"where": [],
"columns": ["_uuid", "a2a", "a2a1", "a2b"],
"sort": ["a"]}]]],
[[["weak",
{"op": "select",
"table": "b",
"where": [],
"columns": ["_uuid", "b", "b2a"],
"sort": ["b"]}]]],
dnl Try to insert invalid all-zeros weak reference (the default) into
dnl "a2b", which requires exactly one value.
[[["weak",
{"op": "insert",
"table": "a",
"row": {}}]]],
dnl Try to delete row from "b" that is referred to by weak references
dnl from "a" table "a2b" column that requires exactly one value.
[[["weak",
{"op": "delete",
"table": "b",
"where": [["b", "==", 3]]}]]],
dnl Try to delete row from "a" that is referred to by weak references
dnl from "a" table "a2a1" column that requires exactly one value.
[[["weak",
{"op": "delete",
"table": "a",
"where": [["a", "==", 1]]}]]],
dnl Delete the row that had the reference that caused the previous
dnl deletion to fail, then check that other rows are unchanged.
[[["weak",
{"op": "delete",
"table": "a",
"where": [["a", "==", 2]]}]]],
[[["weak",
{"op": "select",
"table": "a",
"where": [],
"columns": ["_uuid", "a2a", "a2a1", "a2b"],
"sort": ["a"]}]]],
[[["weak",
{"op": "select",
"table": "b",
"where": [],
"columns": ["_uuid", "b", "b2a"],
"sort": ["b"]}]]],
dnl Delete row a0 then check that references to it were removed.
[[["weak",
{"op": "delete",
"table": "a",
"where": [["a", "==", 0]]}]]],
[[["weak",
{"op": "select",
"table": "a",
"where": [],
"columns": ["_uuid", "a2a", "a2a1", "a2b"],
"sort": ["a"]}]]],
[[["weak",
{"op": "select",
"table": "b",
"where": [],
"columns": ["_uuid", "b", "b2a"],
"sort": ["b"]}]]],
dnl Delete row a1 then check that references to it were removed.
[[["weak",
{"op": "delete",
"table": "a",
"where": [["a", "==", 1]]}]]],
[[["weak",
{"op": "select",
"table": "a",
"where": [],
"columns": ["_uuid", "a2a", "a2a1", "a2b"],
"sort": ["a"]}]]],
[[["weak",
{"op": "select",
"table": "b",
"where": [],
"columns": ["_uuid", "b", "b2a"],
"sort": ["b"]}]]]],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]}]
[{"rows":[{"_uuid":["uuid","<0>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<0>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<1>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<2>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<4>"]}]}]
[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["uuid","<0>"]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}]
[{"uuid":["uuid","<5>"]},{"details":"Weak reference column \"a2b\" in \"a\" row <5> (inserted within this transaction) contained all-zeros UUID (probably as the default value for this column) but deleting this value caused a constraint volation because this column is not allowed to be empty.","error":"constraint violation"}]
[{"count":1},{"details":"Deletion of 1 weak reference(s) to deleted (or never-existing) rows from column \"a2b\" in \"a\" row <2> caused this column to become empty, but constraints on this column disallow an empty column.","error":"constraint violation"}]
[{"count":1},{"details":"Deletion of 1 weak reference(s) to deleted (or never-existing) rows from column \"a2a1\" in \"a\" row <2> caused this column to become empty, but constraints on this column disallow an empty column.","error":"constraint violation"}]
[{"count":1}]
[{"rows":[{"_uuid":["uuid","<0>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<0>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<1>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]}]}]
[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["uuid","<0>"]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}]
[{"count":1}]
[{"rows":[{"_uuid":["uuid","<1>"],"a2a":["uuid","<1>"],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]}]}]
[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}]
[{"count":1}]
[{"rows":[]}]
[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["set",[]]}]}]
]])])
EXECUTION_EXAMPLES

49
tests/ovsdb-monitor-sort.pl Executable file
View File

@ -0,0 +1,49 @@
#! /usr/bin/perl
use strict;
use warnings;
# Breaks lines read from <STDIN> into groups using blank lines as
# group separators, then sorts lines within the groups for
# reproducibility.
sub compare_lines {
my ($a, $b) = @_;
my $u = '[0-9a-fA-F]';
my $uuid_re = "${u}{8}-${u}{4}-${u}{4}-${u}{4}-${u}{12}";
if ($a =~ /^$uuid_re/) {
if ($b =~ /^$uuid_re/) {
return substr($a, 36) cmp substr($b, 36);
} else {
return 1;
}
} elsif ($b =~ /^$uuid_re/) {
return -1;
} else {
return $a cmp $b;
}
}
sub output_group {
my (@group) = @_;
print "$_\n" foreach sort { compare_lines($a, $b) } @group;
}
my @group = ();
while (<STDIN>) {
chomp;
if ($_ eq '') {
output_group(@group);
@group = ();
print "\n";
} else {
if (/^,/ && @group) {
$group[$#group] .= "\n" . $_;
} else {
push(@group, $_);
}
}
}
output_group(@group) if @group;

View File

@ -1,6 +1,6 @@
AT_BANNER([OVSDB -- ovsdb-server monitors])
# OVSDB_CHECK_MONITOR(TITLE, SCHEMA, [PRE-MONITOR-TXN], MONITOR-ARGS,
# OVSDB_CHECK_MONITOR(TITLE, SCHEMA, [PRE-MONITOR-TXN], DB, TABLE,
# TRANSACTIONS, OUTPUT, [KEYWORDS])
#
# Creates a database with the given SCHEMA, starts an ovsdb-server on
@ -17,29 +17,29 @@ AT_BANNER([OVSDB -- ovsdb-server monitors])
# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
m4_define([OVSDB_CHECK_MONITOR],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb server monitor positive $7])
AT_KEYWORDS([ovsdb server monitor positive $8])
AT_DATA([schema], [$2
])
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
m4_foreach([txn], [$3],
[AT_CHECK([ovsdb-tool transact db 'txn'], [0], [ignore], [ignore])])
AT_CHECK([ovsdb-server --detach --pidfile=$PWD/server-pid --remote=punix:socket --unixctl=$PWD/unixctl db], [0], [ignore], [ignore])
AT_CHECK([ovsdb-client --detach --pidfile=$PWD/client-pid -d json monitor --format=csv unix:socket ordinals $4 > output],
AT_CHECK([ovsdb-client --detach --pidfile=$PWD/client-pid -d json monitor --format=csv unix:socket $4 $5 > output],
[0], [ignore], [ignore], [kill `cat server-pid`])
m4_foreach([txn], [$5],
m4_foreach([txn], [$6],
[AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0],
[ignore], [ignore], [kill `cat server-pid client-pid`])])
AT_CHECK([ovsdb-client transact unix:socket '[[]]'], [0],
AT_CHECK([ovsdb-client transact unix:socket '[["$4"]]'], [0],
[ignore], [ignore], [kill `cat server-pid client-pid`])
AT_CHECK([ovs-appctl -t $PWD/unixctl -e exit], [0], [ignore], [ignore])
OVS_WAIT_UNTIL([test ! -e server-pid && test ! -e client-pid])
AT_CHECK([perl $srcdir/uuidfilt.pl output], [0], [$6], [ignore])
AT_CHECK([perl $srcdir/ovsdb-monitor-sort.pl < output | perl $srcdir/uuidfilt.pl], [0], [$7], [ignore])
AT_CLEANUP])
OVSDB_CHECK_MONITOR([monitor insert into empty table],
[ORDINAL_SCHEMA],
[],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "insert",
"table": "ordinals",
@ -54,7 +54,7 @@ OVSDB_CHECK_MONITOR([monitor insert into populated table],
{"op": "insert",
"table": "ordinals",
"row": {"number": 10, "name": "ten"}}]]]],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "insert",
"table": "ordinals",
@ -72,7 +72,7 @@ OVSDB_CHECK_MONITOR([monitor delete],
{"op": "insert",
"table": "ordinals",
"row": {"number": 10, "name": "ten"}}]]]],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "delete",
"table": "ordinals",
@ -90,7 +90,7 @@ OVSDB_CHECK_MONITOR([monitor row update],
{"op": "insert",
"table": "ordinals",
"row": {"number": 10, "name": "ten"}}]]]],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "update",
"table": "ordinals",
@ -110,7 +110,7 @@ OVSDB_CHECK_MONITOR([monitor no-op row updates],
{"op": "insert",
"table": "ordinals",
"row": {"number": 10, "name": "ten"}}]]]],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "update",
"table": "ordinals",
@ -133,7 +133,7 @@ OVSDB_CHECK_MONITOR([monitor insert-and-update transaction],
{"op": "insert",
"table": "ordinals",
"row": {"number": 10, "name": "ten"}}]]]],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "insert",
"table": "ordinals",
@ -150,14 +150,13 @@ row,action,name,number,_version
<2>,insert,"""three squared""",9,"[""uuid"",""<3>""]"
]])
OVSDB_CHECK_MONITOR([monitor insert-update-and-delete transaction],
[ORDINAL_SCHEMA],
[[[["ordinals",
{"op": "insert",
"table": "ordinals",
"row": {"number": 10, "name": "ten"}}]]]],
[ordinals],
[ordinals], [ordinals],
[[[["ordinals",
{"op": "insert",
"table": "ordinals",
@ -180,3 +179,38 @@ row,action,name,number,_version
<2>,insert,"""seven""",7,"[""uuid"",""<3>""]"
]])
OVSDB_CHECK_MONITOR([monitor weak reference change],
[WEAK_SCHEMA],
[[[["weak",
{"op": "insert",
"table": "a",
"row": {"a": 0,
"a2a1": ["named-uuid", "a0"],
"a2b": ["named-uuid", "b2"]},
"uuid-name": "a0"},
{"op": "insert",
"table": "a",
"row": {"a": 1,
"a2a": ["named-uuid", "a0"],
"a2a1": ["named-uuid", "a1"],
"a2b": ["named-uuid", "b2"]},
"uuid-name": "a1"},
{"op": "insert",
"table": "b",
"row": {"b": 2},
"uuid-name": "b2"}]]]],
[weak], [a],
[[[["weak",
{"op": "delete",
"table": "a",
"where": [["a", "==", 0]]}]]]],
[[row,action,a,a2a,a2b,a2a1,_version
<0>,initial,0,"[""set"",[]]","[""uuid"",""<1>""]","[""uuid"",""<0>""]","[""uuid"",""<2>""]"
<3>,initial,1,"[""uuid"",""<0>""]","[""uuid"",""<1>""]","[""uuid"",""<3>""]","[""uuid"",""<4>""]"
row,action,a,a2a,a2b,a2a1,_version
<0>,delete,0,"[""set"",[]]","[""uuid"",""<1>""]","[""uuid"",""<0>""]","[""uuid"",""<2>""]"
<3>,old,,"[""uuid"",""<0>""]",,,
,new,1,"[""set"",[]]","[""uuid"",""<1>""]","[""uuid"",""<3>""]","[""uuid"",""<5>""]"
]])

View File

@ -13,9 +13,21 @@ sub lookup_uuid {
return "<$uuids{$uuid}>";
}
sub sort_set {
my ($s) = @_;
my (@uuids) = sort { $a <=> $b } (grep(/\d+/, split(/(\d+)/, $s)));
return '["set",[' . join(',', map('["uuid","<' . $_ . '>"]', @uuids)) . ']]';
}
my $u = '[0-9a-fA-F]';
my $uuid_re = "${u}{8}-${u}{4}-${u}{4}-${u}{4}-${u}{12}";
while (<>) {
s/($uuid_re)/lookup_uuid($1)/eg;
# Sort sets like this:
# [["uuid","<1>"],["uuid","<0>"]]
# to look like this:
# [["uuid","<0>"],["uuid","<1>"]]
s/(\["set",\[(,?\["uuid","<\d+>"\])+\]\])/sort_set($1)/ge;
print $_;
}

View File

@ -122,11 +122,14 @@
"type": "string"},
"select_src_port": {
"type": {"key": {"type": "uuid",
"refTable": "Port"},
"refTable": "Port",
"refType": "weak"},
"min": 0, "max": "unlimited"}},
"select_dst_port": {
"type": {"key": {"type": "uuid",
"refTable": "Port"}, "min": 0, "max": "unlimited"}},
"refTable": "Port",
"refType": "weak"},
"min": 0, "max": "unlimited"}},
"select_vlan": {
"type": {"key": {"type": "integer",
"minInteger": 0,
@ -134,7 +137,9 @@
"min": 0, "max": 4096}},
"output_port": {
"type": {"key": {"type": "uuid",
"refTable": "Port"}, "min": 0, "max": 1}},
"refTable": "Port",
"refType": "weak"},
"min": 0, "max": 1}},
"output_vlan": {
"type": {"key": {"type": "integer",
"minInteger": 1,