From c5f341ab193b9126dffef8c77bf8ed35e91290fd Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 10 Mar 2011 11:15:01 -0800 Subject: [PATCH] ovsdb: Implement garbage collection. --- debian/openvswitch-switch.init | 12 ++ lib/ovsdb-data.c | 60 ++++++++-- lib/ovsdb-data.h | 2 + lib/ovsdb-idl-provider.h | 3 +- ovsdb/SPECS | 19 +++- ovsdb/dot2pic | 11 +- ovsdb/ovsdb-doc.in | 6 +- ovsdb/ovsdb-dot.in | 16 ++- ovsdb/ovsdb-idlc.in | 6 +- ovsdb/ovsdb.c | 36 +++++- ovsdb/table.c | 30 ++++- ovsdb/table.h | 11 +- ovsdb/transaction.c | 141 +++++++++++++++++++++--- python/ovs/db/schema.py | 50 ++++++++- tests/ovs-vsctl.at | 54 +++++++-- tests/ovsdb-execution.at | 182 +++++++++++++++++++++++++++++++ tests/ovsdb-table.at | 18 ++- tests/test-ovsdb.c | 9 +- tests/test-ovsdb.py | 11 +- utilities/ovs-vsctl.8.in | 47 ++++++-- utilities/ovs-vsctl.c | 175 +++++++++++------------------ vswitchd/ovs-brcompatd.c | 30 ----- vswitchd/vswitch.gv | 92 +++------------- vswitchd/vswitch.ovsschema | 11 +- vswitchd/vswitch.pic | 49 ++++++++- vswitchd/vswitch.xml | 19 ++-- xenserver/etc_init.d_openvswitch | 13 ++- 27 files changed, 800 insertions(+), 313 deletions(-) diff --git a/debian/openvswitch-switch.init b/debian/openvswitch-switch.init index 92ab77568..8ea586606 100755 --- a/debian/openvswitch-switch.init +++ b/debian/openvswitch-switch.init @@ -232,6 +232,18 @@ case "$1" in cksum=`ovsdb-tool db-cksum "$conf_file" | awk '{print $1}'` cp "$conf_file" "$conf_file.backup$version-$cksum" + # Compact database. This is important if the old schema did not + # enable garbage collection (i.e. if it did not have any tables + # with "isRoot": true) but the new schema does. In that situation + # the old database may contain a transaction that creates a record + # followed by a transaction that creates the first use of the + # record. Replaying that series of transactions against the new + # database schema (as "convert" does) would cause the record to be + # dropped by the first transaction, then the second transaction + # would cause a referential integrity failure (for a strong + # reference). + ovsdb-tool -vANY:console:emer compact $conf_file + # Upgrade or downgrade schema and compact database. ovsdb-tool -vANY:console:emer convert $conf_file $schema_file fi diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c index ac9486413..150ae618e 100644 --- a/lib/ovsdb-data.c +++ b/lib/ovsdb-data.c @@ -285,9 +285,28 @@ parse_json_pair(const struct json *json, return NULL; } +static void +ovsdb_symbol_referenced(struct ovsdb_symbol *symbol, + const struct ovsdb_base_type *base) +{ + assert(base->type == OVSDB_TYPE_UUID); + + if (base->u.uuid.refTableName) { + switch (base->u.uuid.refType) { + case OVSDB_REF_STRONG: + symbol->strong_ref = true; + break; + case OVSDB_REF_WEAK: + symbol->weak_ref = true; + break; + } + } +} + static struct ovsdb_error * WARN_UNUSED_RESULT ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json, - struct ovsdb_symbol_table *symtab) + struct ovsdb_symbol_table *symtab, + const struct ovsdb_base_type *base) { struct ovsdb_error *error0; const struct json *value; @@ -304,14 +323,17 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json, error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value); if (!error1) { - const char *name = json_string(value); + struct ovsdb_symbol *symbol; ovsdb_error_destroy(error0); - *uuid = ovsdb_symbol_table_insert(symtab, name)->uuid; if (!ovsdb_parser_is_id(json_string(value))) { return ovsdb_syntax_error(json, NULL, "named-uuid string is " "not a valid "); } + + symbol = ovsdb_symbol_table_insert(symtab, json_string(value)); + *uuid = symbol->uuid; + ovsdb_symbol_referenced(symbol, base); return NULL; } ovsdb_error_destroy(error1); @@ -321,10 +343,13 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json, } static struct ovsdb_error * WARN_UNUSED_RESULT -ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, +ovsdb_atom_from_json__(union ovsdb_atom *atom, + const struct ovsdb_base_type *base, const struct json *json, struct ovsdb_symbol_table *symtab) { + enum ovsdb_atomic_type type = base->type; + switch (type) { case OVSDB_TYPE_VOID: NOT_REACHED(); @@ -364,7 +389,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, break; case OVSDB_TYPE_UUID: - return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab); + return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab, base); case OVSDB_N_TYPES: default: @@ -384,7 +409,9 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, * * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to * ovsdb/SPECS for information about this, and for the syntax that this - * function accepts. */ + * function accepts. If 'base' is a reference and a symbol is parsed, then the + * symbol's 'strong_ref' or 'weak_ref' member is set to true, as + * appropriate. */ struct ovsdb_error * ovsdb_atom_from_json(union ovsdb_atom *atom, const struct ovsdb_base_type *base, @@ -393,7 +420,7 @@ ovsdb_atom_from_json(union ovsdb_atom *atom, { struct ovsdb_error *error; - error = ovsdb_atom_from_json__(atom, base->type, json, symtab); + error = ovsdb_atom_from_json__(atom, base, json, symtab); if (error) { return error; } @@ -440,9 +467,12 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) } static char * -ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, - const char *s, struct ovsdb_symbol_table *symtab) +ovsdb_atom_from_string__(union ovsdb_atom *atom, + const struct ovsdb_base_type *base, const char *s, + struct ovsdb_symbol_table *symtab) { + enum ovsdb_atomic_type type = base->type; + switch (type) { case OVSDB_TYPE_VOID: NOT_REACHED(); @@ -503,7 +533,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, case OVSDB_TYPE_UUID: if (*s == '@') { - atom->uuid = ovsdb_symbol_table_insert(symtab, s)->uuid; + struct ovsdb_symbol *symbol = ovsdb_symbol_table_insert(symtab, s); + atom->uuid = symbol->uuid; + ovsdb_symbol_referenced(symbol, base); } else if (!uuid_from_string(&atom->uuid, s)) { return xasprintf("\"%s\" is not a valid UUID", s); } @@ -535,7 +567,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, * then an identifier beginning with '@' is also acceptable. If the * named identifier is already in 'symtab', then the associated UUID is * used; otherwise, a new, random UUID is used and added to the symbol - * table. + * table. If 'base' is a reference and a symbol is parsed, then the + * symbol's 'strong_ref' or 'weak_ref' member is set to true, as + * appropriate. * * Returns a null pointer if successful, otherwise an error message describing * the problem. On failure, the contents of 'atom' are indeterminate. The @@ -549,7 +583,7 @@ ovsdb_atom_from_string(union ovsdb_atom *atom, struct ovsdb_error *error; char *msg; - msg = ovsdb_atom_from_string__(atom, base->type, s, symtab); + msg = ovsdb_atom_from_string__(atom, base, s, symtab); if (msg) { return msg; } @@ -1829,6 +1863,8 @@ ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name, symbol = xmalloc(sizeof *symbol); symbol->uuid = *uuid; symbol->created = created; + symbol->strong_ref = false; + symbol->weak_ref = false; shash_add(&symtab->sh, name, symbol); return symbol; } diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h index fe71f9084..181df3b12 100644 --- a/lib/ovsdb-data.h +++ b/lib/ovsdb-data.h @@ -236,6 +236,8 @@ struct ovsdb_symbol_table { struct ovsdb_symbol { struct uuid uuid; /* The UUID that the symbol represents. */ bool created; /* Already used to create row? */ + bool strong_ref; /* Parsed a strong reference to this row? */ + bool weak_ref; /* Parsed a weak reference to this row? */ }; struct ovsdb_symbol_table *ovsdb_symbol_table_create(void); diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h index 87f62d64c..ef37d9218 100644 --- a/lib/ovsdb-idl-provider.h +++ b/lib/ovsdb-idl-provider.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010 Nicira Networks. +/* Copyright (c) 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ struct ovsdb_idl_column { struct ovsdb_idl_table_class { char *name; + bool is_root; const struct ovsdb_idl_column *columns; size_t n_columns; size_t allocation_size; diff --git a/ovsdb/SPECS b/ovsdb/SPECS index d64812ae6..3a9af9f39 100644 --- a/ovsdb/SPECS +++ b/ovsdb/SPECS @@ -131,6 +131,7 @@ is represented by , as described below. "columns": {: , ...} required "maxRows": optional + "isRoot": optional The value of "columns" is a JSON object whose names are column names and whose values are s. @@ -152,12 +153,28 @@ is represented by , as described below. the database process is stopped and then started again, each "_version" also changes to a new random value. + If "isRoot" is omitted or specified as false, then any given row + in the table may exist only when there is at least one reference + to it, with refType "strong", from a different row (in the same + table or a different table). This is a "deferred" action: + unreferenced rows in the table are deleted just before transaction + commit. If "isRoot" is specified as true, then rows in the table + exist independent of any references (they can be thought of as + part of the "root set" in a garbage collector). + + For compatibility with schemas created before "isRoot" was + introduced, if "isRoot" is omitted or false in every + in a given , then every table is + part of the root set. + If "maxRows" is specified, as a positive integer, it limits the maximum number of rows that may be present in the table. This is a "deferred" constraint, enforced only at transaction commit time (see the "transact" request below). If "maxRows" is not specified, the size of the table is limited only by the resources - available to the database server. + available to the database server. "maxRows" constraints are + enforced after unreferenced rows are deleted from tables with a + false "isRoot". diff --git a/ovsdb/dot2pic b/ovsdb/dot2pic index caca9f8d0..52de5e821 100755 --- a/ovsdb/dot2pic +++ b/ovsdb/dot2pic @@ -1,6 +1,6 @@ #! /usr/bin/perl -# Copyright (c) 2009, 2010 Nicira Networks +# Copyright (c) 2009, 2010, 2011 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,7 +29,14 @@ while (<>) { $y *= $scale; $width *= $scale; $height *= $scale; + print "linethick = ", ($style eq 'bold' ? 0.5 : 1.0), ";\n"; print "box at $x,$y wid $width height $height \"$name\"\n"; + if ($style eq 'bold') { + my $inset = 2.0 / 72.0; + $width -= $inset * 2; + $height -= $inset * 2; + print "box at $x,$y wid $width height $height\n"; + } } elsif (/edge/) { my (undef, $tail, $head, $n, $rest) = split(' ', $_, 5); my @xy; @@ -51,6 +58,8 @@ while (<>) { } my ($style, $color) = split(' ', $rest); + print "linethick = ", ($style eq 'dotted' ? 0.5 : 1), ";\n"; + print "spline -> from $xy[0][0],$xy[0][1]"; for (my ($i) = 0; $i <= $#xy; $i++) { print " to $xy[$i][0],$xy[$i][1]"; diff --git a/ovsdb/ovsdb-doc.in b/ovsdb/ovsdb-doc.in index 5f3033480..5ba4e7168 100755 --- a/ovsdb/ovsdb-doc.in +++ b/ovsdb/ovsdb-doc.in @@ -290,9 +290,11 @@ Table Purpose .SH "TABLE RELATIONSHIPS" .PP The following diagram shows the relationship among tables in the -database. Each node represents a table. Each edge leads from the +database. Each node represents a table. Tables that are part of the +``root set'' are shown with double borders. Each edge leads from the table that contains it and points to the table that its value -represents. Edges are labeled with their column names. +represents. Edges are labeled with their column names. Thick lines +represent strong references; thin lines represent weak references. .RS -1in """ erStream = open(erFile, "r") diff --git a/ovsdb/ovsdb-dot.in b/ovsdb/ovsdb-dot.in index 571ac8f0c..d41728627 100755 --- a/ovsdb/ovsdb-dot.in +++ b/ovsdb/ovsdb-dot.in @@ -16,6 +16,7 @@ def printEdge(tableName, baseType, label): options['label'] = '"%s"' % label if baseType.ref_type == 'weak': options['constraint'] = 'false' + options['style'] = 'dotted' print "\t%s -> %s [%s];" % ( tableName, baseType.ref_table, @@ -25,12 +26,17 @@ def schemaToDot(schemaFile): schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile)) print "digraph %s {" % schema.name + print '\tsize="6.5,4";' + print '\tmargin="0";' + print "\tnode [shape=box];" + print "\tedge [dir=none, arrowhead=none, arrowtail=none];" for tableName, table in schema.tables.iteritems(): - print '\tsize="6.5,4";' - print '\tmargin="0";' - print "\tnode [shape=box];" - print "\tedge [dir=none, arrowhead=none, arrowtail=none];" - print "\t%s;" % tableName + options = {} + if table.is_root: + options['style'] = 'bold' + print "\t%s [%s];" % ( + tableName, + ', '.join(['%s=%s' % (k,v) for k,v in options.items()])) for columnName, column in table.columns.iteritems(): if column.type.value: printEdge(tableName, column.type.key, "%s key" % columnName) diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in index 418308995..2a4c67ca3 100755 --- a/ovsdb/ovsdb-idlc.in +++ b/ovsdb/ovsdb-idlc.in @@ -487,7 +487,11 @@ static void\n%s_columns_init(void) print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper()) for tableName, table in sorted(schema.tables.iteritems()): structName = "%s%s" % (prefix, tableName.lower()) - print " {\"%s\"," % tableName + if table.is_root: + is_root = "true" + else: + is_root = "false" + print " {\"%s\", %s," % (tableName, is_root) print " %s_columns, ARRAY_SIZE(%s_columns)," % ( structName, structName) print " sizeof(struct %s)}," % structName diff --git a/ovsdb/ovsdb.c b/ovsdb/ovsdb.c index e76544e36..aad84152b 100644 --- a/ovsdb/ovsdb.c +++ b/ovsdb/ovsdb.c @@ -127,6 +127,21 @@ is_valid_version(const char *s) return n != -1 && s[n] == '\0'; } +/* Returns the number of tables in 'schema''s root set. */ +static size_t +root_set_size(const struct ovsdb_schema *schema) +{ + struct shash_node *node; + size_t n_root; + + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + + n_root += table->is_root; + } + return n_root; +} + struct ovsdb_error * ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) { @@ -205,6 +220,18 @@ ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) } } + /* "isRoot" was not part of the original schema definition. Before it was + * added, there was no support for garbage collection. So, for backward + * compatibility, if the root set is empty then assume that every table is + * in the root set. */ + if (root_set_size(schema) == 0) { + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + + table->is_root = true; + } + } + *schemap = schema; return 0; } @@ -214,6 +241,7 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema) { struct json *json, *tables; struct shash_node *node; + bool default_is_root; json = json_object_create(); json_object_put_string(json, "name", schema->name); @@ -224,12 +252,18 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema) json_object_put_string(json, "cksum", schema->cksum); } + /* "isRoot" was not part of the original schema definition. Before it was + * added, there was no support for garbage collection. So, for backward + * compatibility, if every table is in the root set then do not output + * "isRoot" in table schemas. */ + default_is_root = root_set_size(schema) == shash_count(&schema->tables); + tables = json_object_create(); SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *table = node->data; json_object_put(tables, table->name, - ovsdb_table_schema_to_json(table)); + ovsdb_table_schema_to_json(table, default_is_root)); } json_object_put(json, "tables", tables); diff --git a/ovsdb/table.c b/ovsdb/table.c index 5e83683b7..2f693501b 100644 --- a/ovsdb/table.c +++ b/ovsdb/table.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010 Nicira Networks +/* Copyright (c) 2009, 2010, 2011 Nicira Networks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column) struct ovsdb_table_schema * ovsdb_table_schema_create(const char *name, bool mutable, - unsigned int max_rows) + unsigned int max_rows, bool is_root) { struct ovsdb_column *uuid, *version; struct ovsdb_table_schema *ts; @@ -47,6 +47,7 @@ ovsdb_table_schema_create(const char *name, bool mutable, ts->mutable = mutable; shash_init(&ts->columns); ts->max_rows = max_rows; + ts->is_root = is_root; uuid = ovsdb_column_create("_uuid", false, true, &ovsdb_type_uuid); add_column(ts, uuid); @@ -65,7 +66,8 @@ ovsdb_table_schema_clone(const struct ovsdb_table_schema *old) struct ovsdb_table_schema *new; struct shash_node *node; - new = ovsdb_table_schema_create(old->name, old->mutable, old->max_rows); + new = ovsdb_table_schema_create(old->name, old->mutable, + old->max_rows, old->is_root); SHASH_FOR_EACH (node, &old->columns) { const struct ovsdb_column *column = node->data; @@ -97,7 +99,7 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name, struct ovsdb_table_schema **tsp) { struct ovsdb_table_schema *ts; - const struct json *columns, *mutable, *max_rows; + const struct json *columns, *mutable, *max_rows, *is_root; struct shash_node *node; struct ovsdb_parser parser; struct ovsdb_error *error; @@ -111,6 +113,7 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name, OP_TRUE | OP_FALSE | OP_OPTIONAL); max_rows = ovsdb_parser_member(&parser, "maxRows", OP_INTEGER | OP_OPTIONAL); + is_root = ovsdb_parser_member(&parser, "isRoot", OP_BOOLEAN | OP_OPTIONAL); error = ovsdb_parser_finish(&parser); if (error) { return error; @@ -133,7 +136,8 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name, ts = ovsdb_table_schema_create(name, mutable ? json_boolean(mutable) : true, - MIN(n_max_rows, UINT_MAX)); + MIN(n_max_rows, UINT_MAX), + is_root ? json_boolean(is_root) : false); SHASH_FOR_EACH (node, json_object(columns)) { struct ovsdb_column *column; @@ -156,8 +160,19 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name, return 0; } +/* Returns table schema 'ts' serialized into JSON. + * + * The "isRoot" member is included in the JSON only if its value would differ + * from 'default_is_root'. Ordinarily 'default_is_root' should be false, + * because ordinarily a table would be not be part of the root set if its + * "isRoot" member is omitted. However, garbage collection was not orginally + * included in OVSDB, so in older schemas that do not include any "isRoot" + * members, every table is implicitly part of the root set. To serialize such + * a schema in a way that can be read by older OVSDB tools, specify + * 'default_is_root' as true. */ struct json * -ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts) +ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts, + bool default_is_root) { struct json *json, *columns; struct shash_node *node; @@ -166,6 +181,9 @@ ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts) if (!ts->mutable) { json_object_put(json, "mutable", json_boolean_create(false)); } + if (default_is_root != ts->is_root) { + json_object_put(json, "isRoot", json_boolean_create(ts->is_root)); + } columns = json_object_create(); diff --git a/ovsdb/table.h b/ovsdb/table.h index 4d3b9ee72..95da74035 100644 --- a/ovsdb/table.h +++ b/ovsdb/table.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010 Nicira Networks +/* Copyright (c) 2009, 2010, 2011 Nicira Networks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,11 +30,11 @@ struct ovsdb_table_schema { bool mutable; struct shash columns; /* Contains "struct ovsdb_column *"s. */ unsigned int max_rows; /* Maximum number of rows. */ + bool is_root; /* Part of garbage collection root set? */ }; -struct ovsdb_table_schema *ovsdb_table_schema_create(const char *name, - bool mutable, - unsigned int max_rows); +struct ovsdb_table_schema *ovsdb_table_schema_create( + const char *name, bool mutable, unsigned int max_rows, bool is_root); struct ovsdb_table_schema *ovsdb_table_schema_clone( const struct ovsdb_table_schema *); void ovsdb_table_schema_destroy(struct ovsdb_table_schema *); @@ -43,7 +43,8 @@ struct ovsdb_error *ovsdb_table_schema_from_json(const struct json *, const char *name, struct ovsdb_table_schema **) WARN_UNUSED_RESULT; -struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *); +struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *, + bool default_is_root); const struct ovsdb_column *ovsdb_table_schema_get_column( const struct ovsdb_table_schema *, const char *name); diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c index 615c164b2..c07541ed3 100644 --- a/ovsdb/transaction.c +++ b/ovsdb/transaction.c @@ -81,6 +81,8 @@ struct ovsdb_txn_row { unsigned long changed[]; /* Bits set to 1 for columns that changed. */ }; +static struct ovsdb_error * WARN_UNUSED_RESULT +delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *r); static void ovsdb_txn_row_prefree(struct ovsdb_txn_row *); static struct ovsdb_error * WARN_UNUSED_RESULT for_each_txn_row(struct ovsdb_txn *txn, @@ -158,6 +160,20 @@ find_txn_row(const struct ovsdb_table *table, const struct uuid *uuid) return NULL; } +static struct ovsdb_txn_row * +find_or_make_txn_row(struct ovsdb_txn *txn, const struct ovsdb_table *table, + const struct uuid *uuid) +{ + struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid); + if (!txn_row) { + const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid); + if (row) { + txn_row = ovsdb_txn_row_modify(txn, row)->txn_row; + } + } + return txn_row; +} + static struct ovsdb_error * WARN_UNUSED_RESULT ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r, const struct ovsdb_column *c, @@ -175,24 +191,22 @@ ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r, table = base->u.uuid.refTable; for (i = 0; i < n; i++) { const struct uuid *uuid = &atoms[i].uuid; - struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid); + struct ovsdb_txn_row *txn_row; + if (uuid_equals(uuid, ovsdb_row_get_uuid(r))) { /* Self-references don't count. */ continue; } + + txn_row = find_or_make_txn_row(txn, table, uuid); if (!txn_row) { - const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid); - if (row) { - txn_row = ovsdb_txn_row_modify(txn, row)->txn_row; - } else { - return ovsdb_error("referential integrity violation", - "Table %s column %s row "UUID_FMT" " - "references nonexistent row "UUID_FMT" in " - "table %s.", - r->table->schema->name, c->name, - UUID_ARGS(ovsdb_row_get_uuid(r)), - UUID_ARGS(uuid), table->schema->name); - } + return ovsdb_error("referential integrity violation", + "Table %s column %s row "UUID_FMT" " + "references nonexistent row "UUID_FMT" in " + "table %s.", + r->table->schema->name, c->name, + UUID_ARGS(ovsdb_row_get_uuid(r)), + UUID_ARGS(uuid), table->schema->name); } txn_row->n_refs += delta; } @@ -257,6 +271,92 @@ check_ref_count(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *r) } } +static struct ovsdb_error * WARN_UNUSED_RESULT +delete_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *row, + const struct ovsdb_base_type *base, + const union ovsdb_atom *atoms, unsigned int n) +{ + const struct ovsdb_table *table; + unsigned int i; + + if (!ovsdb_base_type_is_strong_ref(base)) { + return NULL; + } + + table = base->u.uuid.refTable; + for (i = 0; i < n; i++) { + const struct uuid *uuid = &atoms[i].uuid; + struct ovsdb_txn_row *txn_row; + + if (uuid_equals(uuid, ovsdb_row_get_uuid(row))) { + /* Self-references don't count. */ + continue; + } + + txn_row = find_or_make_txn_row(txn, table, uuid); + if (!txn_row) { + return OVSDB_BUG("strong ref target missing"); + } else if (!txn_row->n_refs) { + return OVSDB_BUG("strong ref target has zero n_refs"); + } else if (!txn_row->new) { + return OVSDB_BUG("deleted strong ref target"); + } + + if (--txn_row->n_refs == 0) { + struct ovsdb_error *error = delete_garbage_row(txn, txn_row); + if (error) { + return error; + } + } + } + + return NULL; +} + +static struct ovsdb_error * WARN_UNUSED_RESULT +delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) +{ + struct shash_node *node; + struct ovsdb_row *row; + + if (txn_row->table->schema->is_root) { + return NULL; + } + + row = txn_row->new; + txn_row->new = NULL; + hmap_remove(&txn_row->table->rows, &row->hmap_node); + SHASH_FOR_EACH (node, &txn_row->table->schema->columns) { + const struct ovsdb_column *column = node->data; + const struct ovsdb_datum *field = &row->fields[column->index]; + struct ovsdb_error *error; + + error = delete_row_refs(txn, row, + &column->type.key, field->keys, field->n); + if (error) { + return error; + } + + error = delete_row_refs(txn, row, + &column->type.value, field->values, field->n); + if (error) { + return error; + } + } + ovsdb_row_destroy(row); + + return NULL; +} + +static struct ovsdb_error * WARN_UNUSED_RESULT +collect_garbage(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) +{ + if (txn_row->new && !txn_row->n_refs) { + return delete_garbage_row(txn, txn_row); + } + return NULL; +} + static struct ovsdb_error * WARN_UNUSED_RESULT update_ref_counts(struct ovsdb_txn *txn) { @@ -491,15 +591,22 @@ ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable) return NULL; } - /* Check maximum rows table constraints. */ - error = check_max_rows(txn); + /* Update reference counts and check referential integrity. */ + error = update_ref_counts(txn); if (error) { ovsdb_txn_abort(txn); return error; } - /* Update reference counts and check referential integrity. */ - error = update_ref_counts(txn); + /* Delete unreferenced, non-root rows. */ + error = for_each_txn_row(txn, collect_garbage); + if (error) { + ovsdb_txn_abort(txn); + return OVSDB_WRAP_BUG("can't happen", error); + } + + /* Check maximum rows table constraints. */ + error = check_max_rows(txn); if (error) { ovsdb_txn_abort(txn); return error; diff --git a/python/ovs/db/schema.py b/python/ovs/db/schema.py index c12eda2e2..e0e0daf5e 100644 --- a/python/ovs/db/schema.py +++ b/python/ovs/db/schema.py @@ -1,4 +1,4 @@ -# Copyright (c) 2009, 2010 Nicira Networks +# Copyright (c) 2009, 2010, 2011 Nicira Networks # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,6 +34,22 @@ class DbSchema(object): self.__check_ref_table(column, column.type.key, "key") self.__check_ref_table(column, column.type.value, "value") + # "isRoot" was not part of the original schema definition. Before it + # was added, there was no support for garbage collection. So, for + # backward compatibility, if the root set is empty then assume that + # every table is in the root set. + if self.__root_set_size() == 0: + for table in self.tables.itervalues(): + table.is_root = True + + def __root_set_size(self): + """Returns the number of tables in the schema's root set.""" + n_root = 0 + for table in self.tables.itervalues(): + if table.is_root: + n_root += 1 + return n_root + @staticmethod def from_json(json): parser = ovs.db.parser.Parser(json, "database schema") @@ -60,9 +76,15 @@ class DbSchema(object): return DbSchema(name, version, tables) def to_json(self): + # "isRoot" was not part of the original schema definition. Before it + # was added, there was no support for garbage collection. So, for + # backward compatibility, if every table is in the root set then do not + # output "isRoot" in table schemas. + default_is_root = self.__root_set_size() == len(self.tables) + tables = {} for table in self.tables.itervalues(): - tables[table.name] = table.to_json() + tables[table.name] = table.to_json(default_is_root) json = {"name": self.name, "tables": tables} if self.version: json["version"] = self.version @@ -96,11 +118,13 @@ class IdlSchema(DbSchema): idlPrefix, idlHeader) class TableSchema(object): - def __init__(self, name, columns, mutable=True, max_rows=sys.maxint): + def __init__(self, name, columns, mutable=True, max_rows=sys.maxint, + is_root=True): self.name = name self.columns = columns self.mutable = mutable - self.max_rows = max_rows + self.max_rows = max_rows + self.is_root = is_root @staticmethod def from_json(json, name): @@ -108,6 +132,7 @@ class TableSchema(object): columnsJson = parser.get("columns", [dict]) mutable = parser.get_optional("mutable", [bool], True) max_rows = parser.get_optional("maxRows", [int]) + is_root = parser.get_optional("isRoot", [bool], False) parser.finish() if max_rows == None: @@ -128,12 +153,25 @@ class TableSchema(object): columns[columnName] = ColumnSchema.from_json(columnJson, columnName) - return TableSchema(name, columns, mutable, max_rows) + return TableSchema(name, columns, mutable, max_rows, is_root) - def to_json(self): + def to_json(self, default_is_root=False): + """Returns this table schema serialized into JSON. + + The "isRoot" member is included in the JSON only if its value would + differ from 'default_is_root'. Ordinarily 'default_is_root' should be + false, because ordinarily a table would be not be part of the root set + if its "isRoot" member is omitted. However, garbage collection was not + orginally included in OVSDB, so in older schemas that do not include + any "isRoot" members, every table is implicitly part of the root set. + To serialize such a schema in a way that can be read by older OVSDB + tools, specify 'default_is_root' as True. + """ json = {} if not self.mutable: json["mutable"] = False + if default_is_root != self.is_root: + json["isRoot"] = self.is_root json["columns"] = columns = {} for column in self.columns.itervalues(): diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at index b0c102673..2e8af852c 100644 --- a/tests/ovs-vsctl.at +++ b/tests/ovs-vsctl.at @@ -535,7 +535,9 @@ 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([create b name=br0])], +AT_CHECK( + [RUN_OVS_VSCTL_TOGETHER([--id=@br0 create b name=br0], + [set o . bridges=@br0])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) cp stdout out1 AT_CHECK([RUN_OVS_VSCTL([list b], [get b br0 _uuid])], @@ -543,6 +545,7 @@ AT_CHECK([RUN_OVS_VSCTL([list b], [get b br0 _uuid])], cp stdout out2 AT_CHECK([perl $srcdir/uuidfilt.pl out1 out2], [0], [[<0> + _uuid : <0> controller : [] datapath_id : [] @@ -572,8 +575,10 @@ AT_CHECK( name : "br0" datapath_type : "" ]], [ignore], [test ! -e pid || kill `cat pid`]) -AT_CHECK([RUN_OVS_VSCTL([create b name=br1 datapath_type="foo"], - [create b name=br2 external-ids:bar=quux])], +AT_CHECK([ + RUN_OVS_VSCTL_TOGETHER([--id=@br1 create b name=br1 datapath_type="foo"], + [--id=@br2 create b name=br2 external-ids:bar=quux], + [add o . bridges @br1 @br2])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) AT_CHECK( [RUN_OVS_VSCTL([--columns=name find b datapath_type!=foo])], [0], [stdout], @@ -608,7 +613,8 @@ AT_CHECK([RUN_OVS_VSCTL([clear br br0 external-ids -- get br br0 external_ids])] ], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER([destroy b br0], [destroy b br1], - [destroy b br2])], + [destroy b br2], + [clear o . bridges])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([list b])], [0], [], [], [OVS_VSCTL_CLEANUP]) @@ -618,19 +624,22 @@ AT_CLEANUP AT_SETUP([database commands -- negative checks]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP -AT_CHECK([RUN_OVS_VSCTL([create b name=br0])], +AT_CHECK([RUN_OVS_VSCTL([add-br 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([create n targets='"1.2.3.4:567"'])], +AT_CHECK([ + RUN_OVS_VSCTL_TOGETHER([--id=@n create n targets='"1.2.3.4:567"'], + [set bridge br0 netflow=@n])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) cp stdout netflow-uuid AT_CHECK([RUN_OVS_VSCTL([list n `cat netflow-uuid`])], [0], [stdout], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([perl $srcdir/uuidfilt.pl netflow-uuid stdout], [0], [[<0> + _uuid : <0> active_timeout : 0 add_id_to_interface : false @@ -806,13 +815,44 @@ name : "br0" OVS_VSCTL_CLEANUP AT_CLEANUP +AT_SETUP([unreferenced record warnings]) +AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP +AT_CHECK( + [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \ + -- create Bridge name=br0 | $srcdir/uuidfilt.pl], + [0], [<0> +], [vsctl|WARN|applying "create" command to table Bridge without --id option will have no effect +], [OVS_VSCTL_CLEANUP]) +AT_CHECK( + [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \ + -- --id=@br0 create Bridge name=br0 | $srcdir/uuidfilt.pl], + [0], [<0> +], [vsctl|WARN|row id "@br0" was created but no reference to it was inserted, so it will not actually appear in the database +], [OVS_VSCTL_CLEANUP]) +AT_CHECK( + [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \ + -- --id=@eth0_iface create Interface name=eth0 \ + -- --id=@eth0 create Port name=eth0 interfaces=@eth0_iface \ + -- --id=@m0 create Mirror name=m0 output_port=@eth0 \ + -- --id=@br0 create Bridge name=br0 mirrors=@m0 \ + -- set Open_vSwitch . bridges=@br0 | $srcdir/uuidfilt.pl], + [0], [<0> +<1> +<2> +<3> +], [vsctl|WARN|row id "@eth0" was created but only a weak reference to it was inserted, so it will not actually appear in the database +], [OVS_VSCTL_CLEANUP]) +OVS_VSCTL_CLEANUP +AT_CLEANUP + dnl This test really shows a bug -- "create" followed by "list" in dnl the same execution shows the wrong UUID on the "list" command. 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([create Bridge name=br0 -- list b])], +AT_CHECK([RUN_OVS_VSCTL([--id=@br0 create Bridge name=br0 -- add Open_vSwitch . bridges @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 f98519faf..ebf118672 100644 --- a/tests/ovsdb-execution.at +++ b/tests/ovsdb-execution.at @@ -57,6 +57,44 @@ m4_define([WEAK_SCHEMA], "refType": "weak"}, "min": 0, "max": "unlimited"}}}}}}]]) +m4_define([GC_SCHEMA], + [[{"name": "gc", + "tables": { + "root": { + "columns": { + "a": {"type": {"key": {"type": "uuid", + "refTable": "a"}, + "min": 0, "max": "unlimited"}}}, + "isRoot": true}, + "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"}}, + "wa2a": {"type": {"key": {"type": "uuid", + "refTable": "a", + "refType": "weak"}, + "min": 0, "max": "unlimited"}}, + "wa2b": {"type": {"key": {"type": "uuid", + "refTable": "b", + "refType": "weak"}, + "min": 0, "max": "unlimited"}}}}, + "b": { + "columns": { + "b": {"type": "integer"}, + "b2a": {"type": {"key": {"type": "uuid", + "refTable": "a"}, + "min": 0, "max": "unlimited"}}, + "wb2a": {"type": {"key": {"type": "uuid", + "refTable": "a", + "refType": "weak"}, + "min": 0, "max": "unlimited"}}}, + "isRoot": false}}}]]) + # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Runs "test-ovsdb execute" with the given SCHEMA and each of the @@ -760,6 +798,150 @@ OVSDB_CHECK_EXECUTION([weak references], [{"count":1}] [{"rows":[]}] [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["set",[]]}]}] +]]) + +OVSDB_CHECK_EXECUTION([garbage collection], + [GC_SCHEMA], + [dnl Check that inserting a row without any references is a no-op. + [[["gc", + {"op": "insert", + "table": "a", + "row": {"a": 0}}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"]}]]], + dnl Check that inserting a chain of rows that reference each other + dnl in turn is also a no-op. + [[["gc", + {"op": "insert", + "table": "a", + "row": {"a": 0, "a2a": ["named-uuid", "row1"]}, + "uuid-name": "row0"}, + {"op": "insert", + "table": "a", + "row": {"a": 1, "a2a": ["named-uuid", "row2"]}, + "uuid-name": "row1"}, + {"op": "insert", + "table": "a", + "row": {"a": 2, "a2a": ["named-uuid", "row3"]}, + "uuid-name": "row2"}, + {"op": "insert", + "table": "a", + "row": {"a": 3}, + "uuid-name": "row3"}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"]}]]], + dnl Check that inserting a pair of rows that mutually reference each + dnl other causes the rows to be retained. + [[["gc", + {"op": "insert", + "table": "a", + "row": {"a": 4, "a2a": ["named-uuid", "row5"]}, + "uuid-name": "row4"}, + {"op": "insert", + "table": "a", + "row": {"a": 5, "a2a": ["named-uuid", "row4"]}, + "uuid-name": "row5"}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"], + "sort": ["a"]}]]], + dnl Check that unreferencing one of the rows causes the other to be deleted. + [[["gc", + {"op": "update", + "table": "a", + "where": [["a", "==", 4]], + "row": {"a2a": ["set", []]}}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"]}]]], + dnl Check that inserting a pair of rows that mutually weak reference each + dnl other is a no-op. + [[["gc", + {"op": "insert", + "table": "a", + "row": {"a": 6, "wa2a": ["named-uuid", "row7"]}, + "uuid-name": "row6"}, + {"op": "insert", + "table": "a", + "row": {"a": 7, "wa2a": ["named-uuid", "row6"]}, + "uuid-name": "row7"}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"]}]]], + dnl Check that a circular chain of rows is retained. + [[["gc", + {"op": "insert", + "table": "a", + "row": {"a": 8, "a2a": ["named-uuid", "row9"]}, + "uuid-name": "row8"}, + {"op": "insert", + "table": "a", + "row": {"a": 9, "a2a": ["named-uuid", "row10"]}, + "uuid-name": "row9"}, + {"op": "insert", + "table": "a", + "row": {"a": 10, "a2a": ["named-uuid", "row11"]}, + "uuid-name": "row10"}, + {"op": "insert", + "table": "a", + "row": {"a": 11, "a2a": ["named-uuid", "row8"]}, + "uuid-name": "row11"}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"], + "sort": ["a"]}]]], + dnl Check that breaking the chain causes all of the rows to be deleted. + [[["gc", + {"op": "update", + "table": "a", + "where": [["a", "==", 9]], + "row": {"a2a": ["set", []]}}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"]}]]], + dnl Check that inserting a row only referenced by itself is a no-op. + [[["gc", + {"op": "insert", + "table": "a", + "row": {"a": 12, "a2a": ["named-uuid", "self"]}, + "uuid-name": "self"}]]], + [[["gc", + {"op": "select", + "table": "a", + "where": [], + "columns": ["a"]}]]]], + [[[{"uuid":["uuid","<0>"]}] +[{"rows":[]}] +[{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]}] +[{"rows":[]}] +[{"uuid":["uuid","<5>"]},{"uuid":["uuid","<6>"]}] +[{"rows":[{"a":4},{"a":5}]}] +[{"count":1}] +[{"rows":[]}] +[{"uuid":["uuid","<7>"]},{"uuid":["uuid","<8>"]}] +[{"rows":[]}] +[{"uuid":["uuid","<9>"]},{"uuid":["uuid","<10>"]},{"uuid":["uuid","<11>"]},{"uuid":["uuid","<12>"]}] +[{"rows":[{"a":8},{"a":9},{"a":10},{"a":11}]}] +[{"count":1}] +[{"rows":[]}] +[{"uuid":["uuid","<13>"]}] +[{"rows":[]}] ]])]) EXECUTION_EXAMPLES diff --git a/tests/ovsdb-table.at b/tests/ovsdb-table.at index 70f8ac252..cf206c423 100644 --- a/tests/ovsdb-table.at +++ b/tests/ovsdb-table.at @@ -1,6 +1,6 @@ AT_BANNER([OVSDB -- tables]) -OVSDB_CHECK_POSITIVE_CPY([table with one column], +OVSDB_CHECK_POSITIVE_CPY([non-root table with one column], [[parse-table mytable '{"columns": {"name": {"type": "string"}}}']], [[{"columns":{"name":{"type":"string"}}}]]) @@ -10,6 +10,22 @@ OVSDB_CHECK_POSITIVE_CPY([immutable table with one column], "mutable": false}']], [[{"columns":{"name":{"type":"string"}},"mutable":false}]]) +OVSDB_CHECK_POSITIVE_CPY([root table with one column], + [[parse-table mytable \ + '{"columns": {"name": {"type": "string"}}, + "isRoot": true}']], + [[{"columns":{"name":{"type":"string"}},"isRoot":true}]]) + +OVSDB_CHECK_POSITIVE_CPY([non-root table with default_is_root=true], + [[parse-table mytable '{"columns": {"name": {"type": "string"}}}' true]], + [[{"columns":{"name":{"type":"string"}},"isRoot":false}]]) + +OVSDB_CHECK_POSITIVE_CPY([root table with default_is_root=true], + [[parse-table mytable \ + '{"columns": {"name": {"type": "string"}}, + "isRoot": true}' true]], + [[{"columns":{"name":{"type":"string"}}}]]) + OVSDB_CHECK_POSITIVE_CPY([table with maxRows of 2], [[parse-table mytable '{"columns": {"name": {"type": "string"}}, "maxRows": 2}']], diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c index c424abe6d..990bf6dde 100644 --- a/tests/test-ovsdb.c +++ b/tests/test-ovsdb.c @@ -141,7 +141,7 @@ usage(void) " parse string DATUMs as data of given TYPE, and re-serialize\n" " parse-column NAME OBJECT\n" " parse column NAME with info OBJECT, and re-serialize\n" - " parse-table NAME OBJECT\n" + " parse-table NAME OBJECT [DEFAULT-IS-ROOT]\n" " parse table NAME with info OBJECT\n" " parse-row TABLE ROW..., and re-serialize\n" " parse each ROW of defined TABLE\n" @@ -608,12 +608,15 @@ static void do_parse_table(int argc OVS_UNUSED, char *argv[]) { struct ovsdb_table_schema *ts; + bool default_is_root; struct json *json; + default_is_root = argc > 3 && !strcmp(argv[3], "true"); + json = parse_json(argv[2]); check_ovsdb_error(ovsdb_table_schema_from_json(json, argv[1], &ts)); json_destroy(json); - print_and_free_json(ovsdb_table_schema_to_json(ts)); + print_and_free_json(ovsdb_table_schema_to_json(ts, default_is_root)); ovsdb_table_schema_destroy(ts); } @@ -1932,7 +1935,7 @@ static struct command all_commands[] = { { "parse-data-strings", 2, INT_MAX, do_parse_data_strings }, { "sort-atoms", 2, 2, do_sort_atoms }, { "parse-column", 2, 2, do_parse_column }, - { "parse-table", 2, 2, do_parse_table }, + { "parse-table", 2, 3, do_parse_table }, { "parse-rows", 2, INT_MAX, do_parse_rows }, { "compare-rows", 2, INT_MAX, do_compare_rows }, { "parse-conditions", 2, INT_MAX, do_parse_conditions }, diff --git a/tests/test-ovsdb.py b/tests/test-ovsdb.py index 863bcb8fd..2eafe99f7 100644 --- a/tests/test-ovsdb.py +++ b/tests/test-ovsdb.py @@ -1,4 +1,4 @@ -# Copyright (c) 2009, 2010 Nicira Networks +# Copyright (c) 2009, 2010, 2011 Nicira Networks # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -121,10 +121,11 @@ def do_parse_column(name, column_string): column = ovs.db.schema.ColumnSchema.from_json(column_json, name) print ovs.json.to_string(column.to_json(), sort_keys=True) -def do_parse_table(name, table_string): +def do_parse_table(name, table_string, default_is_root_string='false'): + default_is_root = default_is_root_string == 'true' table_json = unbox_json(ovs.json.from_string(table_string)) table = ovs.db.schema.TableSchema.from_json(table_json, name) - print ovs.json.to_string(table.to_json(), sort_keys=True) + print ovs.json.to_string(table.to_json(default_is_root), sort_keys=True) def do_parse_rows(table_string, *rows): table_json = unbox_json(ovs.json.from_string(table_string)) @@ -272,7 +273,7 @@ parse-data TYPE DATUM... parse JSON DATUMs as data of given TYPE, and re-serialize parse-column NAME OBJECT parse column NAME with info OBJECT, and re-serialize -parse-table NAME OBJECT +parse-table NAME OBJECT [DEFAULT-IS-ROOT] parse table NAME with info OBJECT parse-schema JSON parse JSON as an OVSDB schema, and re-serialize @@ -332,7 +333,7 @@ def main(argv): "parse-data": (do_parse_data, (2,)), "sort-atoms": (do_sort_atoms, 2), "parse-column": (do_parse_column, 2), - "parse-table": (do_parse_table, 2), + "parse-table": (do_parse_table, (2, 3)), "parse-schema": (do_parse_schema, 1), "idl": (do_idl, (1,))} diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in index 3b35179ab..ee76b8321 100644 --- a/utilities/ovs-vsctl.8.in +++ b/utilities/ovs-vsctl.8.in @@ -478,9 +478,11 @@ A bridge port. Records may be identified by port name. A network device attached to a port. Records may be identified by name. .IP "\fBQoS\fR" -Quality-of-service configuration for an \fBInterface\fR. +Quality-of-service configuration for a \fBPort\fR. Records may be +identified by port name. .IP "\fBQueue\fR" -Configuration for one queue within a \fBQoS\fR configuration. +Configuration for one queue within a \fBQoS\fR configuration. Records +may only be identified by UUID. .IP "\fBMonitor\fR" Connectivity Monitoring attached to an \fBInterface\fR. Records may be identified by \fBInterface\fR name. @@ -637,10 +639,30 @@ If \fB@\fIname\fR is specified, then the UUID for the new row may be referred to by that name elsewhere in the same \fBovs\-vsctl\fR invocation in contexts where a UUID is expected. Such references may precede or follow the \fBcreate\fR command. +.IP +Records in the Open vSwitch database are significant only when they +can be reached directly or indirectly from the \fBOpen_vSwitch\fR +table. Except for records in the \fBQoS\fR or \fBQueue\fR tables, +records that are not reachable from the \fBOpen_vSwitch\fR table are +automatically deleted from the database. This deletion happens +immediately, without waiting for additional \fBovs\-vsctl\fR commands +or other database activity. Thus, a \fBcreate\fR command must +generally be accompanied by additional commands \fIwithin the same +\fBovs\-vsctl\fI invocation\fR to add a chain of references to the +newly created record from the top-level \fBOpen_vSwitch\fR record. +The \fBEXAMPLES\fR section gives some examples that show how to do +this. . .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 +It is often unnecessary to specify explicit \fBdestroy\fR commands, +because (except for records in the \fBQoS\fR or \fBQueue\fR tables) +records that are not reachable from the \fBOpen_vSwitch\fR table are +automatically deleted from the database. This means that deleting the +last reference to a record is sufficient for deleting the record +itself. See the \fBEXAMPLES\fR section below for more information. . .IP "\fBwait\-until \fItable record \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR]..." Waits until \fItable\fR contains a record named \fIrecord\fR whose @@ -720,10 +742,10 @@ ignored): .IP .B "\-\- \-\-id=@m create Mirror name=mymirror select-dst-port=@eth0,@eth1 select-src-port=@eth0,@eth1 output-port=@eth2" .PP -Remove the mirror created above from \fBbr0\fR and destroy the Mirror -record (to avoid having an unreferenced record in the database): +Remove the mirror created above from \fBbr0\fR, which also destroys +the Mirror record (since it is now unreferenced): .IP -.B "ovs\-vsctl destroy Mirror mymirror \-\- clear Bridge br0 mirrors" +.B "remove Bridge br0 mirrors mymirror" .SS "Quality of Service (QoS)" .PP Create a \fBlinux\-htb\fR QoS record that points to a few queues and @@ -744,7 +766,8 @@ Deconfigure the QoS record above from \fBeth1\fR only: .B "ovs\-vsctl clear Port eth1 qos" .PP To deconfigure the QoS record from both \fBeth0\fR and \fBeth1\fR and -then delete the QoS record: +then delete the QoS record (which must be done explicitly because +unreferenced QoS records are not automatically destroyed): .IP .B "ovs\-vsctl \-\- destroy QoS eth0 \-\- clear Port eth0 qos \-\- clear Port eth1 qos" .PP @@ -781,10 +804,10 @@ instead use an active timeout of 60 seconds: .IP .B "ovs\-vsctl set NetFlow br0 active_timeout=60" .PP -Deconfigure the NetFlow settings from \fBbr0\fR and delete the NetFlow -record (to avoid having an unreferenced record in the database): +Deconfigure the NetFlow settings from \fBbr0\fR, which also destroys +the NetFlow record (since it is now unreferenced): .IP -.B "ovs\-vsctl destroy NetFlow br0 \-\- clear Bridge br0 netflow" +.B "ovs\-vsctl clear Bridge br0 netflow" .SS "sFlow" .PP Configure bridge \fBbr0\fR to send sFlow records to a collector on @@ -795,10 +818,10 @@ with specific sampling parameters: .IP .B "\-\- set Bridge br0 sflow=@s" .PP -Deconfigure sFlow from br0 and destroy the sFlow record (to avoid -having an unreferenced record in the database): +Deconfigure sFlow from br0, which also destroys the sFlow record +(since it is now unreferenced): .IP -.B "ovs\-vsctl \-\- destroy sFlow br0 \-\- clear Bridge br0 sflow" +.B "ovs\-vsctl \-\- clear Bridge br0 sflow" .SH "EXIT STATUS" .IP "0" Successful program execution. diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c index e5e03f738..80c9048b1 100644 --- a/utilities/ovs-vsctl.c +++ b/utilities/ovs-vsctl.c @@ -1035,12 +1035,6 @@ cmd_emer_reset(struct vsctl_context *ctx) const struct ovsrec_bridge *br; const struct ovsrec_port *port; const struct ovsrec_interface *iface; - const struct ovsrec_mirror *mirror, *next_mirror; - const struct ovsrec_controller *ctrl, *next_ctrl; - const struct ovsrec_manager *mgr, *next_mgr; - const struct ovsrec_netflow *nf, *next_nf; - const struct ovsrec_ssl *ssl, *next_ssl; - const struct ovsrec_sflow *sflow, *next_sflow; /* Reset the Open_vSwitch table. */ ovsrec_open_vswitch_set_manager_options(ctx->ovs, NULL, 0); @@ -1084,30 +1078,6 @@ cmd_emer_reset(struct vsctl_context *ctx) ovsrec_interface_set_ingress_policing_rate(iface, 0); ovsrec_interface_set_ingress_policing_burst(iface, 0); } - - OVSREC_MIRROR_FOR_EACH_SAFE (mirror, next_mirror, idl) { - ovsrec_mirror_delete(mirror); - } - - OVSREC_CONTROLLER_FOR_EACH_SAFE (ctrl, next_ctrl, idl) { - ovsrec_controller_delete(ctrl); - } - - OVSREC_MANAGER_FOR_EACH_SAFE (mgr, next_mgr, idl) { - ovsrec_manager_delete(mgr); - } - - OVSREC_NETFLOW_FOR_EACH_SAFE (nf, next_nf, idl) { - ovsrec_netflow_delete(nf); - } - - OVSREC_SSL_FOR_EACH_SAFE (ssl, next_ssl, idl) { - ovsrec_ssl_delete(ssl); - } - - OVSREC_SFLOW_FOR_EACH_SAFE (sflow, next_sflow, idl) { - ovsrec_sflow_delete(sflow); - } } static void @@ -1218,18 +1188,8 @@ cmd_add_br(struct vsctl_context *ctx) } static void -del_port(struct vsctl_info *info, struct vsctl_port *port) +del_port(struct vsctl_port *port) { - struct shash_node *node; - - SHASH_FOR_EACH (node, &info->ifaces) { - struct vsctl_iface *iface = node->data; - if (iface->port == port) { - ovsrec_interface_delete(iface->iface_cfg); - } - } - ovsrec_port_delete(port->port_cfg); - bridge_delete_port((port->bridge->parent ? port->bridge->parent->br_cfg : port->bridge->br_cfg), port->port_cfg); @@ -1245,18 +1205,18 @@ cmd_del_br(struct vsctl_context *ctx) get_info(ctx, &info); bridge = find_bridge(&info, ctx->argv[1], must_exist); if (bridge) { - struct shash_node *node; - - SHASH_FOR_EACH (node, &info.ports) { - struct vsctl_port *port = node->data; - if (port->bridge == bridge || port->bridge->parent == bridge - || !strcmp(port->port_cfg->name, bridge->name)) { - del_port(&info, port); - } - } if (bridge->br_cfg) { - ovsrec_bridge_delete(bridge->br_cfg); ovs_delete_bridge(ctx->ovs, bridge->br_cfg); + } else { + struct shash_node *node; + + SHASH_FOR_EACH (node, &info.ports) { + struct vsctl_port *port = node->data; + if (port->bridge == bridge || port->bridge->parent == bridge + || !strcmp(port->port_cfg->name, bridge->name)) { + del_port(port); + } + } } } free_info(&info); @@ -1641,7 +1601,7 @@ cmd_del_port(struct vsctl_context *ctx) } } - del_port(&info, port); + del_port(port); } free_info(&info); @@ -1773,17 +1733,6 @@ cmd_get_controller(struct vsctl_context *ctx) free_info(&info); } -static void -delete_controllers(struct ovsrec_controller **controllers, - size_t n_controllers) -{ - size_t i; - - for (i = 0; i < n_controllers; i++) { - ovsrec_controller_delete(controllers[i]); - } -} - static void cmd_del_controller(struct vsctl_context *ctx) { @@ -1791,13 +1740,9 @@ cmd_del_controller(struct vsctl_context *ctx) struct vsctl_bridge *br; get_info(ctx, &info); - br = find_real_bridge(&info, ctx->argv[1], true); - verify_controllers(br->br_cfg); - if (br->ctrl) { - delete_controllers(br->ctrl, br->n_ctrl); - ovsrec_bridge_set_controller(br->br_cfg, NULL, 0); - } + br = find_real_bridge(&info, ctx->argv[1], true); + ovsrec_bridge_set_controller(br->br_cfg, NULL, 0); free_info(&info); } @@ -1827,9 +1772,6 @@ cmd_set_controller(struct vsctl_context *ctx) get_info(ctx, &info); br = find_real_bridge(&info, ctx->argv[1], true); - verify_controllers(br->br_cfg); - - delete_controllers(br->ctrl, br->n_ctrl); n = ctx->argc - 2; controllers = insert_controllers(ctx->txn, &ctx->argv[2], n); @@ -1935,28 +1877,12 @@ cmd_get_manager(struct vsctl_context *ctx) svec_destroy(&targets); } -static void -delete_managers(const struct vsctl_context *ctx) -{ - const struct ovsrec_open_vswitch *ovs = ctx->ovs; - size_t i; - - /* Delete Manager rows pointed to by 'manager_options' column. */ - for (i = 0; i < ovs->n_manager_options; i++) { - ovsrec_manager_delete(ovs->manager_options[i]); - } - - /* Delete 'Manager' row refs in 'manager_options' column. */ - ovsrec_open_vswitch_set_manager_options(ovs, NULL, 0); -} - static void cmd_del_manager(struct vsctl_context *ctx) { const struct ovsrec_open_vswitch *ovs = ctx->ovs; - verify_managers(ovs); - delete_managers(ctx); + ovsrec_open_vswitch_set_manager_options(ovs, NULL, 0); } static void @@ -1982,8 +1908,6 @@ cmd_set_manager(struct vsctl_context *ctx) { const size_t n = ctx->argc - 1; - verify_managers(ctx->ovs); - delete_managers(ctx); insert_managers(ctx, &ctx->argv[1], n); } @@ -2027,13 +1951,7 @@ pre_cmd_del_ssl(struct vsctl_context *ctx) static void cmd_del_ssl(struct vsctl_context *ctx) { - struct ovsrec_ssl *ssl = ctx->ovs->ssl; - - if (ssl) { - ovsrec_open_vswitch_verify_ssl(ctx->ovs); - ovsrec_ssl_delete(ssl); - ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL); - } + ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL); } static void @@ -2046,12 +1964,8 @@ static void cmd_set_ssl(struct vsctl_context *ctx) { bool bootstrap = shash_find(&ctx->options, "--bootstrap"); - struct ovsrec_ssl *ssl = ctx->ovs->ssl; + struct ovsrec_ssl *ssl; - ovsrec_open_vswitch_verify_ssl(ctx->ovs); - if (ssl) { - ovsrec_ssl_delete(ssl); - } ssl = ovsrec_ssl_insert(ctx->txn); ovsrec_ssl_set_private_key(ssl, ctx->argv[1]); @@ -2351,7 +2265,7 @@ get_column(const struct vsctl_table_class *table, const char *column_name, } } -static struct uuid * +static struct ovsdb_symbol * create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp) { struct ovsdb_symbol *symbol; @@ -2370,7 +2284,7 @@ create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp) id); } symbol->created = true; - return &symbol->uuid; + return symbol; } static void @@ -2578,13 +2492,19 @@ cmd_get(struct vsctl_context *ctx) table = get_table(table_name); row = must_get_row(ctx, table, record_id); if (id) { + struct ovsdb_symbol *symbol; bool new; - *create_symbol(ctx->symtab, id, &new) = row->uuid; + symbol = create_symbol(ctx->symtab, id, &new); if (!new) { vsctl_fatal("row id \"%s\" specified on \"get\" command was used " "before it was defined", id); } + symbol->uuid = row->uuid; + + /* This symbol refers to a row that already exists, so disable warnings + * about it being unreferenced. */ + symbol->strong_ref = true; } for (i = 3; i < ctx->argc; i++) { const struct ovsdb_idl_column *column; @@ -3093,18 +3013,42 @@ cmd_clear(struct vsctl_context *ctx) } static void -cmd_create(struct vsctl_context *ctx) +pre_create(struct vsctl_context *ctx) { const char *id = shash_find_data(&ctx->options, "--id"); const char *table_name = ctx->argv[1]; const struct vsctl_table_class *table; + + table = get_table(table_name); + if (!id && !table->class->is_root) { + VLOG_WARN("applying \"create\" command to table %s without --id " + "option will have no effect", table->class->name); + } +} + +static void +cmd_create(struct vsctl_context *ctx) +{ + const char *id = shash_find_data(&ctx->options, "--id"); + const char *table_name = ctx->argv[1]; + const struct vsctl_table_class *table = get_table(table_name); const struct ovsdb_idl_row *row; const struct uuid *uuid; int i; - uuid = id ? create_symbol(ctx->symtab, id, NULL) : NULL; + if (id) { + struct ovsdb_symbol *symbol = create_symbol(ctx->symtab, id, NULL); + if (table->class->is_root) { + /* This table is in the root set, meaning that rows created in it + * won't disappear even if they are unreferenced, so disable + * warnings about that by pretending that there is a reference. */ + symbol->strong_ref = true; + } + uuid = &symbol->uuid; + } else { + uuid = NULL; + } - table = get_table(table_name); row = ovsdb_idl_txn_insert(ctx->txn, table->class, uuid); for (i = 2; i < ctx->argc; i++) { set_column(table, row, ctx->argv[i], ctx->symtab); @@ -3402,6 +3346,17 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, "with \"-- --id=%s create ...\")", node->name, node->name); } + if (!symbol->strong_ref) { + if (!symbol->weak_ref) { + VLOG_WARN("row id \"%s\" was created but no reference to it " + "was inserted, so it will not actually appear in " + "the database", node->name); + } else { + VLOG_WARN("row id \"%s\" was created but only a weak " + "reference to it was inserted, so it will not " + "actually appear in the database", node->name); + } + } } status = ovsdb_idl_txn_commit_block(txn); @@ -3577,7 +3532,7 @@ static const struct vsctl_command_syntax all_commands[] = { {"add", 4, INT_MAX, pre_cmd_add, cmd_add, NULL, "", RW}, {"remove", 4, INT_MAX, pre_cmd_remove, cmd_remove, NULL, "", RW}, {"clear", 3, INT_MAX, pre_cmd_clear, cmd_clear, NULL, "", RW}, - {"create", 2, INT_MAX, NULL, cmd_create, post_create, "--id=", RW}, + {"create", 2, INT_MAX, pre_create, cmd_create, post_create, "--id=", RW}, {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL, "--if-exists", RW}, {"wait-until", 2, INT_MAX, pre_cmd_wait_until, cmd_wait_until, NULL, "", diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index 607446270..4a80289b3 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -497,14 +497,6 @@ del_port(const struct ovsrec_bridge *br, const struct ovsrec_port *port) } ovsrec_bridge_set_ports(br, ports, n); free(ports); - - /* Delete all of the port's interfaces. */ - for (i = 0; i < port->n_interfaces; i++) { - ovsrec_interface_delete(port->interfaces[i]); - } - - /* Delete the port itself. */ - ovsrec_port_delete(port); } /* Delete 'iface' from 'port' (which must be within 'br'). If 'iface' was @@ -530,7 +522,6 @@ del_interface(const struct ovsrec_bridge *br, } ovsrec_port_set_interfaces(port, ifaces, n); free(ifaces); - ovsrec_interface_delete(iface); } } @@ -591,24 +582,6 @@ del_bridge(struct ovsdb_idl *idl, ovsdb_idl_txn_add_comment(txn, "ovs-brcompatd: delbr %s", br_name); - /* Delete everything that the bridge points to, then delete the bridge - * itself. */ - while (br->n_ports > 0) { - del_port(br, br->ports[0]); - } - for (i = 0; i < br->n_mirrors; i++) { - ovsrec_mirror_delete(br->mirrors[i]); - } - if (br->netflow) { - ovsrec_netflow_delete(br->netflow); - } - if (br->sflow) { - ovsrec_sflow_delete(br->sflow); - } - for (i = 0; i < br->n_controller; i++) { - ovsrec_controller_delete(br->controller[i]); - } - /* Remove 'br' from the vswitch's list of bridges. */ bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges); for (i = n = 0; i < ovs->n_bridges; i++) { @@ -619,9 +592,6 @@ del_bridge(struct ovsdb_idl *idl, ovsrec_open_vswitch_set_bridges(ovs, bridges, n); free(bridges); - /* Delete the bridge itself. */ - ovsrec_bridge_delete(br); - return commit_txn(txn, true); } diff --git a/vswitchd/vswitch.gv b/vswitchd/vswitch.gv index 1ab56e658..5988698eb 100644 --- a/vswitchd/vswitch.gv +++ b/vswitchd/vswitch.gv @@ -3,92 +3,36 @@ digraph Open_vSwitch { margin="0"; node [shape=box]; edge [dir=none, arrowhead=none, arrowtail=none]; - Bridge; + Bridge [style=bold]; Bridge -> sFlow [label="sflow"]; Bridge -> Mirror [label="mirrors"]; Bridge -> Port [label="ports"]; Bridge -> Controller [label="controller"]; Bridge -> NetFlow [label="netflow"]; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - QoS; + QoS [style=bold]; QoS -> Queue [label="queues value"]; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Monitor; + Monitor [style=bold]; Monitor -> Maintenance_Point [label="remote_mps"]; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - sFlow; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Open_vSwitch; + sFlow [style=bold]; + Open_vSwitch [style=bold]; Open_vSwitch -> Bridge [label="bridges"]; Open_vSwitch -> Capability [label="capabilities value"]; Open_vSwitch -> SSL [label="ssl"]; Open_vSwitch -> Manager [label="manager_options"]; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Controller; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Queue; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - SSL; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Manager; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Capability; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Mirror; - Mirror -> Port [constraint=false, label="select_src_port"]; - Mirror -> Port [constraint=false, label="output_port"]; - Mirror -> Port [constraint=false, label="select_dst_port"]; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Interface; + Controller [style=bold]; + Queue [style=bold]; + SSL [style=bold]; + Manager [style=bold]; + Capability [style=bold]; + Mirror [style=bold]; + Mirror -> Port [style=dotted, constraint=false, label="select_src_port"]; + Mirror -> Port [style=dotted, constraint=false, label="output_port"]; + Mirror -> Port [style=dotted, constraint=false, label="select_dst_port"]; + Interface [style=bold]; Interface -> Monitor [label="monitor"]; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - NetFlow; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Maintenance_Point; - size="6.5,4"; - margin="0"; - node [shape=box]; - edge [dir=none, arrowhead=none, arrowtail=none]; - Port; + NetFlow [style=bold]; + Maintenance_Point [style=bold]; + Port [style=bold]; Port -> QoS [label="qos"]; Port -> Interface [label="interfaces"]; } diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index 3537d2876..7618e2d5a 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "2.0.0", - "cksum": "4107852581 15651", + "version": "2.1.0", + "cksum": "1990266641 15714", "tables": { "Open_vSwitch": { "columns": { @@ -43,6 +43,7 @@ "system_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}}, + "isRoot": true, "maxRows": 1}, "Capability": { "columns": { @@ -267,7 +268,8 @@ "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", - "min": 0, "max": "unlimited"}}}}, + "min": 0, "max": "unlimited"}}}, + "isRoot": true}, "Queue": { "columns": { "other_config": { @@ -275,7 +277,8 @@ "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", - "min": 0, "max": "unlimited"}}}}, + "min": 0, "max": "unlimited"}}}, + "isRoot": true}, "Mirror": { "columns": { "name": { diff --git a/vswitchd/vswitch.pic b/vswitchd/vswitch.pic index 99bcaaae1..86296e8ab 100644 --- a/vswitchd/vswitch.pic +++ b/vswitchd/vswitch.pic @@ -1,53 +1,100 @@ -.\" Generated from vswitch.gv with cksum "3734436941 2390" +.\" Generated from vswitch.gv with cksum "1122829786 1269" .PS linethick = 1; +linethick = 0.5; box at 2.320997253,3.1110975 wid 0.5020540998 height 0.296295 "Bridge" +box at 2.320997253,3.1110975 wid 0.446498544244444 height 0.240739444444444 +linethick = 0.5; box at 0.2304523251,2.37036 wid 0.4609046502 height 0.296295 "sFlow" +box at 0.2304523251,2.37036 wid 0.405349094644444 height 0.240739444444444 +linethick = 0.5; box at 0.847759254,2.37036 wid 0.4855919496 height 0.296295 "Mirror" +box at 0.847759254,2.37036 wid 0.430036394044444 height 0.240739444444444 +linethick = 0.5; box at 2.320997253,2.37036 wid 0.4444425 height 0.296295 "Port" +box at 2.320997253,2.37036 wid 0.388886944444444 height 0.240739444444444 +linethick = 0.5; box at 3.045260751,2.37036 wid 0.707789496 height 0.296295 "Controller" +box at 3.045260751,2.37036 wid 0.652233940444444 height 0.240739444444444 +linethick = 0.5; box at 3.851835,2.37036 wid 0.609064002 height 0.296295 "NetFlow" +box at 3.851835,2.37036 wid 0.553508446444444 height 0.240739444444444 +linethick = 0.5; box at 2.057590998,1.6296225 wid 0.4444425 height 0.296295 "QoS" +box at 2.057590998,1.6296225 wid 0.388886944444444 height 0.240739444444444 +linethick = 0.5; box at 1.991754249,0.888885 wid 0.5102851749 height 0.296295 "Queue" +box at 1.991754249,0.888885 wid 0.454729619344444 height 0.240739444444444 +linethick = 0.5; box at 2.938238997,0.888885 wid 0.5761278498 height 0.296295 "Monitor" +box at 2.938238997,0.888885 wid 0.520572294244444 height 0.240739444444444 +linethick = 0.5; box at 2.938238997,0.1481475 wid 1.218128004 height 0.296295 "Maintenance_Point" +box at 2.938238997,0.1481475 wid 1.16257244844444 height 0.240739444444444 +linethick = 0.5; box at 3.637850751,3.851835 wid 0.954721749 height 0.296295 "Open_vSwitch" +box at 3.637850751,3.851835 wid 0.899166193444444 height 0.240739444444444 +linethick = 0.5; box at 3.061734753,3.1110975 wid 0.699611754 height 0.296295 "Capability" +box at 3.061734753,3.1110975 wid 0.644056198444444 height 0.240739444444444 +linethick = 0.5; box at 4.22220375,3.1110975 wid 0.4444425 height 0.296295 "SSL" +box at 4.22220375,3.1110975 wid 0.388886944444444 height 0.240739444444444 +linethick = 0.5; box at 4.905341502,3.1110975 wid 0.633715746 height 0.296295 "Manager" +box at 4.905341502,3.1110975 wid 0.578160190444444 height 0.240739444444444 +linethick = 0.5; box at 2.872402248,1.6296225 wid 0.641952747 height 0.296295 "Interface" +box at 2.872402248,1.6296225 wid 0.586397191444444 height 0.240739444444444 +linethick = 1; spline -> from 2.072227971,3.066534732 to 2.072227971,3.066534732 to 1.825829049,3.018534942 to 1.439934441,2.932787169 to 1.119343251,2.8148025 to 0.887818338,2.729588058 to 0.637567581,2.60087751 to 0.4617876093,2.503870527 "sflow" at 1.271579622,2.74072875 +linethick = 1; spline -> from 2.071042791,2.98546842 to 2.071042791,2.98546842 to 1.796021772,2.847157914 to 1.357208877,2.626536657 to 1.086632283,2.490477993 "mirrors" at 1.9259175,2.74072875 +linethick = 1; spline -> from 2.320997253,2.96117223 to 2.320997253,2.96117223 to 2.320997253,2.832698718 to 2.320997253,2.648462487 to 2.320997253,2.520048234 "ports" at 2.469144753,2.74072875 +linethick = 1; spline -> from 2.495218713,2.960816676 to 2.495218713,2.960816676 to 2.546240712,2.915068728 to 2.601410841,2.863868952 to 2.650180998,2.8148025 to 2.743336146,2.721054762 to 2.842120899,2.609944137 to 2.917557606,2.522300076 "controller" at 3.065823624,2.74072875 +linethick = 1; spline -> from 2.571070233,2.988964701 to 2.571070233,2.988964701 to 2.594773833,2.979424002 to 2.61865521,2.970594411 to 2.641943997,2.96295 to 2.957675949,2.859720822 to 3.079038381,2.966624058 to 3.374444496,2.8148025 to 3.514473513,2.742862074 to 3.643184061,2.618003361 to 3.731598489,2.518922313 "netflow" at 3.802472253,2.74072875 +linethick = 0.5; spline -> from 1.091017449,2.37036 to 1.091017449,2.37036 to 1.370542152,2.37036 to 1.825414236,2.37036 to 2.096701938,2.37036 "select_src_port" at 1.588496754,2.44443375 +linethick = 0.5; spline -> from 1.067787921,2.221501392 to 1.067787921,2.221501392 to 1.095284097,2.208345894 to 1.123787676,2.197086684 to 1.152231996,2.189264496 to 1.526156286,2.08650939 to 1.651429812,2.084257548 to 2.024702253,2.189264496 to 2.052553983,2.197145943 to 2.080346454,2.208405153 to 2.107190781,2.221501392 "output_port" at 1.588496754,2.263338246 +linethick = 0.5; spline -> from 0.905240484,2.221264356 to 0.905240484,2.221264356 to 0.953892123,2.117798142 to 1.034780658,1.987843155 to 1.152231996,1.9259175 to 1.495282347,1.745118291 to 1.682837082,1.742984967 to 2.024702253,1.9259175 to 2.140790634,1.988080191 to 2.21924955,2.117975919 to 2.266123419,2.221442133 "select_dst_port" at 1.588496754,1.99999125 +linethick = 1; spline -> from 2.267664153,2.22043473 to 2.267664153,2.22043473 to 2.221975464,2.091961218 to 2.156494269,1.907724987 to 2.11080558,1.779310734 "qos" at 2.312760252,1.99999125 +linethick = 1; spline -> from 2.43258195,2.22043473 to 2.43258195,2.22043473 to 2.528225976,2.091961218 to 2.665351302,1.907724987 to 2.760995328,1.779310734 "interfaces" at 2.930001996,1.99999125 +linethick = 1; spline -> from 2.023220778,1.479637971 to 2.023220778,1.479637971 to 2.013917115,1.433001138 to 2.005028265,1.381268031 to 1.99999125,1.3333275 to 1.989620925,1.235076078 to 1.98754686,1.123965453 to 1.988080191,1.037447313 "queues value" at 2.378597001,1.25925375 +linethick = 1; spline -> from 2.938238997,0.73895973 to 2.938238997,0.73895973 to 2.938238997,0.610486218 to 2.938238997,0.4262736906 to 2.938238997,0.2978238822 "remote_mps" at 3.288044874,0.51851625 +linethick = 1; spline -> from 3.159808398,3.796309317 to 3.159808398,3.796309317 to 2.96591295,3.754828017 to 2.749321305,3.682295001 to 2.584344249,3.55554 to 2.486211345,3.480162552 to 2.417352387,3.356903832 to 2.374685907,3.259245 "bridges" at 2.790091497,3.48146625 +linethick = 1; spline -> from 3.219067398,3.702146766 to 3.219067398,3.702146766 to 3.159926916,3.663332121 to 3.106830852,3.615095295 to 3.069971754,3.55554 to 3.015927546,3.468251493 to 3.015809028,3.35168904 to 3.028194159,3.25983759 "capabilities value" at 3.55554,3.48146625 +linethick = 1; spline -> from 3.884901522,3.703568982 to 3.884901522,3.703568982 to 3.941612385,3.661080279 to 3.997849176,3.611065683 to 4.041108246,3.55554 to 4.109256096,3.468132975 to 4.155478116,3.351570522 to 4.184159472,3.259778331 "ssl" at 4.205729748,3.48146625 +linethick = 1; spline -> from 4.020249078,3.702146766 to 4.020249078,3.702146766 to 4.117848651,3.659065473 to 4.22101857,3.609110136 to 4.312751502,3.55554 to 4.46042493,3.469258896 to 4.614616848,3.352696443 to 4.728690423,3.260548698 "manager_options" at 5.024689128,3.48146625 +linethick = 1; spline -> from 2.885735523,1.47969723 to 2.885735523,1.47969723 to 2.89717251,1.351223718 to 2.913527994,1.166987487 to 2.924964981,1.038573234 "monitor" at 3.139897374,1.25925375 .PE diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index e0245cdde..89354daed 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1,15 +1,20 @@ -

