Add documentation for the vswitch database schema.
[openvswitch] / ovsdb / ovsdb-idlc.in
1 #! @PYTHON@
2
3 import getopt
4 import os
5 import re
6 import sys
7
8 sys.path.insert(0, "@abs_top_srcdir@/ovsdb")
9 import simplejson as json
10
11 from OVSDB import *
12
13 argv0 = sys.argv[0]
14
15 class Datum:
16     def __init__(self, type, values):
17         self.type = type
18         self.values = values
19
20     @staticmethod
21     def fromJson(type_, json):
22         if not type_.value:
23             if len(json) == 2 and json[0] == "set":
24                 values = []
25                 for atomJson in json[1]:
26                     values += [Atom.fromJson(type_.key, atomJson)]
27             else:
28                 values = [Atom.fromJson(type_.key, json)]
29         else:
30             if len(json) != 2 or json[0] != "map":
31                 raise Error("%s is not valid JSON for a map" % json)
32             values = []
33             for pairJson in json[1]:
34                 values += [(Atom.fromJson(type_.key, pairJson[0]),
35                             Atom.fromJson(type_.value, pairJson[1]))]
36         return Datum(type_, values)
37
38     def cInitDatum(self, var):
39         if len(self.values) == 0:
40             return ["ovsdb_datum_init_empty(%s);" % var]
41
42         s = ["%s->n = %d;" % (var, len(self.values))]
43         s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);"
44               % (var, len(self.values), var)]
45
46         for i in range(len(self.values)):
47             key = self.values[i]
48             if self.type.value:
49                 key = key[0]
50             s += key.cInitAtom("%s->keys[%d]" % (var, i))
51         
52         if self.type.value:
53             s += ["%s->values = xmalloc(%d * sizeof *%s->values);"
54                   % (var, len(self.values), var)]
55             for i in range(len(self.values)):
56                 value = self.values[i][1]
57                 s += key.cInitAtom("%s->values[%d]" % (var, i))
58         else:
59             s += ["%s->values = NULL;" % var]
60
61         if len(self.values) > 1:
62             s += ["ovsdb_datum_sort_assert(%s, OVSDB_TYPE_%s);"
63                   % (var, self.type.key.upper())]
64
65         return s
66
67 def parseSchema(filename):
68     return IdlSchema.fromJson(json.load(open(filename, "r")))
69
70 def annotateSchema(schemaFile, annotationFile):
71     schemaJson = json.load(open(schemaFile, "r"))
72     execfile(annotationFile, globals(), {"s": schemaJson})
73     json.dump(schemaJson, sys.stdout)
74
75 def constify(cType, const):
76     if (const
77         and cType.endswith('*') and not cType.endswith('**')
78         and (cType.startswith('struct uuid') or cType.startswith('char'))):
79         return 'const %s' % cType
80     else:
81         return cType
82
83 def cMembers(prefix, columnName, column, const):
84     type = column.type
85     if type.min == 1 and type.max == 1:
86         singleton = True
87         pointer = ''
88     else:
89         singleton = False
90         if type.isOptionalPointer():
91             pointer = ''
92         else:
93             pointer = '*'
94
95     if type.value:
96         key = {'name': "key_%s" % columnName,
97                'type': constify(type.key.toCType(prefix) + pointer, const),
98                'comment': ''}
99         value = {'name': "value_%s" % columnName,
100                  'type': constify(type.value.toCType(prefix) + pointer, const),
101                  'comment': ''}
102         members = [key, value]
103     else:
104         m = {'name': columnName,
105              'type': constify(type.key.toCType(prefix) + pointer, const),
106              'comment': type.cDeclComment()}
107         members = [m]
108
109     if not singleton and not type.isOptionalPointer():
110         members.append({'name': 'n_%s' % columnName,
111                         'type': 'size_t ',
112                         'comment': ''})
113     return members
114
115 def printCIDLHeader(schemaFile):
116     schema = parseSchema(schemaFile)
117     prefix = schema.idlPrefix
118     print '''\
119 /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
120
121 #ifndef %(prefix)sIDL_HEADER
122 #define %(prefix)sIDL_HEADER 1
123
124 #include <stdbool.h>
125 #include <stddef.h>
126 #include <stdint.h>
127 #include "ovsdb-idl-provider.h"
128 #include "uuid.h"''' % {'prefix': prefix.upper()}
129
130     for tableName, table in sorted(schema.tables.iteritems()):
131         structName = "%s%s" % (prefix, tableName.lower())
132
133         print "\f"
134         print "/* %s table. */" % tableName
135         print "struct %s {" % structName
136         print "\tstruct ovsdb_idl_row header_;"
137         for columnName, column in sorted(table.columns.iteritems()):
138             print "\n\t/* %s column. */" % columnName
139             for member in cMembers(prefix, columnName, column, False):
140                 print "\t%(type)s%(name)s;%(comment)s" % member
141         print "};"
142
143         # Column indexes.
144         printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper())
145                    for columnName in sorted(table.columns)]
146                   + ["%s_N_COLUMNS" % structName.upper()])
147
148         print
149         for columnName in table.columns:
150             print "#define %(s)s_col_%(c)s (%(s)s_columns[%(S)s_COL_%(C)s])" % {
151                 's': structName,
152                 'S': structName.upper(),
153                 'c': columnName,
154                 'C': columnName.upper()}
155
156         print "\nextern struct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (structName, structName.upper())
157
158         print '''
159 const struct %(s)s *%(s)s_first(const struct ovsdb_idl *);
160 const struct %(s)s *%(s)s_next(const struct %(s)s *);
161 #define %(S)s_FOR_EACH(ROW, IDL) for ((ROW) = %(s)s_first(IDL); (ROW); (ROW) = %(s)s_next(ROW))
162
163 void %(s)s_delete(const struct %(s)s *);
164 struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
165 ''' % {'s': structName, 'S': structName.upper()}
166
167         for columnName, column in sorted(table.columns.iteritems()):
168             print 'void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': structName, 'c': columnName}
169
170         print
171         for columnName, column in sorted(table.columns.iteritems()):
172
173             print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName},
174             args = ['%(type)s%(name)s' % member for member
175                     in cMembers(prefix, columnName, column, True)]
176             print '%s);' % ', '.join(args)
177
178     # Table indexes.
179     printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()])
180     print
181     for tableName in schema.tables:
182         print "#define %(p)stable_%(t)s (%(p)stable_classes[%(P)sTABLE_%(T)s])" % {
183             'p': prefix,
184             'P': prefix.upper(),
185             't': tableName.lower(),
186             'T': tableName.upper()}
187     print "\nextern struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper())
188
189     print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix
190     print "\nvoid %sinit(void);" % prefix
191     print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
192
193 def printEnum(members):
194     if len(members) == 0:
195         return
196
197     print "\nenum {";
198     for member in members[:-1]:
199         print "    %s," % member
200     print "    %s" % members[-1]
201     print "};"
202
203 def printCIDLSource(schemaFile):
204     schema = parseSchema(schemaFile)
205     prefix = schema.idlPrefix
206     print '''\
207 /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
208
209 #include <config.h>
210 #include %s
211 #include <assert.h>
212 #include <limits.h>
213 #include "ovsdb-data.h"
214 #include "ovsdb-error.h"
215
216 static bool inited;
217 ''' % schema.idlHeader
218
219     # Cast functions.
220     for tableName, table in sorted(schema.tables.iteritems()):
221         structName = "%s%s" % (prefix, tableName.lower())
222         print '''
223 static struct %(s)s *
224 %(s)s_cast(const struct ovsdb_idl_row *row)
225 {
226     return row ? CONTAINER_OF(row, struct %(s)s, header_) : NULL;
227 }\
228 ''' % {'s': structName}
229
230
231     for tableName, table in sorted(schema.tables.iteritems()):
232         structName = "%s%s" % (prefix, tableName.lower())
233         print "\f"
234         if table.comment != None:
235             print "/* %s table (%s). */" % (tableName, table.comment)
236         else:
237             print "/* %s table. */" % (tableName)
238
239         # Parse functions.
240         for columnName, column in sorted(table.columns.iteritems()):
241             print '''
242 static void
243 %(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum)
244 {
245     struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
246                                                 'c': columnName}
247
248             type = column.type
249             if type.value:
250                 keyVar = "row->key_%s" % columnName
251                 valueVar = "row->value_%s" % columnName
252             else:
253                 keyVar = "row->%s" % columnName
254                 valueVar = None
255
256             if (type.min == 1 and type.max == 1) or type.isOptionalPointer():
257                 print
258                 print "    assert(inited);"
259                 print "    if (datum->n >= 1) {"
260                 if not type.key.refTable:
261                     print "        %s = datum->keys[0].%s;" % (keyVar, type.key.type)
262                 else:
263                     print "        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
264
265                 if valueVar:
266                     if type.value.refTable:
267                         print "        %s = datum->values[0].%s;" % (valueVar, type.value.type)
268                     else:
269                         print "        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
270                 print "    } else {"
271                 print "        %s" % type.key.initCDefault(keyVar, type.min == 0)
272                 if valueVar:
273                     print "        %s" % type.value.initCDefault(valueVar, type.min == 0)
274                 print "    }"
275             else:
276                 if type.max != 'unlimited':
277                     print "    size_t n = MIN(%d, datum->n);" % type.max
278                     nMax = "n"
279                 else:
280                     nMax = "datum->n"
281                 print "    size_t i;"
282                 print
283                 print "    assert(inited);"
284                 print "    %s = NULL;" % keyVar
285                 if valueVar:
286                     print "    %s = NULL;" % valueVar
287                 print "    row->n_%s = 0;" % columnName
288                 print "    for (i = 0; i < %s; i++) {" % nMax
289                 refs = []
290                 if type.key.refTable:
291                     print "        struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.key.refTable.lower(), prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
292                     keySrc = "keyRow"
293                     refs.append('keyRow')
294                 else:
295                     keySrc = "datum->keys[i].%s" % type.key.type
296                 if type.value and type.value.refTable:
297                     print "        struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.value.refTable.lower(), prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
298                     valueSrc = "valueRow"
299                     refs.append('valueRow')
300                 elif valueVar:
301                     valueSrc = "datum->values[i].%s" % type.value.type
302                 if refs:
303                     print "        if (%s) {" % ' && '.join(refs)
304                     indent = "            "
305                 else:
306                     indent = "        "
307                 print "%sif (!row->n_%s) {" % (indent, columnName)
308                 print "%s    %s = xmalloc(%s * sizeof *%s);" % (indent, keyVar, nMax, keyVar)
309                 if valueVar:
310                     print "%s    %s = xmalloc(%s * sizeof %s);" % (indent, valueVar, nMax, valueVar)
311                 print "%s}" % indent
312                 print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc)
313                 if valueVar:
314                     print "%s%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc)
315                 print "%srow->n_%s++;" % (indent, columnName)
316                 if refs:
317                     print "        }"
318                 print "    }"
319             print "}"
320
321         # Unparse functions.
322         for columnName, column in sorted(table.columns.iteritems()):
323             type = column.type
324             if (type.min != 1 or type.max != 1) and not type.isOptionalPointer():
325                 print '''
326 static void
327 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
328 {
329     struct %(s)s *row = %(s)s_cast(row_);
330
331     assert(inited);''' % {'s': structName, 'c': columnName}
332                 if type.value:
333                     keyVar = "row->key_%s" % columnName
334                     valueVar = "row->value_%s" % columnName
335                 else:
336                     keyVar = "row->%s" % columnName
337                     valueVar = None
338                 print "    free(%s);" % keyVar
339                 if valueVar:
340                     print "    free(%s);" % valueVar
341                 print '}'
342             else:
343                 print '''
344 static void
345 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row OVS_UNUSED)
346 {
347     /* Nothing to do. */
348 }''' % {'s': structName, 'c': columnName}
349  
350         # First, next functions.
351         print '''
352 const struct %(s)s *
353 %(s)s_first(const struct ovsdb_idl *idl)
354 {
355     return %(s)s_cast(ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
356 }
357
358 const struct %(s)s *
359 %(s)s_next(const struct %(s)s *row)
360 {
361     return %(s)s_cast(ovsdb_idl_next_row(&row->header_));
362 }''' % {'s': structName,
363         'p': prefix,
364         'P': prefix.upper(),
365         'T': tableName.upper()}
366
367         print '''
368 void
369 %(s)s_delete(const struct %(s)s *row)
370 {
371     ovsdb_idl_txn_delete(&row->header_);
372 }
373
374 struct %(s)s *
375 %(s)s_insert(struct ovsdb_idl_txn *txn)
376 {
377     return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
378 }
379 ''' % {'s': structName,
380        'p': prefix,
381        'P': prefix.upper(),
382        'T': tableName.upper()}
383
384         # Verify functions.
385         for columnName, column in sorted(table.columns.iteritems()):
386             print '''
387 void
388 %(s)s_verify_%(c)s(const struct %(s)s *row)
389 {
390     assert(inited);
391     ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]);
392 }''' % {'s': structName,
393         'S': structName.upper(),
394         'c': columnName,
395         'C': columnName.upper()}
396
397         # Set functions.
398         for columnName, column in sorted(table.columns.iteritems()):
399             type = column.type
400             print '\nvoid'
401             members = cMembers(prefix, columnName, column, True)
402             keyVar = members[0]['name']
403             nVar = None
404             valueVar = None
405             if type.value:
406                 valueVar = members[1]['name']
407                 if len(members) > 2:
408                     nVar = members[2]['name']
409             else:
410                 if len(members) > 1:
411                     nVar = members[1]['name']
412             print '%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)' % \
413                 {'s': structName, 'c': columnName,
414                  'args': ', '.join(['%(type)s%(name)s' % m for m in members])}
415             print "{"
416             print "    struct ovsdb_datum datum;"
417             if type.min == 1 and type.max == 1:
418                 print
419                 print "    assert(inited);"
420                 print "    datum.n = 1;"
421                 print "    datum.keys = xmalloc(sizeof *datum.keys);"
422                 print "    " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
423                 if type.value:
424                     print "    datum.values = xmalloc(sizeof *datum.values);"
425                     print "    "+ type.value.copyCValue("datum.values[0].%s" % type.value.type, valueVar)
426                 else:
427                     print "    datum.values = NULL;"
428             elif type.isOptionalPointer():
429                 print
430                 print "    assert(inited);"
431                 print "    if (%s) {" % keyVar
432                 print "        datum.n = 1;"
433                 print "        datum.keys = xmalloc(sizeof *datum.keys);"
434                 print "        " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
435                 print "    } else {"
436                 print "        datum.n = 0;"
437                 print "        datum.keys = NULL;"
438                 print "    }"
439                 print "    datum.values = NULL;"
440             else:
441                 print "    size_t i;"
442                 print
443                 print "    assert(inited);"
444                 print "    datum.n = %s;" % nVar
445                 print "    datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar
446                 if type.value:
447                     print "    datum.values = xmalloc(%s * sizeof *datum.values);" % nVar
448                 else:
449                     print "    datum.values = NULL;"
450                 print "    for (i = 0; i < %s; i++) {" % nVar
451                 print "        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type, "%s[i]" % keyVar)
452                 if type.value:
453                     print "        " + type.value.copyCValue("datum.values[i].%s" % type.value.type, "%s[i]" % valueVar)
454                 print "    }"
455             print "    ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
456                 % {'s': structName,
457                    'S': structName.upper(),
458                    'C': columnName.upper()}
459             print "}"
460
461         # Table columns.
462         print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
463             structName, structName.upper())
464         print """
465 static void\n%s_columns_init(void)
466 {
467     struct ovsdb_idl_column *c;\
468 """ % structName
469         for columnName, column in sorted(table.columns.iteritems()):
470             cs = "%s_col_%s" % (structName, columnName)
471             d = {'cs': cs, 'c': columnName, 's': structName}
472             print
473             print "    /* Initialize %(cs)s. */" % d
474             print "    c = &%(cs)s;" % d
475             print "    c->name = \"%(c)s\";" % d
476             print column.type.cInitType("    ", "c->type")
477             print "    c->parse = %(s)s_parse_%(c)s;" % d
478             print "    c->unparse = %(s)s_unparse_%(c)s;" % d
479         print "}"
480
481     # Table classes.
482     print "\f"
483     print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
484     for tableName, table in sorted(schema.tables.iteritems()):
485         structName = "%s%s" % (prefix, tableName.lower())
486         print "    {\"%s\"," % tableName
487         print "     %s_columns, ARRAY_SIZE(%s_columns)," % (
488             structName, structName)
489         print "     sizeof(struct %s)}," % structName
490     print "};"
491
492     # IDL class.
493     print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix
494     print "    \"%s\", %stable_classes, ARRAY_SIZE(%stable_classes)" % (
495         schema.name, prefix, prefix)
496     print "};"
497
498     # global init function
499     print """
500 void
501 %sinit(void)
502 {
503     if (inited) {
504         return;
505     }
506     inited = true;
507 """ % prefix
508     for tableName, table in sorted(schema.tables.iteritems()):
509         structName = "%s%s" % (prefix, tableName.lower())
510         print "    %s_columns_init();" % structName
511     print "}"
512
513 def ovsdb_escape(string):
514     def escape(match):
515         c = match.group(0)
516         if c == '\0':
517             raise Error("strings may not contain null bytes")
518         elif c == '\\':
519             return '\\\\'
520         elif c == '\n':
521             return '\\n'
522         elif c == '\r':
523             return '\\r'
524         elif c == '\t':
525             return '\\t'
526         elif c == '\b':
527             return '\\b'
528         elif c == '\a':
529             return '\\a'
530         else:
531             return '\\x%02x' % ord(c)
532     return re.sub(r'["\\\000-\037]', escape, string)
533
534
535
536 def usage():
537     print """\
538 %(argv0)s: ovsdb schema compiler
539 usage: %(argv0)s [OPTIONS] COMMAND ARG...
540
541 The following commands are supported:
542   annotate SCHEMA ANNOTATIONS print SCHEMA combined with ANNOTATIONS
543   c-idl-header IDL            print C header file for IDL
544   c-idl-source IDL            print C source file for IDL implementation
545   nroff IDL                   print schema documentation in nroff format
546
547 The following options are also available:
548   -h, --help                  display this help message
549   -V, --version               display version information\
550 """ % {'argv0': argv0}
551     sys.exit(0)
552
553 if __name__ == "__main__":
554     try:
555         try:
556             options, args = getopt.gnu_getopt(sys.argv[1:], 'C:hV',
557                                               ['directory',
558                                                'help',
559                                                'version'])
560         except getopt.GetoptError, geo:
561             sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
562             sys.exit(1)
563             
564         for key, value in options:
565             if key in ['-h', '--help']:
566                 usage()
567             elif key in ['-V', '--version']:
568                 print "ovsdb-idlc (Open vSwitch) @VERSION@"
569             elif key in ['-C', '--directory']:
570                 os.chdir(value)
571             else:
572                 sys.exit(0)
573             
574         optKeys = [key for key, value in options]
575
576         if not args:
577             sys.stderr.write("%s: missing command argument "
578                              "(use --help for help)\n" % argv0)
579             sys.exit(1)
580
581         commands = {"annotate": (annotateSchema, 2),
582                     "c-idl-header": (printCIDLHeader, 1),
583                     "c-idl-source": (printCIDLSource, 1)}
584
585         if not args[0] in commands:
586             sys.stderr.write("%s: unknown command \"%s\" "
587                              "(use --help for help)\n" % (argv0, args[0]))
588             sys.exit(1)
589
590         func, n_args = commands[args[0]]
591         if len(args) - 1 != n_args:
592             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
593                              "provided\n"
594                              % (argv0, args[0], n_args, len(args) - 1))
595             sys.exit(1)
596
597         func(*args[1:])
598     except Error, e:
599         sys.stderr.write("%s: %s\n" % (argv0, e.msg))
600         sys.exit(1)
601
602 # Local variables:
603 # mode: python
604 # End: