Hoststack perf infrastructure refactoring
[csit.git] / resources / libraries / python / IPUtil.py
1 # Copyright (c) 2020 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 """Common IP utilities library."""
15 import re
16
17 from enum import IntEnum
18
19 from ipaddress import ip_address
20
21 from resources.libraries.python.Constants import Constants
22 from resources.libraries.python.InterfaceUtil import InterfaceUtil
23 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
24 from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd
25 from resources.libraries.python.topology import Topology
26 from resources.libraries.python.VatExecutor import VatTerminal
27 from resources.libraries.python.Namespaces import Namespaces
28
29
30 # from vpp/src/vnet/vnet/mpls/mpls_types.h
31 MPLS_IETF_MAX_LABEL = 0xfffff
32 MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1
33
34
35 class AddressFamily(IntEnum):
36     """IP address family."""
37     ADDRESS_IP4 = 0
38     ADDRESS_IP6 = 1
39
40
41 class FibPathType(IntEnum):
42     """FIB path types."""
43     FIB_PATH_TYPE_NORMAL = 0
44     FIB_PATH_TYPE_LOCAL = 1
45     FIB_PATH_TYPE_DROP = 2
46     FIB_PATH_TYPE_UDP_ENCAP = 3
47     FIB_PATH_TYPE_BIER_IMP = 4
48     FIB_PATH_TYPE_ICMP_UNREACH = 5
49     FIB_PATH_TYPE_ICMP_PROHIBIT = 6
50     FIB_PATH_TYPE_SOURCE_LOOKUP = 7
51     FIB_PATH_TYPE_DVR = 8
52     FIB_PATH_TYPE_INTERFACE_RX = 9
53     FIB_PATH_TYPE_CLASSIFY = 10
54
55
56 class FibPathFlags(IntEnum):
57     """FIB path flags."""
58     FIB_PATH_FLAG_NONE = 0
59     # TODO: Name too long for pylint, fix in VPP.
60     FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED = 1
61     FIB_PATH_FLAG_RESOLVE_VIA_HOST = 2
62
63
64 class FibPathNhProto(IntEnum):
65     """FIB path next-hop protocol."""
66     FIB_PATH_NH_PROTO_IP4 = 0
67     FIB_PATH_NH_PROTO_IP6 = 1
68     FIB_PATH_NH_PROTO_MPLS = 2
69     FIB_PATH_NH_PROTO_ETHERNET = 3
70     FIB_PATH_NH_PROTO_BIER = 4
71
72
73 class IPUtil:
74     """Common IP utilities"""
75
76     @staticmethod
77     def ip_to_int(ip_str):
78         """Convert IP address from string format (e.g. 10.0.0.1) to integer
79         representation (167772161).
80
81         :param ip_str: IP address in string representation.
82         :type ip_str: str
83         :returns: Integer representation of IP address.
84         :rtype: int
85         """
86         return int(ip_address(ip_str))
87
88     @staticmethod
89     def int_to_ip(ip_int):
90         """Convert IP address from integer representation (e.g. 167772161) to
91         string format (10.0.0.1).
92
93         :param ip_int: IP address in integer representation.
94         :type ip_int: int
95         :returns: String representation of IP address.
96         :rtype: str
97         """
98         return str(ip_address(ip_int))
99
100     @staticmethod
101     def vpp_get_interface_ip_addresses(node, interface, ip_version):
102         """Get list of IP addresses from an interface on a VPP node.
103
104         :param node: VPP node.
105         :param interface: Name of an interface on the VPP node.
106         :param ip_version: IP protocol version (ipv4 or ipv6).
107         :type node: dict
108         :type interface: str
109         :type ip_version: str
110         :returns: List of dictionaries, each containing IP address, subnet
111             prefix length and also the subnet mask for ipv4 addresses.
112             Note: A single interface may have multiple IP addresses assigned.
113         :rtype: list
114         """
115         sw_if_index = InterfaceUtil.get_interface_index(node, interface)
116
117         if not sw_if_index:
118             return list()
119
120         cmd = u"ip_address_dump"
121         args = dict(
122             sw_if_index=sw_if_index,
123             is_ipv6=bool(ip_version == u"ipv6")
124         )
125         err_msg = f"Failed to get L2FIB dump on host {node[u'host']}"
126
127         with PapiSocketExecutor(node) as papi_exec:
128             details = papi_exec.add(cmd, **args).get_details(err_msg)
129
130         # TODO: CSIT currently looks only whether the list is empty.
131         # Add proper value processing if values become important.
132
133         return details
134
135     @staticmethod
136     def vpp_get_ip_tables(node):
137         """Get dump of all IP FIB tables on a VPP node.
138
139         :param node: VPP node.
140         :type node: dict
141         """
142         PapiSocketExecutor.run_cli_cmd(node, u"show ip fib")
143         PapiSocketExecutor.run_cli_cmd(node, u"show ip fib summary")
144         PapiSocketExecutor.run_cli_cmd(node, u"show ip6 fib")
145         PapiSocketExecutor.run_cli_cmd(node, u"show ip6 fib summary")
146
147     @staticmethod
148     def vpp_get_ip_tables_prefix(node, address):
149         """Get dump of all IP FIB tables on a VPP node.
150
151         :param node: VPP node.
152         :param address: IP address.
153         :type node: dict
154         :type address: str
155         """
156         addr = ip_address(address)
157         ip_ver = u"ip6" if addr.version == 6 else u"ip"
158
159         PapiSocketExecutor.run_cli_cmd(
160             node, f"show {ip_ver} fib {addr}/{addr.max_prefixlen}"
161         )
162
163     @staticmethod
164     def get_interface_vrf_table(node, interface, ip_version='ipv4'):
165         """Get vrf ID for the given interface.
166
167         :param node: VPP node.
168         :param interface: Name or sw_if_index of a specific interface.
169         :type node: dict
170         :param ip_version: IP protocol version (ipv4 or ipv6).
171         :type interface: str or int
172         :type ip_version: str
173         :returns: vrf ID of the specified interface.
174         :rtype: int
175         """
176         sw_if_index = InterfaceUtil.get_interface_index(node, interface)
177
178         cmd = u"sw_interface_get_table"
179         args = dict(
180             sw_if_index=sw_if_index,
181             is_ipv6=bool(ip_version == u"ipv6")
182         )
183         err_msg = f"Failed to get VRF id assigned to interface {interface}"
184
185         with PapiSocketExecutor(node) as papi_exec:
186             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
187
188         return reply[u"vrf_id"]
189
190     @staticmethod
191     def vpp_ip_source_check_setup(node, if_name):
192         """Setup Reverse Path Forwarding source check on interface.
193
194         :param node: VPP node.
195         :param if_name: Interface name to setup RPF source check.
196         :type node: dict
197         :type if_name: str
198         """
199         cmd = u"ip_source_check_interface_add_del"
200         args = dict(
201             sw_if_index=InterfaceUtil.get_interface_index(node, if_name),
202             is_add=1,
203             loose=0
204         )
205         err_msg = f"Failed to enable source check on interface {if_name}"
206         with PapiSocketExecutor(node) as papi_exec:
207             papi_exec.add(cmd, **args).get_reply(err_msg)
208
209     @staticmethod
210     def vpp_ip_probe(node, interface, addr):
211         """Run ip probe on VPP node.
212
213         :param node: VPP node.
214         :param interface: Interface key or name.
215         :param addr: IPv4/IPv6 address.
216         :type node: dict
217         :type interface: str
218         :type addr: str
219         """
220         cmd = u"ip_probe_neighbor"
221         args = dict(
222             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
223             dst=str(addr)
224         )
225         err_msg = f"VPP ip probe {interface} {addr} failed on {node[u'host']}"
226
227         with PapiSocketExecutor(node) as papi_exec:
228             papi_exec.add(cmd, **args).get_reply(err_msg)
229
230     @staticmethod
231     def ip_addresses_should_be_equal(ip1, ip2):
232         """Fails if the given IP addresses are unequal.
233
234         :param ip1: IPv4 or IPv6 address.
235         :param ip2: IPv4 or IPv6 address.
236         :type ip1: str
237         :type ip2: str
238         """
239         addr1 = ip_address(ip1)
240         addr2 = ip_address(ip2)
241
242         if addr1 != addr2:
243             raise AssertionError(f"IP addresses are not equal: {ip1} != {ip2}")
244
245     @staticmethod
246     def setup_network_namespace(node, namespace_name, interface_name,
247                                 ip_addr_list, prefix_length):
248         """Setup namespace on given node and attach interface and IP to
249         this namespace. Applicable also on TG node.
250
251         :param node: VPP node.
252         :param namespace_name: Namespace name.
253         :param interface_name: Interface name.
254         :param ip_addr_list: List of IP addresses of namespace's interface.
255         :param prefix_length: IP address prefix length.
256         :type node: dict
257         :type namespace_name: str
258         :type interface_name: str
259         :type ip_addr_list: list
260         :type prefix_length: int
261         """
262         Namespaces.create_namespace(node, namespace_name)
263
264         cmd = f"ip netns exec {namespace_name} ip link set {interface_name} up"
265         exec_cmd_no_error(node, cmd, sudo=True)
266
267         for ip_addr in ip_addr_list:
268             cmd = f"ip netns exec {namespace_name} ip addr add " \
269                 f"{ip_addr}/{prefix_length} dev {interface_name}"
270             exec_cmd_no_error(node, cmd, sudo=True)
271
272     @staticmethod
273     def linux_enable_forwarding(node, ip_ver=u"ipv4"):
274         """Enable forwarding on a Linux node, e.g. VM.
275
276         :param node: VPP node.
277         :param ip_ver: IP version, 'ipv4' or 'ipv6'.
278         :type node: dict
279         :type ip_ver: str
280         """
281         cmd = f"sysctl -w net.{ip_ver}.ip_forward=1"
282         exec_cmd_no_error(node, cmd, sudo=True)
283
284     @staticmethod
285     def get_linux_interface_name(node, pci_addr):
286         """Get the interface name.
287
288         :param node: VPP/TG node.
289         :param pci_addr: PCI address
290         :type node: dict
291         :type pci_addr: str
292         :returns: Interface name
293         :rtype: str
294         :raises RuntimeError: If cannot get the information about interfaces.
295         """
296         regex_intf_info = \
297             r"pci@([0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}.[0-9a-f])\s" \
298             r"*([a-zA-Z0-9]*)\s*network"
299
300         cmd = u"lshw -class network -businfo"
301         ret_code, stdout, stderr = exec_cmd(node, cmd, timeout=30, sudo=True)
302         if ret_code != 0:
303             raise RuntimeError(
304                 f"Could not get information about interfaces:\n{stderr}"
305             )
306
307         for line in stdout.splitlines()[2:]:
308             try:
309                 if re.search(regex_intf_info, line).group(1) == pci_addr:
310                     return re.search(regex_intf_info, line).group(2)
311             except AttributeError:
312                 continue
313         return None
314
315     @staticmethod
316     def set_linux_interface_up(node, interface):
317         """Set the specified interface up.
318
319         :param node: VPP/TG node.
320         :param interface: Interface in namespace.
321         :type node: dict
322         :type interface: str
323         :raises RuntimeError: If the interface could not be set up.
324         """
325         cmd = f"ip link set {interface} up"
326         exec_cmd_no_error(node, cmd, timeout=30, sudo=True)
327
328     @staticmethod
329     def set_linux_interface_ip(
330             node, interface, ip_addr, prefix, namespace=None):
331         """Set IP address to interface in linux.
332
333         :param node: VPP/TG node.
334         :param interface: Interface in namespace.
335         :param ip_addr: IP to be set on interface.
336         :param prefix: IP prefix.
337         :param namespace: Execute command in namespace. Optional
338         :type node: dict
339         :type interface: str
340         :type ip_addr: str
341         :type prefix: int
342         :type namespace: str
343         :raises RuntimeError: IP could not be set.
344         """
345         if namespace is not None:
346             cmd = f"ip netns exec {namespace} ip addr add {ip_addr}/{prefix}" \
347                 f" dev {interface}"
348         else:
349             cmd = f"ip addr add {ip_addr}/{prefix} dev {interface}"
350
351         exec_cmd_no_error(node, cmd, timeout=5, sudo=True)
352
353     @staticmethod
354     def delete_linux_interface_ip(
355             node, interface, ip_addr, prefix_length, namespace=None):
356         """Delete IP address from interface in linux.
357
358         :param node: VPP/TG node.
359         :param interface: Interface in namespace.
360         :param ip_addr: IP to be deleted from interface.
361         :param prefix_length: IP prefix length.
362         :param namespace: Execute command in namespace. Optional
363         :type node: dict
364         :type interface: str
365         :type ip_addr: str
366         :type prefix_length: int
367         :type namespace: str
368         :raises RuntimeError: IP could not be deleted.
369         """
370         # TODO: Refactor command execution in namespaces into central
371         # methods (e.g. Namespace.exec_cmd_in_namespace)
372         if namespace is not None:
373             cmd = f"ip netns exec {namespace} ip addr del " \
374                 f"{ip_addr}/{prefix_length} dev {interface}"
375         else:
376             cmd = f"ip addr del {ip_addr}/{prefix_length} dev {interface}"
377
378         exec_cmd_no_error(node, cmd, timeout=5, sudo=True)
379
380     @staticmethod
381     def linux_interface_has_ip(
382             node, interface, ip_addr, prefix_length, namespace=None):
383         """Return True if interface in linux has IP address.
384
385         :param node: VPP/TG node.
386         :param interface: Interface in namespace.
387         :param ip_addr: IP to be queried on interface.
388         :param prefix_length: IP prefix length.
389         :param namespace: Execute command in namespace. Optional
390         :type node: dict
391         :type interface: str
392         :type ip_addr: str
393         :type prefix_length: int
394         :type namespace: str
395         :rtype boolean
396         :raises RuntimeError: Request fails.
397         """
398         ip_addr_with_prefix = f"{ip_addr}/{prefix_length}"
399         if namespace is not None:
400             cmd = f"ip netns exec {namespace} ip addr show dev {interface}"
401         else:
402             cmd = f"ip addr show dev {interface}"
403
404         cmd += u" | grep 'inet ' | awk -e '{print $2}'"
405         cmd += f" | grep '{ip_addr_with_prefix}'"
406         _, stdout, _ = exec_cmd(node, cmd, timeout=5, sudo=True)
407
408         has_ip = stdout.rstrip()
409         return bool(has_ip == ip_addr_with_prefix)
410
411     @staticmethod
412     def add_linux_route(node, ip_addr, prefix, gateway, namespace=None):
413         """Add linux route in namespace.
414
415         :param node: Node where to execute command.
416         :param ip_addr: Route destination IP address.
417         :param prefix: IP prefix.
418         :param namespace: Execute command in namespace. Optional.
419         :param gateway: Gateway address.
420         :type node: dict
421         :type ip_addr: str
422         :type prefix: int
423         :type gateway: str
424         :type namespace: str
425         """
426         if namespace is not None:
427             cmd = f"ip netns exec {namespace} ip route add {ip_addr}/{prefix}" \
428                 f" via {gateway}"
429         else:
430             cmd = f"ip route add {ip_addr}/{prefix} via {gateway}"
431
432         exec_cmd_no_error(node, cmd, sudo=True)
433
434     @staticmethod
435     def vpp_interface_set_ip_address(
436             node, interface, address, prefix_length=None):
437         """Set IP address to VPP interface.
438
439         :param node: VPP node.
440         :param interface: Interface name.
441         :param address: IP address.
442         :param prefix_length: Prefix length.
443         :type node: dict
444         :type interface: str
445         :type address: str
446         :type prefix_length: int
447         """
448         ip_addr = ip_address(address)
449
450         cmd = u"sw_interface_add_del_address"
451         args = dict(
452             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
453             is_add=True,
454             del_all=False,
455             prefix=IPUtil.create_prefix_object(
456                 ip_addr,
457                 prefix_length if prefix_length else 128
458                 if ip_addr.version == 6 else 32
459             )
460         )
461         err_msg = f"Failed to add IP address on interface {interface}"
462
463         with PapiSocketExecutor(node) as papi_exec:
464             papi_exec.add(cmd, **args).get_reply(err_msg)
465
466     @staticmethod
467     def vpp_interface_set_ip_addresses(node, interface, ip_addr_list,
468                                        prefix_length=None):
469         """Set IP addresses to VPP interface.
470
471         :param node: VPP node.
472         :param interface: Interface name.
473         :param ip_addr_list: IP addresses.
474         :param prefix_length: Prefix length.
475         :type node: dict
476         :type interface: str
477         :type ip_addr_list: list
478         :type prefix_length: int
479         """
480         for ip_addr in ip_addr_list:
481             IPUtil.vpp_interface_set_ip_address(node, interface, ip_addr,
482                                                 prefix_length)
483
484     @staticmethod
485     def vpp_add_ip_neighbor(node, iface_key, ip_addr, mac_address):
486         """Add IP neighbor on DUT node.
487
488         :param node: VPP node.
489         :param iface_key: Interface key.
490         :param ip_addr: IP address of the interface.
491         :param mac_address: MAC address of the interface.
492         :type node: dict
493         :type iface_key: str
494         :type ip_addr: str
495         :type mac_address: str
496         """
497         dst_ip = ip_address(ip_addr)
498
499         neighbor = dict(
500             sw_if_index=Topology.get_interface_sw_index(node, iface_key),
501             flags=0,
502             mac_address=str(mac_address),
503             ip_address=str(dst_ip)
504         )
505         cmd = u"ip_neighbor_add_del"
506         args = dict(
507             is_add=True,
508             neighbor=neighbor
509         )
510         err_msg = f"Failed to add IP neighbor on interface {iface_key}"
511
512         with PapiSocketExecutor(node) as papi_exec:
513             papi_exec.add(cmd, **args).get_reply(err_msg)
514
515     @staticmethod
516     def union_addr(ip_addr):
517         """Creates union IP address.
518
519         :param ip_addr: IPv4 or IPv6 address.
520         :type ip_addr: IPv4Address or IPv6Address
521         :returns: Union IP address.
522         :rtype: dict
523         """
524         return dict(ip6=ip_addr.packed) if ip_addr.version == 6 \
525             else dict(ip4=ip_addr.packed)
526
527     @staticmethod
528     def create_ip_address_object(ip_addr):
529         """Create IP address object.
530
531         :param ip_addr: IPv4 or IPv6 address
532         :type ip_addr: IPv4Address or IPv6Address
533         :returns: IP address object.
534         :rtype: dict
535         """
536         return dict(
537             af=getattr(
538                 AddressFamily, u"ADDRESS_IP6" if ip_addr.version == 6
539                 else u"ADDRESS_IP4"
540             ).value,
541             un=IPUtil.union_addr(ip_addr)
542         )
543
544     @staticmethod
545     def create_prefix_object(ip_addr, addr_len):
546         """Create prefix object.
547
548         :param ip_addr: IPv4 or IPv6 address.
549         :para, addr_len: Length of IP address.
550         :type ip_addr: IPv4Address or IPv6Address
551         :type addr_len: int
552         :returns: Prefix object.
553         :rtype: dict
554         """
555         addr = IPUtil.create_ip_address_object(ip_addr)
556
557         return dict(
558             len=int(addr_len),
559             address=addr
560         )
561
562     @staticmethod
563     def compose_vpp_route_structure(node, network, prefix_len, **kwargs):
564         """Create route object for ip_route_add_del api call.
565
566         :param node: VPP node.
567         :param network: Route destination network address.
568         :param prefix_len: Route destination network prefix length.
569         :param kwargs: Optional key-value arguments:
570
571             gateway: Route gateway address. (str)
572             interface: Route interface. (str)
573             vrf: VRF table ID. (int)
574             count: number of IP addresses to add starting from network IP (int)
575             local: The route is local with same prefix (increment is 1).
576                 If None, then is not used. (bool)
577             lookup_vrf: VRF table ID for lookup. (int)
578             multipath: Enable multipath routing. (bool)
579             weight: Weight value for unequal cost multipath routing. (int)
580
581         :type node: dict
582         :type network: str
583         :type prefix_len: int
584         :type kwargs: dict
585         :returns: route parameter basic structure
586         :rtype: dict
587         """
588         interface = kwargs.get(u"interface", u"")
589         gateway = kwargs.get(u"gateway", u"")
590
591         net_addr = ip_address(network)
592
593         prefix = IPUtil.create_prefix_object(net_addr, prefix_len)
594
595         paths = list()
596         n_hop = dict(
597             address=IPUtil.union_addr(ip_address(gateway)) if gateway else 0,
598             via_label=MPLS_LABEL_INVALID,
599             obj_id=Constants.BITWISE_NON_ZERO
600         )
601         path = dict(
602             sw_if_index=InterfaceUtil.get_interface_index(node, interface)
603             if interface else Constants.BITWISE_NON_ZERO,
604             table_id=int(kwargs.get(u"lookup_vrf", 0)),
605             rpf_id=Constants.BITWISE_NON_ZERO,
606             weight=int(kwargs.get(u"weight", 1)),
607             preference=1,
608             type=getattr(
609                 FibPathType, u"FIB_PATH_TYPE_LOCAL"
610                 if kwargs.get(u"local", False)
611                 else u"FIB_PATH_TYPE_NORMAL"
612             ).value,
613             flags=getattr(FibPathFlags, u"FIB_PATH_FLAG_NONE").value,
614             proto=getattr(
615                 FibPathNhProto, u"FIB_PATH_NH_PROTO_IP6"
616                 if net_addr.version == 6
617                 else u"FIB_PATH_NH_PROTO_IP4"
618             ).value,
619             nh=n_hop,
620             n_labels=0,
621             label_stack=list(0 for _ in range(16))
622         )
623         paths.append(path)
624
625         route = dict(
626             table_id=int(kwargs.get(u"vrf", 0)),
627             prefix=prefix,
628             n_paths=len(paths),
629             paths=paths
630         )
631         return route
632
633     @staticmethod
634     def vpp_route_add(node, network, prefix_len, **kwargs):
635         """Add route to the VPP node.
636
637         :param node: VPP node.
638         :param network: Route destination network address.
639         :param prefix_len: Route destination network prefix length.
640         :param kwargs: Optional key-value arguments:
641
642             gateway: Route gateway address. (str)
643             interface: Route interface. (str)
644             vrf: VRF table ID. (int)
645             count: number of IP addresses to add starting from network IP (int)
646             local: The route is local with same prefix (increment is 1).
647                 If None, then is not used. (bool)
648             lookup_vrf: VRF table ID for lookup. (int)
649             multipath: Enable multipath routing. (bool)
650             weight: Weight value for unequal cost multipath routing. (int)
651
652         :type node: dict
653         :type network: str
654         :type prefix_len: int
655         :type kwargs: dict
656         """
657         count = kwargs.get(u"count", 1)
658
659         if count > 100:
660             gateway = kwargs.get(u"gateway", '')
661             interface = kwargs.get(u"interface", '')
662             vrf = kwargs.get(u"vrf", None)
663             multipath = kwargs.get(u"multipath", False)
664
665             with VatTerminal(node, json_param=False) as vat:
666
667                 vat.vat_terminal_exec_cmd_from_template(
668                     u"vpp_route_add.vat",
669                     network=network,
670                     prefix_length=prefix_len,
671                     via=f"via {gateway}" if gateway else u"",
672                     sw_if_index=f"sw_if_index "
673                     f"{InterfaceUtil.get_interface_index(node, interface)}"
674                     if interface else u"",
675                     vrf=f"vrf {vrf}" if vrf else u"",
676                     count=f"count {count}" if count else u"",
677                     multipath=u"multipath" if multipath else u""
678                 )
679             return
680
681         net_addr = ip_address(network)
682         cmd = u"ip_route_add_del"
683         args = dict(
684             is_add=True,
685             is_multipath=kwargs.get(u"multipath", False),
686             route=None
687         )
688         err_msg = f"Failed to add route(s) on host {node[u'host']}"
689
690         with PapiSocketExecutor(node) as papi_exec:
691             for i in range(kwargs.get(u"count", 1)):
692                 args[u"route"] = IPUtil.compose_vpp_route_structure(
693                     node, net_addr + i, prefix_len, **kwargs
694                 )
695                 history = bool(not 1 < i < kwargs.get(u"count", 1))
696                 papi_exec.add(cmd, history=history, **args)
697             papi_exec.get_replies(err_msg)
698
699     @staticmethod
700     def flush_ip_addresses(node, interface):
701         """Flush all IP addresses from specified interface.
702
703         :param node: VPP node.
704         :param interface: Interface name.
705         :type node: dict
706         :type interface: str
707         """
708         cmd = u"sw_interface_add_del_address"
709         args = dict(
710             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
711             is_add=False,
712             del_all=True
713         )
714         err_msg = f"Failed to flush IP address on interface {interface}"
715
716         with PapiSocketExecutor(node) as papi_exec:
717             papi_exec.add(cmd, **args).get_reply(err_msg)
718
719     @staticmethod
720     def add_fib_table(node, table_id, ipv6=False):
721         """Create new FIB table according to ID.
722
723         :param node: Node to add FIB on.
724         :param table_id: FIB table ID.
725         :param ipv6: Is this an IPv6 table
726         :type node: dict
727         :type table_id: int
728         :type ipv6: bool
729         """
730         cmd = u"ip_table_add_del"
731         table = dict(
732             table_id=int(table_id),
733             is_ip6=ipv6
734         )
735         args = dict(
736             table=table,
737             is_add=True
738         )
739         err_msg = f"Failed to add FIB table on host {node[u'host']}"
740
741         with PapiSocketExecutor(node) as papi_exec:
742             papi_exec.add(cmd, **args).get_reply(err_msg)