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