16c6b89fb3dd7c0ee8758c524b03e9a1846f20a6
[csit.git] / resources / libraries / python / model / ExportResult.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 with keywords that publish parts of result structure."""
15
16 from robot.libraries.BuiltIn import BuiltIn
17
18 from resources.libraries.python.model.util import descend, get_export_data
19
20
21 def export_dut_type_and_version(dut_type=u"unknown", dut_version=u"unknown"):
22     """Export the arguments as dut type and version.
23
24     Robot tends to convert "none" into None, hence the unusual default values.
25
26     If either argument is missing, the value from robot variable is used.
27     If argument is present, the value is also stored to robot suite variable.
28
29     :param dut_type: DUT type, e.g. VPP or DPDK.
30     :param dut_version: DUT version as determined by the caller.
31     :type dut_type: Optional[str]
32     :type dut_version: Optiona[str]
33     :raises RuntimeError: If value is neither in argument not robot variable.
34     """
35     if dut_type == u"unknown":
36         dut_type = BuiltIn().get_variable_value(u"\\${DUT_TYPE}", u"unknown")
37         if dut_type == u"unknown":
38             raise RuntimeError(u"Dut type not provided.")
39     else:
40         # We want to set a variable in higher level suite setup
41         # to be available to test setup several levels lower.
42         BuiltIn().set_suite_variable(
43             u"\\${DUT_TYPE}", dut_type, u"children=True"
44         )
45     if dut_version == u"unknown":
46         dut_version = BuiltIn().get_variable_value(
47             u"\\${DUT_VERSION}", u"unknown"
48         )
49         if dut_type == u"unknown":
50             raise RuntimeError(u"Dut version not provided.")
51     else:
52         BuiltIn().set_suite_variable(
53             u"\\${DUT_VERSION}", dut_version, u"children=True"
54         )
55     data = get_export_data()
56     data[u"dut_type"] = dut_type.lower()
57     data[u"dut_version"] = dut_version
58
59
60 def export_tg_type_and_version(tg_type=u"unknown", tg_version=u"unknown"):
61     """Export the arguments as tg type and version.
62
63     Robot tends to convert "none" into None, hence the unusual default values.
64
65     If either argument is missing, the value from robot variable is used.
66     If argument is present, the value is also stored to robot suite variable.
67
68     :param tg_type: TG type, e.g. TREX.
69     :param tg_version: TG version as determined by the caller.
70     :type tg_type: Optional[str]
71     :type tg_version: Optiona[str]
72     :raises RuntimeError: If value is neither in argument not robot variable.
73     """
74     if tg_type == u"unknown":
75         tg_type = BuiltIn().get_variable_value(u"\\${TG_TYPE}", u"unknown")
76         if tg_type == u"unknown":
77             raise RuntimeError(u"TG type not provided.")
78     else:
79         # We want to set a variable in higher level suite setup
80         # to be available to test setup several levels lower.
81         BuiltIn().set_suite_variable(
82             u"\\${TG_TYPE}", tg_type, u"children=True"
83         )
84     if tg_version == u"unknown":
85         tg_version = BuiltIn().get_variable_value(
86             u"\\${TG_VERSION}", u"unknown"
87         )
88         if tg_type == u"unknown":
89             raise RuntimeError(u"TG version not provided.")
90     else:
91         BuiltIn().set_suite_variable(
92             u"\\${TG_VERSION}", tg_version, u"children=True"
93         )
94     data = get_export_data()
95     data[u"tg_type"] = tg_type.lower()
96     data[u"tg_version"] = tg_version
97
98
99 def append_mrr_value(mrr_value, unit):
100     """Store mrr value to proper place so it is dumped into json.
101
102     The value is appended only when unit is not empty.
103
104     :param mrr_value: Forwarding rate from MRR trial.
105     :param unit: Unit of measurement for the rate.
106     :type mrr_value: float
107     :type unit: str
108     """
109     if not unit:
110         return
111     data = get_export_data()
112     data[u"result"][u"type"] = u"mrr"
113     rate_node = descend(descend(data[u"result"], u"receive_rate"), "rate")
114     rate_node[u"unit"] = str(unit)
115     values_list = descend(rate_node, u"values", list)
116     values_list.append(float(mrr_value))
117     # TODO: Fill in the bandwidth part for pps?
118
119
120 def export_search_bound(text, value, unit, bandwidth=None):
121     """Store bound value and unit.
122
123     This function works for both NDRPDR and SOAK, decided by text.
124
125     If a node does not exist, it is created.
126     If a previous value exists, it is overwritten silently.
127     Result type is set (overwritten) to ndrpdr (or soak).
128
129     Text is used to determine whether it is ndr or pdr, upper or lower bound,
130     as the Robot caller has the information only there.
131
132     :param text: Info from Robot caller to determime bound type.
133     :param value: The bound value in packets (or connections) per second.
134     :param unit: Rate unit the bound is measured (or estimated) in.
135     :param bandwidth: The same value recomputed into L1 bits per second.
136     :type text: str
137     :type value: float
138     :type unit: str
139     :type bandwidth: Optional[float]
140     """
141     value = float(value)
142     text = str(text).lower()
143     result_type = u"soak" if u"plrsearch" in text else u"ndrpdr"
144     upper_or_lower = u"upper" if u"upper" in text else u"lower"
145     ndr_or_pdr = u"ndr" if u"ndr" in text else u"pdr"
146
147     data = get_export_data()
148     result_node = data[u"result"]
149     result_node[u"type"] = result_type
150     rate_item = dict(rate=dict(value=value, unit=unit))
151     if bandwidth:
152         rate_item[u"bandwidth"] = dict(value=float(bandwidth), unit=u"bps")
153     if result_type == u"soak":
154         descend(result_node, u"critical_rate")[upper_or_lower] = rate_item
155         return
156     descend(result_node, ndr_or_pdr)[upper_or_lower] = rate_item
157
158
159 def _add_latency(result_node, percent, whichward, latency_string):
160     """Descend to a corresponding node and add values from latency string.
161
162     This is an internal block, moved out from export_ndrpdr_latency,
163     as it can be called up to 4 times.
164
165     :param result_node: UTI tree node to descend from.
166     :param percent: Percent value to use in node key (90, 50, 10, 0).
167     :param whichward: "forward" or "reverse".
168     :param latency_item: Unidir output from TRex utility, min/avg/max/hdrh.
169     :type result_node: dict
170     :type percent: int
171     :type whichward: str
172     :latency_string: str
173     """
174     l_min, l_avg, l_max, l_hdrh = latency_string.split(u"/", 3)
175     whichward_node = descend(result_node, f"latency_{whichward}")
176     percent_node = descend(whichward_node, f"pdr_{percent}")
177     percent_node[u"min"] = int(l_min)
178     percent_node[u"avg"] = int(l_avg)
179     percent_node[u"max"] = int(l_max)
180     percent_node[u"hdrh"] = l_hdrh
181     percent_node[u"unit"] = u"us"
182
183
184 def export_ndrpdr_latency(text, latency):
185     """Store NDRPDR hdrh latency data.
186
187     If "latency" node does not exist, it is created.
188     If a previous value exists, it is overwritten silently.
189
190     Text is used to determine what percentage of PDR is the load,
191     as the Robot caller has the information only there.
192
193     Reverse data may be missing, we assume the test was unidirectional.
194
195     :param text: Info from Robot caller to determime load.
196     :param latency: Output from TRex utility, min/avg/max/hdrh.
197     :type text: str
198     :type latency: 1-tuple or 2-tuple of str
199     """
200     data = get_export_data()
201     result_node = data[u"result"]
202     percent = 0
203     if u"90" in text:
204         percent = 90
205     elif u"50" in text:
206         percent = 50
207     elif u"10" in text:
208         percent = 10
209     _add_latency(result_node, percent, u"forward", latency[0])
210     # Else TRex does not support latency measurement for this traffic profile.
211     if len(latency) < 2:
212         return
213     _add_latency(result_node, percent, u"reverse", latency[1])