]> git.proxmox.com Git - ceph.git/blob - ceph/src/dpdk/examples/ip_pipeline/config/diagram-generator.py
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / dpdk / examples / ip_pipeline / config / diagram-generator.py
1 #!/usr/bin/env python
2
3 # BSD LICENSE
4 #
5 # Copyright(c) 2016 Intel Corporation. All rights reserved.
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 #
12 # * Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 # * Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in
16 # the documentation and/or other materials provided with the
17 # distribution.
18 # * Neither the name of Intel Corporation nor the names of its
19 # contributors may be used to endorse or promote products derived
20 # from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34 #
35 # This script creates a visual representation for a configuration file used by
36 # the DPDK ip_pipeline application.
37 #
38 # The input configuration file is translated to an output file in DOT syntax,
39 # which is then used to create the image file using graphviz (www.graphviz.org).
40 #
41
42 from __future__ import print_function
43 import argparse
44 import re
45 import os
46
47 #
48 # Command to generate the image file
49 #
50 DOT_COMMAND = 'dot -Gsize=20,30 -Tpng %s > %s'
51
52 #
53 # Layout of generated DOT file
54 #
55 DOT_INTRO = \
56 '#\n# Command to generate image file:\n# \t%s\n#\n\n'
57 DOT_GRAPH_BEGIN = \
58 'digraph g {\n graph [ splines = true rankdir = "LR" ]\n'
59 DOT_NODE_LINK_RX = \
60 ' "%s RX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
61 DOT_NODE_LINK_TX = \
62 ' "%s TX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
63 DOT_NODE_KNI_RX = \
64 ' "%s RX" [ shape = box style = filled fillcolor = orange ]\n'
65 DOT_NODE_KNI_TX = \
66 ' "%s TX" [ shape = box style = filled fillcolor = orange ]\n'
67 DOT_NODE_TAP_RX = \
68 ' "%s RX" [ shape = box style = filled fillcolor = gold ]\n'
69 DOT_NODE_TAP_TX = \
70 ' "%s TX" [ shape = box style = filled fillcolor = gold ]\n'
71 DOT_NODE_SOURCE = \
72 ' "%s" [ shape = box style = filled fillcolor = darkgreen ]\n'
73 DOT_NODE_SINK = \
74 ' "%s" [ shape = box style = filled fillcolor = peachpuff ]\n'
75 DOT_NODE_PIPELINE = \
76 ' "%s" [ shape = box style = filled fillcolor = royalblue ]\n'
77 DOT_EDGE_PKTQ = \
78 ' "%s" -> "%s" [ label = "%s" color = gray ]\n'
79 DOT_GRAPH_END = \
80 '}\n'
81
82 # Relationships between the graph nodes and the graph edges:
83 #
84 # Edge ID | Edge Label | Writer Node | Reader Node | Dependencies
85 # --------+------------+-------------+---------------+--------------
86 # RXQx.y | RXQx.y | LINKx | PIPELINEz | LINKx
87 # TXQx.y | TXQx.y | PIPELINEz | LINKx | LINKx
88 # SWQx | SWQx | PIPELINEy | PIPELINEz | -
89 # TMx | TMx | PIPELINEy | PIPELINEz | LINKx
90 # KNIx RX | KNIx | KNIx RX | PIPELINEy | KNIx, LINKx
91 # KNIx TX | KNIx | PIPELINEy | KNIx TX | KNIx, LINKx
92 # TAPx RX | TAPx | TAPx RX | PIPELINEy | TAPx
93 # TAPx TX | TAPx | PIPELINEy | TAPx TX | TAPx
94 # SOURCEx | SOURCEx | SOURCEx | PIPELINEy | SOURCEx
95 # SINKx | SINKx | PIPELINEy | SINKx | SINKx
96
97 #
98 # Parse the input configuration file to detect the graph nodes and edges
99 #
100 def process_config_file(cfgfile):
101 edges = {}
102 links = set()
103 knis = set()
104 taps = set()
105 sources = set()
106 sinks = set()
107 pipelines = set()
108 pipeline = ''
109
110 dotfile = cfgfile + '.txt'
111 imgfile = cfgfile + '.png'
112
113 #
114 # Read configuration file
115 #
116 lines = open(cfgfile, 'r')
117 for line in lines:
118 # Remove any leading and trailing white space characters
119 line = line.strip()
120
121 # Remove any comment at end of line
122 line, sep, tail = line.partition(';')
123
124 # Look for next "PIPELINE" section
125 match = re.search(r'\[(PIPELINE\d+)\]', line)
126 if match:
127 pipeline = match.group(1)
128 continue
129
130 # Look for next "pktq_in" section entry
131 match = re.search(r'pktq_in\s*=\s*(.+)', line)
132 if match:
133 pipelines.add(pipeline)
134 for q in re.findall('\S+', match.group(1)):
135 match_rxq = re.search(r'^RXQ(\d+)\.\d+$', q)
136 match_swq = re.search(r'^SWQ\d+$', q)
137 match_tm = re.search(r'^TM(\d+)$', q)
138 match_kni = re.search(r'^KNI(\d+)$', q)
139 match_tap = re.search(r'^TAP\d+$', q)
140 match_source = re.search(r'^SOURCE\d+$', q)
141
142 # Set ID for the current packet queue (graph edge)
143 q_id = ''
144 if match_rxq or match_swq or match_tm or match_source:
145 q_id = q
146 elif match_kni or match_tap:
147 q_id = q + ' RX'
148 else:
149 print('Error: Unrecognized pktq_in element "%s"' % q)
150 return
151
152 # Add current packet queue to the set of graph edges
153 if q_id not in edges:
154 edges[q_id] = {}
155 if 'label' not in edges[q_id]:
156 edges[q_id]['label'] = q
157 if 'readers' not in edges[q_id]:
158 edges[q_id]['readers'] = []
159 if 'writers' not in edges[q_id]:
160 edges[q_id]['writers'] = []
161
162 # Add reader for the new edge
163 edges[q_id]['readers'].append(pipeline)
164
165 # Check for RXQ
166 if match_rxq:
167 link = 'LINK' + str(match_rxq.group(1))
168 edges[q_id]['writers'].append(link + ' RX')
169 links.add(link)
170 continue
171
172 # Check for SWQ
173 if match_swq:
174 continue
175
176 # Check for TM
177 if match_tm:
178 link = 'LINK' + str(match_tm.group(1))
179 links.add(link)
180 continue
181
182 # Check for KNI
183 if match_kni:
184 link = 'LINK' + str(match_kni.group(1))
185 edges[q_id]['writers'].append(q_id)
186 knis.add(q)
187 links.add(link)
188 continue
189
190 # Check for TAP
191 if match_tap:
192 edges[q_id]['writers'].append(q_id)
193 taps.add(q)
194 continue
195
196 # Check for SOURCE
197 if match_source:
198 edges[q_id]['writers'].append(q)
199 sources.add(q)
200 continue
201
202 continue
203
204 # Look for next "pktq_out" section entry
205 match = re.search(r'pktq_out\s*=\s*(.+)', line)
206 if match:
207 for q in re.findall('\S+', match.group(1)):
208 match_txq = re.search(r'^TXQ(\d+)\.\d+$', q)
209 match_swq = re.search(r'^SWQ\d+$', q)
210 match_tm = re.search(r'^TM(\d+)$', q)
211 match_kni = re.search(r'^KNI(\d+)$', q)
212 match_tap = re.search(r'^TAP(\d+)$', q)
213 match_sink = re.search(r'^SINK(\d+)$', q)
214
215 # Set ID for the current packet queue (graph edge)
216 q_id = ''
217 if match_txq or match_swq or match_tm or match_sink:
218 q_id = q
219 elif match_kni or match_tap:
220 q_id = q + ' TX'
221 else:
222 print('Error: Unrecognized pktq_out element "%s"' % q)
223 return
224
225 # Add current packet queue to the set of graph edges
226 if q_id not in edges:
227 edges[q_id] = {}
228 if 'label' not in edges[q_id]:
229 edges[q_id]['label'] = q
230 if 'readers' not in edges[q_id]:
231 edges[q_id]['readers'] = []
232 if 'writers' not in edges[q_id]:
233 edges[q_id]['writers'] = []
234
235 # Add writer for the new edge
236 edges[q_id]['writers'].append(pipeline)
237
238 # Check for TXQ
239 if match_txq:
240 link = 'LINK' + str(match_txq.group(1))
241 edges[q_id]['readers'].append(link + ' TX')
242 links.add(link)
243 continue
244
245 # Check for SWQ
246 if match_swq:
247 continue
248
249 # Check for TM
250 if match_tm:
251 link = 'LINK' + str(match_tm.group(1))
252 links.add(link)
253 continue
254
255 # Check for KNI
256 if match_kni:
257 link = 'LINK' + str(match_kni.group(1))
258 edges[q_id]['readers'].append(q_id)
259 knis.add(q)
260 links.add(link)
261 continue
262
263 # Check for TAP
264 if match_tap:
265 edges[q_id]['readers'].append(q_id)
266 taps.add(q)
267 continue
268
269 # Check for SINK
270 if match_sink:
271 edges[q_id]['readers'].append(q)
272 sinks.add(q)
273 continue
274
275 continue
276
277 #
278 # Write DOT file
279 #
280 print('Creating DOT file "%s" ...' % dotfile)
281 dot_cmd = DOT_COMMAND % (dotfile, imgfile)
282 file = open(dotfile, 'w')
283 file.write(DOT_INTRO % dot_cmd)
284 file.write(DOT_GRAPH_BEGIN)
285
286 # Write the graph nodes to the DOT file
287 for l in sorted(links):
288 file.write(DOT_NODE_LINK_RX % l)
289 file.write(DOT_NODE_LINK_TX % l)
290 for k in sorted(knis):
291 file.write(DOT_NODE_KNI_RX % k)
292 file.write(DOT_NODE_KNI_TX % k)
293 for t in sorted(taps):
294 file.write(DOT_NODE_TAP_RX % t)
295 file.write(DOT_NODE_TAP_TX % t)
296 for s in sorted(sources):
297 file.write(DOT_NODE_SOURCE % s)
298 for s in sorted(sinks):
299 file.write(DOT_NODE_SINK % s)
300 for p in sorted(pipelines):
301 file.write(DOT_NODE_PIPELINE % p)
302
303 # Write the graph edges to the DOT file
304 for q in sorted(edges.keys()):
305 rw = edges[q]
306 if 'writers' not in rw:
307 print('Error: "%s" has no writer' % q)
308 return
309 if 'readers' not in rw:
310 print('Error: "%s" has no reader' % q)
311 return
312 for w in rw['writers']:
313 for r in rw['readers']:
314 file.write(DOT_EDGE_PKTQ % (w, r, rw['label']))
315
316 file.write(DOT_GRAPH_END)
317 file.close()
318
319 #
320 # Execute the DOT command to create the image file
321 #
322 print('Creating image file "%s" ...' % imgfile)
323 if os.system('which dot > /dev/null'):
324 print('Error: Unable to locate "dot" executable.' \
325 'Please install the "graphviz" package (www.graphviz.org).')
326 return
327
328 os.system(dot_cmd)
329
330
331 if __name__ == '__main__':
332 parser = argparse.ArgumentParser(description=\
333 'Create diagram for IP pipeline configuration file.')
334
335 parser.add_argument(
336 '-f',
337 '--file',
338 help='input configuration file (e.g. "ip_pipeline.cfg")',
339 required=True)
340
341 args = parser.parse_args()
342
343 process_config_file(args.file)