]> git.proxmox.com Git - mirror_ovs.git/blame - build-aux/extract-ofp-errors
tests: Add mirror-related keywords to all the mirroring tests.
[mirror_ovs.git] / build-aux / extract-ofp-errors
CommitLineData
dc4762ed
BP
1#! /usr/bin/python
2
3import sys
4import os.path
5import re
6
7macros = {}
8
514887ee
BP
9# Map from OpenFlow version number to version ID used in ofp_header.
10version_map = {"1.0": 0x01,
11 "1.1": 0x02,
12 "1.2": 0x03,
c37c0382 13 "1.3": 0x04,
42dccab5
BP
14 "1.4": 0x05,
15 "1.5": 0x06}
514887ee
BP
16version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
17
dc4762ed
BP
18token = None
19line = ""
20idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
21tokenRe = "#?" + idRe + "|[0-9]+|."
22inComment = False
23inDirective = False
90bf1e07 24
514887ee
BP
25def open_file(fn):
26 global fileName
27 global inputFile
28 global lineNumber
29 fileName = fn
30 inputFile = open(fileName)
31 lineNumber = 0
32
33def tryGetLine():
34 global inputFile
90bf1e07
BP
35 global line
36 global lineNumber
37 line = inputFile.readline()
38 lineNumber += 1
514887ee
BP
39 return line != ""
40
41def getLine():
42 if not tryGetLine():
90bf1e07
BP
43 fatal("unexpected end of input")
44
dc4762ed
BP
45def getToken():
46 global token
47 global line
48 global inComment
49 global inDirective
50 while True:
51 line = line.lstrip()
52 if line != "":
53 if line.startswith("/*"):
54 inComment = True
55 line = line[2:]
56 elif inComment:
57 commentEnd = line.find("*/")
58 if commentEnd < 0:
59 line = ""
60 else:
61 inComment = False
62 line = line[commentEnd + 2:]
63 else:
64 match = re.match(tokenRe, line)
65 token = match.group(0)
66 line = line[len(token):]
67 if token.startswith('#'):
68 inDirective = True
69 elif token in macros and not inDirective:
70 line = macros[token] + line
71 continue
72 return True
73 elif inDirective:
74 token = "$"
75 inDirective = False
76 return True
77 else:
78 global lineNumber
79 line = inputFile.readline()
80 lineNumber += 1
81 while line.endswith("\\\n"):
82 line = line[:-2] + inputFile.readline()
83 lineNumber += 1
84 if line == "":
85 if token == None:
86 fatal("unexpected end of input")
87 token = None
88 return False
89
2e0525bc
SH
90n_errors = 0
91def error(msg):
92 global n_errors
90bf1e07 93 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
2e0525bc
SH
94 n_errors += 1
95
96def fatal(msg):
97 error(msg)
dc4762ed
BP
98 sys.exit(1)
99
100def skipDirective():
101 getToken()
102 while token != '$':
103 getToken()
104
105def isId(s):
106 return re.match(idRe + "$", s) != None
107
108def forceId():
109 if not isId(token):
110 fatal("identifier expected")
111
112def forceInteger():
113 if not re.match('[0-9]+$', token):
114 fatal("integer expected")
115
116def match(t):
117 if token == t:
118 getToken()
119 return True
120 else:
121 return False
122
123def forceMatch(t):
124 if not match(t):
125 fatal("%s expected" % t)
126
127def parseTaggedName():
128 assert token in ('struct', 'union')
129 name = token
130 getToken()
131 forceId()
132 name = "%s %s" % (name, token)
133 getToken()
134 return name
135
136def print_enum(tag, constants, storage_class):
f4d90e0d 137 print ("""
dc4762ed
BP
138%(storage_class)sconst char *
139%(tag)s_to_string(uint16_t value)
140{
141 switch (value) {\
142""" % {"tag": tag,
143 "bufferlen": len(tag) + 32,
f4d90e0d 144 "storage_class": storage_class})
dc4762ed 145 for constant in constants:
f4d90e0d
DM
146 print (" case %s: return \"%s\";" % (constant, constant))
147 print ("""\
dc4762ed
BP
148 }
149 return NULL;
150}\
f4d90e0d 151""" % {"tag": tag})
dc4762ed
BP
152
153def usage():
154 argv0 = os.path.basename(sys.argv[0])
f4d90e0d 155 print ('''\
dc4762ed 156%(argv0)s, for extracting OpenFlow error codes from header files
514887ee 157usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
dc4762ed 158
514887ee
BP
159This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
160experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
161It outputs a C source file for translating OpenFlow error codes into
162strings.
dc4762ed 163
514887ee
BP
164ERROR_HEADER should point to lib/ofp-errors.h.
165VENDOR_HEADER should point to include/openflow/openflow-common.h.
166The output is suitable for use as lib/ofp-errors.inc.\
f4d90e0d 167''' % {"argv0": argv0})
dc4762ed
BP
168 sys.exit(0)
169
514887ee
BP
170def extract_vendor_ids(fn):
171 global vendor_map
172 vendor_map = {}
173 vendor_loc = {}
174
175 open_file(fn)
176 while tryGetLine():
177 m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
178 if not m:
179 continue
180
181 name = m.group(1)
182 id_ = int(m.group(2), 0)
183
184 if name in vendor_map:
185 error("%s: duplicate definition of vendor" % name)
186 sys.stderr.write("%s: Here is the location of the previous "
187 "definition.\n" % vendor_loc[name])
188 sys.exit(1)
189
190 vendor_map[name] = id_
191 vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
192
193 if not vendor_map:
194 fatal("%s: no vendor definitions found" % fn)
195
196 inputFile.close()
197
198 vendor_reverse_map = {}
199 for name, id_ in vendor_map.items():
200 if id_ in vendor_reverse_map:
201 fatal("%s: duplicate vendor id for vendors %s and %s"
202 % (id_, vendor_reverse_map[id_], name))
203 vendor_reverse_map[id_] = name
83ed4519 204
514887ee 205def extract_ofp_errors(fn):
dc4762ed
BP
206 error_types = {}
207
90bf1e07
BP
208 comments = []
209 names = []
210 domain = {}
211 reverse = {}
514887ee 212 for domain_name in version_map.values():
90bf1e07
BP
213 domain[domain_name] = {}
214 reverse[domain_name] = {}
215
2e0525bc
SH
216 n_errors = 0
217 expected_errors = {}
218
514887ee 219 open_file(fn)
90bf1e07 220
514887ee
BP
221 while True:
222 getLine()
223 if re.match('enum ofperr', line):
224 break
225
226 while True:
227 getLine()
228 if line.startswith('/*') or not line or line.isspace():
229 continue
230 elif re.match('}', line):
231 break
232
233 if not line.lstrip().startswith('/*'):
234 fatal("unexpected syntax between errors")
90bf1e07 235
514887ee
BP
236 comment = line.lstrip()[2:].strip()
237 while not comment.endswith('*/'):
90bf1e07
BP
238 getLine()
239 if line.startswith('/*') or not line or line.isspace():
514887ee
BP
240 fatal("unexpected syntax within error")
241 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
242 comment = comment[:-2].rstrip()
2e0525bc 243
514887ee
BP
244 m = re.match('Expected: (.*)\.$', comment)
245 if m:
246 expected_errors[m.group(1)] = (fileName, lineNumber)
247 continue
2e0525bc 248
514887ee
BP
249 m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
250 if not m:
251 fatal("unexpected syntax between errors")
2e0525bc 252
514887ee 253 dsts, comment = m.groups()
90bf1e07 254
514887ee
BP
255 getLine()
256 m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
257 line)
258 if not m:
259 fatal("syntax error expecting enum value")
90bf1e07 260
514887ee
BP
261 enum = m.group(1)
262 if enum in names:
263 fatal("%s specified twice" % enum)
90bf1e07 264
514887ee
BP
265 comments.append(re.sub('\[[^]]*\]', '', comment))
266 names.append(enum)
267
268 for dst in dsts.split(', '):
269 m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
270 if not m:
271 fatal("%r: syntax error in destination" % dst)
272 vendor_name = m.group(1)
273 version1_name = m.group(2)
274 version2_name = m.group(3)
275 type_ = int(m.group(4))
276 if m.group(5):
277 code = int(m.group(5))
278 else:
279 code = None
280
281 if vendor_name not in vendor_map:
282 fatal("%s: unknown vendor" % vendor_name)
283 vendor = vendor_map[vendor_name]
284
285 if version1_name not in version_map:
286 fatal("%s: unknown OpenFlow version" % version1_name)
287 v1 = version_map[version1_name]
288
289 if version2_name is None:
290 v2 = v1
291 elif version2_name == "+":
292 v2 = max(version_map.values())
293 elif version2_name[1:] not in version_map:
294 fatal("%s: unknown OpenFlow version" % version2_name[1:])
295 else:
296 v2 = version_map[version2_name[1:]]
297
298 if v2 < v1:
299 fatal("%s%s: %s precedes %s"
300 % (version1_name, version2_name,
301 version2_name, version1_name))
302
d12f128a
BP
303 if vendor == vendor_map['OF']:
304 # All standard OpenFlow errors have a type and a code.
305 if code is None:
306 fatal("%s: %s domain requires code" % (dst, vendor_name))
307 elif vendor == vendor_map['NX']:
308 # Before OpenFlow 1.2, OVS used a Nicira extension to
309 # define errors that included a type and a code.
310 #
311 # In OpenFlow 1.2 and later, Nicira extension errors
312 # are defined using the OpenFlow experimenter error
313 # mechanism that includes a type but not a code.
314 if v1 < version_map['1.2'] or v2 < version_map['1.2']:
315 if code is None:
316 fatal("%s: NX1.0 and NX1.1 domains require code"
317 % (dst, vendor_name))
514887ee
BP
318 if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
319 if code is not None:
320 fatal("%s: NX1.2+ domains do not have codes" % dst)
d12f128a
BP
321 else:
322 # Experimenter extension error for OF1.2+ only.
323 if v1 < version_map['1.2']:
324 fatal("%s: %s domain not supported before OF1.2"
325 % (dst, vendor_name))
514887ee 326 if code is not None:
d12f128a
BP
327 fatal("%s: %s domains do not have codes"
328 % (dst, vendor_name))
329 if code is None:
330 code = 0
514887ee
BP
331
332 for version in range(v1, v2 + 1):
333 domain[version].setdefault(vendor, {})
334 domain[version][vendor].setdefault(type_, {})
335 if code in domain[version][vendor][type_]:
336 msg = "%#x,%d,%d in OF%s means both %s and %s" % (
337 vendor, type_, code, version_reverse_map[version],
338 domain[version][vendor][type_][code][0], enum)
339 if msg in expected_errors:
340 del expected_errors[msg]
2e0525bc 341 else:
514887ee
BP
342 error("%s: %s." % (dst, msg))
343 sys.stderr.write("%s:%d: %s: Here is the location "
344 "of the previous definition.\n"
345 % (domain[version][vendor][type_][code][1],
346 domain[version][vendor][type_][code][2],
347 dst))
348 else:
349 domain[version][vendor][type_][code] = (enum, fileName,
350 lineNumber)
2e0525bc 351
514887ee
BP
352 assert enum not in reverse[version]
353 reverse[version][enum] = (vendor, type_, code)
90bf1e07 354
514887ee 355 inputFile.close()
dc4762ed 356
f4d90e0d 357 for fn, ln in expected_errors.values():
2e0525bc
SH
358 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
359 n_errors += 1
360
361 if n_errors:
362 sys.exit(1)
363
f4d90e0d 364 print ("""\
90bf1e07
BP
365/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
366
367#define OFPERR_N_ERRORS %d
368
369struct ofperr_domain {
370 const char *name;
371 uint8_t version;
514887ee
BP
372 enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
373 struct triplet errors[OFPERR_N_ERRORS];
90bf1e07
BP
374};
375
376static const char *error_names[OFPERR_N_ERRORS] = {
377%s
378};
379
380static const char *error_comments[OFPERR_N_ERRORS] = {
381%s
382};\
383""" % (len(names),
384 '\n'.join(' "%s",' % name for name in names),
385 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
f4d90e0d 386 for comment in comments)))
90bf1e07
BP
387
388 def output_domain(map, name, description, version):
f4d90e0d 389 print ("""
90bf1e07 390static enum ofperr
514887ee 391%s_decode(uint32_t vendor, uint16_t type, uint16_t code)
90bf1e07 392{
514887ee 393 switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
2e0525bc 394 found = set()
90bf1e07
BP
395 for enum in names:
396 if enum not in map:
397 continue
514887ee 398 vendor, type_, code = map[enum]
514887ee 399 value = (vendor << 32) | (type_ << 16) | code
2e0525bc
SH
400 if value in found:
401 continue
402 found.add(value)
514887ee
BP
403 if vendor:
404 vendor_s = "(%#xULL << 32) | " % vendor
405 else:
406 vendor_s = ""
407 print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code))
f4d90e0d
DM
408 print (" return OFPERR_%s;" % enum)
409 print ("""\
dc4762ed 410 }
90bf1e07 411
90bf1e07 412 return 0;
f4d90e0d 413}""")
90bf1e07 414
f4d90e0d 415 print ("""
688e86e1 416static const struct ofperr_domain %s = {
90bf1e07
BP
417 "%s",
418 %d,
419 %s_decode,
f4d90e0d 420 {""" % (name, description, version, name))
90bf1e07
BP
421 for enum in names:
422 if enum in map:
514887ee 423 vendor, type_, code = map[enum]
90bf1e07
BP
424 if code == None:
425 code = -1
514887ee 426 print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
90bf1e07 427 else:
514887ee 428 print (" { -1, -1, -1 }, /* %s */" % enum)
f4d90e0d 429 print ("""\
90bf1e07 430 },
f4d90e0d 431};""")
90bf1e07 432
514887ee
BP
433 for version_name, id_ in version_map.items():
434 var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
435 description = "OpenFlow %s" % version_name
436 output_domain(reverse[id_], var, description, id_)
dc4762ed
BP
437
438if __name__ == '__main__':
439 if '--help' in sys.argv:
440 usage()
514887ee
BP
441 elif len(sys.argv) != 3:
442 sys.stderr.write("exactly two non-options arguments required; "
dc4762ed
BP
443 "use --help for help\n")
444 sys.exit(1)
445 else:
514887ee
BP
446 extract_vendor_ids(sys.argv[2])
447 extract_ofp_errors(sys.argv[1])