1 # Copyright (c) 2009, 2010, 2011 Nicira Networks
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
18 from ovs.db import error
20 from ovs.db import types
22 class DbSchema(object):
23 """Schema for an OVSDB database."""
25 def __init__(self, name, version, tables):
27 self.version = version
30 # Validate that all ref_tables refer to the names of tables
32 for table in self.tables.itervalues():
33 for column in table.columns.itervalues():
34 self.__check_ref_table(column, column.type.key, "key")
35 self.__check_ref_table(column, column.type.value, "value")
37 # "isRoot" was not part of the original schema definition. Before it
38 # was added, there was no support for garbage collection. So, for
39 # backward compatibility, if the root set is empty then assume that
40 # every table is in the root set.
41 if self.__root_set_size() == 0:
42 for table in self.tables.itervalues():
45 def __root_set_size(self):
46 """Returns the number of tables in the schema's root set."""
48 for table in self.tables.itervalues():
55 parser = ovs.db.parser.Parser(json, "database schema")
56 name = parser.get("name", ['id'])
57 version = parser.get_optional("version", [unicode])
58 parser.get_optional("cksum", [unicode])
59 tablesJson = parser.get("tables", [dict])
62 if (version is not None and
63 not re.match('[0-9]+\.[0-9]+\.[0-9]+$', version)):
64 raise error.Error("schema version \"%s\" not in format x.y.z"
68 for tableName, tableJson in tablesJson.iteritems():
69 if tableName.startswith('_'):
70 raise error.Error("names beginning with \"_\" are reserved",
72 elif not ovs.db.parser.is_identifier(tableName):
73 raise error.Error("name must be a valid id", json)
74 tables[tableName] = TableSchema.from_json(tableJson, tableName)
76 return DbSchema(name, version, tables)
79 # "isRoot" was not part of the original schema definition. Before it
80 # was added, there was no support for garbage collection. So, for
81 # backward compatibility, if every table is in the root set then do not
82 # output "isRoot" in table schemas.
83 default_is_root = self.__root_set_size() == len(self.tables)
86 for table in self.tables.itervalues():
87 tables[table.name] = table.to_json(default_is_root)
88 json = {"name": self.name, "tables": tables}
90 json["version"] = self.version
93 def __check_ref_table(self, column, base, base_name):
94 if (base and base.type == types.UuidType and base.ref_table and
95 base.ref_table not in self.tables):
96 raise error.Error("column %s %s refers to undefined table %s"
97 % (column.name, base_name, base.ref_table),
100 class IdlSchema(DbSchema):
101 def __init__(self, name, version, tables, idlPrefix, idlHeader):
102 DbSchema.__init__(self, name, version, tables)
103 self.idlPrefix = idlPrefix
104 self.idlHeader = idlHeader
108 parser = ovs.db.parser.Parser(json, "IDL schema")
109 idlPrefix = parser.get("idlPrefix", [unicode])
110 idlHeader = parser.get("idlHeader", [unicode])
113 del subjson["idlPrefix"]
114 del subjson["idlHeader"]
115 schema = DbSchema.from_json(subjson)
117 return IdlSchema(schema.name, schema.version, schema.tables,
118 idlPrefix, idlHeader)
120 class TableSchema(object):
121 def __init__(self, name, columns, mutable=True, max_rows=sys.maxint,
124 self.columns = columns
125 self.mutable = mutable
126 self.max_rows = max_rows
127 self.is_root = is_root
130 def from_json(json, name):
131 parser = ovs.db.parser.Parser(json, "table schema for table %s" % name)
132 columnsJson = parser.get("columns", [dict])
133 mutable = parser.get_optional("mutable", [bool], True)
134 max_rows = parser.get_optional("maxRows", [int])
135 is_root = parser.get_optional("isRoot", [bool], False)
139 max_rows = sys.maxint
141 raise error.Error("maxRows must be at least 1", json)
144 raise error.Error("table must have at least one column", json)
147 for columnName, columnJson in columnsJson.iteritems():
148 if columnName.startswith('_'):
149 raise error.Error("names beginning with \"_\" are reserved",
151 elif not ovs.db.parser.is_identifier(columnName):
152 raise error.Error("name must be a valid id", json)
153 columns[columnName] = ColumnSchema.from_json(columnJson,
156 return TableSchema(name, columns, mutable, max_rows, is_root)
158 def to_json(self, default_is_root=False):
159 """Returns this table schema serialized into JSON.
161 The "isRoot" member is included in the JSON only if its value would
162 differ from 'default_is_root'. Ordinarily 'default_is_root' should be
163 false, because ordinarily a table would be not be part of the root set
164 if its "isRoot" member is omitted. However, garbage collection was not
165 orginally included in OVSDB, so in older schemas that do not include
166 any "isRoot" members, every table is implicitly part of the root set.
167 To serialize such a schema in a way that can be read by older OVSDB
168 tools, specify 'default_is_root' as True.
172 json["mutable"] = False
173 if default_is_root != self.is_root:
174 json["isRoot"] = self.is_root
176 json["columns"] = columns = {}
177 for column in self.columns.itervalues():
178 if not column.name.startswith("_"):
179 columns[column.name] = column.to_json()
181 if self.max_rows != sys.maxint:
182 json["maxRows"] = self.max_rows
186 class ColumnSchema(object):
187 def __init__(self, name, mutable, persistent, type):
189 self.mutable = mutable
190 self.persistent = persistent
194 def from_json(json, name):
195 parser = ovs.db.parser.Parser(json, "schema for column %s" % name)
196 mutable = parser.get_optional("mutable", [bool], True)
197 ephemeral = parser.get_optional("ephemeral", [bool], False)
198 type = types.Type.from_json(parser.get("type", [dict, unicode]))
201 return ColumnSchema(name, mutable, not ephemeral, type)
204 json = {"type": self.type.to_json()}
206 json["mutable"] = False
207 if not self.persistent:
208 json["ephemeral"] = True