~james-page/ubuntu/saucy/openvswitch/1.12-snapshot

« back to all changes in this revision

Viewing changes to build-aux/extract-ofp-errors

  • Committer: James Page
  • Date: 2013-08-21 10:16:57 UTC
  • mfrom: (1.1.20)
  • Revision ID: james.page@canonical.com-20130821101657-3o0z0qeiv5zkwlzi
New upstream snapshot

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 
7
7
macros = {}
8
8
 
 
9
# Map from OpenFlow version number to version ID used in ofp_header.
 
10
version_map = {"1.0": 0x01,
 
11
               "1.1": 0x02,
 
12
               "1.2": 0x03,
 
13
               "1.3": 0x04}
 
14
version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
 
15
 
9
16
token = None
10
17
line = ""
11
18
idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
13
20
inComment = False
14
21
inDirective = False
15
22
 
16
 
def getLine():
 
23
def open_file(fn):
 
24
    global fileName
 
25
    global inputFile
 
26
    global lineNumber
 
27
    fileName = fn
 
28
    inputFile = open(fileName)
 
29
    lineNumber = 0
 
30
 
 
31
def tryGetLine():
 
32
    global inputFile
17
33
    global line
18
34
    global lineNumber
19
35
    line = inputFile.readline()
20
36
    lineNumber += 1
21
 
    if line == "":
 
37
    return line != ""
 
38
 
 
39
def getLine():
 
40
    if not tryGetLine():
22
41
        fatal("unexpected end of input")
23
42
 
24
43
def getToken():
113
132
    return name
114
133
 
115
134
def print_enum(tag, constants, storage_class):
116
 
    print """
 
135
    print ("""
