Implement database schema versioning.
authorBen Pfaff <blp@nicira.com>
Mon, 27 Dec 2010 22:26:47 +0000 (14:26 -0800)
committerBen Pfaff <blp@nicira.com>
Mon, 27 Dec 2010 22:26:47 +0000 (14:26 -0800)
As the database schema evolves, it might be useful to have an identifier
for the particular version in use.  This commit adds that feature.

17 files changed:
ovsdb/SPECS
ovsdb/ovsdb-client.1.in
ovsdb/ovsdb-client.c
ovsdb/ovsdb-tool.1.in
ovsdb/ovsdb-tool.c
ovsdb/ovsdb.c
ovsdb/ovsdb.h
python/ovs/db/schema.py
tests/idltest.ovsschema
tests/ovsdb-execution.at
tests/ovsdb-schema.at
tests/ovsdb-server.at
tests/ovsdb-tool.at
vswitchd/automake.mk
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
xenserver/etc_init.d_openvswitch

index 326293c..97f9882 100644 (file)
@@ -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.  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:
index 22b4e23..725211d 100644 (file)
@@ -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
index 6d19e5d..da6a6d6 100644 (file)
@@ -182,6 +182,9 @@ usage(void)
            "    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"
@@ -781,6 +784,14 @@ do_get_schema(int argc OVS_UNUSED, char *argv[])
     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[])
 {
@@ -1344,6 +1355,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 },
index b073f6d..069ab1a 100644 (file)
@@ -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
index 5eeb28f..130e737 100644 (file)
@@ -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",
@@ -239,6 +241,28 @@ do_convert(int argc OVS_UNUSED, char *argv[])
     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)
 {
@@ -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 },
index 4568376..b767d38 100644 (file)
 #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();
 
index a83412d..642f686 100644 (file)
@@ -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 *);
 
index 189b137..c12eda2 100644 (file)
@@ -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):
index 545242b..275a49e 100644 (file)
@@ -1,5 +1,6 @@
 {
-  "name": "idltest", 
+  "name": "idltest",
+  "version": "1.2.3",
   "tables": {
     "link1": {
       "columns": {
index 394fcba..a457b2a 100644 (file)
@@ -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",
index 008cc43..d129093 100644 (file)
@@ -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]])
index 52c1969..23125ab 100644 (file)
@@ -38,6 +38,17 @@ cat stdout >> output
 
 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
@@ -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"}}}
index c2ec881..664e616 100644 (file)
@@ -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
index 4c3c076..abcf762 100644 (file)
@@ -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
index 04b691a..a1917ee 100644 (file)
@@ -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}},
index 9f079e8..b3f0075 100644 (file)
        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
index 0a2fc78..9d7ad2d 100755 (executable)
@@ -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