PapiExecutor always verifies
[csit.git] / resources / libraries / python / VppCounters.py
1 # Copyright (c) 2019 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 """VPP counters utilities library."""
15
16 import time
17
18 from pprint import pformat
19
20 from robot.api import logger
21 from resources.libraries.python.PapiExecutor import PapiExecutor
22 from resources.libraries.python.topology import NodeType, Topology
23
24
25 class VppCounters(object):
26     """VPP counters utilities."""
27
28     def __init__(self):
29         self._stats_table = None
30
31     @staticmethod
32     def _get_non_zero_items(data):
33         """Extract and return non-zero items from the input data.
34
35         :param data: Data to filter.
36         :type data: dict
37         :returns: Dictionary with non-zero items.
38         :rtype dict
39         """
40         return {k: data[k] for k in data.keys() if sum(data[k])}
41
42     @staticmethod
43     def vpp_show_errors(node):
44         """Run "show errors" debug CLI command.
45
46         :param node: Node to run command on.
47         :type node: dict
48         """
49         PapiExecutor.run_cli_cmd(node, 'show errors')
50
51     @staticmethod
52     def vpp_show_errors_verbose(node):
53         """Run "show errors verbose" debug CLI command.
54
55         :param node: Node to run command on.
56         :type node: dict
57         """
58         PapiExecutor.run_cli_cmd(node, 'show errors verbose')
59
60     @staticmethod
61     def vpp_show_errors_on_all_duts(nodes, verbose=False):
62         """Show errors on all DUTs.
63
64         :param nodes: VPP nodes.
65         :param verbose: If True show verbose output.
66         :type nodes: dict
67         :type verbose: bool
68         """
69         for node in nodes.values():
70             if node['type'] == NodeType.DUT:
71                 if verbose:
72                     VppCounters.vpp_show_errors_verbose(node)
73                 else:
74                     VppCounters.vpp_show_errors(node)
75
76     @staticmethod
77     def vpp_show_runtime(node, log_zeros=False):
78         """Run "show runtime" CLI command.
79
80         :param node: Node to run command on.
81         :param log_zeros: Log also items with zero values.
82         :type node: dict
83         :type log_zeros: bool
84         """
85         args = dict(path='^/sys/node')
86         with PapiExecutor(node) as papi_exec:
87             stats = papi_exec.add("vpp-stats", **args).get_stats()[0]
88             # TODO: Introduce get_stat?
89
90         names = stats['/sys/node/names']
91
92         if not names:
93             return
94
95         runtime = []
96         runtime_non_zero = []
97
98         for name in names:
99             runtime.append({'name': name})
100
101         for idx, runtime_item in enumerate(runtime):
102
103             calls_th = []
104             for thread in stats['/sys/node/calls']:
105                 calls_th.append(thread[idx])
106             runtime_item["calls"] = calls_th
107
108             vectors_th = []
109             for thread in stats['/sys/node/vectors']:
110                 vectors_th.append(thread[idx])
111             runtime_item["vectors"] = vectors_th
112
113             suspends_th = []
114             for thread in stats['/sys/node/suspends']:
115                 suspends_th.append(thread[idx])
116             runtime_item["suspends"] = suspends_th
117
118             clocks_th = []
119             for thread in stats['/sys/node/clocks']:
120                 clocks_th.append(thread[idx])
121             runtime_item["clocks"] = clocks_th
122
123             if (sum(calls_th) or sum(vectors_th) or
124                     sum(suspends_th) or sum(clocks_th)):
125                 runtime_non_zero.append(runtime_item)
126
127         if log_zeros:
128             logger.info("Runtime:\n{runtime}".format(
129                 runtime=pformat(runtime)))
130         else:
131             logger.info("Runtime:\n{runtime}".format(
132                 runtime=pformat(runtime_non_zero)))
133
134     @staticmethod
135     def vpp_show_runtime_verbose(node):
136         """Run "show runtime verbose" CLI command.
137
138         TODO: Remove?
139               Only verbose output is possible to get using VPPStats.
140
141         :param node: Node to run command on.
142         :type node: dict
143         """
144         VppCounters.vpp_show_runtime(node)
145
146     @staticmethod
147     def show_runtime_counters_on_all_duts(nodes):
148         """Clear VPP runtime counters on all DUTs.
149
150         :param nodes: VPP nodes.
151         :type nodes: dict
152         """
153         for node in nodes.values():
154             if node['type'] == NodeType.DUT:
155                 VppCounters.vpp_show_runtime(node)
156
157     @staticmethod
158     def vpp_show_hardware_detail(node):
159         """Run "show hardware-interfaces detail" debug CLI command.
160
161         :param node: Node to run command on.
162         :type node: dict
163         """
164         PapiExecutor.run_cli_cmd(node, 'show hardware detail')
165
166     @staticmethod
167     def vpp_clear_runtime(node):
168         """Run "clear runtime" CLI command.
169
170         :param node: Node to run command on.
171         :type node: dict
172         :returns: Verified data from PAPI response.
173         :rtype: dict
174         """
175         return PapiExecutor.run_cli_cmd(node, 'clear runtime', log=False)
176
177     @staticmethod
178     def clear_runtime_counters_on_all_duts(nodes):
179         """Run "clear runtime" CLI command on all DUTs.
180
181         :param nodes: VPP nodes.
182         :type nodes: dict
183         """
184         for node in nodes.values():
185             if node['type'] == NodeType.DUT:
186                 VppCounters.vpp_clear_runtime(node)
187
188     @staticmethod
189     def vpp_clear_interface_counters(node):
190         """Run "clear interfaces" CLI command.
191
192         :param node: Node to run command on.
193         :type node: dict
194         :returns: Verified data from PAPI response.
195         :rtype: dict
196         """
197         return PapiExecutor.run_cli_cmd(node, 'clear interfaces', log=False)
198
199     @staticmethod
200     def clear_interface_counters_on_all_duts(nodes):
201         """Clear interface counters on all DUTs.
202
203         :param nodes: VPP nodes.
204         :type nodes: dict
205         """
206         for node in nodes.values():
207             if node['type'] == NodeType.DUT:
208                 VppCounters.vpp_clear_interface_counters(node)
209
210     @staticmethod
211     def vpp_clear_hardware_counters(node):
212         """Run "clear hardware" CLI command.
213
214         :param node: Node to run command on.
215         :type node: dict
216         :returns: Verified data from PAPI response.
217         :rtype: dict
218         """
219         return PapiExecutor.run_cli_cmd(node, 'clear hardware', log=False)
220
221     @staticmethod
222     def clear_hardware_counters_on_all_duts(nodes):
223         """Clear hardware counters on all DUTs.
224
225         :param nodes: VPP nodes.
226         :type nodes: dict
227         """
228         for node in nodes.values():
229             if node['type'] == NodeType.DUT:
230                 VppCounters.vpp_clear_hardware_counters(node)
231
232     @staticmethod
233     def vpp_clear_errors_counters(node):
234         """Run "clear errors" CLI command.
235
236         :param node: Node to run command on.
237         :type node: dict
238         :returns: Verified data from PAPI response.
239         :rtype: dict
240         """
241         return PapiExecutor.run_cli_cmd(node, 'clear errors', log=False)
242
243     @staticmethod
244     def clear_error_counters_on_all_duts(nodes):
245         """Clear VPP errors counters on all DUTs.
246
247         :param nodes: VPP nodes.
248         :type nodes: dict
249         """
250         for node in nodes.values():
251             if node['type'] == NodeType.DUT:
252                 VppCounters.vpp_clear_errors_counters(node)
253
254     def vpp_get_ipv4_interface_counter(self, node, interface):
255         """
256
257         :param node: Node to get interface IPv4 counter on.
258         :param interface: Interface name.
259         :type node: dict
260         :type interface: str
261         :returns: Interface IPv4 counter.
262         :rtype: int
263         """
264         return self.vpp_get_ipv46_interface_counter(node, interface, False)
265
266     def vpp_get_ipv6_interface_counter(self, node, interface):
267         """
268
269         :param node: Node to get interface IPv6 counter on.
270         :param interface: Interface name.
271         :type node: dict
272         :type interface: str
273         :returns: Interface IPv6 counter.
274         :rtype: int
275         """
276         return self.vpp_get_ipv46_interface_counter(node, interface, True)
277
278     def vpp_get_ipv46_interface_counter(self, node, interface, is_ipv6=True):
279         """Return interface IPv4/IPv6 counter.
280
281         :param node: Node to get interface IPv4/IPv6 counter on.
282         :param interface: Interface name.
283         :param is_ipv6: Specify IP version.
284         :type node: dict
285         :type interface: str
286         :type is_ipv6: bool
287         :returns: Interface IPv4/IPv6 counter.
288         :rtype: int
289         """
290         version = 'ip6' if is_ipv6 else 'ip4'
291         topo = Topology()
292         sw_if_index = topo.get_interface_sw_index(node, interface)
293         if sw_if_index is None:
294             logger.trace('{i} sw_if_index not found.'.format(i=interface))
295             return 0
296
297         if_counters = self._stats_table.get('interface_counters')
298         if not if_counters:
299             logger.trace('No interface counters.')
300             return 0
301         for counter in if_counters:
302             if counter['vnet_counter_type'] == version:
303                 data = counter['data']
304                 return data[sw_if_index]
305         logger.trace('{i} {v} counter not found.'.format(
306             i=interface, v=version))
307         return 0
308
309     @staticmethod
310     def show_vpp_statistics(node):
311         """Show [error, hardware, interface] stats.
312
313         :param node: VPP node.
314         :type node: dict
315         """
316         VppCounters.vpp_show_errors(node)
317         VppCounters.vpp_show_hardware_detail(node)
318         VppCounters.vpp_show_runtime(node)
319
320     @staticmethod
321     def show_statistics_on_all_duts(nodes, sleeptime=5):
322         """Show VPP statistics on all DUTs.
323
324         :param nodes: VPP nodes.
325         :type nodes: dict
326         :param sleeptime: Time to wait for traffic to arrive back to TG.
327         :type sleeptime: int
328         """
329         logger.trace('Waiting for statistics to be collected')
330         time.sleep(sleeptime)
331         for node in nodes.values():
332             if node['type'] == NodeType.DUT:
333                 VppCounters.show_vpp_statistics(node)