feat(core): Remove some dead code
[csit.git] / resources / libraries / python / VPPUtil.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 """VPP util library."""
15
16 from robot.api import logger
17
18 from resources.libraries.python.Constants import Constants
19 from resources.libraries.python.DUTSetup import DUTSetup
20 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
21 from resources.libraries.python.model.ExportResult import (
22     export_dut_type_and_version
23 )
24 from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd
25 from resources.libraries.python.topology import Topology, SocketType, NodeType
26
27
28 class VPPUtil:
29     """General class for any VPP related methods/functions."""
30
31     @staticmethod
32     def restart_vpp_service(node, node_key=None):
33         """Restart VPP service on the specified topology node.
34
35         Disconnect possibly connected PAPI executor.
36
37         :param node: Topology node.
38         :param node_key: Topology node key.
39         :type node: dict
40         :type node_key: str
41         """
42         # Containers have a separate lifecycle, but better be safe.
43         PapiSocketExecutor.disconnect_all_sockets_by_node(node)
44         DUTSetup.restart_service(node, Constants.VPP_UNIT)
45         if node_key:
46             Topology.add_new_socket(
47                 node, SocketType.CLI, node_key, Constants.SOCKCLI_PATH)
48             Topology.add_new_socket(
49                 node, SocketType.PAPI, node_key, Constants.SOCKSVR_PATH)
50             Topology.add_new_socket(
51                 node, SocketType.STATS, node_key, Constants.SOCKSTAT_PATH)
52
53     @staticmethod
54     def restart_vpp_service_on_all_duts(nodes):
55         """Restart VPP service on all DUT nodes.
56
57         :param nodes: Topology nodes.
58         :type nodes: dict
59         """
60         for node_key, node in nodes.items():
61             if node[u"type"] == NodeType.DUT:
62                 VPPUtil.restart_vpp_service(node, node_key)
63
64     @staticmethod
65     def stop_vpp_service(node, node_key=None):
66         """Stop VPP service on the specified topology node.
67
68         Disconnect possibly connected PAPI executor.
69
70         :param node: Topology node.
71         :param node_key: Topology node key.
72         :type node: dict
73         :type node_key: str
74         """
75         # Containers have a separate lifecycle, but better be safe.
76         PapiSocketExecutor.disconnect_all_sockets_by_node(node)
77         DUTSetup.stop_service(node, Constants.VPP_UNIT)
78         if node_key:
79             Topology.del_node_socket_id(node, SocketType.PAPI, node_key)
80             Topology.del_node_socket_id(node, SocketType.STATS, node_key)
81
82     @staticmethod
83     def stop_vpp_service_on_all_duts(nodes):
84         """Stop VPP service on all DUT nodes.
85
86         :param nodes: Topology nodes.
87         :type nodes: dict
88         """
89         for node_key, node in nodes.items():
90             if node[u"type"] == NodeType.DUT:
91                 VPPUtil.stop_vpp_service(node, node_key)
92
93     @staticmethod
94     def verify_vpp_installed(node):
95         """Verify that VPP is installed on the specified topology node.
96
97         :param node: Topology node.
98         :type node: dict
99         """
100         DUTSetup.verify_program_installed(node, u"vpp")
101
102     @staticmethod
103     def adjust_privileges(node):
104         """Adjust privileges to control VPP without sudo.
105
106         :param node: Topology node.
107         :type node: dict
108         """
109         cmd = u"chmod -R o+rwx /run/vpp"
110         exec_cmd_no_error(
111             node, cmd, sudo=True, message=u"Failed to adjust privileges!",
112             retries=120)
113
114     @staticmethod
115     def verify_vpp_started(node):
116         """Verify that VPP is started on the specified topology node.
117
118         :param node: Topology node.
119         :type node: dict
120         """
121         cmd = u"echo \"show pci\" | sudo socat - UNIX-CONNECT:/run/vpp/cli.sock"
122         exec_cmd_no_error(
123             node, cmd, sudo=False, message=u"VPP failed to start!", retries=120
124         )
125
126         cmd = u"vppctl show pci 2>&1 | fgrep -v \"Connection refused\" | " \
127               u"fgrep -v \"No such file or directory\""
128         exec_cmd_no_error(
129             node, cmd, sudo=True, message=u"VPP failed to start!", retries=120
130         )
131
132         # Properly enable cards in case they were disabled. This will be
133         # followed in https://jira.fd.io/browse/VPP-1934.
134         cmd = u"for i in $(sudo vppctl sho int | grep Eth | cut -d' ' -f1); do"\
135               u" sudo vppctl set int sta $i up; done"
136         exec_cmd(node, cmd, sudo=False)
137
138     @staticmethod
139     def verify_vpp(node):
140         """Verify that VPP is installed and started on the specified topology
141         node. Adjust privileges so user can connect without sudo.
142
143         :param node: Topology node.
144         :type node: dict
145         :raises RuntimeError: If VPP service fails to start.
146         """
147         DUTSetup.verify_program_installed(node, 'vpp')
148         try:
149             # Verify responsiveness of vppctl.
150             VPPUtil.verify_vpp_started(node)
151             # Adjust privileges.
152             VPPUtil.adjust_privileges(node)
153             # Verify responsiveness of PAPI.
154             VPPUtil.show_log(node)
155             VPPUtil.vpp_show_version(node)
156         finally:
157             DUTSetup.get_service_logs(node, Constants.VPP_UNIT)
158
159     @staticmethod
160     def verify_vpp_on_all_duts(nodes):
161         """Verify that VPP is installed and started on all DUT nodes.
162
163         :param nodes: Nodes in the topology.
164         :type nodes: dict
165         """
166         for node in nodes.values():
167             if node[u"type"] == NodeType.DUT:
168                 VPPUtil.verify_vpp(node)
169
170     @staticmethod
171     def vpp_show_version(
172             node, remote_vpp_socket=Constants.SOCKSVR_PATH, log=True):
173         """Run "show_version" PAPI command.
174
175         Socket is configurable, so VPP inside container can be accessed.
176         The result is exported to JSON UTI output as "dut-version".
177
178         :param node: Node to run command on.
179         :param remote_vpp_socket: Path to remote socket to target VPP.
180         :param log: If true, show the result in Robot log.
181         :type node: dict
182         :type remote_vpp_socket: str
183         :type log: bool
184         :returns: VPP version.
185         :rtype: str
186         :raises RuntimeError: If PAPI connection fails.
187         :raises AssertionError: If PAPI retcode is nonzero.
188         """
189         cmd = u"show_version"
190         with PapiSocketExecutor(node, remote_vpp_socket) as papi_exec:
191             reply = papi_exec.add(cmd).get_reply()
192         if log:
193             logger.info(f"VPP version: {reply[u'version']}\n")
194         version = f"{reply[u'version']}"
195         export_dut_type_and_version(u"VPP", version)
196         return version
197
198     @staticmethod
199     def show_vpp_version_on_all_duts(nodes):
200         """Show VPP version verbose on all DUTs.
201
202         :param nodes: Nodes in the topology.
203         :type nodes: dict
204         """
205         for node in nodes.values():
206             if node[u"type"] == NodeType.DUT:
207                 VPPUtil.vpp_show_version(node)
208
209     @staticmethod
210     def vpp_show_interfaces(node):
211         """Run "show interface" CLI command.
212
213         :param node: Node to run command on.
214         :type node: dict
215         """
216
217         cmd = u"sw_interface_dump"
218         args = dict(
219             name_filter_valid=False,
220             name_filter=u""
221         )
222         err_msg = f"Failed to get interface dump on host {node[u'host']}"
223         with PapiSocketExecutor(node) as papi_exec:
224             details = papi_exec.add(cmd, **args).get_details(err_msg)
225
226         for if_dump in details:
227             if_dump[u"l2_address"] = str(if_dump[u"l2_address"])
228             if_dump[u"b_dmac"] = str(if_dump[u"b_dmac"])
229             if_dump[u"b_smac"] = str(if_dump[u"b_smac"])
230             if_dump[u"flags"] = if_dump[u"flags"].value
231             if_dump[u"type"] = if_dump[u"type"].value
232             if_dump[u"link_duplex"] = if_dump[u"link_duplex"].value
233             if_dump[u"sub_if_flags"] = if_dump[u"sub_if_flags"].value \
234                 if hasattr(if_dump[u"sub_if_flags"], u"value") \
235                 else int(if_dump[u"sub_if_flags"])
236         # TODO: return only base data
237         logger.trace(f"Interface data of host {node[u'host']}:\n{details}")
238
239     @staticmethod
240     def vpp_enable_traces_on_dut(node, fail_on_error=False):
241         """Enable vpp packet traces on the DUT node.
242
243         :param node: DUT node to set up.
244         :param fail_on_error: If True, keyword fails if an error occurs,
245             otherwise passes.
246         :type node: dict
247         :type fail_on_error: bool
248         """
249         cmds = [
250             u"trace add dpdk-input 50",
251             u"trace add vhost-user-input 50",
252             u"trace add memif-input 50",
253             u"trace add avf-input 50"
254         ]
255
256         for cmd in cmds:
257             try:
258                 PapiSocketExecutor.run_cli_cmd_on_all_sockets(node, cmd)
259             except AssertionError:
260                 if fail_on_error:
261                     raise
262
263     @staticmethod
264     def vpp_enable_traces_on_all_duts(nodes, fail_on_error=False):
265         """Enable vpp packet traces on all DUTs in the given topology.
266
267         :param nodes: Nodes in the topology.
268         :param fail_on_error: If True, keyword fails if an error occurs,
269             otherwise passes.
270         :type nodes: dict
271         :type fail_on_error: bool
272         """
273         for node in nodes.values():
274             if node[u"type"] == NodeType.DUT:
275                 VPPUtil.vpp_enable_traces_on_dut(node, fail_on_error)
276
277     @staticmethod
278     def vpp_enable_elog_traces(node):
279         """Enable API/CLI/Barrier traces on the specified topology node.
280
281         :param node: Topology node.
282         :type node: dict
283         """
284         try:
285             PapiSocketExecutor.run_cli_cmd_on_all_sockets(
286                 node, u"event-logger trace api cli barrier")
287         except AssertionError:
288             # Perhaps an older VPP build is tested.
289             PapiSocketExecutor.run_cli_cmd_on_all_sockets(
290                 node, u"elog trace api cli barrier")
291
292     @staticmethod
293     def vpp_enable_elog_traces_on_all_duts(nodes):
294         """Enable API/CLI/Barrier traces on all DUTs in the given topology.
295
296         :param nodes: Nodes in the topology.
297         :type nodes: dict
298         """
299         for node in nodes.values():
300             if node[u"type"] == NodeType.DUT:
301                 VPPUtil.vpp_enable_elog_traces(node)
302
303     @staticmethod
304     def show_event_logger(node):
305         """Show event logger on the specified topology node.
306
307         :param node: Topology node.
308         :type node: dict
309         """
310         PapiSocketExecutor.run_cli_cmd_on_all_sockets(
311             node, u"show event-logger")
312
313     @staticmethod
314     def show_event_logger_on_all_duts(nodes):
315         """Show event logger on all DUTs in the given topology.
316
317         :param nodes: Nodes in the topology.
318         :type nodes: dict
319         """
320         for node in nodes.values():
321             if node[u"type"] == NodeType.DUT:
322                 VPPUtil.show_event_logger(node)
323
324     @staticmethod
325     def show_log(node):
326         """Show logging on the specified topology node.
327
328         :param node: Topology node.
329         :type node: dict
330         """
331         PapiSocketExecutor.run_cli_cmd(node, u"show logging")
332
333     @staticmethod
334     def show_log_on_all_duts(nodes):
335         """Show logging on all DUTs in the given topology.
336
337         :param nodes: Nodes in the topology.
338         :type nodes: dict
339         """
340         for node in nodes.values():
341             if node[u"type"] == NodeType.DUT:
342                 VPPUtil.show_log(node)
343
344     @staticmethod
345     def vpp_show_threads(node):
346         """Show VPP threads on node.
347
348         :param node: Node to run command on.
349         :type node: dict
350         :returns: VPP thread data.
351         :rtype: list
352         """
353         cmd = u"show_threads"
354         with PapiSocketExecutor(node) as papi_exec:
355             reply = papi_exec.add(cmd).get_reply()
356
357         threads_data = reply[u"thread_data"]
358         logger.trace(f"show threads:\n{threads_data}")
359
360         return threads_data
361
362     @staticmethod
363     def vpp_add_graph_node_next(node, graph_node_name, graph_next_name):
364         """Set the next node for a given node.
365
366         :param node: Node to run command on.
367         :param graph_node_name: Graph node to add the next node on.
368         :param graph_next_name: Graph node to add as the next node.
369         :type node: dict
370         :type graph_node_name: str
371         :type graph_next_name: str
372         :returns: The index of the next graph node.
373         :rtype: int
374         """
375         cmd = u"add_node_next"
376         args = dict(
377             node_name=graph_node_name,
378             next_name=graph_next_name
379         )
380         with PapiSocketExecutor(node) as papi_exec:
381             reply = papi_exec.add(cmd, **args).get_reply()
382
383         return reply[u"next_index"]
384
385     @staticmethod
386     def vpp_set_neighbor_limit_on_all_duts(nodes, count):
387         """VPP set neighbor count limit on all DUTs in the given topology.
388
389         :param nodes: Nodes in the topology.
390         :param count: Neighbor count need to set.
391         :type nodes: dict
392         :type count: int
393         """
394         for node in nodes.values():
395             if node[u"type"] == NodeType.DUT:
396                 cmd = f"set ip neighbor-config ip4 limit {count}"
397                 PapiSocketExecutor.run_cli_cmd(node, cmd)
398
399                 cmd = f"set ip neighbor-config ip6 limit {count}"
400                 PapiSocketExecutor.run_cli_cmd(node, cmd)