[ovs-dev] [PATCH 2/2] Implement database schema versioning.

Ben Pfaff blp at nicira.com
Wed Dec 22 20:51:30 UTC 2010


As the database schema evolves, it might be useful to have an identifier
for the particular version in use.  This commit adds that feature.
---
 ovsdb/SPECS                      |   12 ++++++++++++
 ovsdb/ovsdb-client.1.in          |   15 +++++++++++++++
 ovsdb/ovsdb-client.c             |    9 +++++++++
 ovsdb/ovsdb-tool.1.in            |   29 +++++++++++++++++++++++++++++
 ovsdb/ovsdb-tool.c               |   26 ++++++++++++++++++++++++++
 ovsdb/ovsdb.c                    |   37 ++++++++++++++++++++++++++++++++-----
 ovsdb/ovsdb.h                    |    4 +++-
 python/ovs/db/schema.py          |   25 +++++++++++++++++++------
 tests/idltest.ovsschema          |    3 ++-
 tests/ovsdb-execution.at         |    3 ++-
 tests/ovsdb-schema.at            |   26 +++++++++++++++++++++++++-
 tests/ovsdb-server.at            |   13 ++++++++++++-
 tests/ovsdb-tool.at              |   20 +++++++++++++++++++-
 vswitchd/automake.mk             |   12 ++++++++++++
 vswitchd/vswitch.ovsschema       |    5 +++++
 vswitchd/vswitch.xml             |   21 +++++++++++++++++++++
 xenserver/etc_init.d_openvswitch |   11 ++++++++++-
 17 files changed, 253 insertions(+), 18 deletions(-)

diff --git a/ovsdb/SPECS b/ovsdb/SPECS
index 326293c..6d395be 100644
--- a/ovsdb/SPECS
+++ b/ovsdb/SPECS
@@ -33,6 +33,11 @@ values.  Additional notation is presented later.
     <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.
@@ -102,6 +107,7 @@ is represented by <database-schema>, as described below.
     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
@@ -109,6 +115,12 @@ is represented by <database-schema>, as described below.
     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.  This is
+    a recent addition to the schema format, so OVSDB permits it to be
+    absent.  This is deprecated and may be removed in the future.
+    Open vSwitch semantics for "version" are described in
+    ovs-vswitchd.conf.db(5).
+
 <table-schema>
 
     A JSON object with the following members:
diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in
index 22b4e23..725211d 100644
--- a/ovsdb/ovsdb-client.1.in
+++ b/ovsdb/ovsdb-client.1.in
@@ -19,6 +19,8 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
 .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]
@@ -61,6 +63,19 @@ be used for \fIdatabase\fR in the following commands.
 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
diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c
index 6d19e5d..f700216 100644
--- a/ovsdb/ovsdb-client.c
+++ b/ovsdb/ovsdb-client.c
@@ -782,6 +782,14 @@ do_get_schema(int argc OVS_UNUSED, char *argv[])
 }
 
 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[])
 {
     struct ovsdb_schema *schema;
@@ -1344,6 +1352,7 @@ do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 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 },
diff --git a/ovsdb/ovsdb-tool.1.in b/ovsdb/ovsdb-tool.1.in
index b073f6d..069ab1a 100644
--- a/ovsdb/ovsdb-tool.1.in
+++ b/ovsdb/ovsdb-tool.1.in
@@ -14,6 +14,15 @@ ovsdb\-tool \- Open vSwitch database management utility
 .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
@@ -63,6 +72,26 @@ ignored.  Columns that exist in \fIschema\fR but not in \fIdb\fR are
 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
diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c
index 5eeb28f..130e737 100644
--- a/ovsdb/ovsdb-tool.c
+++ b/ovsdb/ovsdb-tool.c
@@ -110,6 +110,8 @@ usage(void)
            "  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",
@@ -240,6 +242,28 @@ do_convert(int argc OVS_UNUSED, char *argv[])
 }
 
 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)
 {
     struct json *request, *result;
@@ -410,6 +434,8 @@ static const struct command all_commands[] = {
     { "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 },
diff --git a/ovsdb/ovsdb.c b/ovsdb/ovsdb.c
index 4568376..b767d38 100644
--- a/ovsdb/ovsdb.c
+++ b/ovsdb/ovsdb.c
@@ -26,12 +26,13 @@
 #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;
@@ -43,7 +44,7 @@ ovsdb_schema_clone(const struct ovsdb_schema *old)
     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;
 
@@ -52,7 +53,6 @@ ovsdb_schema_clone(const struct ovsdb_schema *old)
     return new;
 }
 
-
 void
 ovsdb_schema_destroy(struct ovsdb_schema *schema)
 {
@@ -67,6 +67,7 @@ ovsdb_schema_destroy(struct ovsdb_schema *schema)
     }
     shash_destroy(&schema->tables);
     free(schema->name);
+    free(schema->version);
     free(schema);
 }
 
@@ -116,26 +117,49 @@ ovsdb_schema_check_ref_table(const struct ovsdb_column *column,
     }
 }
 
