ce8f21d4b6b89ac1eca396f54737401feffe1be9
[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(
154         testcase, nic_code, suite_id, file_out, tc_kwargs_list):
155     """Add default testcases to file.
156
157     :param testcase: Testcase class.
158     :param nic_code: NIC code.
159     :param suite_id: Suite ID.
160     :param file_out: File to write testcases to.
161     :param tc_kwargs_list: Key-value pairs used to construct testcases.
162     :type testcase: Testcase
163     :type nic_code: str
164     :type suite_id: str
165     :type file_out: file
166     :type tc_kwargs_list: dict
167     """
168     for kwas in tc_kwargs_list:
169         # We may edit framesize for ASTF, the copy should be local.
170         kwargs = copy.deepcopy(kwas)
171         # TODO: Is there a better way to disable some combinations?
172         emit = True
173         core_scale = Constants.NIC_CODE_TO_CORESCALE[nic_code]
174         if u"soak" in suite_id:
175             # Soak test take too long, do not risk other than tc01.
176             if kwargs[u"phy_cores"] != 1:
177                 emit = False
178             if u"reassembly" in suite_id:
179                 if kwargs[u"frame_size"] != 1518:
180                     emit = False
181
182         kwargs.update({'phy_cores': kwas['phy_cores']*core_scale})
183
184         kwargs = filter_and_edit_kwargs_for_astf(suite_id, kwargs)
185         if emit and kwargs is not None:
186             file_out.write(testcase.generate(**kwargs))
187
188
189 def add_tcp_testcases(testcase, file_out, tc_kwargs_list):
190     """Add TCP testcases to file.
191
192     :param testcase: Testcase class.
193     :param file_out: File to write testcases to.
194     :param tc_kwargs_list: Key-value pairs used to construct testcases.
195     :type testcase: Testcase
196     :type file_out: file
197     :type tc_kwargs_list: dict
198     """
199     for kwargs in tc_kwargs_list:
200         file_out.write(testcase.generate(**kwargs))
201
202
203 def add_iperf3_testcases(testcase, file_out, tc_kwargs_list):
204     """Add iperf3 testcases to file.
205
206     :param testcase: Testcase class.
207     :param file_out: File to write testcases to.
208     :param tc_kwargs_list: Key-value pairs used to construct testcases.
209     :type testcase: Testcase
210     :type file_out: file
211     :type tc_kwargs_list: dict
212     """
213     for kwargs in tc_kwargs_list:
214         file_out.write(testcase.generate(**kwargs))
215
216
217 def add_trex_testcases(testcase, suite_id, file_out, tc_kwargs_list):
218     """Add trex testcases to file.
219
220     :param testcase: Testcase class.
221     :param suite_id: Suite ID.
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 suite_id: str
226     :type file_out: file
227     :type tc_kwargs_list: dict
228     """
229     for kwas in tc_kwargs_list:
230         # We may edit framesize for ASTF, the copy should be local.
231         kwargs = copy.deepcopy(kwas)
232         kwargs = filter_and_edit_kwargs_for_astf(suite_id, kwargs)
233         if kwargs is not None:
234             file_out.write(testcase.generate(**kwargs))
235
236
237 def write_default_files(in_filename, in_prolog, kwargs_list):
238     """Using given filename and prolog, write all generated suites.
239
240     :param in_filename: Template filename to derive real filenames from.
241     :param in_prolog: Template content to derive real content from.
242     :param kwargs_list: List of kwargs for add_default_testcase.
243     :type in_filename: str
244     :type in_prolog: str
245     :type kwargs_list: list of dict
246     """
247     for suite_type in Constants.PERF_TYPE_TO_KEYWORD:
248         tmp_filename = replace_defensively(
249             in_filename, "ndrpdr", suite_type, 1,
250             "File name should contain suite type once.", in_filename
251         )
252         tmp_prolog = replace_defensively(
253             in_prolog, "ndrpdr".upper(), suite_type.upper(), 1,
254             "Suite type should appear once in uppercase (as tag).",
255             in_filename
256         )
257         tmp_prolog = replace_defensively(
258             tmp_prolog,
259             "Find NDR and PDR intervals using optimized search",
260             Constants.PERF_TYPE_TO_KEYWORD[suite_type], 1,
261             "Main search keyword should appear once in suite.",
262             in_filename
263         )
264         tmp_prolog = replace_defensively(
265             tmp_prolog,
266             Constants.PERF_TYPE_TO_SUITE_DOC_VER["ndrpdr"],
267             Constants.PERF_TYPE_TO_SUITE_DOC_VER[suite_type],
268             1, "Exact suite type doc not found.", in_filename
269         )
270         tmp_prolog = replace_defensively(
271             tmp_prolog,
272             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER["ndrpdr"],
273             Constants.PERF_TYPE_TO_TEMPLATE_DOC_VER[suite_type],
274             1, "Exact template type doc not found.", in_filename
275         )
276         _, suite_id, _ = get_iface_and_suite_ids(tmp_filename)
277         testcase = Testcase.default(suite_id)
278         for nic_code in Constants.NIC_CODE_TO_NAME:
279             nic_name = Constants.NIC_CODE_TO_NAME[nic_code]
280             tmp2_filename = replace_defensively(
281                 tmp_filename, "10ge2p1x710", nic_code, 1,
282                 "File name should contain NIC code once.", in_filename
283             )
284             tmp2_prolog = replace_defensively(
285                 tmp_prolog, "Intel-X710", nic_name, 2,
286                 "NIC name should appear twice (tag and variable).",
287                 in_filename
288             )
289             if tmp2_prolog.count("HW_") == 2:
290                 # TODO CSIT-1481: Crypto HW should be read
291                 #      from topology file instead.
292                 if nic_name in Constants.NIC_NAME_TO_CRYPTO_HW:
293                     tmp2_prolog = replace_defensively(
294                         tmp2_prolog, "HW_DH895xcc",
295                         Constants.NIC_NAME_TO_CRYPTO_HW[nic_name], 1,
296                         "HW crypto name should appear.", in_filename
297                     )
298             iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
299                 tmp2_filename
300             )
301             if "DPDK" in in_prolog:
302                 for driver in Constants.DPDK_NIC_NAME_TO_DRIVER[nic_name]:
303                     out_filename = replace_defensively(
304                         tmp2_filename, old_suite_id,
305                         Constants.DPDK_NIC_DRIVER_TO_SUITE_PREFIX[driver] \
306                             + old_suite_id,
307                         1, "Error adding driver prefix.", in_filename
308                     )
309                     out_prolog = replace_defensively(
310                         tmp2_prolog, "vfio-pci", driver, 1,
311                         "Driver name should appear once.", in_filename
312                     )
313                     out_prolog = replace_defensively(
314                         out_prolog,
315                         Constants.DPDK_NIC_DRIVER_TO_TAG["vfio-pci"],
316                         Constants.DPDK_NIC_DRIVER_TO_TAG[driver], 1,
317                         "Driver tag should appear once.", in_filename
318                     )
319                     iface, suite_id, suite_tag = get_iface_and_suite_ids(
320                         out_filename
321                     )
322                     # The next replace is probably a noop, but it is safer to
323                     # maintain the same structure as for other edits.
324                     out_prolog = replace_defensively(
325                         out_prolog, old_suite_tag, suite_tag, 1,
326                         f"Perf suite tag {old_suite_tag} should appear once.",
327                         in_filename
328                     )
329                     check_suite_tag(suite_tag, out_prolog)
330                     # TODO: Reorder loops so suite_id is finalized sooner.
331                     testcase = Testcase.default(suite_id)
332                     with open(out_filename, "wt") as file_out:
333                         file_out.write(out_prolog)
334                         add_default_testcases(
335                             testcase, nic_code, suite_id, file_out, kwargs_list
336                         )
337                 continue
338             for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
339                 out_filename = replace_defensively(
340                     tmp2_filename, old_suite_id,
341                     Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
342                     1, "Error adding driver prefix.", in_filename
343                 )
344                 out_prolog = replace_defensively(
345                     tmp2_prolog, "vfio-pci", driver, 1,
346                     "Driver name should appear once.", in_filename
347                 )
348                 out_prolog = replace_defensively(
349                     out_prolog, Constants.NIC_DRIVER_TO_TAG["vfio-pci"],
350                     Constants.NIC_DRIVER_TO_TAG[driver], 1,
351                     "Driver tag should appear once.", in_filename
352                 )
353                 out_prolog = replace_defensively(
354                     out_prolog, Constants.NIC_DRIVER_TO_PLUGINS["vfio-pci"],
355                     Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
356                     "Driver plugin should appear once.", in_filename
357                 )
358                 out_prolog = replace_defensively(
359                     out_prolog, Constants.NIC_DRIVER_TO_VFS["vfio-pci"],
360                     Constants.NIC_DRIVER_TO_VFS[driver], 1,
361                     "NIC VFs argument should appear once.", in_filename
362                 )
363                 out_prolog = replace_defensively(
364                     out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
365                     Constants.NIC_CODE_TO_PFS[nic_code], 1,
366                     "NIC PFs argument should appear once.", in_filename
367                 )
368                 iface, suite_id, suite_tag = get_iface_and_suite_ids(
369                     out_filename
370                 )
371                 # The next replace is probably a noop, but it is safer to
372                 # maintain the same structure as for other edits.
373                 out_prolog = replace_defensively(
374                     out_prolog, old_suite_tag, suite_tag, 1,
375                     f"Perf suite tag {old_suite_tag} should appear once.",
376                     in_filename
377                 )
378                 check_suite_tag(suite_tag, out_prolog)
379                 # TODO: Reorder loops so suite_id is finalized sooner.
380                 testcase = Testcase.default(suite_id)
381                 with open(out_filename, "wt") as file_out:
382                     file_out.write(out_prolog)
383                     add_default_testcases(
384                         testcase, nic_code, suite_id, file_out, kwargs_list
385                     )
386
387
388 def write_reconf_files(in_filename, in_prolog, kwargs_list):
389     """Using given filename and prolog, write all generated reconf suites.
390
391     Use this for suite type reconf, as its local template
392     is incompatible with mrr/ndrpdr/soak local template,
393     while test cases are compatible.
394
395     :param in_filename: Template filename to derive real filenames from.
396     :param in_prolog: Template content to derive real content from.
397     :param kwargs_list: List of kwargs for add_testcase.
398     :type in_filename: str
399     :type in_prolog: str
400     :type kwargs_list: list of dict
401     """
402     _, suite_id, _ = get_iface_and_suite_ids(in_filename)
403     testcase = Testcase.default(suite_id)
404     for nic_code in Constants.NIC_CODE_TO_NAME:
405         nic_name = Constants.NIC_CODE_TO_NAME[nic_code]
406         tmp_filename = replace_defensively(
407             in_filename, u"10ge2p1x710", nic_code, 1,
408             u"File name should contain NIC code once.", in_filename
409         )
410         tmp_prolog = replace_defensively(
411             in_prolog, u"Intel-X710", nic_name, 2,
412             u"NIC name should appear twice (tag and variable).",
413             in_filename
414         )
415         if tmp_prolog.count(u"HW_") == 2:
416             # TODO CSIT-1481: Crypto HW should be read
417             #      from topology file instead.
418             if nic_name in Constants.NIC_NAME_TO_CRYPTO_HW.keys():
419                 tmp_prolog = replace_defensively(
420                     tmp_prolog, u"HW_DH895xcc",
421                     Constants.NIC_NAME_TO_CRYPTO_HW[nic_name], 1,
422                     u"HW crypto name should appear.", in_filename
423                 )
424         iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
425             tmp_filename
426         )
427         for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
428             out_filename = replace_defensively(
429                 tmp_filename, old_suite_id,
430                 Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
431                 1, u"Error adding driver prefix.", in_filename
432             )
433             out_prolog = replace_defensively(
434                 tmp_prolog, u"vfio-pci", driver, 1,
435                 u"Driver name should appear once.", in_filename
436             )
437             out_prolog = replace_defensively(
438                 out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
439                 Constants.NIC_DRIVER_TO_TAG[driver], 1,
440                 u"Driver tag should appear once.", in_filename
441             )
442             out_prolog = replace_defensively(
443                 out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
444                 Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
445                 u"Driver plugin should appear once.", in_filename
446             )
447             out_prolog = replace_defensively(
448                 out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
449                 Constants.NIC_DRIVER_TO_VFS[driver], 1,
450                 u"NIC VFs argument should appear once.", in_filename
451             )
452             out_prolog = replace_defensively(
453                 out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
454                 Constants.NIC_CODE_TO_PFS[nic_code], 1,
455                 "NIC PFs argument should appear once.", in_filename
456             )
457             iface, suite_id, suite_tag = get_iface_and_suite_ids(out_filename)
458             out_prolog = replace_defensively(
459                 out_prolog, old_suite_tag, suite_tag, 1,
460                 u"Perf suite tag should appear once.", in_filename
461             )
462             check_suite_tag(suite_tag, out_prolog)
463             # TODO: Reorder loops so suite_id is finalized sooner.
464             testcase = Testcase.default(suite_id)
465             with open(out_filename, u"wt") as file_out:
466                 file_out.write(out_prolog)
467                 add_default_testcases(
468                     testcase, iface, suite_id, file_out, kwargs_list
469                 )
470
471
472 def write_tcp_files(in_filename, in_prolog, kwargs_list):
473     """Using given filename and prolog, write all generated tcp suites.
474
475     :param in_filename: Template filename to derive real filenames from.
476     :param in_prolog: Template content to derive real content from.
477     :param kwargs_list: List of kwargs for add_default_testcase.
478     :type in_filename: str
479     :type in_prolog: str
480     :type kwargs_list: list of dict
481     """
482     # TODO: Generate rps from cps? There are subtle differences.
483     _, suite_id, suite_tag = get_iface_and_suite_ids(in_filename)
484     testcase = Testcase.tcp(suite_id)
485     for nic_code in Constants.NIC_CODE_TO_NAME:
486         nic_name = Constants.NIC_CODE_TO_NAME[nic_code]
487         tmp_filename = replace_defensively(
488             in_filename, u"10ge2p1x710", nic_code, 1,
489             u"File name should contain NIC code once.", in_filename
490         )
491         tmp_prolog = replace_defensively(
492             in_prolog, u"Intel-X710", nic_name, 2,
493             u"NIC name should appear twice (tag and variable).",
494             in_filename
495         )
496         iface, old_suite_id, old_suite_tag = get_iface_and_suite_ids(
497             tmp_filename
498         )
499         for driver in Constants.NIC_NAME_TO_DRIVER[nic_name]:
500             out_filename = replace_defensively(
501                 tmp_filename, old_suite_id,
502                 Constants.NIC_DRIVER_TO_SUITE_PREFIX[driver] + old_suite_id,
503                 1, u"Error adding driver prefix.", in_filename
504             )
505             out_prolog = replace_defensively(
506                 tmp_prolog, u"vfio-pci", driver, 1,
507                 u"Driver name should appear once.", in_filename
508             )
509             out_prolog = replace_defensively(
510                 out_prolog, Constants.NIC_DRIVER_TO_TAG[u"vfio-pci"],
511                 Constants.NIC_DRIVER_TO_TAG[driver], 1,
512                 u"Driver tag should appear once.", in_filename
513             )
514             out_prolog = replace_defensively(
515                 out_prolog, Constants.NIC_DRIVER_TO_PLUGINS[u"vfio-pci"],
516                 Constants.NIC_DRIVER_TO_PLUGINS[driver], 1,
517                 u"Driver plugin should appear once.", in_filename
518             )
519             out_prolog = replace_defensively(
520                 out_prolog, Constants.NIC_DRIVER_TO_VFS[u"vfio-pci"],
521                 Constants.NIC_DRIVER_TO_VFS[driver], 1,
522                 u"NIC VFs argument should appear once.", in_filename
523             )
524             out_prolog = replace_defensively(
525                 out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
526                 Constants.NIC_CODE_TO_PFS[nic_code], 1,
527                 "NIC PFs 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_code in Constants.NIC_CODE_TO_NAME:
554         nic_name = Constants.NIC_CODE_TO_NAME[nic_code]
555         out_filename = replace_defensively(
556             in_filename, u"10ge2p1x710", nic_code, 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_code in Constants.NIC_CODE_TO_NAME:
612             nic_name = Constants.NIC_CODE_TO_NAME[nic_code]
613             out_filename = replace_defensively(
614                 tmp_filename, u"10ge2p1x710", nic_code, 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_code in Constants.NIC_CODE_TO_NAME:
646             nic_name = Constants.NIC_CODE_TO_NAME[nic_code]
647             tmp2_filename = replace_defensively(
648                 tmp_filename, u"10ge2p1x710", nic_code, 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                 out_prolog = replace_defensively(
685                     out_prolog, Constants.NIC_CODE_TO_PFS["10ge2p1x710"],
686                     Constants.NIC_CODE_TO_PFS[nic_code], 1,
687                     "NIC PFs argument should appear once.", in_filename
688                 )
689                 iface, suite_id, suite_tag = get_iface_and_suite_ids(
690                     out_filename
691                 )
692                 check_suite_tag(suite_tag, out_prolog)
693                 # TODO: Reorder loops so suite_id is finalized sooner.
694                 testcase = Testcase.default(suite_id)
695                 with open(out_filename, u"wt") as file_out:
696                     file_out.write(out_prolog)
697                     add_default_testcases(
698                         testcase, iface, suite_id, file_out, kwargs_list
699                     )
700
701
702 class Regenerator:
703     """Class containing file generating methods."""
704
705     def __init__(self, quiet=True):
706         """Initialize the instance.
707
708         :param quiet: Reduce log prints (to stderr) when True (default).
709         :type quiet: boolean
710         """
711         self.quiet = quiet
712
713     def regenerate_glob(self, pattern, protocol=u"ip4"):
714         """Regenerate files matching glob pattern based on arguments.
715
716         In the current working directory, find all files matching
717         the glob pattern. Use testcase template to regenerate test cases
718         according to suffix, governed by protocol, autonumbering them.
719         Also generate suites for other NICs and drivers.
720
721         Log-like prints are emitted to sys.stderr.
722
723         :param pattern: Glob pattern to select files. Example: \*-ndrpdr.robot
724         :param protocol: String determining minimal frame size. Default: "ip4"
725         :type pattern: str
726         :type protocol: str
727         :raises RuntimeError: If invalid source suite is encountered.
728         """
729         if not self.quiet:
730             print(f"Regenerator starts at {getcwd()}", file=sys.stderr)
731
732         min_frame_size = PROTOCOL_TO_MIN_FRAME_SIZE[protocol]
733         default_kwargs_list = [
734             {u"frame_size": min_frame_size, u"phy_cores": 1},
735             {u"frame_size": min_frame_size, u"phy_cores": 2},
736             {u"frame_size": min_frame_size, u"phy_cores": 4},
737             {u"frame_size": 1518, u"phy_cores": 1},
738             {u"frame_size": 1518, u"phy_cores": 2},
739             {u"frame_size": 1518, u"phy_cores": 4},
740             {u"frame_size": 9000, u"phy_cores": 1},
741             {u"frame_size": 9000, u"phy_cores": 2},
742             {u"frame_size": 9000, u"phy_cores": 4},
743             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 1},
744             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 2},
745             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 4}
746         ]
747         hs_bps_kwargs_list = [
748             {u"frame_size": 1460, u"phy_cores": 1},
749         ]
750         hs_quic_kwargs_list = [
751             {u"frame_size": 1280, u"phy_cores": 1},
752         ]
753         iperf3_kwargs_list = [
754             {u"frame_size": 128000, u"phy_cores": 1},
755             {u"frame_size": 128000, u"phy_cores": 2},
756             {u"frame_size": 128000, u"phy_cores": 4}
757         ]
758         # List for tests with one dataplane core
759         # (and variable number of other cores).
760         dp1_kwargs_list = [
761             {u"frame_size": min_frame_size, u"phy_cores": 2},
762             {u"frame_size": min_frame_size, u"phy_cores": 3},
763             {u"frame_size": min_frame_size, u"phy_cores": 4},
764             {u"frame_size": 1518, u"phy_cores": 2},
765             {u"frame_size": 1518, u"phy_cores": 3},
766             {u"frame_size": 1518, u"phy_cores": 4},
767             {u"frame_size": 9000, u"phy_cores": 2},
768             {u"frame_size": 9000, u"phy_cores": 3},
769             {u"frame_size": 9000, u"phy_cores": 4},
770             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 2},
771             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 3},
772             {u"frame_size": u"IMIX_v4_1", u"phy_cores": 4}
773         ]
774
775         http_kwargs_list = [
776             {u"frame_size": 0, u"phy_cores": 1},
777             {u"frame_size": 0, u"phy_cores": 2},
778             {u"frame_size": 64, u"phy_cores": 1},
779             {u"frame_size": 64, u"phy_cores": 2},
780             {u"frame_size": 1024, u"phy_cores": 1},
781             {u"frame_size": 1024, u"phy_cores": 2},
782             {u"frame_size": 2048, u"phy_cores": 1},
783             {u"frame_size": 2048, u"phy_cores": 2}
784         ]
785
786         device_kwargs_list = [
787             {u"frame_size": min_frame_size, u"phy_cores": 0}
788         ]
789
790         trex_kwargs_list = [
791             {u"frame_size": min_frame_size},
792             {u"frame_size": 1518},
793             {u"frame_size": 9000},
794             {u"frame_size": u"IMIX_v4_1"}
795         ]
796
797         for in_filename in glob(pattern):
798             if not self.quiet:
799                 print(
800                     u"Regenerating in_filename:", in_filename, file=sys.stderr
801                 )
802             iface, _, _ = get_iface_and_suite_ids(in_filename)
803             if not iface.endswith(u"10ge2p1x710"):
804                 raise RuntimeError(
805                     f"Error in {in_filename}: non-primary NIC found."
806                 )
807             for prefix in Constants.FORBIDDEN_SUITE_PREFIX_LIST:
808                 if prefix in in_filename:
809                     raise RuntimeError(
810                         f"Error in {in_filename}: non-primary driver found."
811                     )
812             with open(in_filename, u"rt") as file_in:
813                 in_prolog = u"".join(
814                     file_in.read().partition(u"*** Test Cases ***")[:-1]
815                 )
816             if "-tg" in in_filename:
817                 write_trex_files(in_filename, in_prolog, trex_kwargs_list)
818                 continue
819             if in_filename.endswith(u"-ndrpdr.robot"):
820                 if u"scheduler" in in_filename:
821                     write_default_files(
822                         in_filename, in_prolog, dp1_kwargs_list
823                     )
824                 else:
825                     write_default_files(
826                         in_filename, in_prolog, default_kwargs_list
827                     )
828             elif in_filename.endswith(u"-reconf.robot"):
829                 write_reconf_files(in_filename, in_prolog, default_kwargs_list)
830             elif in_filename.endswith(u"-rps.robot") \
831                     or in_filename.endswith(u"-cps.robot"):
832                 write_tcp_files(in_filename, in_prolog, http_kwargs_list)
833             elif in_filename.endswith(u"-bps.robot"):
834                 hoststack_kwargs_list = \
835                     hs_quic_kwargs_list if u"quic" in in_filename \
836                     else hs_bps_kwargs_list
837                 write_tcp_files(in_filename, in_prolog, hoststack_kwargs_list)
838             elif in_filename.endswith(u"-iperf3-mrr.robot"):
839                 write_iperf3_files(in_filename, in_prolog, iperf3_kwargs_list)
840             elif in_filename.endswith(u"-scapy.robot"):
841                 write_device_files(in_filename, in_prolog, device_kwargs_list)
842             else:
843                 raise RuntimeError(
844                     f"Error in {in_filename}: non-primary suite type found."
845                 )
846         if not self.quiet:
847             print(u"Regenerator ends.", file=sys.stderr)
848         print(file=sys.stderr)  # To make autogen check output more readable.