feat(tests): 6p3nic ip4 tests
[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                 nic_code = Constants.NIC_NAME_TO_CODE[nic_name]
355                 out_filename = replace_defensively(
356                     tmp2_filename, old_suite_id,
357                     Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
358                     1, u"Error adding driver prefix.", in_filename
359                 )
360                 out_prolog = replace_defensively(
361                     tmp2_prolog, u"vfio-pci", driver, 1,
362                     u"Driver name should appear once.", in_filename
363                 )
364                 out_prolog = replace_defensively(
365                     out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
366                     Constants.NIC_DRIVER_TO_TAG[driver], 1,
367                     u"Driver tag should appear once.", in_filename
368                 )
369                 out_prolog = replace_defensively(
370                     out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
371                     Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
372                     u"Driver plugin should appear once.", in_filename
373                 )
374                 out_prolog = replace_defensively(
375                     out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
376                     Constants.NIC_DRIVER_TO_VFS[driver], 1,
377                     u"NIC VFs argument should appear once.", in_filename
378                 )
379                 out_prolog = replace_defensively(
380                     out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
381                     Constants.NIC_CODE_TO_PFS[nic_code], 1,
382                     "NIC PFs argument should appear once.", in_filename
383                 )
384
385                 iface, suite_id, suite_tag = get_iface_and_suite_ids(
386                     out_filename
387                 )
388                 # The next replace is probably a noop, but it is safer to
389                 # maintain the same structure as for other edits.
390                 out_prolog = replace_defensively(
391                     out_prolog, old_suite_tag, suite_tag, 1,
392                     f"Perf suite tag {old_suite_tag} should appear once.",
393                     in_filename
394                 )
395                 check_suite_tag(suite_tag, out_prolog)
396                 # TODO: Reorder loops so suite_id is finalized sooner.
397                 testcase = Testcase.default(suite_id)
398                 with open(out_filename, u"wt") as file_out:
399                     file_out.write(out_prolog)
400                     add_default_testcases(
401                         testcase, iface, suite_id, file_out, kwargs_list
402                     )
403
404
405 def write_reconf_files(in_filename, in_prolog, kwargs_list):
406     """Using given filename and prolog, write all generated reconf suites.
407
408     Use this for suite type reconf, as its local template
409     is incompatible with mrr/ndrpdr/soak local template,
410     while test cases are compatible.
411
412     :param in_filename: Template filename to derive real filenames from.
413     :param in_prolog: Template content to derive real content from.
414     :param kwargs_list: List of kwargs for add_testcase.
415     :type in_filename: str
416     :type in_prolog: str
417     :type kwargs_list: list of dict
418     """
419     _, suite_id, _ = get_iface_and_suite_ids(in_filename)
420     testcase = Testcase.default(suite_id)
421     for nic_name in Constants.NIC_NAME_TO_CODE:
422         tmp_filename = replace_defensively(
423             in_filename, u"10ge2p1x710",
424             Constants.NIC_NAME_TO_CODE[nic_name], 1,
425             u"File name should contain NIC code once.", in_filename
426         )
427         tmp_prolog = replace_defensively(
428             in_prolog, u"Intel-X710", nic_name, 2,
429             u"NIC name should appear twice (tag and variable).",
430             in_filename
431         )
432         if tmp_prolog.count(u"HW_") == 2:
433             # TODO CSIT-1481: Crypto HW should be read
434             #      from topology file instead.
435             if nic_name in Constants.NIC_NAME_TO_CRYPTO_HW.keys():
436                 tmp_prolog = replace_defensively(
437                     tmp_prolog, u"HW_DH895xcc",
438                     Constants.NIC_NAME_TO_CRYPTO_HW[nic_name], 1,
439                     u"HW crypto name should appear.", in_filename
440                 )
441         iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
442             tmp_filename
443         )
444         for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
445             nic_code = Constants.NIC_NAME_TO_CODE[nic_name]
446             out_filename = replace_defensively(
447                 tmp_filename, old_suite_id,
448                 Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
449                 1, u"Error adding driver prefix.", in_filename
450             )
451             out_prolog = replace_defensively(
452                 tmp_prolog, u"vfio-pci", driver, 1,
453                 u"Driver name should appear once.", in_filename
454             )
455             out_prolog = replace_defensively(
456                 out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
457                 Constants.NIC_DRIVER_TO_TAG[driver], 1,
458                 u"Driver tag should appear once.", in_filename
459             )
460             out_prolog = replace_defensively(
461                 out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
462                 Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
463                 u"Driver plugin should appear once.", in_filename
464             )
465             out_prolog = replace_defensively(
466                 out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
467                 Constants.NIC_DRIVER_TO_VFS[driver], 1,
468                 u"NIC VFs argument should appear once.", in_filename
469             )
470             out_prolog = replace_defensively(
471                 out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
472                 Constants.NIC_CODE_TO_PFS[nic_code], 1,
473                 "NIC PFs argument should appear once.", in_filename
474             )
475
476             iface, suite_id, suite_tag = get_iface_and_suite_ids(out_filename)
477             out_prolog = replace_defensively(
478                 out_prolog, old_suite_tag, suite_tag, 1,
479                 u"Perf suite tag should appear once.", in_filename
480             )
481             check_suite_tag(suite_tag, out_prolog)
482             # TODO: Reorder loops so suite_id is finalized sooner.
483             testcase = Testcase.default(suite_id)
484             with open(out_filename, u"wt") as file_out:
485                 file_out.write(out_prolog)
486                 add_default_testcases(
487                     testcase, iface, suite_id, file_out, kwargs_list
488                 )
489
490
491 def write_tcp_files(in_filename, in_prolog, kwargs_list):
492     """Using given filename and prolog, write all generated tcp suites.
493
494     :param in_filename: Template filename to derive real filenames from.
495     :param in_prolog: Template content to derive real content from.
496     :param kwargs_list: List of kwargs for add_default_testcase.
497     :type in_filename: str
498     :type in_prolog: str
499     :type kwargs_list: list of dict
500     """
501     # TODO: Generate rps from cps? There are subtle differences.
502     _, suite_id, suite_tag = get_iface_and_suite_ids(in_filename)
503     testcase = Testcase.tcp(suite_id)
504     for nic_name in Constants.NIC_NAME_TO_CODE:
505         tmp_filename = replace_defensively(
506             in_filename, u"10ge2p1x710",
507             Constants.NIC_NAME_TO_CODE[nic_name], 1,
508             u"File name should contain NIC code once.", in_filename
509         )
510         tmp_prolog = replace_defensively(
511             in_prolog, u"Intel-X710", nic_name, 2,
512             u"NIC name should appear twice (tag and variable).",
513             in_filename
514         )
515         iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
516             tmp_filename
517         )
518         for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
519             nic_code = Constants.NIC_NAME_TO_CODE[nic_name]
520             out_filename = replace_defensively(
521                 tmp_filename, old_suite_id,
522                 Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
523                 1, u"Error adding driver prefix.", in_filename
524             )
525             out_prolog = replace_defensively(
526                 tmp_prolog, u"vfio-pci", driver, 1,
527                 u"Driver name should appear once.", in_filename
528             )
529             out_prolog = replace_defensively(
530                 out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
531                 Constants.NIC_DRIVER_TO_TAG[driver], 1,
532                 u"Driver tag should appear once.", in_filename
533             )
534             out_prolog = replace_defensively(
535                 out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
536                 Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
537                 u"Driver plugin should appear once.", in_filename
538             )
539             out_prolog = replace_defensively(
540                 out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
541                 Constants.NIC_DRIVER_TO_VFS[driver], 1,
542                 u"NIC VFs argument should appear once.", in_filename
543             )
544             out_prolog = replace_defensively(
545                 out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
546                 Constants.NIC_CODE_TO_PFS[nic_code], 1,
547                 "NIC PFs argument should appear once.", in_filename
548             )
549
550             iface, suite_id, suite_tag = get_iface_and_suite_ids(out_filename)
551             out_prolog = replace_defensively(
552                 out_prolog, old_suite_tag, suite_tag, 1,
553                 u"Perf suite tag should appear once.", in_filename
554             )
555             check_suite_tag(suite_tag, out_prolog)
556             testcase = Testcase.tcp(suite_id)
557             with open(out_filename, u"wt") as file_out:
558                 file_out.write(out_prolog)
559                 add_tcp_testcases(testcase, file_out, kwargs_list)
560
561
562 def write_iperf3_files(in_filename, in_prolog, kwargs_list):
563     """Using given filename and prolog, write all generated iperf3 suites.
564
565     :param in_filename: Template filename to derive real filenames from.
566     :param in_prolog: Template content to derive real content from.
567     :param kwargs_list: List of kwargs for add_default_testcase.
568     :type in_filename: str
569     :type in_prolog: str
570     :type kwargs_list: list of dict
571     """
572     _, suite_id, suite_tag = get_iface_and_suite_ids(in_filename)
573     testcase = Testcase.iperf3(suite_id)
574     for nic_name in Constants.NIC_NAME_TO_CODE:
575         out_filename = replace_defensively(
576             in_filename, u"10ge2p1x710",
577             Constants.NIC_NAME_TO_CODE[nic_name], 1,
578             u"File name should contain NIC code once.", in_filename
579         )
580         out_prolog = replace_defensively(
581             in_prolog, u"Intel-X710", nic_name, 2,
582             u"NIC name should appear twice (tag and variable).",
583             in_filename
584         )
585         check_suite_tag(suite_tag, out_prolog)
586         with open(out_filename, u"wt") as file_out:
587             file_out.write(out_prolog)
588             add_iperf3_testcases(testcase, file_out, kwargs_list)
589
590
591 def write_trex_files(in_filename, in_prolog, kwargs_list):
592     """Using given filename and prolog, write all generated trex suites.
593
594     :param in_filename: Template filename to derive real filenames from.
595     :param in_prolog: Template content to derive real content from.
596     :param kwargs_list: List of kwargs for add_trex_testcase.
597     :type in_filename: str
598     :type in_prolog: str
599     :type kwargs_list: list of dict
600     """
601     for suite_type in Constants.PERF_TYPE_TO_KEYWORD:
602         tmp_filename = replace_defensively(
603             in_filename, u"ndrpdr", suite_type, 1,
604             u"File name should contain suite type once.", in_filename
605         )
606         tmp_prolog = replace_defensively(
607             in_prolog, u"ndrpdr".upper(), suite_type.upper(), 1,
608             u"Suite type should appear once in uppercase (as tag).",
609             in_filename
610         )
611         tmp_prolog = replace_defensively(
612             tmp_prolog,
613             u"Find NDR and PDR intervals using optimized search",
614             Constants.PERF_TYPE_TO_KEYWORD[suite_type], 1,
615             u"Main search keyword should appear once in suite.",
616             in_filename
617         )
618         tmp_prolog = replace_defensively(
619             tmp_prolog,
620             Constants.PERF_TYPE_TO_SUITE_DOC_VER[u"ndrpdr"],
621             Constants.PERF_TYPE_TO_SUITE_DOC_VER[suite_type],
622             1, u"Exact suite type doc not found.", in_filename
623         )
624         tmp_prolog = replace_defensively(
625             tmp_prolog,
626             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[u"ndrpdr"],
627             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[suite_type],
628             1, u"Exact template type doc not found.", in_filename
629         )
630         _, suite_id, suite_tag = get_iface_and_suite_ids(tmp_filename)
631         testcase = Testcase.trex(suite_id)
632         for nic_name in Constants.NIC_NAME_TO_CODE:
633             out_filename = replace_defensively(
634                 tmp_filename, u"10ge2p1x710",
635                 Constants.NIC_NAME_TO_CODE[nic_name], 1,
636                 u"File name should contain NIC code once.", in_filename
637             )
638             out_prolog = replace_defensively(
639                 tmp_prolog, u"Intel-X710", nic_name, 2,
640                 u"NIC name should appear twice (tag and variable).",
641                 in_filename
642             )
643             check_suite_tag(suite_tag, out_prolog)
644             with open(out_filename, u"wt") as file_out:
645                 file_out.write(out_prolog)
646                 add_trex_testcases(testcase, suite_id, file_out, kwargs_list)
647
648
649 def write_device_files(in_filename, in_prolog, kwargs_list):
650     """Using given filename and prolog, write all generated suites.
651
652     :param in_filename: Template filename to derive real filenames from.
653     :param in_prolog: Template content to derive real content from.
654     :param kwargs_list: List of kwargs for add_default_testcase.
655     :type in_filename: str
656     :type in_prolog: str
657     :type kwargs_list: list of dict
658     """
659     for suite_type in Constants.DEVICE_TYPE_TO_KEYWORD:
660         tmp_filename = replace_defensively(
661             in_filename, u"scapy", suite_type, 1,
662             u"File name should contain suite type once.", in_filename
663         )
664         _, suite_id, _ = get_iface_and_suite_ids(tmp_filename)
665         testcase = Testcase.default(suite_id)
666         for nic_name in Constants.NIC_NAME_TO_CODE:
667             tmp2_filename = replace_defensively(
668                 tmp_filename, u"10ge2p1x710",
669                 Constants.NIC_NAME_TO_CODE[nic_name], 1,
670                 u"File name should contain NIC code once.", in_filename
671             )
672             tmp2_prolog = replace_defensively(
673                 in_prolog, u"Intel-X710", nic_name, 2,
674                 u"NIC name should appear twice (tag and variable).",
675                 in_filename
676             )
677             iface, old_suite_id, _ = get_iface_and_suite_ids(
678                 tmp2_filename
679             )
680             for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
681                 nic_code = Constants.NIC_NAME_TO_CODE[nic_name]
682                 out_filename = replace_defensively(
683                     tmp2_filename, old_suite_id,
684                     Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
685                     1, u"Error adding driver prefix.", in_filename
686                 )
687                 out_prolog = replace_defensively(
688                     tmp2_prolog, u"vfio-pci", driver, 1,
689                     u"Driver name should appear once.", in_filename
690                 )
691                 out_prolog = replace_defensively(
692                     out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
693                     Constants.NIC_DRIVER_TO_TAG[driver], 1,
694                     u"Driver tag should appear once.", in_filename
695                 )
696                 out_prolog = replace_defensively(
697                     out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
698                     Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
699                     u"Driver plugin should appear once.", in_filename
700                 )
701                 out_prolog = replace_defensively(
702                     out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
703                     Constants.NIC_DRIVER_TO_VFS[driver], 1,
704                     u"NIC VFs argument should appear once.", in_filename
705                 )
706                 out_prolog = replace_defensively(
707                     out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
708                     Constants.NIC_CODE_TO_PFS[nic_code], 1,
709                     "NIC PFs argument should appear once.", in_filename
710                 )
711
712                 iface, suite_id, suite_tag = get_iface_and_suite_ids(
713                     out_filename
714                 )
715                 check_suite_tag(suite_tag, out_prolog)
716                 # TODO: Reorder loops so suite_id is finalized sooner.
717                 testcase = Testcase.default(suite_id)
718                 with open(out_filename, u"wt") as file_out:
719                     file_out.write(out_prolog)
720                     add_default_testcases(
721                         testcase, iface, suite_id, file_out, kwargs_list
722                     )
723
724
725 class Regenerator:
726     """Class containing file generating methods."""
727
728     def __init__(self, quiet=True):
729         """Initialize the instance.
730
731         :param quiet: Reduce log prints (to stderr) when True (default).
732         :type quiet: boolean
733         """
734         self.quiet = quiet
735
736     def regenerate_glob(self, pattern, protocol=u"ip4"):
737         """Regenerate files matching glob pattern based on arguments.
738
739         In the current working directory, find all files matching
740         the glob pattern. Use testcase template to regenerate test cases
741         according to suffix, governed by protocol, autonumbering them.
742         Also generate suites for other NICs and drivers.
743
744         Log-like prints are emitted to sys.stderr.
745
746         :param pattern: Glob pattern to select files. Example: \*-ndrpdr.robot
747         :param protocol: String determining minimal frame size. Default: "ip4"
748         :type pattern: str
749         :type protocol: str
750         :raises RuntimeError: If invalid source suite is encountered.
751         """
752         if not self.quiet:
753             print(f"Regenerator starts at {getcwd()}", file=sys.stderr)
754
755         min_frame_size = PROTOCOL_TO_MIN_FRAME_SIZE[protocol]
756         default_kwargs_list = [
757             {u"frame_size": min_frame_size, u"phy_cores": 1},
758             {u"frame_size": min_frame_size, u"phy_cores": 2},
759             {u"frame_size": min_frame_size, u"phy_cores": 4},
760             {u"frame_size": 1518, u"phy_cores": 1},
761             {u"frame_size": 1518, u"phy_cores": 2},
762             {u"frame_size": 1518, u"phy_cores": 4},
763             {u"frame_size": 9000, u"phy_cores": 1},
764             {u"frame_size": 9000, u"phy_cores": 2},
765             {u"frame_size": 9000, u"phy_cores": 4},
766             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 1},
767             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 2},
768             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 4}
769         ]
770         hs_bps_kwargs_list = [
771             {u"frame_size": 1460, u"phy_cores": 1},
772         ]
773         hs_quic_kwargs_list = [
774             {u"frame_size": 1280, u"phy_cores": 1},
775         ]
776         iperf3_kwargs_list = [
777             {u"frame_size": 128000, u"phy_cores": 1},
778             {u"frame_size": 128000, u"phy_cores": 2},
779             {u"frame_size": 128000, u"phy_cores": 4}
780         ]
781         # List for tests with one dataplane core
782         # (and variable number of other cores).
783         dp1_kwargs_list = [
784             {u"frame_size": min_frame_size, u"phy_cores": 2},
785             {u"frame_size": min_frame_size, u"phy_cores": 3},
786             {u"frame_size": min_frame_size, u"phy_cores": 4},
787             {u"frame_size": 1518, u"phy_cores": 2},
788             {u"frame_size": 1518, u"phy_cores": 3},
789             {u"frame_size": 1518, u"phy_cores": 4},
790             {u"frame_size": 9000, u"phy_cores": 2},
791             {u"frame_size": 9000, u"phy_cores": 3},
792             {u"frame_size": 9000, u"phy_cores": 4},
793             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 2},
794             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 3},
795             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 4}
796         ]
797
798         http_kwargs_list = [
799             {u"frame_size": 0, u"phy_cores": 1},
800             {u"frame_size": 0, u"phy_cores": 2},
801             {u"frame_size": 64, u"phy_cores": 1},
802             {u"frame_size": 64, u"phy_cores": 2},
803             {u"frame_size": 1024, u"phy_cores": 1},
804             {u"frame_size": 1024, u"phy_cores": 2},
805             {u"frame_size": 2048, u"phy_cores": 1},
806             {u"frame_size": 2048, u"phy_cores": 2}
807         ]
808
809         device_kwargs_list = [
810             {u"frame_size": min_frame_size, u"phy_cores": 0}
811         ]
812
813         trex_kwargs_list = [
814             {u"frame_size": min_frame_size},
815             {u"frame_size": 1518},
816             {u"frame_size": 9000},
817             {u"frame_size": u"IMIX_v4_1"}
818         ]
819
820         for in_filename in glob(pattern):
821             if not self.quiet:
822                 print(
823                     u"Regenerating in_filename:", in_filename, file=sys.stderr
824                 )
825             iface, _, _ = get_iface_and_suite_ids(in_filename)
826             if not iface.endswith(u"10ge2p1x710"):
827                 raise RuntimeError(
828                     f"Error in {in_filename}: non-primary NIC found."
829                 )
830             for prefix in Constants.FORBIDDEN_SUITE_PREFIX_LIST:
831                 if prefix in in_filename:
832                     raise RuntimeError(
833                         f"Error in {in_filename}: non-primary driver found."
834                     )
835             with open(in_filename, u"rt") as file_in:
836                 in_prolog = u"".join(
837                     file_in.read().partition(u"*** Test Cases ***")[:-1]
838                 )
839             if "-tg" in in_filename:
840                 write_trex_files(in_filename, in_prolog, trex_kwargs_list)
841                 continue
842             if in_filename.endswith(u"-ndrpdr.robot"):
843                 if u"scheduler" in in_filename:
844                     write_default_files(
845                         in_filename, in_prolog, dp1_kwargs_list
846                     )
847                 else:
848                     write_default_files(
849                         in_filename, in_prolog, default_kwargs_list
850                     )
851             elif in_filename.endswith(u"-reconf.robot"):
852                 write_reconf_files(in_filename, in_prolog, default_kwargs_list)
853             elif in_filename.endswith(u"-rps.robot") \
854                     or in_filename.endswith(u"-cps.robot"):
855                 write_tcp_files(in_filename, in_prolog, http_kwargs_list)
856             elif in_filename.endswith(u"-bps.robot"):
857                 hoststack_kwargs_list = \
858                     hs_quic_kwargs_list if u"quic" in in_filename \
859                     else hs_bps_kwargs_list
860                 write_tcp_files(in_filename, in_prolog, hoststack_kwargs_list)
861             elif in_filename.endswith(u"-iperf3-mrr.robot"):
862                 write_iperf3_files(in_filename, in_prolog, iperf3_kwargs_list)
863             elif in_filename.endswith(u"-scapy.robot"):
864                 write_device_files(in_filename, in_prolog, device_kwargs_list)
865             else:
866                 raise RuntimeError(
867                     f"Error in {in_filename}: non-primary suite type found."
868                 )
869         if not self.quiet:
870             print(u"Regenerator ends.", file=sys.stderr)
871         print(file=sys.stderr)  # To make autogen check output more readable.