A database with this schema holds the configuration for one Open - vSwitch daemon. The root of the configuration for the daemon is - the table, which must have exactly one +

+ A database with this schema holds the configuration for one Open + vSwitch daemon. The top-level configuration for the daemon is the + table, which must have exactly one record. Records in other tables are significant only when they - can be reached directly or indirectly from the - table.

+ can be reached directly or indirectly from the table. Records that are not reachable from + the table are automatically deleted + from the database, except for records in a few distinguished + ``root set'' tables noted below. +

- Configuration for an Open vSwitch daemon. There must be exactly one record - in the table. + Configuration for an Open vSwitch daemon. There must be exactly + one record in the table. diff --git a/xenserver/etc_init.d_openvswitch b/xenserver/etc_init.d_openvswitch index 13b9d40ad..730098134 100755 --- a/xenserver/etc_init.d_openvswitch +++ b/xenserver/etc_init.d_openvswitch @@ -341,7 +341,18 @@ function start { cksum=`$ovsdb_tool db-cksum "$OVSDB_SERVER_DB" | awk '{print $1}'` cp "$OVSDB_SERVER_DB" "$OVSDB_SERVER_DB.backup$version-$cksum" - # Upgrade or downgrade schema and compact database. + # Compact database. This is important if the old schema did not enable + # garbage collection (i.e. if it did not have any tables with "isRoot": + # true) but the new schema does. In that situation the old database + # may contain a transaction that creates a record followed by a + # transaction that creates the first use of the record. Replaying that + # series of transactions against the new database schema (as "convert" + # does) would cause the record to be dropped by the first transaction, + # then the second transaction would cause a referential integrity + # failure (for a strong reference). + $ovsdb_tool -vANY:console:emer compact "$OVSDB_SERVER_DB" + + # Upgrade or downgrade schema. $ovsdb_tool -vANY:console:emer convert "$OVSDB_SERVER_DB" "$VSWITCHD_OVSDB_SCHEMA" fi -- 2.20.1