<id>s that begin with _ are reserved to the implementation and may
not be used by the user.
+<version>
+
+ A JSON string that contains a version number that matches
+ [0-9]+\.[0-9]+\.[0-9]+
+
<boolean>
A JSON true or false value.
A JSON object with the following members:
"name": <id> required
+ "version": <version> required
"tables": {<id>: <table-schema>, ...} required
The "name" identifies the database as a whole. It must be
operated on. The value of "tables" is a JSON object whose names
are table names and whose values are <table-schema>s.
+ The "version" reports the version of the database schema. Because
+ this is a recent addition to the schema format, OVSDB permits it
+ to be omitted, but future versions of OVSDB will require it to be
+ present. Open vSwitch semantics for "version" are described in
+ ovs-vswitchd.conf.db(5).
+
<table-schema>
A JSON object with the following members:
.br
\fBovsdb\-client \fR[\fIoptions\fR] \fBget\-schema\fI server database\fR
.br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBget\-schema\-version\fI server database\fR
+.br
\fBovsdb\-client \fR[\fIoptions\fR] \fBlist\-tables\fI server database\fR
.br
\fBovsdb\-client \fR[\fIoptions\fR] \fBlist\-columns\fI server database \fR[\fItable\fR]
Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and
prints it in JSON format.
.
+.IP "\fBget\-schema\-version\fI server database\fR"
+Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and
+prints its version number on stdout. A schema version number has the form
+\fIx\fB.\fIy\fB.\fIz\fR. See \fBovs\-vswitchd.conf.db\fR(5) for
+details.
+.IP
+Schema version numbers and Open vSwitch version numbers are
+independent.
+.IP
+If \fIdatabase\fR was created before schema versioning was introduced,
+then it will not have a version number and this command will print a
+blank line.
+.
.IP "\fBlist\-tables\fI server database\fR"
Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and
prints a table listing the name of each table
" list databases available on SERVER\n"
"\n get-schema SERVER DATABASE\n"
" retrieve schema for DATABASE from SERVER\n"
+ "\n get-schema-version SERVER DATABASE\n"
+ " retrieve schema for DATABASE from SERVER and report only its\n"
+ " version number on stdout\n"
"\n list-tables SERVER DATABASE\n"
" list tables for DATABASE on SERVER\n"
"\n list-columns SERVER DATABASE [TABLE]\n"
ovsdb_schema_destroy(schema);
}
+static void
+do_get_schema_version(int argc OVS_UNUSED, char *argv[])
+{
+ struct ovsdb_schema *schema = fetch_schema(argv[1], argv[2]);
+ puts(schema->version);
+ ovsdb_schema_destroy(schema);
+}
+
static void
do_list_tables(int argc OVS_UNUSED, char *argv[])
{
static const struct command all_commands[] = {
{ "list-dbs", 1, 1, do_list_dbs },
{ "get-schema", 2, 2, do_get_schema },
+ { "get-schema-version", 2, 2, do_get_schema_version },
{ "list-tables", 2, 2, do_list_tables },
{ "list-columns", 2, 3, do_list_columns },
{ "transact", 2, 2, do_transact },
.SH SYNOPSIS
\fBovsdb\-tool \fR[\fIoptions\fR] \fBcreate\fI db schema\fR
.br
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBcompact \fIdb \fR[\fItarget\fR]
+.br
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBconvert\fI db schema
+\fR[\fItarget\fR]
+.br
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBdb\-version\fI db\fR
+.br
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBschema\-version\fI schema\fR
+.br
\fBovsdb\-tool \fR[\fIoptions\fR] \fBquery\fI db transaction\fR
.br
\fBovsdb\-tool \fR[\fIoptions\fR] \fBtransact\fI db transaction\fR
set to their default values. All of \fIschema\fR's constraints apply
in full.
.
+.IP "\fBdb\-version\fI db\fR"
+Reads \fIdb\fR and prints the version number of the schema embedded
+within the database on stdout. A schema version number has the form
+\fIx\fB.\fIy\fB.\fIz\fR. See \fBovs\-vswitchd.conf.db\fR(5) for
+details.
+.IP
+Schema version numbers and Open vSwitch version numbers are
+independent.
+.IP
+If \fIdb\fR was created before schema versioning was introduced, then
+it will not have a version number and this command will print a blank
+line.
+.
+.IP "\fBschema\-version\fI schema\fR"
+Reads \fIschema\fR and prints the schema's version number on stdout.
+.IP
+If \fIschema\fR was created before versioning was introduced, then it
+does not have a version number and this command will print a blank
+line.
+.
.IP "\fBquery\fI db transaction\fR"
Opens \fIdb\fR, executes \fItransaction\fR on it, and prints the
results. The \fItransaction\fR must be a JSON array in the format of
" create DB SCHEMA create DB with the given SCHEMA\n"
" compact DB [DST] compact DB in-place (or to DST)\n"
" convert DB SCHEMA [DST] convert DB to SCHEMA (to DST)\n"
+ " db-version DB report version of schema used by DB\n"
+ " schema-version SCHEMA report SCHEMA's schema version\n"
" query DB TRNS execute read-only transaction on DB\n"
" transact DB TRNS execute read/write transaction on DB\n"
" show-log DB prints information about DB's log entries\n",
ovsdb_schema_destroy(new_schema);
}
+static void
+do_db_version(int argc OVS_UNUSED, char *argv[])
+{
+ const char *db_file_name = argv[1];
+ struct ovsdb *db;
+
+ check_ovsdb_error(ovsdb_file_open(db_file_name, true, &db, NULL));
+ puts(db->schema->version);
+ ovsdb_destroy(db);
+}
+
+static void
+do_schema_version(int argc OVS_UNUSED, char *argv[])
+{
+ const char *schema_file_name = argv[1];
+ struct ovsdb_schema *schema;
+
+ check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
+ puts(schema->version);
+ ovsdb_schema_destroy(schema);
+}
+
static void
transact(bool read_only, const char *db_file_name, const char *transaction)
{
{ "create", 2, 2, do_create },
{ "compact", 1, 2, do_compact },
{ "convert", 2, 3, do_convert },
+ { "db-version", 1, 1, do_db_version },
+ { "schema-version", 1, 1, do_schema_version },
{ "query", 2, 2, do_query },
{ "transact", 2, 2, do_transact },
{ "show-log", 1, 1, do_show_log },
#include "transaction.h"
struct ovsdb_schema *
-ovsdb_schema_create(const char *name)
+ovsdb_schema_create(const char *name, const char *version)
{
struct ovsdb_schema *schema;
schema = xzalloc(sizeof *schema);
schema->name = xstrdup(name);
+ schema->version = xstrdup(version);
shash_init(&schema->tables);
return schema;
struct ovsdb_schema *new;
struct shash_node *node;
- new = ovsdb_schema_create(old->name);
+ new = ovsdb_schema_create(old->name, old->version);
SHASH_FOR_EACH (node, &old->tables) {
const struct ovsdb_table_schema *ts = node->data;
return new;
}
-
void
ovsdb_schema_destroy(struct ovsdb_schema *schema)
{
}
shash_destroy(&schema->tables);
free(schema->name);
+ free(schema->version);
free(schema);
}
}
}
+static bool
+is_valid_version(const char *s)
+{
+ int n = -1;
+ sscanf(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n);
+ return n != -1 && s[n] == '\0';
+}
+
struct ovsdb_error *
ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
{
struct ovsdb_schema *schema;
- const struct json *name, *tables;
+ const struct json *name, *tables, *version_json;
struct ovsdb_error *error;
struct shash_node *node;
struct ovsdb_parser parser;
+ const char *version;
*schemap = NULL;
ovsdb_parser_init(&parser, json, "database schema");
name = ovsdb_parser_member(&parser, "name", OP_ID);
+ version_json = ovsdb_parser_member(&parser, "version",
+ OP_STRING | OP_OPTIONAL);
+ ovsdb_parser_member(&parser, "cksum", OP_STRING | OP_OPTIONAL);
tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
error = ovsdb_parser_finish(&parser);
if (error) {
return error;
}
- schema = ovsdb_schema_create(json_string(name));
+ if (version_json) {
+ version = json_string(version_json);
+ if (!is_valid_version(version)) {
+ return ovsdb_syntax_error(json, NULL, "schema version \"%s\" not "
+ "in format x.y.z", version);
+ }
+ } else {
+ /* Backward compatibility with old databases. */
+ version = "";
+ }
+
+ schema = ovsdb_schema_create(json_string(name), version);
SHASH_FOR_EACH (node, json_object(tables)) {
struct ovsdb_table_schema *table;
json = json_object_create();
json_object_put_string(json, "name", schema->name);
+ if (schema->version[0]) {
+ json_object_put_string(json, "version", schema->version);
+ }
tables = json_object_create();
/* Database schema. */
struct ovsdb_schema {
char *name;
+ char *version;
struct shash tables; /* Contains "struct ovsdb_table_schema *"s. */
};
-struct ovsdb_schema *ovsdb_schema_create(const char *name);
+struct ovsdb_schema *ovsdb_schema_create(const char *name,
+ const char *version);
struct ovsdb_schema *ovsdb_schema_clone(const struct ovsdb_schema *);
void ovsdb_schema_destroy(struct ovsdb_schema *);
# See the License for the specific language governing permissions and
# limitations under the License.
+import re
import sys
from ovs.db import error
class DbSchema(object):
"""Schema for an OVSDB database."""
- def __init__(self, name, tables):
+ def __init__(self, name, version, tables):
self.name = name
+ self.version = version
self.tables = tables
# Validate that all ref_tables refer to the names of tables
def from_json(json):
parser = ovs.db.parser.Parser(json, "database schema")
name = parser.get("name", ['id'])
+ version = parser.get_optional("version", [unicode])
+ parser.get_optional("cksum", [unicode])
tablesJson = parser.get("tables", [dict])
parser.finish()
+ if (version is not None and
+ not re.match('[0-9]+\.[0-9]+\.[0-9]+$', version)):
+ raise error.Error("schema version \"%s\" not in format x.y.z"
+ % version)
+
tables = {}
for tableName, tableJson in tablesJson.iteritems():
if tableName.startswith('_'):
raise error.Error("name must be a valid id", json)
tables[tableName] = TableSchema.from_json(tableJson, tableName)
- return DbSchema(name, tables)
+ return DbSchema(name, version, tables)
def to_json(self):
tables = {}
for table in self.tables.itervalues():
tables[table.name] = table.to_json()
- return {"name": self.name, "tables": tables}
+ json = {"name": self.name, "tables": tables}
+ if self.version:
+ json["version"] = self.version
+ return json
def __check_ref_table(self, column, base, base_name):
if (base and base.type == types.UuidType and base.ref_table and
tag="syntax error")
class IdlSchema(DbSchema):
- def __init__(self, name, tables, idlPrefix, idlHeader):
- DbSchema.__init__(self, name, tables)
+ def __init__(self, name, version, tables, idlPrefix, idlHeader):
+ DbSchema.__init__(self, name, version, tables)
self.idlPrefix = idlPrefix
self.idlHeader = idlHeader
del subjson["idlHeader"]
schema = DbSchema.from_json(subjson)
- return IdlSchema(schema.name, schema.tables, idlPrefix, idlHeader)
+ return IdlSchema(schema.name, schema.version, schema.tables,
+ idlPrefix, idlHeader)
class TableSchema(object):
def __init__(self, name, columns, mutable=True, max_rows=sys.maxint):
{
- "name": "idltest",
+ "name": "idltest",
+ "version": "1.2.3",
"tables": {
"link1": {
"columns": {
"ordinals": {
"columns": {
"number": {"type": "integer"},
- "name": {"type": "string"}}}}}]])
+ "name": {"type": "string"}}}},
+ "version": "5.1.3"}]])
m4_define([CONSTRAINT_SCHEMA],
[[{"name": "constraints",
OVSDB_CHECK_POSITIVE_CPY([schema with valid refTables],
[[parse-schema \
'{"name": "mydb",
+ "version": "4.2.1",
"tables": {
"a": {
"columns": {
"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"}}}}}}}]])
+ [[{"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"}}}}}},"version":"4.2.1"}]])
+
+dnl Schemas without version numbers are accepted for backward
+dnl compatibility, but this is a deprecated feature.
+OVSDB_CHECK_POSITIVE_CPY([schema without version number],
+ [[parse-schema \
+ '{"name": "mydb",
+ "tables": {
+ "x": {
+ "columns": {
+ "y": {
+ "type": "integer"}}}}}']],
+ [{"name":"mydb","tables":{"x":{"columns":{"y":{"type":"integer"}}}}}])
OVSDB_CHECK_NEGATIVE_CPY([schema with invalid refTables],
[[parse-schema \
"type": "uuid",
"refTable": "a"}}}}}}}']],
[[syntax error: column map key refers to undefined table c]])
+
+OVSDB_CHECK_NEGATIVE_CPY([schema with invalid version number],
+ [[parse-schema \
+ '{"name": "mydb",
+ "tables": {
+ "x": {
+ "columns": {
+ "y": {
+ "type": "integer"}}}},
+ "version": "xxx"}']],
+ [[schema version "xxx" not in format x.y.z]])
EXECUTION_EXAMPLES
\f
+AT_SETUP([ovsdb-client get-schema-version])
+AT_KEYWORDS([ovsdb server positive])
+AT_DATA([schema], [ORDINAL_SCHEMA
+])
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --pidfile=$PWD/pid --unixctl=$PWD/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-client get-schema-version unix:socket ordinals], [0], [5.1.3
+])
+OVSDB_SERVER_SHUTDOWN
+AT_CLEANUP
+
AT_SETUP([database multiplexing implementation])
AT_KEYWORDS([ovsdb server positive])
AT_DATA([schema], [ORDINAL_SCHEMA
[0], [stdout], [ignore], [test ! -e pid || kill `cat pid`])
dnl Check that all the crap is in fact in the database log.
AT_CHECK([[perl $srcdir/uuidfilt.pl db | grep -v ^OVSDB | sed 's/"_date":[0-9]*/"_date":0/' | test-json --multiple -]], [0],
- [[{"name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}}}}}
+ [[{"name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}}}},"version":"5.1.3"}
{"_comment":"add row for zero 0","_date":0,"ordinals":{"<0>":{"name":"zero"}}}
{"_comment":"delete row for 0","_date":0,"ordinals":{"<0>":null}}
{"_comment":"add back row for zero 0","_date":0,"ordinals":{"<1>":{"name":"zero"}}}
[0], [stdout], [ignore])
dnl Check that all the crap is in fact in the database log.
AT_CHECK([[perl $srcdir/uuidfilt.pl db | grep -v ^OVSDB | sed 's/"_date":[0-9]*/"_date":0/' | test-json --multiple -]], [0],
- [[{"name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}}}}}
+ [[{"name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}}}},"version":"5.1.3"}
{"_comment":"add row for zero 0","_date":0,"ordinals":{"<0>":{"name":"zero"}}}
{"_comment":"delete row for 0","_date":0,"ordinals":{"<0>":null}}
{"_comment":"add back row for zero 0","_date":0,"ordinals":{"<1>":{"name":"zero"}}}
<5> "" 5 @&t@
])
AT_CLEANUP
+
+AT_SETUP([ovsdb-tool schema-version])
+AT_KEYWORDS([ovsdb file positive])
+AT_DATA([schema], [ORDINAL_SCHEMA
+])
+AT_CHECK([ovsdb-tool schema-version schema], [0], [5.1.3
+])
+AT_CLEANUP
+
+AT_SETUP([ovsdb-tool db-version])
+AT_KEYWORDS([ovsdb file positive])
+AT_DATA([schema], [ORDINAL_SCHEMA
+])
+touch .db.~lock~
+AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore])
+AT_CHECK([ovsdb-tool db-version db], [0], [5.1.3
+])
+AT_CLEANUP
$(srcdir)/vswitchd/vswitch.ovsschema \
$(srcdir)/vswitchd/vswitch.xml > $@.tmp
mv $@.tmp $@
+
+# Version checking for vswitch.ovsschema.
+ALL_LOCAL += vswitchd/vswitch.ovsschema.stamp
+vswitchd/vswitch.ovsschema.stamp: vswitchd/vswitch.ovsschema
+ @sum=`sed '/cksum/d' $? | cksum`; \
+ expected=`sed -n 's/.*"cksum": "\(.*\)".*/\1/p' $?`; \
+ if test "X$$sum" = "X$$expected"; then \
+ touch $@; \
+ else \
+ ln=`sed -n '/"cksum":/=' $?`; \
+ echo "$?:$$ln: checksum \"$$sum\" does not match (you should probably update the version number and fix the checksum)"; \
+ fi
{"name": "Open_vSwitch",
+ "version": "1.0.0",
+ "cksum": "514853437 13985",
"tables": {
"Open_vSwitch": {
"columns": {
"ovs_version": {
"type": {"key": {"type": "string"},
"min": 0, "max": 1}},
+ "db_version": {
+ "type": {"key": {"type": "string"},
+ "min": 0, "max": 1}},
"system_type": {
"type": {"key": {"type": "string"},
"min": 0, "max": 1}},
also included, e.g. <code>1.1.0pre2+build4948</code>.
</column>
+ <column name="db_version">
+ <p>
+ The database schema version number in the form
+ <code><var>major</var>.<var>minor</var>.<var>tweak</var></code>,
+ e.g. <code>1.2.3</code>. Whenever the database schema is changed in
+ a non-backward compatible way (e.g. deleting a column or a table),
+ <var>major</var> is incremented. When the database schema is changed
+ in a backward compatible way (e.g. adding a new column),
+ <var>minor</var> is incremented. When the database schema is changed
+ cosmetically (e.g. reindenting its syntax), <var>tweak</var> is
+ incremented.
+ </p>
+
+ <p>
+ The schema version is part of the database schema, so it can also be
+ retrieved by fetching the schema using the Open vSwitch database
+ protocol.
+ </p>
+ </column>
+
<column name="system_type">
<p>
An identifier for the type of system on top of which Open vSwitch
# Allow GRE traffic.
iptables -I INPUT -p gre -j ACCEPT
+ schemaver=`$ovsdb_tool schema-version "$VSWITCHD_OVSDB_SCHEMA"`
if [ ! -e "$OVSDB_SERVER_DB" ]; then
warning "$OVSDB_SERVER_DB does not exist"
install -d -m 755 -o root -g root `dirname $OVSDB_SERVER_DB`
action "Creating empty database $OVSDB_SERVER_DB" true
$ovsdb_tool -vANY:console:emer create "$OVSDB_SERVER_DB" "$VSWITCHD_OVSDB_SCHEMA"
else
+ # If schema version changed, then back up the old version.
+ oldver=`$ovsdb_tool db-version "$OVSDB_SERVER_DB"`
+ if test "X$oldver" != "X$schemaver"; then
+ backup=$OVSDB_SERVER_DB.backup$oldver
+ action "Backing up $OVSDB_SERVER_DB in $backup before converting from schema version \"$oldver\" to \"$schemaver\"" true
+ cp "$OVSDB_SERVER_DB" "$backup"
+ fi
+
# Upgrade or downgrade schema and compact database.
$ovsdb_tool -vANY:console:emer convert "$OVSDB_SERVER_DB" "$VSWITCHD_OVSDB_SCHEMA"
fi
start_ovsdb_server
- $vsctl --no-wait --timeout=5 init
+ $vsctl --no-wait --timeout=5 init -- set Open_vSwitch . db-version="$schemaver"
if [ ! -e /var/run/openvswitch.booted ]; then
touch /var/run/openvswitch.booted
for bridge in $($vsctl list-br); do