+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;
 
@@ -190,6 +214,9 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema)
 
     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();
 
diff --git a/ovsdb/ovsdb.h b/ovsdb/ovsdb.h
index a83412d..642f686 100644
--- a/ovsdb/ovsdb.h
+++ b/ovsdb/ovsdb.h
@@ -29,10 +29,12 @@ struct uuid;
 /* 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 *);
 
diff --git a/python/ovs/db/schema.py b/python/ovs/db/schema.py
index 189b137..c12eda2 100644
--- a/python/ovs/db/schema.py
+++ b/python/ovs/db/schema.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import re
 import sys
 
 from ovs.db import error
@@ -21,8 +22,9 @@ from ovs.db import types
 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
@@ -36,9 +38,16 @@ class DbSchema(object):
     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('_'):
@@ -48,13 +57,16 @@ class DbSchema(object):
                 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
@@ -64,8 +76,8 @@ class DbSchema(object):
                               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
 
@@ -80,7 +92,8 @@ class IdlSchema(DbSchema):
         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):
diff --git a/tests/idltest.ovsschema b/tests/idltest.ovsschema
index 545242b..275a49e 100644
--- a/tests/idltest.ovsschema
+++ b/tests/idltest.ovsschema
@@ -1,5 +1,6 @@
 {
-  "name": "idltest", 
+  "name": "idltest",
+  "version": "1.2.3",
   "tables": {
     "link1": {
       "columns": {
diff --git a/tests/ovsdb-execution.at b/tests/ovsdb-execution.at
index 394fcba..a457b2a 100644
--- a/tests/ovsdb-execution.at
+++ b/tests/ovsdb-execution.at
@@ -6,7 +6,8 @@ m4_define([ORDINAL_SCHEMA],
        "ordinals": {
          "columns": {
            "number": {"type": "integer"},
-           "name": {"type": "string"}}}}}]])
+           "name": {"type": "string"}}}},
+     "version": "5.1.3"}]])
 
 m4_define([CONSTRAINT_SCHEMA],
   [[{"name": "constraints",
diff --git a/tests/ovsdb-schema.at b/tests/ovsdb-schema.at
index 008cc43..d129093 100644
--- a/tests/ovsdb-schema.at
+++ b/tests/ovsdb-schema.at
@@ -3,6 +3,7 @@ AT_BANNER([OVSDB -- schemas])
 OVSDB_CHECK_POSITIVE_CPY([schema with valid refTables],
   [[parse-schema \
       '{"name": "mydb",
+        "version": "4.2.1",
         "tables": {
           "a": {
             "columns": {
@@ -21,7 +22,19 @@ OVSDB_CHECK_POSITIVE_CPY([schema with valid refTables],
                   "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 \
@@ -45,3 +58,14 @@ OVSDB_CHECK_NEGATIVE_CPY([schema with invalid refTables],
                     "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]])
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index 52c1969..23125ab 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -38,6 +38,17 @@ cat stdout >> output
 
 EXECUTION_EXAMPLES
 
+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
@@ -181,7 +192,7 @@ AT_CHECK(
   [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"}}}
diff --git a/tests/ovsdb-tool.at b/tests/ovsdb-tool.at
index c2ec881..664e616 100644
--- a/tests/ovsdb-tool.at
+++ b/tests/ovsdb-tool.at
@@ -83,7 +83,7 @@ AT_CHECK(
   [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"}}}
@@ -270,3 +270,21 @@ _uuid                                name number
 <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
diff --git a/vswitchd/automake.mk b/vswitchd/automake.mk
index 4c3c076..abcf762 100644
--- a/vswitchd/automake.mk
+++ b/vswitchd/automake.mk
@@ -92,3 +92,15 @@ vswitchd/ovs-vswitchd.conf.db.5: \
 		$(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
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 04b691a..a1917ee 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,4 +1,6 @@
 {"name": "Open_vSwitch",
+ "version": "1.0.0",
+ "cksum": "514853437 13985",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -34,6 +36,9 @@
        "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}},
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index ececa18..2ed8dc4 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -204,6 +204,27 @@
 	reporting to human administrators.
       </p>
 
+      <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="ovs_version">
 	The Open vSwitch version number, e.g. <code>1.1.0pre2</code>.  The Open
 	vSwitch build number, if any, is not included.
       </column>
diff --git a/xenserver/etc_init.d_openvswitch b/xenserver/etc_init.d_openvswitch
index 0a2fc78..9d7ad2d 100755
--- a/xenserver/etc_init.d_openvswitch
+++ b/xenserver/etc_init.d_openvswitch
@@ -325,6 +325,7 @@ function start {
     # 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`
@@ -332,12 +333,20 @@ function start {
         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
-- 
1.7.1





More information about the dev mailing list