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