From 0d0f05b909b6428d44eb147bd4edd73782d2a137 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 8 Feb 2010 14:09:41 -0800 Subject: [PATCH] ovsdb: Add support for referential integrity in the database itself. --- lib/ovsdb-data.c | 5 +- lib/ovsdb-types.c | 23 +++++- lib/ovsdb-types.h | 8 +- ovsdb/SPECS | 26 ++++-- ovsdb/ovsdb-idlc.1 | 11 --- ovsdb/ovsdb-idlc.in | 9 +-- ovsdb/ovsdb.c | 67 ++++++++++++++++ ovsdb/row.c | 3 +- ovsdb/row.h | 9 ++- ovsdb/transaction.c | 158 +++++++++++++++++++++++++++++++++++++ tests/automake.mk | 1 + tests/idltest.ann | 4 - tests/idltest.ovsschema | 134 +++++++++++++++++++++++++------ tests/ovs-vsctl.at | 18 ++--- tests/ovsdb-execution.at | 112 ++++++++++++++++++++++++++ tests/ovsdb-idl.at | 88 +++++++++++---------- tests/ovsdb-schema.at | 47 +++++++++++ tests/ovsdb-types.at | 7 ++ tests/ovsdb.at | 1 + tests/test-ovsdb.c | 34 ++++++-- utilities/ovs-vsctl.8.in | 15 +--- utilities/ovs-vsctl.c | 14 +--- vswitchd/vswitch-idl.ann | 12 --- vswitchd/vswitch.ovsschema | 46 ++++++++--- 24 files changed, 688 insertions(+), 164 deletions(-) create mode 100644 tests/ovsdb-schema.at diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c index e79f84997..76d046f17 100644 --- a/lib/ovsdb-data.c +++ b/lib/ovsdb-data.c @@ -619,7 +619,10 @@ check_string_constraints(const char *s, /* 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. */ + * constraints are met, otherwise an error that explains the violation. + * + * Checking UUID constraints is deferred to transaction commit time, so this + * function does nothing for UUID constraints. */ struct ovsdb_error * ovsdb_atom_check_constraints(const union ovsdb_atom *atom, const struct ovsdb_base_type *base) diff --git a/lib/ovsdb-types.c b/lib/ovsdb-types.c index ff819609f..1b5c7ed18 100644 --- a/lib/ovsdb-types.c +++ b/lib/ovsdb-types.c @@ -142,6 +142,8 @@ ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type) break; case OVSDB_TYPE_UUID: + base->u.uuid.refTableName = NULL; + base->u.uuid.refTable = NULL; break; case OVSDB_N_TYPES: @@ -172,6 +174,9 @@ ovsdb_base_type_clone(struct ovsdb_base_type *dst, break; case OVSDB_TYPE_UUID: + if (dst->u.uuid.refTableName) { + dst->u.uuid.refTableName = xstrdup(dst->u.uuid.refTableName); + } break; case OVSDB_N_TYPES: @@ -200,6 +205,7 @@ ovsdb_base_type_destroy(struct ovsdb_base_type *base) break; case OVSDB_TYPE_UUID: + free(base->u.uuid.refTableName); break; case OVSDB_N_TYPES: @@ -263,7 +269,7 @@ ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base) || base->u.string.maxLen != UINT_MAX); case OVSDB_TYPE_UUID: - return false; + return base->u.uuid.refTableName != NULL; case OVSDB_N_TYPES: NOT_REACHED(); @@ -411,6 +417,17 @@ ovsdb_base_type_from_json(struct ovsdb_base_type *base, error = ovsdb_syntax_error(json, NULL, "minLength exceeds maxLength"); } + } else if (base->type == OVSDB_TYPE_UUID) { + const struct json *refTable; + + refTable = ovsdb_parser_member(&parser, "refTable", + OP_ID | OP_OPTIONAL); + if (refTable) { + 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. */ + } } if (error) { @@ -485,6 +502,10 @@ ovsdb_base_type_to_json(const struct ovsdb_base_type *base) break; case OVSDB_TYPE_UUID: + if (base->u.uuid.refTableName) { + json_object_put_string(json, "refTable", + base->u.uuid.refTableName); + } break; case OVSDB_N_TYPES: diff --git a/lib/ovsdb-types.h b/lib/ovsdb-types.h index 633b50f66..b11f82775 100644 --- a/lib/ovsdb-types.h +++ b/lib/ovsdb-types.h @@ -67,6 +67,11 @@ struct ovsdb_base_type { unsigned int minLen; /* minLength or 0. */ unsigned int maxLen; /* maxLength or UINT_MAX. */ } string; + + struct ovsdb_uuid_constraints { + char *refTableName; /* Name of referenced table, or NULL. */ + struct ovsdb_table *refTable; /* Referenced table, if available. */ + } uuid; } u; }; @@ -79,7 +84,8 @@ struct ovsdb_base_type { #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 } +#define OVSDB_BASE_UUID_INIT { .type = OVSDB_TYPE_UUID, \ + .u.uuid = { NULL, NULL } } void ovsdb_base_type_init(struct ovsdb_base_type *, enum ovsdb_atomic_type); void ovsdb_base_type_clone(struct ovsdb_base_type *, diff --git a/ovsdb/SPECS b/ovsdb/SPECS index 75e592bef..c1e3eca67 100644 --- a/ovsdb/SPECS +++ b/ovsdb/SPECS @@ -209,8 +209,14 @@ is represented by , as described below. minLength. String length is measured in characters (not bytes or UTF-16 code units). - The contraints on are "immediate", enforced - immediately by each operation. + 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. + + "refTable" constraints are "deferred" constraints: they are + enforced only at transaction commit time (see the "transact" + request below). The other contraints on are + "immediate", enforced immediately by each operation. @@ -309,9 +315,19 @@ In general, "result" contains some number of successful results, possibly followed by an error, in turn followed by enough JSON null values to match the number of elements in "params". There is one exception: if all of the operations succeed, but the results cannot be -committed (e.g. due to I/O errors), then "result" will have one more -element than "params", with the additional element describing the -error. +committed, then "result" will have one more element than "params", +with the additional element an . The possible "error" strings +include at least the following: + + "error": "referential integrity violation" + + 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 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.) If "params" contains one or more "wait" operations, then the transaction may take an arbitrary amount of time to complete. The diff --git a/ovsdb/ovsdb-idlc.1 b/ovsdb/ovsdb-idlc.1 index 1a7e8041d..a2de7aec7 100644 --- a/ovsdb/ovsdb-idlc.1 +++ b/ovsdb/ovsdb-idlc.1 @@ -39,17 +39,6 @@ It will be output on an \fB#include\fR line in the source file generated by the C bindings. It should include the bracketing \fB""\fR or \fB<>\fR. . -.IP "\fB""\fBkeyRefTable\fR"" member of " -A whose \fBkey\fR is \fB"uuid"\fR may have an additional member -named \fB"keyRefTable"\fR, whose value is a table name. This -expresses the constraint that keys of the given are UUIDs that -reference rows in the named table. This allows the IDL to supply a -structure pointer in place of a raw UUID in its marshalled version of -the given type. -. -.IP "\fB""valueRefTable""\fR member of " -Analogous to \fB"keyRefTable"\fR in meaning and effect, except that it -applies to the \fB"value"\fR member of the . .SS "Commands" .IP "\fBannotate\fI schema annotations\fR" Reads \fIschema\fR, which should be a file in JSON format (ordinarily diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in index 12aa7d9a0..b41113143 100755 --- a/ovsdb/ovsdb-idlc.in +++ b/ovsdb/ovsdb-idlc.in @@ -203,6 +203,9 @@ class BaseType: stmts.append('%s.u.string.minLen = %d;' % (var, self.minLength)) if self.maxLength != None: stmts.append('%s.u.string.maxLen = %d;' % (var, self.maxLength)) + elif self.type == 'uuid': + if self.refTable != None: + stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.refTable))) return '\n'.join([indent + stmt for stmt in stmts]) class Type: @@ -219,17 +222,11 @@ class Type: else: keyJson = mustGetMember(json, 'key', [dict, unicode], description) key = BaseType.fromJson(keyJson, 'key in %s' % description) - keyRefTable = getMember(json, 'keyRefTable', [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 diff --git a/ovsdb/ovsdb.c b/ovsdb/ovsdb.c index e95d23c41..2dea507cb 100644 --- a/ovsdb/ovsdb.c +++ b/ovsdb/ovsdb.c @@ -17,9 +17,11 @@ #include "ovsdb.h" +#include "column.h" #include "json.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" +#include "ovsdb-types.h" #include "table.h" #include "transaction.h" @@ -79,6 +81,23 @@ ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap) return NULL; } +static struct ovsdb_error * WARN_UNUSED_RESULT +ovsdb_schema_check_ref_table(const struct ovsdb_column *column, + const struct shash *tables, + const struct ovsdb_base_type *base, + const char *base_name) +{ + if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName + && !shash_find(tables, base->u.uuid.refTableName)) { + return ovsdb_syntax_error(NULL, NULL, + "column %s %s refers to undefined table %s", + column->name, base_name, + base->u.uuid.refTableName); + } else { + return NULL; + } +} + struct ovsdb_error * ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) { @@ -120,6 +139,29 @@ ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) shash_add(&schema->tables, table->name, table); } + + /* Validate that all refTables refer to the names of tables that exist. */ + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + struct shash_node *node2; + + SHASH_FOR_EACH (node2, &table->columns) { + struct ovsdb_column *column = node2->data; + + error = ovsdb_schema_check_ref_table(column, &schema->tables, + &column->type.key, "key"); + if (!error) { + error = ovsdb_schema_check_ref_table(column, &schema->tables, + &column->type.value, + "value"); + } + if (error) { + ovsdb_schema_destroy(schema); + return error; + } + } + } + *schemap = schema; return 0; } @@ -148,6 +190,18 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema) return json; } +static void +ovsdb_set_ref_table(const struct shash *tables, + struct ovsdb_base_type *base) +{ + if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { + struct ovsdb_table *table; + + table = shash_find_data(tables, base->u.uuid.refTableName); + base->u.uuid.refTable = table; + } +} + struct ovsdb * ovsdb_create(struct ovsdb_schema *schema) { @@ -166,6 +220,19 @@ ovsdb_create(struct ovsdb_schema *schema) shash_add(&db->tables, node->name, ovsdb_table_create(ts)); } + /* Set all the refTables. */ + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + struct shash_node *node2; + + SHASH_FOR_EACH (node2, &table->columns) { + struct ovsdb_column *column = node2->data; + + ovsdb_set_ref_table(&db->tables, &column->type.key); + ovsdb_set_ref_table(&db->tables, &column->type.value); + } + } + return db; } diff --git a/ovsdb/row.c b/ovsdb/row.c index 1b8194201..52c5ddb21 100644 --- a/ovsdb/row.c +++ b/ovsdb/row.c @@ -1,4 +1,4 @@ -/* 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. @@ -35,6 +35,7 @@ allocate_row(const struct ovsdb_table *table) struct ovsdb_row *row = xmalloc(row_size); row->table = (struct ovsdb_table *) table; row->txn_row = NULL; + row->n_refs = 0; return row; } diff --git a/ovsdb/row.h b/ovsdb/row.h index 55c4f1426..d468194a4 100644 --- a/ovsdb/row.h +++ b/ovsdb/row.h @@ -1,4 +1,4 @@ -/* 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. @@ -29,6 +29,13 @@ 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_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. */ + size_t n_refs; + struct ovsdb_datum fields[]; }; diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c index 7ae52ef49..137ae06b7 100644 --- a/ovsdb/transaction.c +++ b/ovsdb/transaction.c @@ -131,12 +131,169 @@ ovsdb_txn_row_commit(struct ovsdb_txn_row *txn_row) ovsdb_row_destroy(txn_row->old); } +static struct ovsdb_txn_row * +find_txn_row(const struct ovsdb_table *table, const struct uuid *uuid) +{ + struct ovsdb_txn_row *txn_row; + + if (!table->txn_table) { + return NULL; + } + + HMAP_FOR_EACH_WITH_HASH (txn_row, struct ovsdb_txn_row, hmap_node, + uuid_hash(uuid), &table->txn_table->txn_rows) { + const struct ovsdb_row *row; + + row = txn_row->old ? txn_row->old : txn_row->new; + if (uuid_equals(uuid, ovsdb_row_get_uuid(row))) { + return txn_row; + } + } + + return NULL; +} + +static void +ovsdb_txn_adjust_atom_refs(const union ovsdb_atom *atoms, unsigned int n, + const struct ovsdb_table *table, + int delta, struct ovsdb_error **errorp) +{ + unsigned int i; + + for (i = 0; i < n; i++) { + const struct uuid *uuid = &atoms[i].uuid; + struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid); + if (txn_row) { + if (txn_row->old) { + txn_row->old->n_refs += delta; + } + if (txn_row->new) { + txn_row->new->n_refs += delta; + } + } else { + const struct ovsdb_row *row_ = ovsdb_table_get_row(table, uuid); + if (row_) { + struct ovsdb_row *row = (struct ovsdb_row *) row_; + row->n_refs += delta; + } else if (errorp) { + if (!*errorp) { + *errorp = ovsdb_error("referential integrity violation", + "reference to nonexistent row " + UUID_FMT, UUID_ARGS(uuid)); + } + } else { + NOT_REACHED(); + } + } + } +} + +static void +ovsdb_txn_adjust_row_refs(const struct ovsdb_row *r, + const struct ovsdb_column *column, int delta, + struct ovsdb_error **errorp) +{ + const struct ovsdb_datum *field = &r->fields[column->index]; + const struct ovsdb_type *type = &column->type; + + if (type->key.type == OVSDB_TYPE_UUID && type->key.u.uuid.refTable) { + ovsdb_txn_adjust_atom_refs(field->keys, field->n, + type->key.u.uuid.refTable, delta, errorp); + } + if (type->value.type == OVSDB_TYPE_UUID && type->value.u.uuid.refTable) { + ovsdb_txn_adjust_atom_refs(field->values, field->n, + type->value.u.uuid.refTable, delta, errorp); + } +} + +static struct ovsdb_error * WARN_UNUSED_RESULT +ovsdb_txn_adjust_ref_counts__(struct ovsdb_txn *txn, int delta) +{ + struct ovsdb_txn_table *t; + struct ovsdb_error *error; + + error = NULL; + LIST_FOR_EACH (t, struct ovsdb_txn_table, node, &txn->txn_tables) { + struct ovsdb_table *table = t->table; + struct ovsdb_txn_row *r; + + HMAP_FOR_EACH (r, struct ovsdb_txn_row, hmap_node, &t->txn_rows) { + struct shash_node *node; + + SHASH_FOR_EACH (node, &table->schema->columns) { + const struct ovsdb_column *column = node->data; + + if (r->old) { + ovsdb_txn_adjust_row_refs(r->old, column, -delta, NULL); + } + if (r->new) { + ovsdb_txn_adjust_row_refs(r->new, column, delta, &error); + } + } + } + } + return error; +} + +static void +ovsdb_txn_rollback_counts(struct ovsdb_txn *txn) +{ + ovsdb_error_destroy(ovsdb_txn_adjust_ref_counts__(txn, -1)); +} + +static struct ovsdb_error * WARN_UNUSED_RESULT +ovsdb_txn_commit_ref_counts(struct ovsdb_txn *txn) +{ + struct ovsdb_error *error = ovsdb_txn_adjust_ref_counts__(txn, 1); + if (error) { + ovsdb_txn_rollback_counts(txn); + } + return error; +} + +static struct ovsdb_error * WARN_UNUSED_RESULT +update_ref_counts(struct ovsdb_txn *txn) +{ + struct ovsdb_error *error; + struct ovsdb_txn_table *t; + + error = ovsdb_txn_commit_ref_counts(txn); + if (error) { + return error; + } + + LIST_FOR_EACH (t, struct ovsdb_txn_table, node, &txn->txn_tables) { + struct ovsdb_txn_row *r; + + HMAP_FOR_EACH (r, struct ovsdb_txn_row, hmap_node, &t->txn_rows) { + if (!r->new && r->old->n_refs) { + error = ovsdb_error("referential integrity violation", + "cannot delete %s row "UUID_FMT" because " + "of %zu remaining reference(s)", + t->table->schema->name, + UUID_ARGS(ovsdb_row_get_uuid(r->old)), + r->old->n_refs); + ovsdb_txn_rollback_counts(txn); + return error; + } + } + } + + return NULL; +} + struct ovsdb_error * ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable) { struct ovsdb_replica *replica; struct ovsdb_error *error; + error = update_ref_counts(txn); + if (error) { + ovsdb_txn_abort(txn); + return error; + } + LIST_FOR_EACH (replica, struct ovsdb_replica, node, &txn->db->replicas) { error = (replica->class->commit)(replica, txn, durable); if (error) { @@ -215,6 +372,7 @@ ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_) struct ovsdb_row *rw_row; rw_row = ovsdb_row_clone(ro_row); + rw_row->n_refs = ro_row->n_refs; uuid_generate(ovsdb_row_get_version_rw(rw_row)); rw_row->txn_row = ovsdb_txn_row_create(txn, table, ro_row, rw_row); hmap_replace(&table->rows, &ro_row->hmap_node, &rw_row->hmap_node); diff --git a/tests/automake.mk b/tests/automake.mk index 8fa6c1580..fe493fd36 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -26,6 +26,7 @@ TESTSUITE_AT = \ tests/ovsdb-column.at \ tests/ovsdb-table.at \ tests/ovsdb-row.at \ + tests/ovsdb-schema.at \ tests/ovsdb-condition.at \ tests/ovsdb-mutation.at \ tests/ovsdb-query.at \ diff --git a/tests/idltest.ann b/tests/idltest.ann index 2ffd1af68..66e863737 100644 --- a/tests/idltest.ann +++ b/tests/idltest.ann @@ -7,7 +7,3 @@ s["idlPrefix"] = "idltest_" s["idlHeader"] = "\"tests/idltest.h\"" -s["tables"]["link1"]["columns"]["k"]["type"]["keyRefTable"] = "link1" -s["tables"]["link1"]["columns"]["ka"]["type"]["keyRefTable"] = "link1" -s["tables"]["link1"]["columns"]["l2"]["type"]["keyRefTable"] = "link2" -s["tables"]["link2"]["columns"]["l1"]["type"]["keyRefTable"] = "link1" diff --git a/tests/idltest.ovsschema b/tests/idltest.ovsschema index 239a34335..545242bea 100644 --- a/tests/idltest.ovsschema +++ b/tests/idltest.ovsschema @@ -1,25 +1,109 @@ -{"name": "idltest", - "tables": { - "simple": { - "columns": { - "i": {"type": "integer"}, - "r": {"type": "real"}, - "b": {"type": "boolean"}, - "s": {"type": "string"}, - "u": {"type": "uuid"}, - "ia": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}, - "ra": {"type": {"key": "real", "min": 0, "max": "unlimited"}}, - "ba": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}}, - "sa": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, - "ua": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}, - "link1": { - "columns": { - "i": {"type": "integer"}, - "k": {"type": {"key": "uuid"}}, - "ka": {"type": {"key": "uuid", - "min": 0, "max": "unlimited"}}, - "l2": {"type": {"key": "uuid", "min": 0, "max": 1}}}}, - "link2": { - "columns": { - "i": {"type": "integer"}, - "l1": {"type": {"key": "uuid", "min": 0, "max": 1}}}}}} +{ + "name": "idltest", + "tables": { + "link1": { + "columns": { + "i": { + "type": "integer" + }, + "k": { + "type": { + "key": { + "type": "uuid", + "refTable": "link1" + } + } + }, + "ka": { + "type": { + "key": { + "type": "uuid", + "refTable": "link1" + }, + "max": "unlimited", + "min": 0 + } + }, + "l2": { + "type": { + "key": { + "type": "uuid", + "refTable": "link2" + }, + "min": 0 + } + } + } + }, + "link2": { + "columns": { + "i": { + "type": "integer" + }, + "l1": { + "type": { + "key": { + "type": "uuid", + "refTable": "link1" + }, + "min": 0 + } + } + } + }, + "simple": { + "columns": { + "b": { + "type": "boolean" + }, + "ba": { + "type": { + "key": "boolean", + "max": "unlimited", + "min": 0 + } + }, + "i": { + "type": "integer" + }, + "ia": { + "type": { + "key": "integer", + "max": "unlimited", + "min": 0 + } + }, + "r": { + "type": "real" + }, + "ra": { + "type": { + "key": "real", + "max": "unlimited", + "min": 0 + } + }, + "s": { + "type": "string" + }, + "sa": { + "type": { + "key": "string", + "max": "unlimited", + "min": 0 + } + }, + "u": { + "type": "uuid" + }, + "ua": { + "type": { + "key": "uuid", + "max": "unlimited", + "min": 0 + } + } + } + } + } +} diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at index 06bff53b5..103b17ae0 100644 --- a/tests/ovs-vsctl.at +++ b/tests/ovs-vsctl.at @@ -451,7 +451,7 @@ AT_BANNER([ovs-vsctl unit tests -- database commands]) AT_SETUP([database commands -- positive checks]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP -AT_CHECK([RUN_OVS_VSCTL([--force create b name=br0])], +AT_CHECK([RUN_OVS_VSCTL([create b name=br0])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) cp stdout out1 AT_CHECK([RUN_OVS_VSCTL([list b])], @@ -496,7 +496,7 @@ AT_CHECK([RUN_OVS_VSCTL([remove br br0 other_config 'datapath_id="0123456789ab"' AT_CHECK([RUN_OVS_VSCTL([clear br br0 external-ids -- get br br0 external_ids])], [0], [{} ], [], [OVS_VSCTL_CLEANUP]) -AT_CHECK([RUN_OVS_VSCTL([--force destroy b br0])], +AT_CHECK([RUN_OVS_VSCTL([destroy b br0])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([list b])], [0], [], [], [OVS_VSCTL_CLEANUP]) @@ -506,13 +506,13 @@ AT_CLEANUP AT_SETUP([database commands -- negative checks]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP -AT_CHECK([RUN_OVS_VSCTL([--force create b name=br0])], +AT_CHECK([RUN_OVS_VSCTL([create b name=br0])], [0], [ignore], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([add-br br1])], [0], [ignore], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([set-controller br1 tcp:127.0.0.1])], [0], [ignore], [], [OVS_VSCTL_CLEANUP]) -AT_CHECK([RUN_OVS_VSCTL([--force create n targets='"1.2.3.4:567"'])], +AT_CHECK([RUN_OVS_VSCTL([create n targets='"1.2.3.4:567"'])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) cp stdout netflow-uuid AT_CHECK([RUN_OVS_VSCTL([list n `cat netflow-uuid`])], @@ -577,13 +577,7 @@ AT_CHECK([RUN_OVS_VSCTL([remove n `cat netflow-uuid` targets '"1.2.3.4:567"'])], 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]) -AT_CHECK([RUN_OVS_VSCTL([create b name=br2])], - [1], [], [ovs-vsctl: "create" requires --force -], [OVS_VSCTL_CLEANUP]) -AT_CHECK([RUN_OVS_VSCTL([destroy b br0])], - [1], [], [ovs-vsctl: "destroy" requires --force -], [OVS_VSCTL_CLEANUP]) -AT_CHECK([RUN_OVS_VSCTL([--force destroy b br2])], +AT_CHECK([RUN_OVS_VSCTL([destroy b br2])], [1], [], [ovs-vsctl: no row "br2" in table Bridge ], [OVS_VSCTL_CLEANUP]) OVS_VSCTL_CLEANUP @@ -595,7 +589,7 @@ dnl The bug is documented in ovs-vsctl.8. AT_SETUP([created row UUID is wrong in same execution]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP -AT_CHECK([RUN_OVS_VSCTL([--force create Bridge name=br0 -- list b])], +AT_CHECK([RUN_OVS_VSCTL([create Bridge name=br0 -- list b])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [[<0> diff --git a/tests/ovsdb-execution.at b/tests/ovsdb-execution.at index bb9d6cf43..334e20848 100644 --- a/tests/ovsdb-execution.at +++ b/tests/ovsdb-execution.at @@ -11,6 +11,20 @@ m4_define([ORDINAL_SCHEMA], m4_define([CONSTRAINT_SCHEMA], [[{"name": "constraints", "tables": { + "a": { + "columns": { + "a": {"type": "integer"}, + "a2a": {"type": {"key": {"type": "uuid", "refTable": "a"}, + "min": 0, "max": "unlimited"}}, + "a2b": {"type": {"key": {"type": "uuid", "refTable": "b"}, + "min": 0, "max": "unlimited"}}}}, + "b": { + "columns": { + "b": {"type": "integer"}, + "b2a": {"type": {"key": {"type": "uuid", "refTable": "a"}, + "min": 0, "max": "unlimited"}}, + "b2b": {"type": {"key": {"type": "uuid", "refTable": "b"}, + "min": 0, "max": "unlimited"}}}}, "constrained": { "columns": { "positive": {"type": {"key": {"type": "integer", @@ -376,6 +390,104 @@ OVSDB_CHECK_EXECUTION([insert and update constraints], [[[{"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"}] +]]) + +OVSDB_CHECK_EXECUTION([referential integrity -- simple], + [CONSTRAINT_SCHEMA], + [[[[{"op": "insert", + "table": "b", + "row": {"b": 1}, + "uuid-name": "brow"}, + {"op": "insert", + "table": "a", + "row": {"a": 0, + "a2b": ["set", [["named-uuid", "brow"]]]}}, + {"op": "insert", + "table": "a", + "row": {"a": 1, + "a2b": ["set", [["named-uuid", "brow"]]]}}, + {"op": "insert", + "table": "a", + "row": {"a": 2, + "a2b": ["set", [["named-uuid", "brow"]]]}}]]], + [[[{"op": "delete", + "table": "b", + "where": []}]]], + [[[{"op": "delete", + "table": "a", + "where": [["a", "==", 0]]}]]], + [[[{"op": "delete", + "table": "b", + "where": []}]]], + [[[{"op": "delete", + "table": "a", + "where": [["a", "==", 1]]}]]], + [[[{"op": "delete", + "table": "b", + "where": []}]]], + [[[{"op": "delete", + "table": "a", + "where": [["a", "==", 2]]}]]], + [[[{"op": "delete", + "table": "b", + "where": []}]]]], + [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}] +[{"count":1},{"details":"cannot delete b row <0> because of 3 remaining reference(s)","error":"referential integrity violation"}] +[{"count":1}] +[{"count":1},{"details":"cannot delete b row <0> because of 2 remaining reference(s)","error":"referential integrity violation"}] +[{"count":1}] +[{"count":1},{"details":"cannot delete b row <0> because of 1 remaining reference(s)","error":"referential integrity violation"}] +[{"count":1}] +[{"count":1}] +]]) + +OVSDB_CHECK_EXECUTION([referential integrity -- mutual references], + [CONSTRAINT_SCHEMA], + [[[[{"op": "declare", + "uuid-name": "row1"}, + {"op": "declare", + "uuid-name": "row2"}, + {"op": "insert", + "table": "a", + "row": {"a": 0, + "a2b": ["set", [["named-uuid", "row2"]]], + "a2a": ["set", [["named-uuid", "row1"]]]}, + "uuid-name": "row1"}, + {"op": "insert", + "table": "b", + "row": {"b": 1, + "b2b": ["set", [["named-uuid", "row2"]]], + "b2a": ["set", [["named-uuid", "row1"]]]}, + "uuid-name": "row2"}]]], + [[[{"op": "insert", + "table": "a", + "row": {"a2b": ["set", [["uuid", "b516b960-5b19-4fc2-bb82-fe1cbd6d0241"]]]}}]]], + [[[{"op": "delete", + "table": "a", + "where": [["a", "==", 0]]}]]], + [[[{"op": "delete", + "table": "b", + "where": [["b", "==", 1]]}]]], + dnl Try the deletions again to make sure that the refcounts got rolled back. + [[[{"op": "delete", + "table": "a", + "where": [["a", "==", 0]]}]]], + [[[{"op": "delete", + "table": "b", + "where": [["b", "==", 1]]}]]], + [[[{"op": "delete", + "table": "a", + "where": [["a", "==", 0]]}, + {"op": "delete", + "table": "b", + "where": [["b", "==", 1]]}]]]], + [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}] +[{"uuid":["uuid","<2>"]},{"details":"reference to nonexistent row <3>","error":"referential integrity violation"}] +[{"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},{"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}] ]])]) EXECUTION_EXAMPLES diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at index 0be2a11e1..552f627f8 100644 --- a/tests/ovsdb-idl.at +++ b/tests/ovsdb-idl.at @@ -227,55 +227,58 @@ OVSDB_CHECK_IDL([self-linking idl, inconsistent ops], [['[{"op": "insert", "table": "link1", "row": {"i": 0, "k": ["uuid", "cf197cc5-c8c9-42f5-82d5-c71a9f2cb96b"]}}]' \ - '[{"op": "update", + '+[{"op": "insert", "table": "link1", - "where": [], - "row": {"k": ["uuid", "#0#"]}}]' \ + "uuid-name": "one", + "row": {"i": 1, "k": ["named-uuid", "one"]}}, + {"op": "insert", + "table": "link1", + "row": {"i": 2, "k": ["named-uuid", "one"]}}]' \ '[{"op": "update", "table": "link1", "where": [], "row": {"k": ["uuid", "c2fca39a-e69a-42a4-9c56-5eca85839ce9"]}}]' \ - '[{"op": "insert", + '+[{"op": "delete", "table": "link1", - "row": {"i": 1, "k": ["uuid", "52d752a3-b062-4668-9446-d2e0d4a14703"]}}]' \ - '[{"op": "update", + "where": [["_uuid", "==", ["uuid", "#1#"]]]}]' \ + '+[{"op": "delete", "table": "link1", - "where": [], - "row": {"k": ["uuid", "#1#"]}}]' \ + "where": [["_uuid", "==", ["uuid", "#2#"]]]}]' \ + '[{"op": "delete", + "table": "link1", + "where": []}]' \ ]], [[000: empty -001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} -002: i=0 k= ka=[] l2= uuid=<0> -003: {"error":null,"result":[{"count":1}]} -004: i=0 k=0 ka=[] l2= uuid=<0> -005: {"error":null,"result":[{"count":1}]} -006: i=0 k= ka=[] l2= uuid=<0> -007: {"error":null,"result":[{"uuid":["uuid","<1>"]}]} -008: i=0 k= ka=[] l2= uuid=<0> -008: i=1 k= ka=[] l2= uuid=<1> -009: {"error":null,"result":[{"count":2}]} -010: i=0 k=1 ka=[] l2= uuid=<0> -010: i=1 k=1 ka=[] l2= uuid=<1> -011: done +001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"details":"reference to nonexistent row <1>","error":"referential integrity violation"}]} +002: {"error":null,"result":[{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}]} +003: i=1 k=1 ka=[] l2= uuid=<2> +003: i=2 k=1 ka=[] l2= uuid=<3> +004: {"error":null,"result":[{"count":2},{"details":"reference to nonexistent row <4>","error":"referential integrity violation"}]} +005: {"error":null,"result":[{"count":1},{"details":"cannot delete link1 row <2> because of 1 remaining reference(s)","error":"referential integrity violation"}]} +006: {"error":null,"result":[{"count":1}]} +007: i=1 k=1 ka=[] l2= uuid=<2> +008: {"error":null,"result":[{"count":1}]} +009: empty +010: done ]]) OVSDB_CHECK_IDL([self-linking idl, sets], [], [['[{"op": "insert", "table": "link1", - "row": {"i": 0, "ka": ["set", [["named-uuid", "i0"]]]}, + "row": {"i": 0, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i0"]]]}, "uuid-name": "i0"}, {"op": "insert", "table": "link1", - "row": {"i": 1, "ka": ["set", [["named-uuid", "i1"]]]}, + "row": {"i": 1, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i1"]]]}, "uuid-name": "i1"}, {"op": "insert", "table": "link1", - "row": {"i": 2, "ka": ["set", [["named-uuid", "i2"]]]}, + "row": {"i": 2, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i2"]]]}, "uuid-name": "i2"}, {"op": "insert", "table": "link1", - "row": {"i": 3, "ka": ["set", [["named-uuid", "i3"]]]}, + "row": {"i": 3, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i3"]]]}, "uuid-name": "i3"}]' \ '[{"op": "update", "table": "link1", @@ -284,24 +287,25 @@ OVSDB_CHECK_IDL([self-linking idl, sets], '[{"op": "update", "table": "link1", "where": [], - "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "88702e78-845b-4a6e-ad08-cf68922ae84a"], ["uuid", "#2#"], ["uuid", "1ac2b12e-b767-4805-a55d-43976e40c465"]]]}}]']], + "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "88702e78-845b-4a6e-ad08-cf68922ae84a"], ["uuid", "#2#"], ["uuid", "1ac2b12e-b767-4805-a55d-43976e40c465"]]]}}]' \ + '+[{"op": "delete", + "table": "link1", + "where": []}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}]} -002: i=0 k= ka=[0] l2= uuid=<0> -002: i=1 k= ka=[1] l2= uuid=<1> -002: i=2 k= ka=[2] l2= uuid=<2> -002: i=3 k= ka=[3] l2= uuid=<3> +002: i=0 k=0 ka=[0] l2= uuid=<0> +002: i=1 k=0 ka=[1] l2= uuid=<1> +002: i=2 k=0 ka=[2] l2= uuid=<2> +002: i=3 k=0 ka=[3] l2= uuid=<3> 003: {"error":null,"result":[{"count":4}]} -004: i=0 k= ka=[0 1 2 3] l2= uuid=<0> -004: i=1 k= ka=[0 1 2 3] l2= uuid=<1> -004: i=2 k= ka=[0 1 2 3] l2= uuid=<2> -004: i=3 k= ka=[0 1 2 3] l2= uuid=<3> -005: {"error":null,"result":[{"count":4}]} -006: i=0 k= ka=[0 2] l2= uuid=<0> -006: i=1 k= ka=[0 2] l2= uuid=<1> -006: i=2 k= ka=[0 2] l2= uuid=<2> -006: i=3 k= ka=[0 2] l2= uuid=<3> -007: done +004: i=0 k=0 ka=[0 1 2 3] l2= uuid=<0> +004: i=1 k=0 ka=[0 1 2 3] l2= uuid=<1> +004: i=2 k=0 ka=[0 1 2 3] l2= uuid=<2> +004: i=3 k=0 ka=[0 1 2 3] l2= uuid=<3> +005: {"error":null,"result":[{"count":4},{"details":"reference to nonexistent row <4>","error":"referential integrity violation"}]} +006: {"error":null,"result":[{"count":4}]} +007: empty +008: done ]]) OVSDB_CHECK_IDL([external-linking idl, consistent ops], @@ -312,11 +316,11 @@ OVSDB_CHECK_IDL([external-linking idl, consistent ops], "uuid-name": "row0"}, {"op": "insert", "table": "link1", - "row": {"i": 1, "l2": ["set", [["named-uuid", "row0"]]]}, + "row": {"i": 1, "k": ["named-uuid", "row1"], "l2": ["set", [["named-uuid", "row0"]]]}, "uuid-name": "row1"}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: i=0 l1= uuid=<0> -002: i=1 k= ka=[] l2=0 uuid=<1> +002: i=1 k=1 ka=[] l2=0 uuid=<1> 003: done ]]) diff --git a/tests/ovsdb-schema.at b/tests/ovsdb-schema.at new file mode 100644 index 000000000..6cd2fa20f --- /dev/null +++ b/tests/ovsdb-schema.at @@ -0,0 +1,47 @@ +AT_BANNER([OVSDB -- schemas]) + +OVSDB_CHECK_POSITIVE([schema with valid refTables], + [[parse-schema \ + '{"name": "mydb", + "tables": { + "a": { + "columns": { + "map": { + "type": { + "key": { + "type": "uuid", + "refTable": "b"}, + "value": { + "type": "uuid", + "refTable": "a"}}}}}, + "b": { + "columns": { + "aRef": { + "type": { + "key": { + "type": "uuid", + "refTable": "a"}}}}}}}']], + [[{"name":"mydb","tables":{"a":{"columns":{"map":{"type":{"key":{"refTable":"b","type":"uuid"},"value":{"refTable":"a","type":"uuid"}}}}},"b":{"columns":{"aRef":{"type":{"key":{"refTable":"a","type":"uuid"}}}}}}}]]) + +OVSDB_CHECK_NEGATIVE([schema with invalid refTables], + [[parse-schema \ + '{"name": "mydb", + "tables": { + "a": { + "columns": { + "map": { + "type": { + "key": { + "type": "uuid", + "refTable": "c"}, + "value": { + "type": "uuid", + "refTable": "a"}}}}}, + "b": { + "columns": { + "aRef": { + "type": { + "key": { + "type": "uuid", + "refTable": "a"}}}}}}}']], + [[test-ovsdb: syntax error: column map key refers to undefined table c]]) diff --git a/tests/ovsdb-types.at b/tests/ovsdb-types.at index b7fddc7e2..4647e69de 100644 --- a/tests/ovsdb-types.at +++ b/tests/ovsdb-types.at @@ -70,6 +70,13 @@ OVSDB_CHECK_NEGATIVE([maxLength must not be negative], [[parse-base-type '{"type": "string", "maxLength": -1}']], [maxLength out of valid range 0 to 4294967295]) +OVSDB_CHECK_POSITIVE([uuid refTable], + [[parse-base-type '{"type": "uuid", "refTable": "myTable"}' ]], + [{"refTable":"myTable","type":"uuid"}]) +OVSDB_CHECK_NEGATIVE([uuid refTable must be valid id], + [[parse-base-type '{"type": "uuid", "refTable": "a-b-c"}' ]], + [Type mismatch for member 'refTable']) + 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], diff --git a/tests/ovsdb.at b/tests/ovsdb.at index d10bedd3d..275c90d6d 100644 --- a/tests/ovsdb.at +++ b/tests/ovsdb.at @@ -41,6 +41,7 @@ m4_include([tests/ovsdb-data.at]) m4_include([tests/ovsdb-column.at]) m4_include([tests/ovsdb-table.at]) m4_include([tests/ovsdb-row.at]) +m4_include([tests/ovsdb-schema.at]) m4_include([tests/ovsdb-condition.at]) m4_include([tests/ovsdb-mutation.at]) m4_include([tests/ovsdb-query.at]) diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c index 4b38ecb5a..3025ce390 100644 --- a/tests/test-ovsdb.c +++ b/tests/test-ovsdb.c @@ -159,6 +159,8 @@ usage(void) " query-distinct TABLE [ROW,...] [CONDITION,...] COLUMNS\n" " add each ROW to TABLE, then query and print the rows that\n" " satisfy each CONDITION and have distinct COLUMNS.\n" + " parse-schema JSON\n" + " parse JSON as an OVSDB schema, and re-serialize\n" " transact COMMAND\n" " execute each specified transactional COMMAND:\n" " commit\n" @@ -1138,6 +1140,19 @@ do_query_distinct(int argc UNUSED, char *argv[]) exit(exit_code); } +static void +do_parse_schema(int argc UNUSED, char *argv[]) +{ + struct ovsdb_schema *schema; + struct json *json; + + json = parse_json(argv[1]); + check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); + json_destroy(json); + print_and_free_json(ovsdb_schema_to_json(schema)); + ovsdb_schema_destroy(schema); +} + static void do_execute(int argc UNUSED, char *argv[]) { @@ -1773,20 +1788,28 @@ do_idl(int argc, char *argv[]) rpc = NULL; } + setvbuf(stdout, NULL, _IOLBF, 0); + symtab = ovsdb_symbol_table_create(); for (i = 2; i < argc; i++) { + char *arg = argv[i]; struct jsonrpc_msg *request, *reply; int error; - seqno = print_updated_idl(idl, rpc, step++, seqno); + if (*arg == '+') { + /* The previous transaction didn't change anything. */ + arg++; + } else { + seqno = print_updated_idl(idl, rpc, step++, seqno); + } - if (!strcmp(argv[i], "reconnect")) { + if (!strcmp(arg, "reconnect")) { printf("%03d: reconnect\n", step++); ovsdb_idl_force_reconnect(idl); - } else if (argv[i][0] != '[') { - idl_set(idl, argv[i], step++); + } else if (arg[0] != '[') { + idl_set(idl, arg, step++); } else { - struct json *json = parse_json(argv[i]); + struct json *json = parse_json(arg); substitute_uuids(json, symtab); request = jsonrpc_create_request("transact", json, NULL); error = jsonrpc_transact_block(rpc, request, &reply); @@ -1833,6 +1856,7 @@ static struct command all_commands[] = { { "query", 3, 3, do_query }, { "query-distinct", 4, 4, do_query_distinct }, { "transact", 1, INT_MAX, do_transact }, + { "parse-schema", 1, 1, do_parse_schema }, { "execute", 2, INT_MAX, do_execute }, { "trigger", 2, INT_MAX, do_trigger }, { "idl", 1, INT_MAX, do_idl }, diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in index 13985a649..81daf719d 100644 --- a/utilities/ovs-vsctl.8.in +++ b/utilities/ovs-vsctl.8.in @@ -444,13 +444,6 @@ as \fB{}\fR, and curly braces may be optionally enclose non-empty maps as well. . .ST "Database Command Syntax" -.PP -By default, database commands refuse to make some kinds of -modifications that could violate database structuring constraints. If -you are sure that you know what you are doing, use \fB\-\-force\fR to -override this safety measure. Constraints that are enforced by the -database server itself, instead of by \fBovs\-vsctl\fR, cannot be -overridden this way. . .IP "\fBlist \fItable \fR[\fIrecord\fR]..." List the values of all columns of each specified \fIrecord\fR. If no @@ -503,18 +496,14 @@ 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. . -.IP "\fB\-\-force create \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..." +.IP "create \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..." Creates a new record in \fItable\fR and sets the initial values of each \fIcolumn\fR. Columns not explicitly set will receive their default values. Outputs the UUID of the new row. -.IP -This command requires the \fB\-\-force\fR option. . -.IP "\fB\-\-force \fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..." +.IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..." Deletes each specified \fIrecord\fR from \fItable\fR. Unless \fB\-\-if\-exists\fR is specified, each \fIrecord\fRs must exist. -.IP -This command requires the \fB\-\-force\fR option. .SH "EXAMPLES" Create a new bridge named br0 and add port eth0 to it: .IP diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c index 1eb6e0fe3..e2e577be8 100644 --- a/utilities/ovs-vsctl.c +++ b/utilities/ovs-vsctl.c @@ -2113,16 +2113,11 @@ cmd_clear(struct vsctl_context *ctx) static void cmd_create(struct vsctl_context *ctx) { - bool force = shash_find(&ctx->options, "--force"); const char *table_name = ctx->argv[1]; const struct vsctl_table_class *table; const struct ovsdb_idl_row *row; int i; - if (!force) { - vsctl_fatal("\"create\" requires --force"); - } - table = get_table(table_name); row = ovsdb_idl_txn_insert(ctx->txn, table->class); for (i = 2; i < ctx->argc; i++) { @@ -2158,16 +2153,11 @@ post_create(struct vsctl_context *ctx) static void cmd_destroy(struct vsctl_context *ctx) { - bool force = shash_find(&ctx->options, "--force"); bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *table_name = ctx->argv[1]; const struct vsctl_table_class *table; int i; - if (!force) { - vsctl_fatal("\"destroy\" requires --force"); - } - table = get_table(table_name); for (i = 2; i < ctx->argc; i++) { const struct ovsdb_idl_row *row; @@ -2397,12 +2387,12 @@ static const struct vsctl_command_syntax all_commands[] = { /* Parameter commands. */ {"get", 3, INT_MAX, cmd_get, NULL, "--if-exists"}, {"list", 1, INT_MAX, cmd_list, NULL, ""}, - {"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, ""}, + {"create", 2, INT_MAX, cmd_create, post_create, ""}, + {"destroy", 1, INT_MAX, cmd_destroy, NULL, "--if-exists"}, {NULL, 0, 0, NULL, NULL, NULL}, }; diff --git a/vswitchd/vswitch-idl.ann b/vswitchd/vswitch-idl.ann index b8e457d5b..7a5cc3147 100644 --- a/vswitchd/vswitch-idl.ann +++ b/vswitchd/vswitch-idl.ann @@ -7,15 +7,3 @@ s["idlPrefix"] = "ovsrec_" s["idlHeader"] = "\"vswitchd/vswitch-idl.h\"" -s["tables"]["Open_vSwitch"]["columns"]["bridges"]["type"]["keyRefTable"] = "Bridge" -s["tables"]["Open_vSwitch"]["columns"]["controller"]["type"]["keyRefTable"] = "Controller" -s["tables"]["Open_vSwitch"]["columns"]["ssl"]["type"]["keyRefTable"] = "SSL" -s["tables"]["Bridge"]["columns"]["ports"]["type"]["keyRefTable"] = "Port" -s["tables"]["Bridge"]["columns"]["mirrors"]["type"]["keyRefTable"] = "Mirror" -s["tables"]["Bridge"]["columns"]["netflow"]["type"]["keyRefTable"] = "NetFlow" -s["tables"]["Bridge"]["columns"]["sflow"]["type"]["keyRefTable"] = "sFlow" -s["tables"]["Bridge"]["columns"]["controller"]["type"]["keyRefTable"] = "Controller" -s["tables"]["Port"]["columns"]["interfaces"]["type"]["keyRefTable"] = "Interface" -s["tables"]["Mirror"]["columns"]["select_src_port"]["type"]["keyRefTable"] = "Port" -s["tables"]["Mirror"]["columns"]["select_dst_port"]["type"]["keyRefTable"] = "Port" -s["tables"]["Mirror"]["columns"]["output_port"]["type"]["keyRefTable"] = "Port" diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index ea6734278..c1fd98e72 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -6,16 +6,22 @@ "columns": { "bridges": { "comment": "Set of bridges managed by the daemon.", - "type": {"key": "uuid", "min": 0, "max": "unlimited"}}, + "type": {"key": {"type": "uuid", + "refTable": "Bridge"}, + "min": 0, "max": "unlimited"}}, "controller": { "comment": "Default Controller used by bridges.", - "type": {"key": "uuid", "min": 0, "max": 1}}, + "type": {"key": {"type": "uuid", + "refTable": "Controller"}, + "min": 0, "max": 1}}, "managers": { "comment": "Remote database clients to which the Open vSwitch's database server should connect or to which it should listen.", "type": {"key": "string", "min": 0, "max": "unlimited"}}, "ssl": { "comment": "SSL used globally by the daemon.", - "type": {"key": "uuid", "min": 0, "max": 1}}, + "type": {"key": {"type": "uuid", + "refTable": "SSL"}, + "min": 0, "max": 1}}, "next_cfg": { "comment": "Sequence number for client to increment when it modifies the configuration and wishes to wait for Open vSwitch to finish applying the changes.", "type": "integer"}, @@ -37,19 +43,29 @@ "ephemeral": true}, "ports": { "comment": "Ports included in the bridge.", - "type": {"key": "uuid", "min": 0, "max": "unlimited"}}, + "type": {"key": {"type": "uuid", + "refTable": "Port"}, + "min": 0, "max": "unlimited"}}, "mirrors": { "comment": "Port mirroring configuration.", - "type": {"key": "uuid", "min": 0, "max": "unlimited"}}, + "type": {"key": {"type": "uuid", + "refTable": "Mirror"}, + "min": 0, "max": "unlimited"}}, "netflow": { "comment": "NetFlow configuration.", - "type": {"key": "uuid", "min": 0, "max": 1}}, + "type": {"key": {"type": "uuid", + "refTable": "NetFlow"}, + "min": 0, "max": 1}}, "sflow": { "comment": "sFlow configuration.", - "type": {"key": "uuid", "min": 0, "max": 1}}, + "type": {"key": {"type": "uuid", + "refTable": "sFlow"}, + "min": 0, "max": 1}}, "controller": { "comment": "OpenFlow controller. If unset, defaults to that specified by the parent Open_vSwitch.", - "type": {"key": "uuid", "min": 0, "max": 1}}, + "type": {"key": {"type": "uuid", + "refTable": "Controller"}, + "min": 0, "max": 1}}, "other_config": { "comment": "Key-value pairs for configuring rarely used bridge features. The currently defined key-value pairs are: \"datapath-id\", exactly 12 hex digits to set the OpenFlow datapath ID to a specific value; \"hwaddr\", exactly 12 hex digits in the form \"XX:XX:XX:XX:XX:XX\" to set the hardware address of the local port and influence the datapath ID.", "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, @@ -70,7 +86,9 @@ "type": "string"}, "interfaces": { "comment": "The Port's Interfaces. If there is more than one, this is a bonded Port.", - "type": {"key": "uuid", "min": 1, "max": "unlimited"}}, + "type": {"key": {"type": "uuid", + "refTable": "Interface"}, + "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": {"type": "integer", @@ -148,10 +166,13 @@ "type": "string"}, "select_src_port": { "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"}}, + "type": {"key": {"type": "uuid", + "refTable": "Port"}, + "min": 0, "max": "unlimited"}}, "select_dst_port": { "comment": "Ports on which departing packets are selected for mirroring.", - "type": {"key": "uuid", "min": 0, "max": "unlimited"}}, + "type": {"key": {"type": "uuid", + "refTable": "Port"}, "min": 0, "max": "unlimited"}}, "select_vlan": { "comment": "VLANs on which packets are selected for mirroring. An empty set selects packets on all VLANs.", "type": {"key": {"type": "integer", @@ -160,7 +181,8 @@ "min": 0, "max": 4096}}, "output_port": { "comment": "Output port for selected packets. Mutually exclusive with output_vlan.", - "type": {"key": "uuid", "min": 0, "max": 1}}, + "type": {"key": {"type": "uuid", + "refTable": "Port"}, "min": 0, "max": 1}}, "output_vlan": { "comment": "Output VLAN for selected packets. Mutually exclusive with output_port.", "type": {"key": {"type": "integer", -- 2.20.1