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