]>
Commit | Line | Data |
---|---|---|
d879a707 BP |
1 | #! @PYTHON@ |
2 | ||
3 | import getopt | |
4 | import re | |
5 | import sha | |
6 | import sys | |
7 | ||
8 | sys.path.insert(0, "@abs_top_srcdir@/ovsdb") | |
9 | import simplejson as json | |
10 | ||
11 | argv0 = sys.argv[0] | |
12 | ||
13 | class Error(Exception): | |
14 | def __init__(self, msg): | |
15 | Exception.__init__(self) | |
16 | self.msg = msg | |
17 | ||
18 | def getMember(json, name, validTypes, description, default=None): | |
19 | if name in json: | |
20 | member = json[name] | |
21 | if type(member) not in validTypes: | |
22 | raise Error("%s: type mismatch for '%s' member" | |
23 | % (description, name)) | |
24 | return member | |
25 | return default | |
26 | ||
27 | def mustGetMember(json, name, expectedType, description): | |
28 | member = getMember(json, name, expectedType, description) | |
29 | if member == None: | |
30 | raise Error("%s: missing '%s' member" % (description, name)) | |
31 | return member | |
32 | ||
33 | class DbSchema: | |
34 | def __init__(self, name, comment, tables): | |
35 | self.name = name | |
36 | self.comment = comment | |
37 | self.tables = tables | |
38 | ||
39 | @staticmethod | |
40 | def fromJson(json): | |
41 | name = mustGetMember(json, 'name', [unicode], 'database') | |
42 | comment = getMember(json, 'comment', [unicode], 'database') | |
43 | tablesJson = mustGetMember(json, 'tables', [dict], 'database') | |
44 | tables = {} | |
45 | for name, json in tablesJson.iteritems(): | |
46 | tables[name] = TableSchema.fromJson(json, "%s table" % name) | |
47 | return DbSchema(name, comment, tables) | |
48 | ||
49 | def toJson(self): | |
50 | d = {"name": self.name, | |
51 | "tables": {}} | |
52 | for name, table in self.tables.iteritems(): | |
53 | d["tables"][name] = table.toJson() | |
54 | if self.comment != None: | |
55 | d["comment"] = self.comment | |
56 | return d | |
57 | ||
58 | class TableSchema: | |
59 | def __init__(self, comment, columns): | |
60 | self.comment = comment | |
61 | self.columns = columns | |
62 | ||
63 | @staticmethod | |
64 | def fromJson(json, description): | |
65 | comment = getMember(json, 'comment', [unicode], description) | |
66 | columnsJson = mustGetMember(json, 'columns', [dict], description) | |
67 | columns = {} | |
68 | for name, json in columnsJson.iteritems(): | |
69 | columns[name] = ColumnSchema.fromJson( | |
70 | json, "column %s in %s" % (name, description)) | |
71 | return TableSchema(comment, columns) | |
72 | ||
73 | def toJson(self): | |
74 | d = {"columns": {}} | |
75 | for name, column in self.columns.iteritems(): | |
76 | d["columns"][name] = column.toJson() | |
77 | if self.comment != None: | |
78 | d["comment"] = self.comment | |
79 | return d | |
80 | ||
81 | class ColumnSchema: | |
82 | def __init__(self, comment, type, persistent): | |
83 | self.comment = comment | |
84 | self.type = type | |
85 | self.persistent = persistent | |
86 | ||
87 | @staticmethod | |
88 | def fromJson(json, description): | |
89 | comment = getMember(json, 'comment', [unicode], description) | |
90 | type = Type.fromJson(mustGetMember(json, 'type', [dict, unicode], | |
91 | description), | |
92 | 'type of %s' % description) | |
93 | ephemeral = getMember(json, 'ephemeral', [True,False], description) | |
94 | persistent = ephemeral != True | |
95 | return ColumnSchema(comment, type, persistent) | |
96 | ||
97 | def toJson(self): | |
98 | d = {"type": self.type.toJson()} | |
99 | if self.persistent == False: | |
100 | d["ephemeral"] = True | |
101 | if self.comment != None: | |
102 | d["comment"] = self.comment | |
103 | return d | |
104 | ||
105 | class Type: | |
106 | def __init__(self, key, keyRefTable=None, value=None, valueRefTable=None, | |
107 | min=1, max=1): | |
108 | self.key = key | |
109 | self.keyRefTable = keyRefTable | |
110 | self.value = value | |
111 | self.valueRefTable = valueRefTable | |
112 | self.min = min | |
113 | self.max = max | |
114 | ||
115 | @staticmethod | |
116 | def fromJson(json, description): | |
117 | if type(json) == unicode: | |
118 | return Type(json) | |
119 | else: | |
120 | key = mustGetMember(json, 'key', [unicode], description) | |
121 | keyRefTable = getMember(json, 'keyRefTable', [unicode], description) | |
122 | value = getMember(json, 'value', [unicode], description) | |
123 | valueRefTable = getMember(json, 'valueRefTable', [unicode], description) | |
124 | min = getMember(json, 'min', [int], description, 1) | |
125 | max = getMember(json, 'max', [int, unicode], description, 1) | |
126 | return Type(key, keyRefTable, value, valueRefTable, min, max) | |
127 | ||
128 | def toJson(self): | |
129 | if self.value == None and self.min == 1 and self.max == 1: | |
130 | return self.key | |
131 | else: | |
132 | d = {"key": self.key} | |
133 | if self.value != None: | |
134 | d["value"] = self.value | |
135 | if self.min != 1: | |
136 | d["min"] = self.min | |
137 | if self.max != 1: | |
138 | d["max"] = self.max | |
139 | return d | |
140 | ||
141 | def parseSchema(filename): | |
142 | file = open(filename, "r") | |
143 | s = "" | |
144 | for line in file: | |
145 | if not line.startswith('//'): | |
146 | s += line | |
147 | return DbSchema.fromJson(json.loads(s)) | |
148 | ||
149 | def cBaseType(prefix, type, refTable=None): | |
150 | if type == 'uuid' and refTable: | |
151 | return "struct %s%s *" % (prefix, refTable.lower()) | |
152 | else: | |
153 | return {'integer': 'int64_t ', | |
154 | 'real': 'double ', | |
155 | 'uuid': 'struct uuid ', | |
156 | 'boolean': 'bool ', | |
157 | 'string': 'char *'}[type] | |
158 | ||
159 | def printCIDLHeader(schema): | |
160 | prefix = 'ovsrec_' | |
161 | print '''\ | |
162 | /* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */ | |
163 | ||
164 | #ifndef %(prefix)sIDL_HEADER | |
165 | #define %(prefix)sIDL_HEADER 1 | |
166 | ||
167 | #include <stdbool.h> | |
168 | #include <stddef.h> | |
169 | #include <stdint.h> | |
170 | #include "uuid.h"''' % {'prefix': prefix.upper()} | |
171 | for tableName, table in schema.tables.iteritems(): | |
172 | ||
173 | if table.comment != None: | |
174 | print "/* %s table (%s). */" % (tableName, table.comment) | |
175 | else: | |
176 | print "/* %s table. */" % (tableName) | |
177 | print "struct %s%s {" % (prefix, tableName.lower()) | |
178 | print "\t/* Columns automatically included in every table. */" | |
179 | print "\tstruct uuid uuid_;" | |
180 | print "\tstruct uuid version_;" | |
181 | for columnName, column in table.columns.iteritems(): | |
182 | print "\n\t/* %s column. */" % columnName | |
183 | type = column.type | |
184 | if type.min == 1 and type.max == 1: | |
185 | singleton = True | |
186 | pointer = '' | |
187 | else: | |
188 | singleton = False | |
189 | pointer = '*' | |
190 | if type.value: | |
191 | print "\tkey_%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName) | |
192 | print "\tvalue_%s%s%s;" % (cBaseType(prefix, type.value, type.valueRefTable), pointer, columnName) | |
193 | else: | |
194 | print "\t%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName) | |
195 | if not singleton: | |
196 | print "\tsize_t n_%s;" % columnName | |
197 | print "};" | |
198 | ||
199 | print "#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()} | |
200 | ||
201 | def ovsdb_escape(string): | |
202 | def escape(match): | |
203 | c = match.group(0) | |
204 | if c == '\0': | |
205 | raise Error("strings may not contain null bytes") | |
206 | elif c == '\\': | |
207 | return '\\\\' | |
208 | elif c == '\n': | |
209 | return '\\n' | |
210 | elif c == '\r': | |
211 | return '\\r' | |
212 | elif c == '\t': | |
213 | return '\\t' | |
214 | elif c == '\b': | |
215 | return '\\b' | |
216 | elif c == '\a': | |
217 | return '\\a' | |
218 | else: | |
219 | return '\\x%02x' % ord(c) | |
220 | return re.sub(r'["\\\000-\037]', escape, string) | |
221 | ||
222 | def printOVSDBSchema(schema): | |
223 | json.dump(schema.toJson(), sys.stdout, sort_keys=True, indent=2) | |
224 | ||
225 | def usage(): | |
226 | print """\ | |
227 | %(argv0)s: ovsdb schema compiler | |
228 | usage: %(argv0)s [OPTIONS] ACTION SCHEMA | |
229 | where SCHEMA is the ovsdb schema to read (in JSON format). | |
230 | ||
231 | One of the following actions must specified: | |
232 | validate validate schema without taking any other action | |
233 | c-idl-header print C header file for IDL | |
234 | c-idl-source print C source file for IDL implementation | |
235 | ovsdb-schema print ovsdb parseable schema | |
236 | ||
237 | The following options are also available: | |
238 | -h, --help display this help message | |
239 | -V, --version display version information\ | |
240 | """ % {'argv0': argv0} | |
241 | sys.exit(0) | |
242 | ||
243 | if __name__ == "__main__": | |
244 | try: | |
245 | try: | |
246 | options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', | |
247 | ['help', | |
248 | 'version']) | |
249 | except getopt.GetoptError, geo: | |
250 | sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) | |
251 | sys.exit(1) | |
252 | ||
253 | optKeys = [key for key, value in options] | |
254 | if '-h' in optKeys or '--help' in optKeys: | |
255 | usage() | |
256 | elif '-V' in optKeys or '--version' in optKeys: | |
257 | print "ovsdb-idlc (Open vSwitch) @VERSION@" | |
258 | sys.exit(0) | |
259 | ||
260 | if len(args) != 2: | |
261 | sys.stderr.write("%s: exactly two non-option arguments are " | |
262 | "required (use --help for help)\n" % argv0) | |
263 | sys.exit(1) | |
264 | ||
265 | action, inputFile = args | |
266 | schema = parseSchema(inputFile) | |
267 | if action == 'validate': | |
268 | pass | |
269 | elif action == 'ovsdb-schema': | |
270 | printOVSDBSchema(schema) | |
271 | elif action == 'c-idl-header': | |
272 | printCIDLHeader(schema) | |
273 | elif action == 'c-idl-source': | |
274 | printCIDLSource(schema) | |
275 | else: | |
276 | sys.stderr.write( | |
277 | "%s: unknown action '%s' (use --help for help)\n" % | |
278 | (argv0, action)) | |
279 | sys.exit(1) | |
280 | except Error, e: | |
281 | sys.stderr.write("%s: %s\n" % (argv0, e.msg)) | |
282 | sys.exit(1) | |
283 | ||
284 | # Local variables: | |
285 | # mode: python | |
286 | # End: |