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