117
136
%(storage_class)sconst char *
118
137
%(tag)s_to_string(uint16_t value)
119
138
{
120
139
    switch (value) {\
121
140
""" % {"tag": tag,
122
141
       "bufferlen": len(tag) + 32,
123
 
       "storage_class": storage_class}
 
142
       "storage_class": storage_class})
124
143
    for constant in constants:
125
 
        print "    case %s: return \"%s\";" % (constant, constant)
126
 
    print """\
 
144
        print ("    case %s: return \"%s\";" % (constant, constant))
 
145
    print ("""\
127
146
    }
128
147
    return NULL;
129
148
}\
130
 
""" % {"tag": tag}
 
149
""" % {"tag": tag})
131
150
 
132
151
def usage():
133
152
    argv0 = os.path.basename(sys.argv[0])
134
 
    print '''\
 
153
    print ('''\
135
154
%(argv0)s, for extracting OpenFlow error codes from header files
136
 
usage: %(argv0)s FILE [FILE...]
137
 
 
138
 
This program reads the header files specified on the command line and
139
 
outputs a C source file for translating OpenFlow error codes into
140
 
strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
141
 
 
142
 
This program is specialized for reading lib/ofp-errors.h.  It will not
143
 
work on arbitrary header files without extensions.\
144
 
''' % {"argv0": argv0}
 
155
usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
 
156
 
 
157
This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
 
158
experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
 
159
It outputs a C source file for translating OpenFlow error codes into
 
160
strings.
 
161
 
 
162
ERROR_HEADER should point to lib/ofp-errors.h.
 
163
VENDOR_HEADER should point to include/openflow/openflow-common.h.
 
164
The output is suitable for use as lib/ofp-errors.inc.\
 
165
''' % {"argv0": argv0})
145
166
    sys.exit(0)
146
167
 
147
 
def extract_ofp_errors(filenames):
 
168
def extract_vendor_ids(fn):
 
169
    global vendor_map
 
170
    vendor_map = {}
 
171
    vendor_loc = {}
 
172
 
 
173
    open_file(fn)
 
174
    while tryGetLine():
 
175
        m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
 
176
        if not m:
 
177
            continue
 
178
 
 
179
        name = m.group(1)
 
180
        id_ = int(m.group(2), 0)
 
181
 
 
182
        if name in vendor_map:
 
183
            error("%s: duplicate definition of vendor" % name)
 
184
            sys.stderr.write("%s: Here is the location of the previous "
 
185
                             "definition.\n" % vendor_loc[name])
 
186
            sys.exit(1)
 
187
 
 
188
        vendor_map[name] = id_
 
189
        vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
 
190
 
 
191
    if not vendor_map:
 
192
        fatal("%s: no vendor definitions found" % fn)
 
193
 
 
194
    inputFile.close()
 
195
 
 
196
    vendor_reverse_map = {}
 
197
    for name, id_ in vendor_map.items():
 
198
        if id_ in vendor_reverse_map:
 
199
            fatal("%s: duplicate vendor id for vendors %s and %s"
 
200
                  % (id_, vendor_reverse_map[id_], name))
 
201
        vendor_reverse_map[id_] = name
 
202
 
 
203
def extract_ofp_errors(fn):
148
204
    error_types = {}
149
205
 
150
206
    comments = []
151
207
    names = []
152
208
    domain = {}
153
209
    reverse = {}
154
 
    for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3",
155
 
                        "NX1.0", "NX1.1", "NX1.2", "NX1.3"):
 
210
    for domain_name in version_map.values():
156
211
        domain[domain_name] = {}
157
212
        reverse[domain_name] = {}
158
213
 
159
214
    n_errors = 0
160
215
    expected_errors = {}
161
216
 
162
 
    global fileName
163
 
    for fileName in filenames:
164
 
        global inputFile
165
 
        global lineNumber
166
 
        inputFile = open(fileName)
167
 
        lineNumber = 0
168
 
 
169
 
        while True:
170
 
            getLine()
171
 
            if re.match('enum ofperr', line):
172
 
                break
173
 
 
174
 
        while True:
 
217
    open_file(fn)
 
218
 
 
219
    while True:
 
220
        getLine()
 
221
        if re.match('enum ofperr', line):
 
222
            break
 
223
 
 
224
    while True:
 
225
        getLine()
 
226
        if line.startswith('/*') or not line or line.isspace():
 
227
            continue
 
228
        elif re.match('}', line):
 
229
            break
 
230
 
 
231
        if not line.lstrip().startswith('/*'):
 
232
            fatal("unexpected syntax between errors")
 
233
 
 
234
        comment = line.lstrip()[2:].strip()
 
235
        while not comment.endswith('*/'):
175
236
            getLine()
176
237
            if line.startswith('/*') or not line or line.isspace():
177
 
                continue
178
 
            elif re.match('}', line):
179
 
                break
180
 
 
181
 
            if not line.lstrip().startswith('/*'):
182
 
                fatal("unexpected syntax between errors")
183
 
 
184
 
            comment = line.lstrip()[2:].strip()
185
 
            while not comment.endswith('*/'):
186
 
                getLine()
187
 
                if line.startswith('/*') or not line or line.isspace():
188
 
                    fatal("unexpected syntax within error")
189
 
                comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
190
 
            comment = comment[:-2].rstrip()
191
 
 
192
 
            m = re.match('Expected: (.*)\.$', comment)
193
 
            if m:
194
 
                expected_errors[m.group(1)] = (fileName, lineNumber)
195
 
                continue
196
 
 
197
 
            m = re.match('((?:.(?!\.  ))+.)\.  (.*)$', comment)
198
 
            if not m:
199
 
                fatal("unexpected syntax between errors")
200
 
 
201
 
            dsts, comment = m.groups()
202
 
 
203
 
            getLine()
204
 
            m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
205
 
                         line)
206
 
            if not m:
207
 
                fatal("syntax error expecting enum value")
208
 
 
209
 
            enum = m.group(1)
210
 
 
211
 
            comments.append(re.sub('\[[^]]*\]', '', comment))
212
 
            names.append(enum)
213
 
 
214
 
            for dst in dsts.split(', '):
215
 
                m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst)
216
 
                if not m:
217
 
                    fatal("%s: syntax error in destination" % dst)
218
 
                targets = m.group(1)
219
 
                if m.group(3):
220
 
                    base = 16
221
 
                else:
222
 
                    base = 10
223
 
                type_ = int(m.group(2), base)
224
 
                if m.group(4):
225
 
                    code = int(m.group(4))
226
 
                else:
227
 
                    code = None
228
 
 
229
 
                target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
230
 
                              "OF1.1+": ("OF1.1", "OF1.2", "OF1.3"),
231
 
                              "OF1.2+": ("OF1.2", "OF1.3"),
232
 
                              "OF1.3+": ("OF1.3",),
233
 
                              "OF1.0":  ("OF1.0",),
234
 
                              "OF1.1":  ("OF1.1",),
235
 
                              "OF1.2":  ("OF1.2",),
236
 
                              "OF1.3":  ("OF1.3",),
237
 
                              "NX1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
238
 
                              "NX1.1+": ("OF1.1", "OF1.2", "OF1.3"),
239
 
                              "NX1.2+": ("OF1.2", "OF1.3"),
240
 
                              "NX1.3+": ("OF1.3",),
241
 
                              "NX1.0":  ("OF1.0",),
242
 
                              "NX1.1":  ("OF1.1",),
243
 
                              "NX1.2":  ("OF1.2",),
244
 
                              "NX1.3":  ("OF1.3",)}
245
 
                if targets not in target_map:
246
 
                    fatal("%s: unknown error domain" % targets)
247
 
                if targets.startswith('NX') and code < 0x100:
248
 
                    fatal("%s: NX domain code cannot be less than 0x100" % dst)
249
 
                if targets.startswith('OF') and code >= 0x100:
250
 
                    fatal("%s: OF domain code cannot be greater than 0x100"
251
 
                          % dst)
252
 
                for target in target_map[targets]:
253
 
                    domain[target].setdefault(type_, {})
254
 
                    if code in domain[target][type_]:
255
 
                        msg = "%d,%d in %s means both %s and %s" % (
256
 
                            type_, code, target,
257
 
                            domain[target][type_][code][0], enum)
258
 
                        if msg in expected_errors:
259
 
                            del expected_errors[msg]
260
 
                        else:
261
 
                            error("%s: %s." % (dst, msg))
262
 
                            sys.stderr.write("%s:%d: %s: Here is the location "
263
 
                                             "of the previous definition.\n"
264
 
                                             % (domain[target][type_][code][1],
265
 
                                                domain[target][type_][code][2],
266
 
                                                dst))
 
238
                fatal("unexpected syntax within error")
 
239
            comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
 
240
        comment = comment[:-2].rstrip()
 
241
 
 
242
        m = re.match('Expected: (.*)\.$', comment)
 
243
        if m:
 
244
            expected_errors[m.group(1)] = (fileName, lineNumber)
 
245
            continue
 
246
 
 
247
        m = re.match('((?:.(?!\.  ))+.)\.  (.*)$', comment)
 
248
        if not m:
 
249
            fatal("unexpected syntax between errors")
 
250
 
 
251
        dsts, comment = m.groups()
 
252
 
 
253
        getLine()
 
254
        m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
 
255
                     line)
 
256
        if not m:
 
257
            fatal("syntax error expecting enum value")
 
258
 
 
259
        enum = m.group(1)
 
260
        if enum in names:
 
261
            fatal("%s specified twice" % enum)
 
262
 
 
263
        comments.append(re.sub('\[[^]]*\]', '', comment))
 
264
        names.append(enum)
 
265
 
 
266
        for dst in dsts.split(', '):
 
267
            m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
 
268
            if not m:
 
269
                fatal("%r: syntax error in destination" % dst)
 
270
            vendor_name = m.group(1)
 
271
            version1_name = m.group(2)
 
272
            version2_name = m.group(3)
 
273
            type_ = int(m.group(4))
 
274
            if m.group(5):
 
275
                code = int(m.group(5))
 
276
            else:
 
277
                code = None
 
278
 
 
279
            if vendor_name not in vendor_map:
 
280
                fatal("%s: unknown vendor" % vendor_name)
 
281
            vendor = vendor_map[vendor_name]
 
282
 
 
283
            if version1_name not in version_map:
 
284
                fatal("%s: unknown OpenFlow version" % version1_name)
 
285
            v1 = version_map[version1_name]
 
286
 
 
287
            if version2_name is None:
 
288
                v2 = v1
 
289
            elif version2_name == "+":
 
290
                v2 = max(version_map.values())
 
291
            elif version2_name[1:] not in version_map:
 
292
                fatal("%s: unknown OpenFlow version" % version2_name[1:])
 
293
            else:
 
294
                v2 = version_map[version2_name[1:]]
 
295
 
 
296
            if v2 < v1:
 
297
                fatal("%s%s: %s precedes %s"
 
298
                      % (version1_name, version2_name,
 
299
                         version2_name, version1_name))
 
300
 
 
301
            if vendor == vendor_map['NX']:
 
302
                if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
 
303
                    if code is not None:
 
304
                        fatal("%s: NX1.2+ domains do not have codes" % dst)
 
305
                    code = 0
 
306
            elif vendor != vendor_map['OF']:
 
307
                if code is not None:
 
308
                    fatal("%s: %s domains do not have codes" % vendor_name)
 
309
 
 
310
            for version in range(v1, v2 + 1):
 
311
                domain[version].setdefault(vendor, {})
 
312
                domain[version][vendor].setdefault(type_, {})
 
313
                if code in domain[version][vendor][type_]:
 
314
                    msg = "%#x,%d,%d in OF%s means both %s and %s" % (
 
315
                        vendor, type_, code, version_reverse_map[version],
 
316
                        domain[version][vendor][type_][code][0], enum)
 
317
                    if msg in expected_errors:
 
318
                        del expected_errors[msg]
267
319
                    else:
268
 
                        domain[target][type_][code] = (enum, fileName,
269
 
                                                       lineNumber)
270
 
 
271
 
                    if enum in reverse[target]:
272
 
                        error("%s: %s in %s means both %d,%d and %d,%d." %
273
 
                              (dst, enum, target,
274
 
                               reverse[target][enum][0],
275
 
                               reverse[target][enum][1],
276
 
                               type_, code))
277
 
                    reverse[target][enum] = (type_, code)
278
 
 
279
 
        inputFile.close()
280
 
 
281
 
    for fn, ln in expected_errors.itervalues():
 
320
                        error("%s: %s." % (dst, msg))
 
321
                        sys.stderr.write("%s:%d: %s: Here is the location "
 
322
                                         "of the previous definition.\n"
 
323
                                         % (domain[version][vendor][type_][code][1],
 
324
                                            domain[version][vendor][type_][code][2],
 
325
                                            dst))
 
326
                else:
 
327
                    domain[version][vendor][type_][code] = (enum, fileName,
 
328
                                                   lineNumber)
 
329
 
 
330
                assert enum not in reverse[version]
 
331
                reverse[version][enum] = (vendor, type_, code)
 
332
 
 
333
    inputFile.close()
 
334
 
 
335
    for fn, ln in expected_errors.values():
282
336
        sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
283
337
        n_errors += 1
284
338
 
285
339
    if n_errors:
286
340
        sys.exit(1)
287
341
 
288
 
    print """\
 
342
    print ("""\
289
343
/* Generated automatically; do not modify!     -*- buffer-read-only: t -*- */
290
344
 
291
345
#define OFPERR_N_ERRORS %d
293
347
struct ofperr_domain {
294
348
    const char *name;
295
349
    uint8_t version;
296
 
    enum ofperr (*decode)(uint16_t type, uint16_t code);
297
 
    struct pair errors[OFPERR_N_ERRORS];
 
350
    enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
 
351
    struct triplet errors[OFPERR_N_ERRORS];
298
352
};
299
353
 
300
354
static const char *error_names[OFPERR_N_ERRORS] = {
307
361
""" % (len(names),
308
362
       '\n'.join('    "%s",' % name for name in names),
309
363
       '\n'.join('    "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
310
 
                 for comment in comments))
 
364
                 for comment in comments)))
311
365
 
312
366
    def output_domain(map, name, description, version):
313
 
        print """
 
367
        print ("""
314
368
static enum ofperr
315
 
%s_decode(uint16_t type, uint16_t code)
 
369
%s_decode(uint32_t vendor, uint16_t type, uint16_t code)
316
370
{
317
 
    switch ((type << 16) | code) {""" % name
 
371
    switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
318
372
        found = set()
319
373
        for enum in names:
320
374
            if enum not in map:
321
375
                continue
322
 
            type_, code = map[enum]
 
376
            vendor, type_, code = map[enum]
323
377
            if code is None:
324
378
                continue
325
 
            value = (type_ << 16) | code
 
379
            value = (vendor << 32) | (type_ << 16) | code
326
380
            if value in found:
327
381
                continue
328
382
            found.add(value)
329
 
            print "    case (%d << 16) | %d:" % (type_, code)
330
 
            print "        return OFPERR_%s;" % enum
331
 
        print """\
 
383
            if vendor:
 
384
                vendor_s = "(%#xULL << 32) | " % vendor
 
385
            else:
 
386
                vendor_s = ""
 
387
            print ("    case %s(%d << 16) | %d:" % (vendor_s, type_, code))
 
388
            print ("        return OFPERR_%s;" % enum)
 
389
        print ("""\
332
390
    }
333
391
 
334
392
    return 0;
335
 
}"""
 
393
}""")
336
394
 
337
 
        print """
 
395
        print ("""
338
396
static const struct ofperr_domain %s = {
339
397
    "%s",
340
398
    %d,
341
399
    %s_decode,
342
 
    {""" % (name, description, version, name)
 
400
    {""" % (name, description, version, name))
343
401
        for enum in names:
344
402
            if enum in map:
345
 
                type_, code = map[enum]
 
403
                vendor, type_, code = map[enum]
346
404
                if code == None:
347
405
                    code = -1
 
406
                print "        { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
348
407
            else:
349
 
                type_ = code = -1
350
 
            print "        { %2d, %3d }, /* %s */" % (type_, code, enum)
351
 
        print """\
 
408
                print ("        {       -1, -1,  -1 }, /* %s */" % enum)
 
409
        print ("""\
352
410
    },
353
 
};"""
 
411
};""")
354
412
 
355
 
    output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
356
 
    output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
357
 
    output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
358
 
    output_domain(reverse["OF1.3"], "ofperr_of13", "OpenFlow 1.3", 0x04)
 
413
    for version_name, id_ in version_map.items():
 
414
        var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
 
415
        description = "OpenFlow %s" % version_name
 
416
        output_domain(reverse[id_], var, description, id_)
359
417
 
360
418
if __name__ == '__main__':
361
419
    if '--help' in sys.argv:
362
420
        usage()
363
 
    elif len(sys.argv) < 2:
364
 
        sys.stderr.write("at least one non-option argument required; "
 
421
    elif len(sys.argv) != 3:
 
422
        sys.stderr.write("exactly two non-options arguments required; "
365
423
                         "use --help for help\n")
366
424
        sys.exit(1)
367
425
    else:
368
 
        extract_ofp_errors(sys.argv[1:])
 
426
        extract_vendor_ids(sys.argv[2])
 
427
        extract_ofp_errors(sys.argv[1])