Merge "next" branch into "master".
[sliver-openvswitch.git] / 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         print "/* %s table. */" % (tableName)
235
236         # Parse functions.
237         for columnName, column in sorted(table.columns.iteritems()):
238             print '''
239 static void
240 %(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum)
241 {
242     struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
243                                                 'c': columnName}
244
245             type = column.type
246             if type.value:
247                 keyVar = "row->key_%s" % columnName
248                 valueVar = "row->value_%s" % columnName
249             else:
250                 keyVar = "row->%s" % columnName
251                 valueVar = None
252
253             if (type.min == 1 and type.max == 1) or type.isOptionalPointer():
254                 print
255                 print "    assert(inited);"
256                 print "    if (datum->n >= 1) {"
257                 if not type.key.refTable:
258                     print "        %s = datum->keys[0].%s;" % (keyVar, type.key.type)
259                 else:
260                     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())
261
262                 if valueVar:
263                     if type.value.refTable:
264                         print "        %s = datum->values[0].%s;" % (valueVar, type.value.type)
265                     else:
266                         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())
267                 print "    } else {"
268                 print "        %s" % type.key.initCDefault(keyVar, type.min == 0)
269                 if valueVar:
270                     print "        %s" % type.value.initCDefault(valueVar, type.min == 0)
271                 print "    }"
272             else:
273                 if type.max != 'unlimited':
274                     print "    size_t n = MIN(%d, datum->n);" % type.max
275                     nMax = "n"
276                 else:
277                     nMax = "datum->n"
278                 print "    size_t i;"
279                 print
280                 print "    assert(inited);"
281                 print "    %s = NULL;" % keyVar
282                 if valueVar:
283                     print "    %s = NULL;" % valueVar
284                 print "    row->n_%s = 0;" % columnName
285                 print "    for (i = 0; i < %s; i++) {" % nMax
286                 refs = []
287                 if type.key.refTable:
288                     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())
289                     keySrc = "keyRow"
290                     refs.append('keyRow')
291                 else:
292                     keySrc = "datum->keys[i].%s" % type.key.type
293                 if type.value and type.value.refTable:
294                     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())
295                     valueSrc = "valueRow"
296                     refs.append('valueRow')
297                 elif valueVar:
298                     valueSrc = "datum->values[i].%s" % type.value.type
299                 if refs:
300                     print "        if (%s) {" % ' && '.join(refs)
301                     indent = "            "
302                 else:
303                     indent = "        "
304                 print "%sif (!row->n_%s) {" % (indent, columnName)
305                 print "%s    %s = xmalloc(%s * sizeof *%s);" % (indent, keyVar, nMax, keyVar)
306                 if valueVar:
307                     print "%s    %s = xmalloc(%s * sizeof %s);" % (indent, valueVar, nMax, valueVar)
308                 print "%s}" % indent
309                 print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc)
310                 if valueVar:
311                     print "%s%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc)
312                 print "%srow->n_%s++;" % (indent, columnName)
313                 if refs:
314                     print "        }"
315                 print "    }"
316             print "}"
317
318         # Unparse functions.
319         for columnName, column in sorted(table.columns.iteritems()):
320             type = column.type
321             if (type.min != 1 or type.max != 1) and not type.isOptionalPointer():
322                 print '''
323 static void
324 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
325 {
326     struct %(s)s *row = %(s)s_cast(row_);
327
328     assert(inited);''' % {'s': structName, 'c': columnName}
329                 if type.value:
330                     keyVar = "row->key_%s" % columnName
331                     valueVar = "row->value_%s" % columnName
332                 else:
333                     keyVar = "row->%s" % columnName
334                     valueVar = None
335                 print "    free(%s);" % keyVar
336                 if valueVar:
337                     print "    free(%s);" % valueVar
338                 print '}'
339             else:
340                 print '''
341 static void
342 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row OVS_UNUSED)
343 {
344     /* Nothing to do. */
345 }''' % {'s': structName, 'c': columnName}
346  
347         # First, next functions.
348         print '''
349 const struct %(s)s *
350 %(s)s_first(const struct ovsdb_idl *idl)
351 {
352     return %(s)s_cast(ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
353 }
354
355 const struct %(s)s *
356 %(s)s_next(const struct %(s)s *row)
357 {
358     return %(s)s_cast(ovsdb_idl_next_row(&row->header_));
359 }''' % {'s': structName,
360         'p': prefix,
361         'P': prefix.upper(),
362         'T': tableName.upper()}
363
364         print '''
365 void
366 %(s)s_delete(const struct %(s)s *row)
367 {
368     ovsdb_idl_txn_delete(&row->header_);
369 }
370
371 struct %(s)s *
372 %(s)s_insert(struct ovsdb_idl_txn *txn)
373 {
374     return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
375 }
376 ''' % {'s': structName,
377        'p': prefix,
378        'P': prefix.upper(),
379        'T': tableName.upper()}
380
381         # Verify functions.
382         for columnName, column in sorted(table.columns.iteritems()):
383             print '''
384 void
385 %(s)s_verify_%(c)s(const struct %(s)s *row)
386 {
387     assert(inited);
388     ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]);
389 }''' % {'s': structName,
390         'S': structName.upper(),
391         'c': columnName,
392         'C': columnName.upper()}
393
394         # Set functions.
395         for columnName, column in sorted(table.columns.iteritems()):
396             type = column.type
397             print '\nvoid'
398             members = cMembers(prefix, columnName, column, True)
399             keyVar = members[0]['name']
400             nVar = None
401             valueVar = None
402             if type.value:
403                 valueVar = members[1]['name']
404                 if len(members) > 2:
405                     nVar = members[2]['name']
406             else:
407                 if len(members) > 1:
408                     nVar = members[1]['name']
409             print '%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)' % \
410                 {'s': structName, 'c': columnName,
411                  'args': ', '.join(['%(type)s%(name)s' % m for m in members])}
412             print "{"
413             print "    struct ovsdb_datum datum;"
414             if type.min == 1 and type.max == 1:
415                 print
416                 print "    assert(inited);"
417                 print "    datum.n = 1;"
418                 print "    datum.keys = xmalloc(sizeof *datum.keys);"
419                 print "    " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
420                 if type.value:
421                     print "    datum.values = xmalloc(sizeof *datum.values);"
422                     print "    "+ type.value.copyCValue("datum.values[0].%s" % type.value.type, valueVar)
423                 else:
424                     print "    datum.values = NULL;"
425             elif type.isOptionalPointer():
426                 print
427                 print "    assert(inited);"
428                 print "    if (%s) {" % keyVar
429                 print "        datum.n = 1;"
430                 print "        datum.keys = xmalloc(sizeof *datum.keys);"
431                 print "        " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
432                 print "    } else {"
433                 print "        datum.n = 0;"
434                 print "        datum.keys = NULL;"
435                 print "    }"
436                 print "    datum.values = NULL;"
437             else:
438                 print "    size_t i;"
439                 print
440                 print "    assert(inited);"
441                 print "    datum.n = %s;" % nVar
442                 print "    datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar
443                 if type.value:
444                     print "    datum.values = xmalloc(%s * sizeof *datum.values);" % nVar
445                 else:
446                     print "    datum.values = NULL;"
447                 print "    for (i = 0; i < %s; i++) {" % nVar
448                 print "        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type, "%s[i]" % keyVar)
449                 if type.value:
450                     print "        " + type.value.copyCValue("datum.values[i].%s" % type.value.type, "%s[i]" % valueVar)
451                 print "    }"
452             print "    ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
453                 % {'s': structName,
454                    'S': structName.upper(),
455                    'C': columnName.upper()}
456             print "}"
457
458         # Table columns.
459         print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
460             structName, structName.upper())
461         print """
462 static void\n%s_columns_init(void)
463 {
464     struct ovsdb_idl_column *c;\
465 """ % structName
466         for columnName, column in sorted(table.columns.iteritems()):
467             cs = "%s_col_%s" % (structName, columnName)
468             d = {'cs': cs, 'c': columnName, 's': structName}
469             print
470             print "    /* Initialize %(cs)s. */" % d
471             print "    c = &%(cs)s;" % d
472             print "    c->name = \"%(c)s\";" % d
473             print column.type.cInitType("    ", "c->type")
474             print "    c->parse = %(s)s_parse_%(c)s;" % d
475             print "    c->unparse = %(s)s_unparse_%(c)s;" % d
476         print "}"
477
478     # Table classes.
479     print "\f"
480     print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
481     for tableName, table in sorted(schema.tables.iteritems()):
482         structName = "%s%s" % (prefix, tableName.lower())
483         print "    {\"%s\"," % tableName
484         print "     %s_columns, ARRAY_SIZE(%s_columns)," % (
485             structName, structName)
486         print "     sizeof(struct %s)}," % structName
487     print "};"
488
489     # IDL class.
490     print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix
491     print "    \"%s\", %stable_classes, ARRAY_SIZE(%stable_classes)" % (
492         schema.name, prefix, prefix)
493     print "};"
494
495     # global init function
496     print """
497 void
498 %sinit(void)
499 {
500     if (inited) {
501         return;
502     }
503     inited = true;
504 """ % prefix
505     for tableName, table in sorted(schema.tables.iteritems()):
506         structName = "%s%s" % (prefix, tableName.lower())
507         print "    %s_columns_init();" % structName
508     print "}"
509
510 def ovsdb_escape(string):
511     def escape(match):
512         c = match.group(0)
513         if c == '\0':
514             raise Error("strings may not contain null bytes")
515         elif c == '\\':
516             return '\\\\'
517         elif c == '\n':
518             return '\\n'
519         elif c == '\r':
520             return '\\r'
521         elif c == '\t':
522             return '\\t'
523         elif c == '\b':
524             return '\\b'
525         elif c == '\a':
526             return '\\a'
527         else:
528             return '\\x%02x' % ord(c)
529     return re.sub(r'["\\\000-\037]', escape, string)
530
531
532
533 def usage():
534     print """\
535 %(argv0)s: ovsdb schema compiler
536 usage: %(argv0)s [OPTIONS] COMMAND ARG...
537
538 The following commands are supported:
539   annotate SCHEMA ANNOTATIONS print SCHEMA combined with ANNOTATIONS
540   c-idl-header IDL            print C header file for IDL
541   c-idl-source IDL            print C source file for IDL implementation
542   nroff IDL                   print schema documentation in nroff format
543
544 The following options are also available:
545   -h, --help                  display this help message
546   -V, --version               display version information\
547 """ % {'argv0': argv0}
548     sys.exit(0)
549
550 if __name__ == "__main__":
551     try:
552         try:
553             options, args = getopt.gnu_getopt(sys.argv[1:], 'C:hV',
554                                               ['directory',
555                                                'help',
556                                                'version'])
557         except getopt.GetoptError, geo:
558             sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
559             sys.exit(1)
560             
561         for key, value in options:
562             if key in ['-h', '--help']:
563                 usage()
564             elif key in ['-V', '--version']:
565                 print "ovsdb-idlc (Open vSwitch) @VERSION@"
566             elif key in ['-C', '--directory']:
567                 os.chdir(value)
568             else:
569                 sys.exit(0)
570             
571         optKeys = [key for key, value in options]
572
573         if not args:
574             sys.stderr.write("%s: missing command argument "
575                              "(use --help for help)\n" % argv0)
576             sys.exit(1)
577
578         commands = {"annotate": (annotateSchema, 2),
579                     "c-idl-header": (printCIDLHeader, 1),
580                     "c-idl-source": (printCIDLSource, 1)}
581
582         if not args[0] in commands:
583             sys.stderr.write("%s: unknown command \"%s\" "
584                              "(use --help for help)\n" % (argv0, args[0]))
585             sys.exit(1)
586
587         func, n_args = commands[args[0]]
588         if len(args) - 1 != n_args:
589             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
590                              "provided\n"
591                              % (argv0, args[0], n_args, len(args) - 1))
592             sys.exit(1)
593
594         func(*args[1:])
595     except Error, e:
596         sys.stderr.write("%s: %s\n" % (argv0, e.msg))
597         sys.exit(1)
598
599 # Local variables:
600 # mode: python
601 # End: