feat(reassembly): add few suites for ip reassembly
[csit.git] / resources / libraries / python / autogen / Regenerator.py
1 # Copyright (c) 2023 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Module defining utilities for test directory regeneration.
15
16 TODO: How can we check each suite id is unique,
17       when currently the suite generation is run on each directory separately?
18 """
19
20 import copy
21 import sys
22
23 from glob import glob
24 from io import open
25 from os import getcwd
26
27
28 from resources.libraries.python.Constants import Constants
29 from resources.libraries.python.autogen.Testcase import Testcase
30
31
32 PROTOCOL_TO_MIN_FRAME_SIZE = {
33     u"ip4": 64,
34     u"ip6": 78,
35     u"ethip4vxlan": 114,  # What is the real minimum for latency stream?
36     u"dot1qip4vxlan": 118
37 }
38 MIN_FRAME_SIZE_VALUES = list(PROTOCOL_TO_MIN_FRAME_SIZE.values())
39
40
41 def replace_defensively(
42         whole, to_replace, replace_with, how_many, msg, in_filename):
43     """Replace substrings while checking the number of occurrences.
44
45     Return edited copy of the text. Assuming "whole" is really a string,
46     or something else with .replace not affecting it.
47
48     :param whole: The text to perform replacements on.
49     :param to_replace: Substring occurrences of which to replace.
50     :param replace_with: Substring to replace occurrences with.
51     :param how_many: Number of occurrences to expect.
52     :param msg: Error message to raise.
53     :param in_filename: File name in which the error occurred.
54     :type whole: str
55     :type to_replace: str
56     :type replace_with: str
57     :type how_many: int
58     :type msg: str
59     :type in_filename: str
60     :returns: The whole text after replacements are done.
61     :rtype: str
62     :raises ValueError: If number of occurrences does not match.
63     """
64     found = whole.count(to_replace)
65     if found != how_many:
66         raise ValueError(f"{in_filename}: {msg}")
67     return whole.replace(to_replace, replace_with)
68
69
70 def get_iface_and_suite_ids(filename):
71     """Get NIC code, suite ID and suite tag.
72
73     NIC code is the part of suite name
74     which should be replaced for other NIC.
75     Suite ID is the part os suite name
76     which is appended to test case names.
77     Suite tag is suite ID without both test type and NIC driver parts.
78
79     :param filename: Suite file.
80     :type filename: str
81     :returns: NIC code, suite ID, suite tag.
82     :rtype: 3-tuple of str
83     """
84     dash_split = filename.split(u"-", 1)
85     if len(dash_split[0]) <= 4:
86         # It was something like "2n1l", we need one more split.
87         dash_split = dash_split[1].split(u"-", 1)
88     nic_code = dash_split[0]
89     suite_id = dash_split[1].split(u".robot", 1)[0]
90     suite_tag = suite_id.rsplit(u"-", 1)[0]
91     for prefix in Constants.FORBIDDEN_SUITE_PREFIX_LIST:
92         if suite_tag.startswith(prefix):
93             suite_tag = suite_tag[len(prefix):]
94     return nic_code, suite_id, suite_tag
95
96
97 def check_suite_tag(suite_tag, prolog):
98     """Verify suite tag occurres once in prolog.
99
100     Call this after all edits are done,
101     to confirm the (edited) suite tag still matches the (edited) suite name.
102
103     Currently, the edited suite tag is expect to be identical
104     to the primary suite tag, but having a function is more flexible.
105
106     The occurences are counted including "| " prefix,
107     to lower the chance to match a comment.
108
109     :param suite_tag: Part of suite name, between NIC driver and suite type.
110     :param prolog: The part of .robot file content without test cases.
111     :type suite_tag: str
112     :type prolog: str
113     :raises ValueError: If suite_tag not found exactly once.
114     """
115     found = prolog.count(u"| " + suite_tag)
116     if found != 1:
117         raise ValueError(f"Suite tag found {found} times for {suite_tag}")
118
119
120 def filter_and_edit_kwargs_for_astf(suite_id, kwargs):
121     """Return possibly edited kwargs, or None if to be skipped.
122
123     This is a code block used in few places.
124     Kwargs is (a copy of) one item from tc_kwargs_list.
125     Currently, the editable field is frame_size,
126     to be increased to for tests with data (not just CPS).
127
128     :param suite_id: Suite ID.
129     :param kwargs: Key-value pairs used to construct one testcase.
130     :type suite_id: str
131     :type tc_kwargs_list: dict
132     :returns: Edited kwargs.
133     :rtype Optional[dict]
134     """
135     if u"-cps-" in suite_id:
136         # Contrary to UDP, there is no place to affect frame size
137         # in TCP CPS tests. Actual frames are close to min size.
138         # UDP uses the min value too, for fairer comparison to TCP.
139         if kwargs[u"frame_size"] not in MIN_FRAME_SIZE_VALUES:
140             return None
141     elif (u"-pps-" in suite_id or u"-tput-" in suite_id):
142         if u"imix" in str(kwargs[u"frame_size"]).lower():
143             # ASTF does not support IMIX (yet).
144             return None
145         if kwargs[u"frame_size"] in MIN_FRAME_SIZE_VALUES:
146             # Minimal (TRex) TCP data frame is 80B for IPv4.
147             # In future, we may want to have also IPv6 TCP.
148             # UDP uses the same value, for fairer comparison to TCP.
149             kwargs[u"frame_size"] = 100
150     return kwargs
151
152
153 def add_default_testcases(testcase, iface, suite_id, file_out, tc_kwargs_list):
154     """Add default testcases to file.
155
156     :param testcase: Testcase class.
157     :param iface: Interface.
158     :param suite_id: Suite ID.
159     :param file_out: File to write testcases to.
160     :param tc_kwargs_list: Key-value pairs used to construct testcases.
161     :type testcase: Testcase
162     :type iface: str
163     :type suite_id: str
164     :type file_out: file
165     :type tc_kwargs_list: dict
166     """
167     for kwas in tc_kwargs_list:
168         # We may edit framesize for ASTF, the copy should be local.
169         kwargs = copy.deepcopy(kwas)
170         # TODO: Is there a better way to disable some combinations?
171         emit = True
172         if kwargs[u"frame_size"] == 9000:
173             if u"vic1227" in iface:
174                 # Not supported in HW.
175                 emit = False
176             if u"vic1385" in iface:
177                 # Not supported in HW.
178                 emit = False
179         if u"-16vm2t-" in suite_id or u"-16dcr2t-" in suite_id:
180             if kwargs[u"phy_cores"] > 3:
181                 # CSIT lab only has 28 (physical) core processors,
182                 # so these test would fail when attempting to assign cores.
183                 emit = False
184         if u"-24vm1t-" in suite_id or u"-24dcr1t-" in suite_id:
185             if kwargs[u"phy_cores"] > 3:
186                 # CSIT lab only has 28 (physical) core processors,
187                 # so these test would fail when attempting to assign cores.
188                 emit = False
189         if u"soak" in suite_id:
190             # Soak test take too long, do not risk other than tc01.
191             if kwargs[u"phy_cores"] != 1:
192                 emit = False
193             if u"reassembly" in suite_id:
194                 if kwargs[u"frame_size"] != 1518:
195                     emit = False
196             else:
197                 if kwargs[u"frame_size"] not in MIN_FRAME_SIZE_VALUES:
198                     emit = False
199         kwargs = filter_and_edit_kwargs_for_astf(suite_id, kwargs)
200         if emit and kwargs is not None:
201             file_out.write(testcase.generate(**kwargs))
202
203
204 def add_tcp_testcases(testcase, file_out, tc_kwargs_list):
205     """Add TCP testcases to file.
206
207     :param testcase: Testcase class.
208     :param file_out: File to write testcases to.
209     :param tc_kwargs_list: Key-value pairs used to construct testcases.
210     :type testcase: Testcase
211     :type file_out: file
212     :type tc_kwargs_list: dict
213     """
214     for kwargs in tc_kwargs_list:
215         file_out.write(testcase.generate(**kwargs))
216
217
218 def add_iperf3_testcases(testcase, file_out, tc_kwargs_list):
219     """Add iperf3 testcases to file.
220
221     :param testcase: Testcase class.
222     :param file_out: File to write testcases to.
223     :param tc_kwargs_list: Key-value pairs used to construct testcases.
224     :type testcase: Testcase
225     :type file_out: file
226     :type tc_kwargs_list: dict
227     """
228     for kwargs in tc_kwargs_list:
229         file_out.write(testcase.generate(**kwargs))
230
231
232 def add_trex_testcases(testcase, suite_id, file_out, tc_kwargs_list):
233     """Add trex testcases to file.
234
235     :param testcase: Testcase class.
236     :param suite_id: Suite ID.
237     :param file_out: File to write testcases to.
238     :param tc_kwargs_list: Key-value pairs used to construct testcases.
239     :type testcase: Testcase
240     :type suite_id: str
241     :type file_out: file
242     :type tc_kwargs_list: dict
243     """
244     for kwas in tc_kwargs_list:
245         # We may edit framesize for ASTF, the copy should be local.
246         kwargs = copy.deepcopy(kwas)
247         kwargs = filter_and_edit_kwargs_for_astf(suite_id, kwargs)
248         if kwargs is not None:
249             file_out.write(testcase.generate(**kwargs))
250
251
252 def write_default_files(in_filename, in_prolog, kwargs_list):
253     """Using given filename and prolog, write all generated suites.
254
255     :param in_filename: Template filename to derive real filenames from.
256     :param in_prolog: Template content to derive real content from.
257     :param kwargs_list: List of kwargs for add_default_testcase.
258     :type in_filename: str
259     :type in_prolog: str
260     :type kwargs_list: list of dict
261     """
262     for suite_type in Constants.PERF_TYPE_TO_KEYWORD:
263         tmp_filename = replace_defensively(
264             in_filename, u"ndrpdr", suite_type, 1,
265             u"File name should contain suite type once.", in_filename
266         )
267         tmp_prolog = replace_defensively(
268             in_prolog, u"ndrpdr".upper(), suite_type.upper(), 1,
269             u"Suite type should appear once in uppercase (as tag).",
270             in_filename
271         )
272         tmp_prolog = replace_defensively(
273             tmp_prolog,
274             u"Find NDR and PDR intervals using optimized search",
275             Constants.PERF_TYPE_TO_KEYWORD[suite_type], 1,
276             u"Main search keyword should appear once in suite.",
277             in_filename
278         )
279         tmp_prolog = replace_defensively(
280             tmp_prolog,
281             Constants.PERF_TYPE_TO_SUITE_DOC_VER[u"ndrpdr"],
282             Constants.PERF_TYPE_TO_SUITE_DOC_VER[suite_type],
283             1, u"Exact suite type doc not found.", in_filename
284         )
285         tmp_prolog = replace_defensively(
286             tmp_prolog,
287             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[u"ndrpdr"],
288             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[suite_type],
289             1, u"Exact template type doc not found.", in_filename
290         )
291         _, suite_id, _ = get_iface_and_suite_ids(tmp_filename)
292         testcase = Testcase.default(suite_id)
293         for nic_name in Constants.NIC_NAME_TO_CODE:
294             tmp2_filename = replace_defensively(
295                 tmp_filename, u"10ge2p1x710",
296                 Constants.NIC_NAME_TO_CODE[nic_name], 1,
297                 u"File name should contain NIC code once.", in_filename
298             )
299             tmp2_prolog = replace_defensively(
300                 tmp_prolog, u"Intel-X710", nic_name, 2,
301                 u"NIC name should appear twice (tag and variable).",
302                 in_filename
303             )
304             if tmp2_prolog.count(u"HW_") == 2:
305                 # TODO CSIT-1481: Crypto HW should be read
306                 #      from topology file instead.
307                 if nic_name in Constants.NIC_NAME_TO_CRYPTO_HW:
308                     tmp2_prolog = replace_defensively(
309                         tmp2_prolog, u"HW_DH895xcc",
310                         Constants.NIC_NAME_TO_CRYPTO_HW[nic_name], 1,
311                         u"HW crypto name should appear.", in_filename
312                     )
313             iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
314                 tmp2_filename
315             )
316             if u"DPDK" in in_prolog:
317                 for driver in Constants.DPDK_NIC_NAME_TO_DRIVER[nic_name]:
318                     out_filename = replace_defensively(
319                         tmp2_filename, old_suite_id,
320                         Constants.DPDK_NIC_DRIVER_TO_SUITE_PREFIX[driver] \
321                             + old_suite_id,
322                         1, u"Error adding driver prefix.", in_filename
323                     )
324                     out_prolog = replace_defensively(
325                         tmp2_prolog, u"vfio-pci", driver, 1,
326                         u"Driver name should appear once.", in_filename
327                     )
328                     out_prolog = replace_defensively(
329                         out_prolog,
330                         Constants.DPDK_NIC_DRIVER_TO_TAG[u"vfio-pci"],
331                         Constants.DPDK_NIC_DRIVER_TO_TAG[driver], 1,
332                         u"Driver tag should appear once.", in_filename
333                     )
334                     iface, suite_id, suite_tag = get_iface_and_suite_ids(
335                         out_filename
336                     )
337                     # The next replace is probably a noop, but it is safer to
338                     # maintain the same structure as for other edits.
339                     out_prolog = replace_defensively(
340                         out_prolog, old_suite_tag, suite_tag, 1,
341                         f"Perf suite tag {old_suite_tag} should appear once.",
342                         in_filename
343                     )
344                     check_suite_tag(suite_tag, out_prolog)
345                     # TODO: Reorder loops so suite_id is finalized sooner.
346                     testcase = Testcase.default(suite_id)
347                     with open(out_filename, u"wt") as file_out:
348                         file_out.write(out_prolog)
349                         add_default_testcases(
350                             testcase, iface, suite_id, file_out, kwargs_list
351                         )
352                 continue
353             for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
354                 out_filename = replace_defensively(
355                     tmp2_filename, old_suite_id,
356                     Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
357                     1, u"Error adding driver prefix.", in_filename
358                 )
359                 out_prolog = replace_defensively(
360                     tmp2_prolog, u"vfio-pci", driver, 1,
361                     u"Driver name should appear once.", in_filename
362                 )
363                 out_prolog = replace_defensively(
364                     out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
365                     Constants.NIC_DRIVER_TO_TAG[driver], 1,
366                     u"Driver tag should appear once.", in_filename
367                 )
368                 out_prolog = replace_defensively(
369                     out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
370                     Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
371                     u"Driver plugin should appear once.", in_filename
372                 )
373                 out_prolog = replace_defensively(
374                     out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
375                     Constants.NIC_DRIVER_TO_VFS[driver], 1,
376                     u"NIC VFs argument should appear once.", in_filename
377                 )
378                 iface, suite_id, suite_tag = get_iface_and_suite_ids(
379                     out_filename
380                 )
381                 # The next replace is probably a noop, but it is safer to
382                 # maintain the same structure as for other edits.
383                 out_prolog = replace_defensively(
384                     out_prolog, old_suite_tag, suite_tag, 1,
385                     f"Perf suite tag {old_suite_tag} should appear once.",
386                     in_filename
387                 )
388                 check_suite_tag(suite_tag, out_prolog)
389                 # TODO: Reorder loops so suite_id is finalized sooner.
390                 testcase = Testcase.default(suite_id)
391                 with open(out_filename, u"wt") as file_out:
392                     file_out.write(out_prolog)
393                     add_default_testcases(
394                         testcase, iface, suite_id, file_out, kwargs_list
395                     )
396
397
398 def write_reconf_files(in_filename, in_prolog, kwargs_list):
399     """Using given filename and prolog, write all generated reconf suites.
400
401     Use this for suite type reconf, as its local template
402     is incompatible with mrr/ndrpdr/soak local template,
403     while test cases are compatible.
404
405     :param in_filename: Template filename to derive real filenames from.
406     :param in_prolog: Template content to derive real content from.
407     :param kwargs_list: List of kwargs for add_testcase.
408     :type in_filename: str
409     :type in_prolog: str
410     :type kwargs_list: list of dict
411     """
412     _, suite_id, _ = get_iface_and_suite_ids(in_filename)
413     testcase = Testcase.default(suite_id)
414     for nic_name in Constants.NIC_NAME_TO_CODE:
415         tmp_filename = replace_defensively(
416             in_filename, u"10ge2p1x710",
417             Constants.NIC_NAME_TO_CODE[nic_name], 1,
418             u"File name should contain NIC code once.", in_filename
419         )
420         tmp_prolog = replace_defensively(
421             in_prolog, u"Intel-X710", nic_name, 2,
422             u"NIC name should appear twice (tag and variable).",
423             in_filename
424         )
425         if tmp_prolog.count(u"HW_") == 2:
426             # TODO CSIT-1481: Crypto HW should be read
427             #      from topology file instead.
428             if nic_name in Constants.NIC_NAME_TO_CRYPTO_HW.keys():
429                 tmp_prolog = replace_defensively(
430                     tmp_prolog, u"HW_DH895xcc",
431                     Constants.NIC_NAME_TO_CRYPTO_HW[nic_name], 1,
432                     u"HW crypto name should appear.", in_filename
433                 )
434         iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
435             tmp_filename
436         )
437         for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
438             out_filename = replace_defensively(
439                 tmp_filename, old_suite_id,
440                 Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
441                 1, u"Error adding driver prefix.", in_filename
442             )
443             out_prolog = replace_defensively(
444                 tmp_prolog, u"vfio-pci", driver, 1,
445                 u"Driver name should appear once.", in_filename
446             )
447             out_prolog = replace_defensively(
448                 out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
449                 Constants.NIC_DRIVER_TO_TAG[driver], 1,
450                 u"Driver tag should appear once.", in_filename
451             )
452             out_prolog = replace_defensively(
453                 out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
454                 Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
455                 u"Driver plugin should appear once.", in_filename
456             )
457             out_prolog = replace_defensively(
458                 out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
459                 Constants.NIC_DRIVER_TO_VFS[driver], 1,
460                 u"NIC VFs argument should appear once.", in_filename
461             )
462             iface, suite_id, suite_tag = get_iface_and_suite_ids(out_filename)
463             out_prolog = replace_defensively(
464                 out_prolog, old_suite_tag, suite_tag, 1,
465                 u"Perf suite tag should appear once.", in_filename
466             )
467             check_suite_tag(suite_tag, out_prolog)
468             # TODO: Reorder loops so suite_id is finalized sooner.
469             testcase = Testcase.default(suite_id)
470             with open(out_filename, u"wt") as file_out:
471                 file_out.write(out_prolog)
472                 add_default_testcases(
473                     testcase, iface, suite_id, file_out, kwargs_list
474                 )
475
476
477 def write_tcp_files(in_filename, in_prolog, kwargs_list):
478     """Using given filename and prolog, write all generated tcp suites.
479
480     :param in_filename: Template filename to derive real filenames from.
481     :param in_prolog: Template content to derive real content from.
482     :param kwargs_list: List of kwargs for add_default_testcase.
483     :type in_filename: str
484     :type in_prolog: str
485     :type kwargs_list: list of dict
486     """
487     # TODO: Generate rps from cps? There are subtle differences.
488     _, suite_id, suite_tag = get_iface_and_suite_ids(in_filename)
489     testcase = Testcase.tcp(suite_id)
490     for nic_name in Constants.NIC_NAME_TO_CODE:
491         tmp_filename = replace_defensively(
492             in_filename, u"10ge2p1x710",
493             Constants.NIC_NAME_TO_CODE[nic_name], 1,
494             u"File name should contain NIC code once.", in_filename
495         )
496         tmp_prolog = replace_defensively(
497             in_prolog, u"Intel-X710", nic_name, 2,
498             u"NIC name should appear twice (tag and variable).",
499             in_filename
500         )
501         iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
502             tmp_filename
503         )
504         for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
505             out_filename = replace_defensively(
506                 tmp_filename, old_suite_id,
507                 Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
508                 1, u"Error adding driver prefix.", in_filename
509             )
510             out_prolog = replace_defensively(
511                 tmp_prolog, u"vfio-pci", driver, 1,
512                 u"Driver name should appear once.", in_filename
513             )
514             out_prolog = replace_defensively(
515                 out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
516                 Constants.NIC_DRIVER_TO_TAG[driver], 1,
517                 u"Driver tag should appear once.", in_filename
518             )
519             out_prolog = replace_defensively(
520                 out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
521                 Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
522                 u"Driver plugin should appear once.", in_filename
523             )
524             out_prolog = replace_defensively(
525                 out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
526                 Constants.NIC_DRIVER_TO_VFS[driver], 1,
527                 u"NIC VFs argument should appear once.", in_filename
528             )
529             iface, suite_id, suite_tag = get_iface_and_suite_ids(out_filename)
530             out_prolog = replace_defensively(
531                 out_prolog, old_suite_tag, suite_tag, 1,
532                 u"Perf suite tag should appear once.", in_filename
533             )
534             check_suite_tag(suite_tag, out_prolog)
535             testcase = Testcase.tcp(suite_id)
536             with open(out_filename, u"wt") as file_out:
537                 file_out.write(out_prolog)
538                 add_tcp_testcases(testcase, file_out, kwargs_list)
539
540
541 def write_iperf3_files(in_filename, in_prolog, kwargs_list):
542     """Using given filename and prolog, write all generated iperf3 suites.
543
544     :param in_filename: Template filename to derive real filenames from.
545     :param in_prolog: Template content to derive real content from.
546     :param kwargs_list: List of kwargs for add_default_testcase.
547     :type in_filename: str
548     :type in_prolog: str
549     :type kwargs_list: list of dict
550     """
551     _, suite_id, suite_tag = get_iface_and_suite_ids(in_filename)
552     testcase = Testcase.iperf3(suite_id)
553     for nic_name in Constants.NIC_NAME_TO_CODE:
554         out_filename = replace_defensively(
555             in_filename, u"10ge2p1x710",
556             Constants.NIC_NAME_TO_CODE[nic_name], 1,
557             u"File name should contain NIC code once.", in_filename
558         )
559         out_prolog = replace_defensively(
560             in_prolog, u"Intel-X710", nic_name, 2,
561             u"NIC name should appear twice (tag and variable).",
562             in_filename
563         )
564         check_suite_tag(suite_tag, out_prolog)
565         with open(out_filename, u"wt") as file_out:
566             file_out.write(out_prolog)
567             add_iperf3_testcases(testcase, file_out, kwargs_list)
568
569
570 def write_trex_files(in_filename, in_prolog, kwargs_list):
571     """Using given filename and prolog, write all generated trex suites.
572
573     :param in_filename: Template filename to derive real filenames from.
574     :param in_prolog: Template content to derive real content from.
575     :param kwargs_list: List of kwargs for add_trex_testcase.
576     :type in_filename: str
577     :type in_prolog: str
578     :type kwargs_list: list of dict
579     """
580     for suite_type in Constants.PERF_TYPE_TO_KEYWORD:
581         tmp_filename = replace_defensively(
582             in_filename, u"ndrpdr", suite_type, 1,
583             u"File name should contain suite type once.", in_filename
584         )
585         tmp_prolog = replace_defensively(
586             in_prolog, u"ndrpdr".upper(), suite_type.upper(), 1,
587             u"Suite type should appear once in uppercase (as tag).",
588             in_filename
589         )
590         tmp_prolog = replace_defensively(
591             tmp_prolog,
592             u"Find NDR and PDR intervals using optimized search",
593             Constants.PERF_TYPE_TO_KEYWORD[suite_type], 1,
594             u"Main search keyword should appear once in suite.",
595             in_filename
596         )
597         tmp_prolog = replace_defensively(
598             tmp_prolog,
599             Constants.PERF_TYPE_TO_SUITE_DOC_VER[u"ndrpdr"],
600             Constants.PERF_TYPE_TO_SUITE_DOC_VER[suite_type],
601             1, u"Exact suite type doc not found.", in_filename
602         )
603         tmp_prolog = replace_defensively(
604             tmp_prolog,
605             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[u"ndrpdr"],
606             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[suite_type],
607             1, u"Exact template type doc not found.", in_filename
608         )
609         _, suite_id, suite_tag = get_iface_and_suite_ids(tmp_filename)
610         testcase = Testcase.trex(suite_id)
611         for nic_name in Constants.NIC_NAME_TO_CODE:
612             out_filename = replace_defensively(
613                 tmp_filename, u"10ge2p1x710",
614                 Constants.NIC_NAME_TO_CODE[nic_name], 1,
615                 u"File name should contain NIC code once.", in_filename
616             )
617             out_prolog = replace_defensively(
618                 tmp_prolog, u"Intel-X710", nic_name, 2,
619                 u"NIC name should appear twice (tag and variable).",
620                 in_filename
621             )
622             check_suite_tag(suite_tag, out_prolog)
623             with open(out_filename, u"wt") as file_out:
624                 file_out.write(out_prolog)
625                 add_trex_testcases(testcase, suite_id, file_out, kwargs_list)
626
627
628 def write_device_files(in_filename, in_prolog, kwargs_list):
629     """Using given filename and prolog, write all generated suites.
630
631     :param in_filename: Template filename to derive real filenames from.
632     :param in_prolog: Template content to derive real content from.
633     :param kwargs_list: List of kwargs for add_default_testcase.
634     :type in_filename: str
635     :type in_prolog: str
636     :type kwargs_list: list of dict
637     """
638     for suite_type in Constants.DEVICE_TYPE_TO_KEYWORD:
639         tmp_filename = replace_defensively(
640             in_filename, u"scapy", suite_type, 1,
641             u"File name should contain suite type once.", in_filename
642         )
643         _, suite_id, _ = get_iface_and_suite_ids(tmp_filename)
644         testcase = Testcase.default(suite_id)
645         for nic_name in Constants.NIC_NAME_TO_CODE:
646             tmp2_filename = replace_defensively(
647                 tmp_filename, u"10ge2p1x710",
648                 Constants.NIC_NAME_TO_CODE[nic_name], 1,
649                 u"File name should contain NIC code once.", in_filename
650             )
651             tmp2_prolog = replace_defensively(
652                 in_prolog, u"Intel-X710", nic_name, 2,
653                 u"NIC name should appear twice (tag and variable).",
654                 in_filename
655             )
656             iface, old_suite_id, _ = get_iface_and_suite_ids(
657                 tmp2_filename
658             )
659             for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
660                 out_filename = replace_defensively(
661                     tmp2_filename, old_suite_id,
662                     Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
663                     1, u"Error adding driver prefix.", in_filename
664                 )
665                 out_prolog = replace_defensively(
666                     tmp2_prolog, u"vfio-pci", driver, 1,
667                     u"Driver name should appear once.", in_filename
668                 )
669                 out_prolog = replace_defensively(
670                     out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
671                     Constants.NIC_DRIVER_TO_TAG[driver], 1,
672                     u"Driver tag should appear once.", in_filename
673                 )
674                 out_prolog = replace_defensively(
675                     out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
676                     Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
677                     u"Driver plugin should appear once.", in_filename
678                 )
679                 out_prolog = replace_defensively(
680                     out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
681                     Constants.NIC_DRIVER_TO_VFS[driver], 1,
682                     u"NIC VFs argument should appear once.", in_filename
683                 )
684                 iface, suite_id, suite_tag = get_iface_and_suite_ids(
685                     out_filename
686                 )
687                 check_suite_tag(suite_tag, out_prolog)
688                 # TODO: Reorder loops so suite_id is finalized sooner.
689                 testcase = Testcase.default(suite_id)
690                 with open(out_filename, u"wt") as file_out:
691                     file_out.write(out_prolog)
692                     add_default_testcases(
693                         testcase, iface, suite_id, file_out, kwargs_list
694                     )
695
696
697 class Regenerator:
698     """Class containing file generating methods."""
699
700     def __init__(self, quiet=True):
701         """Initialize the instance.
702
703         :param quiet: Reduce log prints (to stderr) when True (default).
704         :type quiet: boolean
705         """
706         self.quiet = quiet
707
708     def regenerate_glob(self, pattern, protocol=u"ip4"):
709         """Regenerate files matching glob pattern based on arguments.
710
711         In the current working directory, find all files matching
712         the glob pattern. Use testcase template to regenerate test cases
713         according to suffix, governed by protocol, autonumbering them.
714         Also generate suites for other NICs and drivers.
715
716         Log-like prints are emitted to sys.stderr.
717
718         :param pattern: Glob pattern to select files. Example: \*-ndrpdr.robot
719         :param protocol: String determining minimal frame size. Default: "ip4"
720         :type pattern: str
721         :type protocol: str
722         :raises RuntimeError: If invalid source suite is encountered.
723         """
724         if not self.quiet:
725             print(f"Regenerator starts at {getcwd()}", file=sys.stderr)
726
727         min_frame_size = PROTOCOL_TO_MIN_FRAME_SIZE[protocol]
728         default_kwargs_list = [
729             {u"frame_size": min_frame_size, u"phy_cores": 1},
730             {u"frame_size": min_frame_size, u"phy_cores": 2},
731             {u"frame_size": min_frame_size, u"phy_cores": 4},
732             {u"frame_size": 1518, u"phy_cores": 1},
733             {u"frame_size": 1518, u"phy_cores": 2},
734             {u"frame_size": 1518, u"phy_cores": 4},
735             {u"frame_size": 9000, u"phy_cores": 1},
736             {u"frame_size": 9000, u"phy_cores": 2},
737             {u"frame_size": 9000, u"phy_cores": 4},
738             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 1},
739             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 2},
740             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 4}
741         ]
742         hs_bps_kwargs_list = [
743             {u"frame_size": 1460, u"phy_cores": 1},
744         ]
745         hs_quic_kwargs_list = [
746             {u"frame_size": 1280, u"phy_cores": 1},
747         ]
748         iperf3_kwargs_list = [
749             {u"frame_size": 128000, u"phy_cores": 1},
750             {u"frame_size": 128000, u"phy_cores": 2},
751             {u"frame_size": 128000, u"phy_cores": 4}
752         ]
753         # List for tests with one dataplane core
754         # (and variable number of other cores).
755         dp1_kwargs_list = [
756             {u"frame_size": min_frame_size, u"phy_cores": 2},
757             {u"frame_size": min_frame_size, u"phy_cores": 3},
758             {u"frame_size": min_frame_size, u"phy_cores": 4},
759             {u"frame_size": 1518, u"phy_cores": 2},
760             {u"frame_size": 1518, u"phy_cores": 3},
761             {u"frame_size": 1518, u"phy_cores": 4},
762             {u"frame_size": 9000, u"phy_cores": 2},
763             {u"frame_size": 9000, u"phy_cores": 3},
764             {u"frame_size": 9000, u"phy_cores": 4},
765             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 2},
766             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 3},
767             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 4}
768         ]
769
770         http_kwargs_list = [
771             {u"frame_size": 0, u"phy_cores": 1},
772             {u"frame_size": 0, u"phy_cores": 2},
773             {u"frame_size": 64, u"phy_cores": 1},
774             {u"frame_size": 64, u"phy_cores": 2},
775             {u"frame_size": 1024, u"phy_cores": 1},
776             {u"frame_size": 1024, u"phy_cores": 2},
777             {u"frame_size": 2048, u"phy_cores": 1},
778             {u"frame_size": 2048, u"phy_cores": 2}
779         ]
780
781         device_kwargs_list = [
782             {u"frame_size": min_frame_size, u"phy_cores": 0}
783         ]
784
785         trex_kwargs_list = [
786             {u"frame_size": min_frame_size},
787             {u"frame_size": 1518},
788             {u"frame_size": 9000},
789             {u"frame_size": u"IMIX_v4_1"}
790         ]
791
792         for in_filename in glob(pattern):
793             if not self.quiet:
794                 print(
795                     u"Regenerating in_filename:", in_filename, file=sys.stderr
796                 )
797             iface, _, _ = get_iface_and_suite_ids(in_filename)
798             if not iface.endswith(u"10ge2p1x710"):
799                 raise RuntimeError(
800                     f"Error in {in_filename}: non-primary NIC found."
801                 )
802             for prefix in Constants.FORBIDDEN_SUITE_PREFIX_LIST:
803                 if prefix in in_filename:
804                     raise RuntimeError(
805                         f"Error in {in_filename}: non-primary driver found."
806                     )
807             with open(in_filename, u"rt") as file_in:
808                 in_prolog = u"".join(
809                     file_in.read().partition(u"*** Test Cases ***")[:-1]
810                 )
811             if "-tg" in in_filename:
812                 write_trex_files(in_filename, in_prolog, trex_kwargs_list)
813                 continue
814             if in_filename.endswith(u"-ndrpdr.robot"):
815                 if u"scheduler" in in_filename:
816                     write_default_files(
817                         in_filename, in_prolog, dp1_kwargs_list
818                     )
819                 else:
820                     write_default_files(
821                         in_filename, in_prolog, default_kwargs_list
822                     )
823             elif in_filename.endswith(u"-reconf.robot"):
824                 write_reconf_files(in_filename, in_prolog, default_kwargs_list)
825             elif in_filename.endswith(u"-rps.robot") \
826                     or in_filename.endswith(u"-cps.robot"):
827                 write_tcp_files(in_filename, in_prolog, http_kwargs_list)
828             elif in_filename.endswith(u"-bps.robot"):
829                 hoststack_kwargs_list = \
830                     hs_quic_kwargs_list if u"quic" in in_filename \
831                     else hs_bps_kwargs_list
832                 write_tcp_files(in_filename, in_prolog, hoststack_kwargs_list)
833             elif in_filename.endswith(u"-iperf3-mrr.robot"):
834                 write_iperf3_files(in_filename, in_prolog, iperf3_kwargs_list)
835             elif in_filename.endswith(u"-scapy.robot"):
836                 write_device_files(in_filename, in_prolog, device_kwargs_list)
837             else:
838                 raise RuntimeError(
839                     f"Error in {in_filename}: non-primary suite type found."
840                 )
841         if not self.quiet:
842             print(u"Regenerator ends.", file=sys.stderr)
843         print(file=sys.stderr)  # To make autogen check output more readable.