Implement initial Python bindings for Open vSwitch database.
[sliver-openvswitch.git] / python / ovs / db / schema.py
1 # Copyright (c) 2009, 2010 Nicira Networks
2 #
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:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 import sys
16
17 from ovs.db import error
18 import ovs.db.parser
19 from ovs.db import types
20
21 class DbSchema(object):
22     """Schema for an OVSDB database."""
23
24     def __init__(self, name, tables):
25         self.name = name
26         self.tables = tables
27
28         # Validate that all ref_tables refer to the names of tables
29         # that exist.
30         for table in self.tables.itervalues():
31             for column in table.columns.itervalues():
32                 self.__check_ref_table(column, column.type.key, "key")
33                 self.__check_ref_table(column, column.type.value, "value")
34
35     @staticmethod
36     def from_json(json):
37         parser = ovs.db.parser.Parser(json, "database schema")
38         name = parser.get("name", ['id'])
39         tablesJson = parser.get("tables", [dict])
40         parser.finish()
41
42         tables = {}
43         for tableName, tableJson in tablesJson.iteritems():
44             if tableName.startswith('_'):
45                 raise error.Error("names beginning with \"_\" are reserved",
46                                   json)
47             elif not ovs.db.parser.is_identifier(tableName):
48                 raise error.Error("name must be a valid id", json)
49             tables[tableName] = TableSchema.from_json(tableJson, tableName)
50
51         return DbSchema(name, tables)
52
53     def to_json(self):
54         tables = {}
55         for table in self.tables.itervalues():
56             tables[table.name] = table.to_json()
57         return {"name": self.name, "tables": tables}
58
59     def __check_ref_table(self, column, base, base_name):
60         if (base and base.type == types.UuidType and base.ref_table and
61             base.ref_table not in self.tables):
62             raise error.Error("column %s %s refers to undefined table %s"
63                               % (column.name, base_name, base.ref_table),
64                               tag="syntax error")
65
66 class IdlSchema(DbSchema):
67     def __init__(self, name, tables, idlPrefix, idlHeader):
68         DbSchema.__init__(self, name, tables)
69         self.idlPrefix = idlPrefix
70         self.idlHeader = idlHeader
71
72     @staticmethod
73     def from_json(json):
74         parser = ovs.db.parser.Parser(json, "IDL schema")
75         idlPrefix = parser.get("idlPrefix", [unicode])
76         idlHeader = parser.get("idlHeader", [unicode])
77
78         subjson = dict(json)
79         del subjson["idlPrefix"]
80         del subjson["idlHeader"]
81         schema = DbSchema.from_json(subjson)
82
83         return IdlSchema(schema.name, schema.tables, idlPrefix, idlHeader)
84
85 class TableSchema(object):
86     def __init__(self, name, columns, mutable=True, max_rows=sys.maxint):
87         self.name = name
88         self.columns = columns
89         self.mutable = mutable
90         self.max_rows = max_rows        
91
92     @staticmethod
93     def from_json(json, name):
94         parser = ovs.db.parser.Parser(json, "table schema for table %s" % name)
95         columnsJson = parser.get("columns", [dict])
96         mutable = parser.get_optional("mutable", [bool], True)
97         max_rows = parser.get_optional("maxRows", [int])
98         parser.finish()
99
100         if max_rows == None:
101             max_rows = sys.maxint
102         elif max_rows <= 0:
103             raise error.Error("maxRows must be at least 1", json)
104
105         if not columnsJson:
106             raise error.Error("table must have at least one column", json)
107
108         columns = {}
109         for columnName, columnJson in columnsJson.iteritems():
110             if columnName.startswith('_'):
111                 raise error.Error("names beginning with \"_\" are reserved",
112                                   json)
113             elif not ovs.db.parser.is_identifier(columnName):
114                 raise error.Error("name must be a valid id", json)
115             columns[columnName] = ColumnSchema.from_json(columnJson,
116                                                          columnName)
117
118         return TableSchema(name, columns, mutable, max_rows)
119
120     def to_json(self):
121         json = {}
122         if not self.mutable:
123             json["mutable"] = False
124
125         json["columns"] = columns = {}
126         for column in self.columns.itervalues():
127             if not column.name.startswith("_"):
128                 columns[column.name] = column.to_json()
129
130         if self.max_rows != sys.maxint:
131             json["maxRows"] = self.max_rows
132
133         return json
134
135 class ColumnSchema(object):
136     def __init__(self, name, mutable, persistent, type):
137         self.name = name
138         self.mutable = mutable
139         self.persistent = persistent
140         self.type = type
141
142     @staticmethod
143     def from_json(json, name):
144         parser = ovs.db.parser.Parser(json, "schema for column %s" % name)
145         mutable = parser.get_optional("mutable", [bool], True)
146         ephemeral = parser.get_optional("ephemeral", [bool], False)
147         type = types.Type.from_json(parser.get("type", [dict, unicode]))
148         parser.finish()
149
150         return ColumnSchema(name, mutable, not ephemeral, type)
151
152     def to_json(self):
153         json = {"type": self.type.to_json()}
154         if not self.mutable:
155             json["mutable"] = False
156         if not self.persistent:
157             json["ephemeral"] = True
158         return json
159