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:
parent
17d18afbfd
commit
7360012bdf
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
33
ovsdb/SPECS
33
ovsdb/SPECS
@ -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
|
||||
|
17
ovsdb/row.c
17
ovsdb/row.c
@ -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);
|
||||
|
34
ovsdb/row.h
34
ovsdb/row.h
@ -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[];
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
49
tests/ovsdb-monitor-sort.pl
Executable 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;
|
@ -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>""]"
|
||||
]])
|
||||
|
||||
|
@ -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 $_;
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user