]>
git.proxmox.com Git - mirror_ovs.git/blob - build-aux/extract-ofp-actions
12 # Map from OpenFlow version number to version ID used in ofp_header.
13 version_map
= {"1.0": 0x01,
19 version_reverse_map
= dict((v
, k
) for (k
, v
) in version_map
.items())
21 # Map from vendor name to the length of the action header.
22 vendor_map
= {"OF": (0x00000000, 4),
23 "ONF": (0x4f4e4600, 10),
24 "NX": (0x00002320, 10)}
26 # Basic types used in action arguments.
28 types
['uint8_t'] = {"size": 1, "align": 1, "ntoh": None, "hton": None}
29 types
['ovs_be16'] = {"size": 2, "align": 2, "ntoh": "ntohs", "hton": "htons"}
30 types
['ovs_be32'] = {"size": 4, "align": 4, "ntoh": "ntohl", "hton": "htonl"}
31 types
['ovs_be64'] = {"size": 8, "align": 8, "ntoh": "ntohll", "hton": "htonll"}
32 types
['uint16_t'] = {"size": 2, "align": 2, "ntoh": None, "hton": None}
33 types
['uint32_t'] = {"size": 4, "align": 4, "ntoh": None, "hton": None}
34 types
['uint64_t'] = {"size": 8, "align": 8, "ntoh": None, "hton": None}
41 return int((x
+ (y
- 1)) / y
) * y
48 input_file
= open(file_name
)
55 line
= input_file
.readline()
58 fatal("unexpected end of input")
64 sys
.stderr
.write("%s:%d: %s\n" % (file_name
, line_number
, msg
))
72 argv0
= os
.path
.basename(sys
.argv
[0])
74 %(argv0)s, for extracting OpenFlow action data
75 usage: %(argv0)s [prototypes | definitions] OFP-ACTIONS.c
76 usage: %(argv0)s ovs-actions OVS-ACTIONS.XML
80 prototypes OFP-ACTIONS.C
81 Reads ofp-actions.c and prints a set of prototypes to #include early in
84 definitions OFP-ACTIONS.C
85 Reads ofp-actions.c and prints a set of definitions to #include late in
88 ovs-actions OVS-ACTIONS.XML
89 Reads ovs-actions.xml and prints documentation in troff format.\
90 ''' % {"argv0": argv0
})
93 def extract_ofp_actions(fn
, definitions
):
99 for code
, size
in vendor_map
.values():
109 if re
.match('enum ofp_raw_action_type {', line
):
114 if line
.startswith('/*') or not line
or line
.isspace():
116 elif re
.match('}', line
):
119 if not line
.lstrip().startswith('/*'):
120 fatal("unexpected syntax between actions")
122 comment
= line
.lstrip()[2:].strip()
123 while not comment
.endswith('*/'):
125 if line
.startswith('/*') or not line
or line
.isspace():
126 fatal("unexpected syntax within action")
127 comment
+= ' %s' % line
.lstrip('* \t').rstrip(' \t\r\n')
128 comment
= re
.sub('\[[^]]*\]', '', comment
)
129 comment
= comment
[:-2].rstrip()
131 m
= re
.match('([^:]+):\s+(.*)$', comment
)
133 fatal("unexpected syntax between actions")
136 argtypes
= m
.group(2).strip().replace('.', '', 1)
138 if 'VLMFF' in argtypes
:
139 arg_vl_mff_map
= True
141 arg_vl_mff_map
= False
142 argtype
= argtypes
.replace('VLMFF', '', 1).rstrip()
145 m
= re
.match(r
'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line
)
147 fatal("syntax error expecting enum value")
151 fatal("%s specified twice" % enum
)
155 for dst
in dsts
.split(', '):
156 m
= re
.match(r
'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst
)
158 fatal("%r: syntax error in destination" % dst
)
159 vendor_name
= m
.group(1)
160 version1_name
= m
.group(2)
161 version2_name
= m
.group(3)
162 type_
= int(m
.group(4))
163 deprecation
= m
.group(5)
165 if vendor_name
not in vendor_map
:
166 fatal("%s: unknown vendor" % vendor_name
)
167 vendor
= vendor_map
[vendor_name
][0]
169 if version1_name
not in version_map
:
170 fatal("%s: unknown OpenFlow version" % version1_name
)
171 v1
= version_map
[version1_name
]
173 if version2_name
is None:
175 elif version2_name
== "+":
176 v2
= max(version_map
.values())
177 elif version2_name
[1:] not in version_map
:
178 fatal("%s: unknown OpenFlow version" % version2_name
[1:])
180 v2
= version_map
[version2_name
[1:]]
183 fatal("%s%s: %s precedes %s"
184 % (version1_name
, version2_name
,
185 version2_name
, version1_name
))
187 for version
in range(v1
, v2
+ 1):
188 domain
[vendor
].setdefault(type_
, {})
189 if version
in domain
[vendor
][type_
]:
190 v
= domain
[vendor
][type_
][version
]
191 msg
= "%#x,%d in OF%s means both %s and %s" % (
192 vendor
, type_
, version_reverse_map
[version
],
194 error("%s: %s." % (dst
, msg
))
195 sys
.stderr
.write("%s:%d: %s: Here is the location "
196 "of the previous definition.\n"
197 % (v
["file_name"], v
["line_number"],
201 header_len
= vendor_map
[vendor_name
][1]
203 base_argtype
= argtype
.replace(', ..', '', 1)
204 if base_argtype
in types
:
205 arg_align
= types
[base_argtype
]['align']
206 arg_len
= types
[base_argtype
]['size']
207 arg_ofs
= round_up(header_len
, arg_align
)
208 min_length
= round_up(arg_ofs
+ arg_len
,
210 elif base_argtype
== 'void':
211 min_length
= round_up(header_len
, OFP_ACTION_ALIGN
)
214 elif re
.match(r
'struct [a-zA-Z0-9_]+$', base_argtype
):
215 min_length
= 'sizeof(%s)' % base_argtype
216 arg_structs
.add(base_argtype
)
219 # should also emit OFP_ACTION_ALIGN assertion
221 fatal("bad argument type %s" % argtype
)
223 ellipsis
= argtype
!= base_argtype
225 max_length
= '65536 - OFP_ACTION_ALIGN'
227 max_length
= min_length
229 info
= {"enum": enum
, # 0
230 "deprecation": deprecation
, # 1
231 "file_name": file_name
, # 2
232 "line_number": line_number
, # 3
233 "min_length": min_length
, # 4
234 "max_length": max_length
, # 5
235 "arg_ofs": arg_ofs
, # 6
236 "arg_len": arg_len
, # 7
237 "base_argtype": base_argtype
, # 8
238 "arg_vl_mff_map": arg_vl_mff_map
, # 9
239 "version": version
, # 10
241 domain
[vendor
][type_
][version
] = info
243 enums
.setdefault(enum
, [])
244 enums
[enum
].append(info
)
252 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
256 print("/* Verify that structs used as actions are reasonable sizes. */")
257 for s
in sorted(arg_structs
):
258 print("BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s
)
260 print("\nstatic struct ofpact_raw_instance all_raw_instances[] = {")
261 for vendor
in domain
:
262 for type_
in domain
[vendor
]:
263 for version
in domain
[vendor
][type_
]:
264 d
= domain
[vendor
][type_
][version
]
265 print(" { { 0x%08x, %2d, 0x%02x }, " % (
266 vendor
, type_
, version
))
267 print(" %s," % d
["enum"])
268 print(" HMAP_NODE_NULL_INITIALIZER,")
269 print(" HMAP_NODE_NULL_INITIALIZER,")
270 print(" %s," % d
["min_length"])
271 print(" %s," % d
["max_length"])
272 print(" %s," % d
["arg_ofs"])
273 print(" %s," % d
["arg_len"])
274 print(" \"%s\"," % re
.sub('_RAW[0-9]*', '', d
["enum"], 1))
276 print(" \"%s\"," % re
.sub(r
'(["\\])', r
'\\\1', d
["deprecation"]))
282 for versions
in enums
.values():
283 need_ofp_version
= False
285 assert v
["arg_len"] == versions
[0]["arg_len"]
286 assert v
["base_argtype"] == versions
[0]["base_argtype"]
287 if (v
["min_length"] != versions
[0]["min_length"] or
288 v
["arg_ofs"] != versions
[0]["arg_ofs"] or
289 v
["type"] != versions
[0]["type"]):
290 need_ofp_version
= True
291 base_argtype
= versions
[0]["base_argtype"]
293 decl
= "static inline "
294 if base_argtype
.startswith('struct'):
295 decl
+= "%s *" %base_argtype
298 decl
+= "\nput_%s(struct ofpbuf *openflow" % versions
[0]["enum"].replace('_RAW', '', 1)
300 decl
+= ", enum ofp_version version"
301 if base_argtype
!= 'void' and not base_argtype
.startswith('struct'):
302 decl
+= ", %s arg" % base_argtype
307 if base_argtype
.startswith('struct'):
309 decl
+= "ofpact_put_raw(openflow, "
313 decl
+= "%s" % versions
[0]["version"]
314 decl
+= ", %s, " % versions
[0]["enum"]
315 if base_argtype
.startswith('struct') or base_argtype
== 'void':
318 ntoh
= types
[base_argtype
]['ntoh']
320 decl
+= "%s(arg)" % ntoh
333 ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
334 enum ofp_version version, uint64_t arg,
335 const struct vl_mff_map *vl_mff_map,
336 uint64_t *tlv_bitmap, struct ofpbuf *out)
340 for versions
in enums
.values():
341 enum
= versions
[0]["enum"]
342 print(" case %s:" % enum
)
343 base_argtype
= versions
[0]["base_argtype"]
344 arg_vl_mff_map
= versions
[0]["arg_vl_mff_map"]
345 if base_argtype
== 'void':
346 print(" return decode_%s(out);" % enum
)
348 if base_argtype
.startswith('struct'):
349 arg
= "ALIGNED_CAST(const %s *, a)" % base_argtype
351 hton
= types
[base_argtype
]['hton']
353 arg
= "%s(arg)" % hton
357 print(" return decode_%s(%s, version, vl_mff_map, tlv_bitmap, out);" % (enum
, arg
))
359 print(" return decode_%s(%s, version, out);" % (enum
, arg
))
368 for versions
in enums
.values():
369 enum
= versions
[0]["enum"]
370 prototype
= "static enum ofperr decode_%s(" % enum
371 base_argtype
= versions
[0]["base_argtype"]
372 arg_vl_mff_map
= versions
[0]["arg_vl_mff_map"]
373 if base_argtype
!= 'void':
374 if base_argtype
.startswith('struct'):
375 prototype
+= "const %s *, enum ofp_version, " % base_argtype
377 prototype
+= "%s, enum ofp_version, " % base_argtype
379 prototype
+= 'const struct vl_mff_map *, uint64_t *, '
380 prototype
+= "struct ofpbuf *);"
384 static enum ofperr ofpact_decode(const struct ofp_action_header *,
385 enum ofp_raw_action_type raw,
386 enum ofp_version version,
387 uint64_t arg, const struct vl_mff_map *vl_mff_map,
388 uint64_t *tlv_bitmap, struct ofpbuf *out);
391 ## ------------------------ ##
392 ## Documentation Generation ##
393 ## ------------------------ ##
395 def action_to_xml(action_node
, body
):
397 for node
in action_node
.childNodes
:
398 if node
.nodeType
== node
.ELEMENT_NODE
and node
.tagName
== 'syntax':
399 if body
[-1].strip() == '.PP':
404 body
+= ['.IP "\\fBSyntax:\\fR"\n']
405 body
+= [build
.nroff
.inline_xml_to_nroff(x
, r
'\fR')
406 for x
in node
.childNodes
] + ['\n']
408 elif (node
.nodeType
== node
.ELEMENT_NODE
409 and node
.tagName
== 'conformance'):
410 body
+= ['.IP "\\fBConformance:\\fR"\n']
411 body
+= [build
.nroff
.block_xml_to_nroff(node
.childNodes
)]
413 body
+= [build
.nroff
.block_xml_to_nroff([node
])]
415 def group_xml_to_nroff(group_node
):
416 title
= group_node
.attributes
['title'].nodeValue
419 for node
in group_node
.childNodes
:
420 if node
.nodeType
== node
.ELEMENT_NODE
and node
.tagName
== 'action':
421 action_to_xml(node
, body
)
423 body
+= [build
.nroff
.block_xml_to_nroff([node
])]
427 '.SH \"%s\"\n' % build
.nroff
.text_to_nroff(title
.upper())]
429 return ''.join(content
)
431 def make_ovs_actions(ovs_actions_xml
):
432 document
= xml
.dom
.minidom
.parse(ovs_actions_xml
)
433 doc
= document
.documentElement
441 .\\" -*- mode: troff; coding: utf-8 -*-
442 .TH "ovs\-actions" 7 "%s" "Open vSwitch" "Open vSwitch Manual"
443 .fp 5 L CR \\" Make fixed-width font available as \\fL.
468 \\\\$2 \\(laURL: \\\\$1 \\(ra\\\\$3
470 .if \\n[.g] .mso www.tmac
472 ovs\-actions \- OpenFlow actions and instructions with Open vSwitch extensions
478 for node
in doc
.childNodes
:
479 if node
.nodeType
== node
.ELEMENT_NODE
and node
.tagName
== "group":
480 s
+= group_xml_to_nroff(node
)
481 elif node
.nodeType
== node
.TEXT_NODE
:
482 assert node
.data
.isspace()
483 elif node
.nodeType
== node
.COMMENT_NODE
:
486 s
+= build
.nroff
.block_xml_to_nroff([node
])
492 for oline
in s
.split("\n"):
493 oline
= oline
.strip()
495 # Life is easier with nroff if we don't try to feed it Unicode.
496 # Fortunately, we only use a few characters outside the ASCII range.
497 oline
= oline
.replace(u
'\u2208', r
'\[mo]')
498 oline
= oline
.replace(u
'\u2260', r
'\[!=]')
499 oline
= oline
.replace(u
'\u2264', r
'\[<=]')
500 oline
= oline
.replace(u
'\u2265', r
'\[>=]')
501 oline
= oline
.replace(u
'\u00d7', r
'\[mu]')
505 # nroff tends to ignore .bp requests if they come after .PP requests,
506 # so remove .PPs that precede .bp.
507 for i
in range(len(output
)):
508 if output
[i
] == '.bp':
510 while j
>= 0 and output
[j
] == '.PP':
513 for i
in range(len(output
)):
514 if output
[i
] is not None:
522 if __name__
== '__main__':
525 options
, args
= getopt
.gnu_getopt(sys
.argv
[1:], 'h',
526 ['help', 'ovs-version='])
527 except getopt
.GetoptError
as geo
:
528 sys
.stderr
.write("%s: %s\n" % (argv0
, geo
.msg
))
533 for key
, value
in options
:
534 if key
in ['-h', '--help']:
536 elif key
== '--ovs-version':
542 sys
.stderr
.write("%s: missing command argument "
543 "(use --help for help)\n" % argv0
)
546 commands
= {"prototypes": (lambda fn
: extract_ofp_actions(fn
, False), 1),
547 "definitions": (lambda fn
: extract_ofp_actions(fn
, True), 1),
548 "ovs-actions": (make_ovs_actions
, 1)}
550 if not args
[0] in commands
:
551 sys
.stderr
.write("%s: unknown command \"%s\" "
552 "(use --help for help)\n" % (argv0
, args
[0]))
555 func
, n_args
= commands
[args
[0]]
556 if len(args
) - 1 != n_args
:
557 sys
.stderr
.write("%s: \"%s\" requires %d arguments but %d "
559 % (argv0
, args
[0], n_args
, len(args
) - 1))