l2: Add bridge_domain_add_del_v2 to l2 api
[vpp.git] / test / vpp_papi_provider.py
1 # NB NB NB NB NB NB NB NB NB NB NB
2 #
3 # NOTE: The API binary wrappers in this file are in the process of being
4 # deprecated. DO NOT ADD NEW WRAPPERS HERE. Call the functions using
5 # named arguments directly instead.
6 #
7
8 import os
9 import time
10 import queue
11 from six import moves, iteritems
12 from config import config
13 from vpp_papi import VPPApiClient
14 from hook import Hook
15 from vpp_papi_exceptions import (
16     CliFailedCommandError,
17     CliSyntaxError,
18     UnexpectedApiReturnValueError,
19 )
20
21 #
22 # Dictionary keyed on message name to override default values for
23 # named parameters
24 #
25 defaultmapping = {
26     "acl_interface_add_del": {"is_add": 1, "is_input": 1},
27     "bd_ip_mac_add_del": {
28         "is_add": 1,
29     },
30     "bfd_udp_add": {"is_authenticated": False, "bfd_key_id": None, "conf_key_id": None},
31     "bfd_udp_auth_activate": {
32         "bfd_key_id": None,
33         "conf_key_id": None,
34         "is_delayed": False,
35     },
36     "bier_disp_entry_add_del": {
37         "next_hop_rpf_id": -1,
38         "next_hop_is_ip4": 1,
39         "is_add": 1,
40     },
41     "bier_disp_table_add_del": {
42         "is_add": 1,
43     },
44     "bier_imp_add": {
45         "is_add": 1,
46     },
47     "bier_route_add_del": {
48         "is_add": 1,
49     },
50     "bier_table_add_del": {
51         "is_add": 1,
52     },
53     "bvi_delete": {},
54     "geneve_add_del_tunnel": {
55         "mcast_sw_if_index": 4294967295,
56         "is_add": 1,
57         "decap_next_index": 4294967295,
58     },
59     "input_acl_set_interface": {
60         "ip4_table_index": 4294967295,
61         "ip6_table_index": 4294967295,
62         "l2_table_index": 4294967295,
63     },
64     "ip6_add_del_address_using_prefix": {
65         "is_add": 1,
66     },
67     "ip6nd_send_router_solicitation": {
68         "irt": 1,
69         "mrt": 120,
70     },
71     "ip_add_del_route": {
72         "next_hop_sw_if_index": 4294967295,
73         "next_hop_weight": 1,
74         "next_hop_via_label": 1048576,
75         "classify_table_index": 4294967295,
76         "is_add": 1,
77     },
78     "ip_mroute_add_del": {
79         "is_add": 1,
80     },
81     "ip_neighbor_add_del": {
82         "is_add": 1,
83     },
84     "ipsec_interface_add_del_spd": {
85         "is_add": 1,
86     },
87     "ipsec_spd_add_del": {
88         "is_add": 1,
89     },
90     "ipsec_spd_dump": {
91         "sa_id": 4294967295,
92     },
93     "ipsec_spd_entry_add_del": {
94         "local_port_stop": 65535,
95         "remote_port_stop": 65535,
96         "priority": 100,
97         "is_outbound": 1,
98         "is_add": 1,
99     },
100     "ipsec_tunnel_if_add_del": {
101         "is_add": 1,
102         "anti_replay": 1,
103     },
104     "l2_emulation": {
105         "enable": 1,
106     },
107     "l2fib_add_del": {
108         "is_add": 1,
109     },
110     "lisp_add_del_adjacency": {
111         "is_add": 1,
112     },
113     "lisp_add_del_local_eid": {
114         "is_add": 1,
115     },
116     "lisp_add_del_locator": {
117         "priority": 1,
118         "weight": 1,
119         "is_add": 1,
120     },
121     "lisp_add_del_locator_set": {
122         "is_add": 1,
123     },
124     "lisp_add_del_remote_mapping": {
125         "is_add": 1,
126     },
127     "macip_acl_interface_add_del": {
128         "is_add": 1,
129     },
130     "mpls_ip_bind_unbind": {
131         "is_ip4": 1,
132         "is_bind": 1,
133     },
134     "mpls_route_add_del": {
135         "mr_next_hop_sw_if_index": 4294967295,
136         "mr_next_hop_weight": 1,
137         "mr_next_hop_via_label": 1048576,
138         "mr_is_add": 1,
139         "mr_classify_table_index": 4294967295,
140     },
141     "mpls_table_add_del": {
142         "is_add": 1,
143     },
144     "mpls_tunnel_add_del": {
145         "next_hop_sw_if_index": 4294967295,
146         "next_hop_weight": 1,
147         "next_hop_via_label": 1048576,
148         "is_add": 1,
149     },
150     "output_acl_set_interface": {
151         "ip4_table_index": 4294967295,
152         "ip6_table_index": 4294967295,
153         "l2_table_index": 4294967295,
154     },
155     "pppoe_add_del_session": {
156         "is_add": 1,
157     },
158     "policer_add_del": {
159         "is_add": 1,
160         "conform_action": {"type": 1},
161     },
162     "set_ipfix_exporter": {
163         "collector_port": 4739,
164     },
165     "sr_policy_add": {
166         "weight": 1,
167         "is_encap": 1,
168     },
169     "sw_interface_add_del_address": {
170         "is_add": 1,
171     },
172     "sw_interface_ip6nd_ra_prefix": {
173         "val_lifetime": 4294967295,
174         "pref_lifetime": 4294967295,
175     },
176     "sw_interface_set_ip_directed_broadcast": {
177         "enable": 1,
178     },
179     "sw_interface_set_l2_bridge": {
180         "enable": 1,
181     },
182     "sw_interface_set_mpls_enable": {
183         "enable": 1,
184     },
185     "sw_interface_set_mtu": {
186         "mtu": [0, 0, 0, 0],
187     },
188     "sw_interface_set_unnumbered": {
189         "is_add": 1,
190     },
191     "sw_interface_span_enable_disable": {
192         "state": 1,
193     },
194     "vxlan_add_del_tunnel": {
195         "mcast_sw_if_index": 4294967295,
196         "is_add": 1,
197         "decap_next_index": 4294967295,
198         "instance": 4294967295,
199     },
200     "want_bfd_events": {
201         "enable_disable": 1,
202     },
203     "want_igmp_events": {
204         "enable": 1,
205     },
206     "want_interface_events": {
207         "enable_disable": 1,
208     },
209     "want_l2_macs_events": {
210         "enable_disable": 1,
211         "pid": os.getpid(),
212     },
213     "want_l2_macs_events2": {
214         "enable_disable": 1,
215         "pid": os.getpid(),
216     },
217 }
218
219
220 def as_fn_signature(d):
221     return ", ".join(f"{k}={v}" for k, v in d.items())
222
223
224 class VppPapiProvider(object):
225     """VPP-api provider using vpp-papi
226
227     @property hook: hook object providing before and after api/cli hooks
228     """
229
230     _zero, _negative = range(2)
231
232     def __init__(self, name, test_class, read_timeout):
233         self.hook = Hook(test_class)
234         self.name = name
235         self.test_class = test_class
236         self._expect_api_retval = self._zero
237         self._expect_stack = []
238
239         # install_dir is a class attribute. We need to set it before
240         # calling the constructor.
241         VPPApiClient.apidir = config.vpp_install_dir
242
243         self.vpp = VPPApiClient(
244             logger=test_class.logger,
245             read_timeout=read_timeout,
246             use_socket=True,
247             server_address=test_class.get_api_sock_path(),
248         )
249         self._events = queue.Queue()
250
251     def __enter__(self):
252         return self
253
254     def assert_negative_api_retval(self):
255         """Expect API failure - used with with, e.g.::
256
257             with self.vapi.assert_negative_api_retval():
258                 self.vapi.<api call expected to fail>
259
260         ..
261         """
262         self._expect_stack.append(self._expect_api_retval)
263         self._expect_api_retval = self._negative
264         return self
265
266     def assert_zero_api_retval(self):
267         """Expect API success - used with with, e.g.::
268
269             with self.vapi.assert_negative_api_retval():
270                 self.vapi.<api call expected to succeed>
271
272         :note: this is useful only inside another with block
273              as success is the default expected value
274         """
275         self._expect_stack.append(self._expect_api_retval)
276         self._expect_api_retval = self._zero
277         return self
278
279     def __exit__(self, exc_type, exc_value, traceback):
280         self._expect_api_retval = self._expect_stack.pop()
281
282     def register_hook(self, hook):
283         """Replace hook registration with new hook
284
285         :param hook:
286
287         """
288         self.hook = hook
289
290     def collect_events(self):
291         """Collect all events from the internal queue and clear the queue."""
292         result = []
293         while True:
294             try:
295                 e = self._events.get(block=False)
296                 result.append(e)
297             except queue.Empty:
298                 return result
299         return result
300
301     def wait_for_event(self, timeout, name=None):
302         """Wait for and return next event."""
303         if name:
304             self.test_class.logger.debug(
305                 "Expecting event '%s' within %ss", name, timeout
306             )
307         else:
308             self.test_class.logger.debug("Expecting event within %ss", timeout)
309         try:
310             e = self._events.get(timeout=timeout)
311         except queue.Empty:
312             raise Exception("Event did not occur within timeout")
313         msgname = type(e).__name__
314         if name and msgname != name:
315             raise Exception("Unexpected event received: %s, expected: %s" % msgname)
316         self.test_class.logger.debug("Returning event %s:%s" % (name, e))
317         return e
318
319     def __call__(self, name, event):
320         """Enqueue event in the internal event queue."""
321         self.test_class.logger.debug("New event: %s: %s" % (name, event))
322         self._events.put(event)
323
324     def factory(self, name, apifn):
325         def f(*a, **ka):
326             fields = apifn._func.msg.fields
327
328             # add positional and kw arguments
329             d = ka
330             for i, o in enumerate(fields[3:]):
331                 try:
332                     d[o] = a[i]
333                 except BaseException:
334                     break
335
336             # Default override
337             if name in defaultmapping:
338                 for k, v in iteritems(defaultmapping[name]):
339                     if k in d:
340                         continue
341                     d[k] = v
342             return self.api(apifn, d)
343
344         return f
345
346     def __getattribute__(self, name):
347         try:
348             method = super(VppPapiProvider, self).__getattribute__(name)
349         except AttributeError:
350             method = self.factory(name, getattr(self.papi, name))
351             # lazily load the method so we don't need to call factory
352             # again for this name.
353             setattr(self, name, method)
354         return method
355
356     def connect(self):
357         """Connect the API to VPP"""
358         # This might be called before VPP is prepared to listen to the socket
359         retries = 0
360         while not os.path.exists(self.test_class.get_api_sock_path()):
361             time.sleep(0.5)
362             retries += 1
363             if retries > 120:
364                 break
365         self.vpp.connect(self.name[:63])
366         self.papi = self.vpp.api
367         self.vpp.register_event_callback(self)
368
369     def disconnect(self):
370         """Disconnect the API from VPP"""
371         self.vpp.disconnect()
372
373     def api(self, api_fn, api_args, expected_retval=0):
374         """Call API function and check it's return value.
375         Call the appropriate hooks before and after the API call
376
377         :param api_fn: API function to call
378         :param api_args: tuple of API function arguments
379         :param expected_retval: Expected return value (Default value = 0)
380         :returns: reply from the API
381
382         """
383         self.hook.before_api(api_fn.__name__, api_args)
384         reply = api_fn(**api_args)
385         if self._expect_api_retval == self._negative:
386             if hasattr(reply, "retval") and reply.retval >= 0:
387                 msg = (
388                     "%s(%s) passed unexpectedly: expected negative "
389                     "return value instead of %d in %s"
390                     % (
391                         api_fn.__name__,
392                         as_fn_signature(api_args),
393                         reply.retval,
394                         moves.reprlib.repr(reply),
395                     )
396                 )
397                 self.test_class.logger.info(msg)
398                 raise UnexpectedApiReturnValueError(reply.retval, msg)
399         elif self._expect_api_retval == self._zero:
400             if hasattr(reply, "retval") and reply.retval != expected_retval:
401                 msg = (
402                     "%s(%s) failed, expected %d return value instead "
403                     "of %d in %s"
404                     % (
405                         api_fn.__name__,
406                         as_fn_signature(api_args),
407                         expected_retval,
408                         reply.retval,
409                         repr(reply),
410                     )
411                 )
412                 self.test_class.logger.info(msg)
413                 raise UnexpectedApiReturnValueError(reply.retval, msg)
414         else:
415             raise Exception(
416                 "Internal error, unexpected value for "
417                 "self._expect_api_retval %s" % self._expect_api_retval
418             )
419         self.hook.after_api(api_fn.__name__, api_args)
420         return reply
421
422     def cli_return_response(self, cli):
423         """Execute a CLI, calling the before/after hooks appropriately.
424         Return the reply without examining it
425
426         :param cli: CLI to execute
427         :returns: response object
428
429         """
430         self.hook.before_cli(cli)
431         cli += "\n"
432         r = self.papi.cli_inband(cmd=cli)
433         self.hook.after_cli(cli)
434         return r
435
436     def cli(self, cli):
437         """Execute a CLI, calling the before/after hooks appropriately.
438
439         :param cli: CLI to execute
440         :returns: CLI output
441
442         """
443         r = self.cli_return_response(cli)
444         if r.retval == -156:
445             raise CliSyntaxError(r.reply)
446         if r.retval != 0:
447             raise CliFailedCommandError(r.reply)
448         if hasattr(r, "reply"):
449             return r.reply
450
451     def ppcli(self, cli):
452         """Helper method to print CLI command in case of info logging level.
453
454         :param cli: CLI to execute
455         :returns: CLI output
456         """
457         return cli + "\n" + self.cli(cli)
458
459     def ip6nd_send_router_solicitation(self, sw_if_index, irt=1, mrt=120, mrc=0, mrd=0):
460         return self.api(
461             self.papi.ip6nd_send_router_solicitation,
462             {
463                 "irt": irt,
464                 "mrt": mrt,
465                 "mrc": mrc,
466                 "mrd": mrd,
467                 "sw_if_index": sw_if_index,
468             },
469         )
470
471     def want_interface_events(self, enable_disable=1):
472         return self.api(
473             self.papi.want_interface_events,
474             {
475                 "enable_disable": enable_disable,
476                 "pid": os.getpid(),
477             },
478         )
479
480     def sw_interface_set_mac_address(self, sw_if_index, mac):
481         return self.api(
482             self.papi.sw_interface_set_mac_address,
483             {"sw_if_index": sw_if_index, "mac_address": mac},
484         )
485
486     def p2p_ethernet_add(self, sw_if_index, remote_mac, subif_id):
487         """Create p2p ethernet subinterface
488
489         :param sw_if_index: main (parent) interface
490         :param remote_mac: client (remote) mac address
491
492         """
493         return self.api(
494             self.papi.p2p_ethernet_add,
495             {
496                 "parent_if_index": sw_if_index,
497                 "remote_mac": remote_mac,
498                 "subif_id": subif_id,
499             },
500         )
501
502     def p2p_ethernet_del(self, sw_if_index, remote_mac):
503         """Delete p2p ethernet subinterface
504
505         :param sw_if_index: main (parent) interface
506         :param remote_mac: client (remote) mac address
507
508         """
509         return self.api(
510             self.papi.p2p_ethernet_del,
511             {"parent_if_index": sw_if_index, "remote_mac": remote_mac},
512         )
513
514     def create_vlan_subif(self, sw_if_index, vlan):
515         """
516
517         :param vlan:
518         :param sw_if_index:
519
520         """
521         return self.api(
522             self.papi.create_vlan_subif, {"sw_if_index": sw_if_index, "vlan_id": vlan}
523         )
524
525     def create_loopback(self, mac=""):
526         """
527
528         :param mac: (Optional)
529         """
530         return self.api(self.papi.create_loopback, {"mac_address": mac})
531
532     def ip_route_dump(self, table_id, is_ip6=False):
533         return self.api(
534             self.papi.ip_route_dump, {"table": {"table_id": table_id, "is_ip6": is_ip6}}
535         )
536
537     def ip_route_v2_dump(self, table_id, is_ip6=False, src=0):
538         return self.api(
539             self.papi.ip_route_v2_dump,
540             {"src": src, "table": {"table_id": table_id, "is_ip6": is_ip6}},
541         )
542
543     def ip_neighbor_add_del(
544         self, sw_if_index, mac_address, ip_address, is_add=1, flags=0
545     ):
546         """Add neighbor MAC to IPv4 or IPv6 address.
547
548         :param sw_if_index:
549         :param mac_address:
550         :param dst_address:
551         :param is_add:  (Default value = 1)
552         :param flags:  (Default value = 0/NONE)
553         """
554         return self.api(
555             self.papi.ip_neighbor_add_del,
556             {
557                 "is_add": is_add,
558                 "neighbor": {
559                     "sw_if_index": sw_if_index,
560                     "flags": flags,
561                     "mac_address": mac_address,
562                     "ip_address": ip_address,
563                 },
564             },
565         )
566
567     def udp_encap_add(self, src_ip, dst_ip, src_port, dst_port, table_id=0):
568         """Add a GRE tunnel
569         :param src_ip:
570         :param dst_ip:
571         :param src_port:
572         :param dst_port:
573         :param outer_fib_id:  (Default value = 0)
574         """
575
576         return self.api(
577             self.papi.udp_encap_add,
578             {
579                 "udp_encap": {
580                     "src_ip": src_ip,
581                     "dst_ip": dst_ip,
582                     "src_port": src_port,
583                     "dst_port": dst_port,
584                     "table_id": table_id,
585                 }
586             },
587         )
588
589     def udp_encap_del(self, id):
590         return self.api(self.papi.udp_encap_del, {"id": id})
591
592     def udp_encap_dump(self):
593         return self.api(self.papi.udp_encap_dump, {})
594
595     def want_udp_encap_stats(self, enable=1):
596         return self.api(
597             self.papi.want_udp_encap_stats, {"enable": enable, "pid": os.getpid()}
598         )
599
600     def mpls_route_dump(self, table_id):
601         return self.api(self.papi.mpls_route_dump, {"table": {"mt_table_id": table_id}})
602
603     def mpls_table_dump(self):
604         return self.api(self.papi.mpls_table_dump, {})
605
606     def mpls_table_add_del(self, table_id, is_add=1):
607         """
608
609         :param table_id
610         :param is_add:  (Default value = 1)
611
612         """
613
614         return self.api(
615             self.papi.mpls_table_add_del,
616             {
617                 "mt_table": {
618                     "mt_table_id": table_id,
619                 },
620                 "mt_is_add": is_add,
621             },
622         )
623
624     def mpls_route_add_del(
625         self, table_id, label, eos, eos_proto, is_multicast, paths, is_add, is_multipath
626     ):
627         """MPLS Route add/del"""
628         return self.api(
629             self.papi.mpls_route_add_del,
630             {
631                 "mr_route": {
632                     "mr_table_id": table_id,
633                     "mr_label": label,
634                     "mr_eos": eos,
635                     "mr_eos_proto": eos_proto,
636                     "mr_is_multicast": is_multicast,
637                     "mr_n_paths": len(paths),
638                     "mr_paths": paths,
639                 },
640                 "mr_is_add": is_add,
641                 "mr_is_multipath": is_multipath,
642             },
643         )
644
645     def mpls_ip_bind_unbind(self, label, prefix, table_id=0, ip_table_id=0, is_bind=1):
646         """ """
647         return self.api(
648             self.papi.mpls_ip_bind_unbind,
649             {
650                 "mb_mpls_table_id": table_id,
651                 "mb_label": label,
652                 "mb_ip_table_id": ip_table_id,
653                 "mb_is_bind": is_bind,
654                 "mb_prefix": prefix,
655             },
656         )
657
658     def mpls_tunnel_add_del(
659         self, tun_sw_if_index, paths, is_add=1, l2_only=0, is_multicast=0
660     ):
661         """ """
662         return self.api(
663             self.papi.mpls_tunnel_add_del,
664             {
665                 "mt_is_add": is_add,
666                 "mt_tunnel": {
667                     "mt_sw_if_index": tun_sw_if_index,
668                     "mt_l2_only": l2_only,
669                     "mt_is_multicast": is_multicast,
670                     "mt_n_paths": len(paths),
671                     "mt_paths": paths,
672                 },
673             },
674         )
675
676     def input_acl_set_interface(
677         self,
678         is_add,
679         sw_if_index,
680         ip4_table_index=0xFFFFFFFF,
681         ip6_table_index=0xFFFFFFFF,
682         l2_table_index=0xFFFFFFFF,
683     ):
684         """
685         :param is_add:
686         :param sw_if_index:
687         :param ip4_table_index:  (Default value = 0xFFFFFFFF)
688         :param ip6_table_index:  (Default value = 0xFFFFFFFF)
689         :param l2_table_index:  (Default value = 0xFFFFFFFF)
690         """
691
692         return self.api(
693             self.papi.input_acl_set_interface,
694             {
695                 "sw_if_index": sw_if_index,
696                 "ip4_table_index": ip4_table_index,
697                 "ip6_table_index": ip6_table_index,
698                 "l2_table_index": l2_table_index,
699                 "is_add": is_add,
700             },
701         )
702
703     def output_acl_set_interface(
704         self,
705         is_add,
706         sw_if_index,
707         ip4_table_index=0xFFFFFFFF,
708         ip6_table_index=0xFFFFFFFF,
709         l2_table_index=0xFFFFFFFF,
710     ):
711         """
712         :param is_add:
713         :param sw_if_index:
714         :param ip4_table_index:  (Default value = 0xFFFFFFFF)
715         :param ip6_table_index:  (Default value = 0xFFFFFFFF)
716         :param l2_table_index:  (Default value = 0xFFFFFFFF)
717         """
718
719         return self.api(
720             self.papi.output_acl_set_interface,
721             {
722                 "sw_if_index": sw_if_index,
723                 "ip4_table_index": ip4_table_index,
724                 "ip6_table_index": ip6_table_index,
725                 "l2_table_index": l2_table_index,
726                 "is_add": is_add,
727             },
728         )
729
730     def set_ipfix_exporter(
731         self,
732         collector_address,
733         src_address,
734         path_mtu,
735         template_interval,
736         vrf_id=0,
737         collector_port=4739,
738         udp_checksum=0,
739     ):
740         return self.api(
741             self.papi.set_ipfix_exporter,
742             {
743                 "collector_address": collector_address,
744                 "collector_port": collector_port,
745                 "src_address": src_address,
746                 "vrf_id": vrf_id,
747                 "path_mtu": path_mtu,
748                 "template_interval": template_interval,
749                 "udp_checksum": udp_checksum,
750             },
751         )
752
753     def mfib_signal_dump(self):
754         return self.api(self.papi.mfib_signal_dump, {})
755
756     def ip_mroute_dump(self, table_id, is_ip6=False):
757         return self.api(
758             self.papi.ip_mroute_dump,
759             {"table": {"table_id": table_id, "is_ip6": is_ip6}},
760         )
761
762     def pppoe_add_del_session(
763         self, client_ip, client_mac, session_id=0, is_add=1, decap_vrf_id=0
764     ):
765         """
766
767         :param is_add:  (Default value = 1)
768         :param is_ipv6:  (Default value = 0)
769         :param client_ip:
770         :param session_id:  (Default value = 0)
771         :param client_mac:
772         :param decap_vrf_id:  (Default value = 0)
773
774         """
775         return self.api(
776             self.papi.pppoe_add_del_session,
777             {
778                 "is_add": is_add,
779                 "session_id": session_id,
780                 "client_ip": client_ip,
781                 "decap_vrf_id": decap_vrf_id,
782                 "client_mac": client_mac,
783             },
784         )
785
786     def sr_mpls_policy_add(self, bsid, weight, type, segments):
787         return self.api(
788             self.papi.sr_mpls_policy_add,
789             {
790                 "bsid": bsid,
791                 "weight": weight,
792                 "is_spray": type,
793                 "n_segments": len(segments),
794                 "segments": segments,
795             },
796         )
797
798     def sr_mpls_policy_del(self, bsid):
799         return self.api(self.papi.sr_mpls_policy_del, {"bsid": bsid})
800
801     def bier_table_add_del(self, bti, mpls_label, is_add=1):
802         """BIER Table add/del"""
803         return self.api(
804             self.papi.bier_table_add_del,
805             {
806                 "bt_tbl_id": {
807                     "bt_set": bti.set_id,
808                     "bt_sub_domain": bti.sub_domain_id,
809                     "bt_hdr_len_id": bti.hdr_len_id,
810                 },
811                 "bt_label": mpls_label,
812                 "bt_is_add": is_add,
813             },
814         )
815
816     def bier_table_dump(self):
817         return self.api(self.papi.bier_table_dump, {})
818
819     def bier_route_add_del(self, bti, bp, paths, is_add=1, is_replace=0):
820         """BIER Route add/del"""
821         return self.api(
822             self.papi.bier_route_add_del,
823             {
824                 "br_route": {
825                     "br_tbl_id": {
826                         "bt_set": bti.set_id,
827                         "bt_sub_domain": bti.sub_domain_id,
828                         "bt_hdr_len_id": bti.hdr_len_id,
829                     },
830                     "br_bp": bp,
831                     "br_n_paths": len(paths),
832                     "br_paths": paths,
833                 },
834                 "br_is_add": is_add,
835                 "br_is_replace": is_replace,
836             },
837         )
838
839     def bier_route_dump(self, bti):
840         return self.api(
841             self.papi.bier_route_dump,
842             {
843                 "br_tbl_id": {
844                     "bt_set": bti.set_id,
845                     "bt_sub_domain": bti.sub_domain_id,
846                     "bt_hdr_len_id": bti.hdr_len_id,
847                 }
848             },
849         )
850
851     def bier_imp_add(self, bti, src, ibytes, is_add=1):
852         """BIER Imposition Add"""
853         return self.api(
854             self.papi.bier_imp_add,
855             {
856                 "bi_tbl_id": {
857                     "bt_set": bti.set_id,
858                     "bt_sub_domain": bti.sub_domain_id,
859                     "bt_hdr_len_id": bti.hdr_len_id,
860                 },
861                 "bi_src": src,
862                 "bi_n_bytes": len(ibytes),
863                 "bi_bytes": ibytes,
864             },
865         )
866
867     def bier_imp_del(self, bi_index):
868         """BIER Imposition del"""
869         return self.api(self.papi.bier_imp_del, {"bi_index": bi_index})
870
871     def bier_imp_dump(self):
872         return self.api(self.papi.bier_imp_dump, {})
873
874     def bier_disp_table_add_del(self, bdti, is_add=1):
875         """BIER Disposition Table add/del"""
876         return self.api(
877             self.papi.bier_disp_table_add_del,
878             {"bdt_tbl_id": bdti, "bdt_is_add": is_add},
879         )
880
881     def bier_disp_table_dump(self):
882         return self.api(self.papi.bier_disp_table_dump, {})
883
884     def bier_disp_entry_add_del(
885         self,
886         bdti,
887         bp,
888         payload_proto,
889         next_hop_afi,
890         next_hop,
891         next_hop_tbl_id=0,
892         next_hop_rpf_id=~0,
893         next_hop_is_ip4=1,
894         is_add=1,
895     ):
896         """BIER Route add/del"""
897         lstack = []
898         while len(lstack) < 16:
899             lstack.append({})
900         return self.api(
901             self.papi.bier_disp_entry_add_del,
902             {
903                 "bde_tbl_id": bdti,
904                 "bde_bp": bp,
905                 "bde_payload_proto": payload_proto,
906                 "bde_n_paths": 1,
907                 "bde_paths": [
908                     {
909                         "table_id": next_hop_tbl_id,
910                         "rpf_id": next_hop_rpf_id,
911                         "n_labels": 0,
912                         "label_stack": lstack,
913                     }
914                 ],
915                 "bde_is_add": is_add,
916             },
917         )
918
919     def bier_disp_entry_dump(self, bdti):
920         return self.api(self.papi.bier_disp_entry_dump, {"bde_tbl_id": bdti})
921
922     def ipsec_spd_add_del(self, spd_id, is_add=1):
923         """SPD add/del - Wrapper to add or del ipsec SPD
924         Sample CLI : 'ipsec spd add 1'
925
926         :param spd_id - SPD ID to be created in the vpp . mandatory
927         :param is_add - create (1) or delete(0) SPD (Default 1 - add) .
928         optional
929         :returns: reply from the API
930         """
931         return self.api(
932             self.papi.ipsec_spd_add_del, {"spd_id": spd_id, "is_add": is_add}
933         )
934
935     def ipsec_spds_dump(self):
936         return self.api(self.papi.ipsec_spds_dump, {})
937
938     def ipsec_interface_add_del_spd(self, spd_id, sw_if_index, is_add=1):
939         """ IPSEC interface SPD add/del - \
940              Wrapper to associate/disassociate SPD to interface in VPP
941         Sample CLI : 'set interface ipsec spd GigabitEthernet0/6/0 1'
942
943         :param spd_id - SPD ID to associate with the interface . mandatory
944         :param sw_if_index - Interface Index which needs to ipsec \
945             association mandatory
946         :param is_add - add(1) or del(0) association with interface \
947                 (Default 1 - add) . optional
948         :returns: reply from the API
949         """
950         return self.api(
951             self.papi.ipsec_interface_add_del_spd,
952             {"spd_id": spd_id, "sw_if_index": sw_if_index, "is_add": is_add},
953         )
954
955     def ipsec_spd_interface_dump(self, spd_index=None):
956         return self.api(
957             self.papi.ipsec_spd_interface_dump,
958             {
959                 "spd_index": spd_index if spd_index else 0,
960                 "spd_index_valid": 1 if spd_index else 0,
961             },
962         )
963
964     def ipsec_spd_entry_add_del(
965         self,
966         spd_id,
967         sa_id,
968         local_address_start,
969         local_address_stop,
970         remote_address_start,
971         remote_address_stop,
972         local_port_start=0,
973         local_port_stop=65535,
974         remote_port_start=0,
975         remote_port_stop=65535,
976         protocol=socket.IPPROTO_RAW,
977         policy=0,
978         priority=100,
979         is_outbound=1,
980         is_add=1,
981         is_ipv6=0,
982         is_ip_any=0,
983     ):
984         """IPSEC policy SPD add/del   -
985                     Wrapper to configure ipsec SPD policy entries in VPP
986
987         :param spd_id: SPD ID for the policy
988         :param local_address_start: local-ip-range start address
989         :param local_address_stop: local-ip-range stop address
990         :param remote_address_start: remote-ip-range start address
991         :param remote_address_stop: remote-ip-range stop address
992         :param local_port_start: (Default value = 0)
993         :param local_port_stop: (Default value = 65535)
994         :param remote_port_start: (Default value = 0)
995         :param remote_port_stop: (Default value = 65535)
996         :param protocol: Any(0), AH(51) & ESP(50) protocol (Default value = 0)
997         :param sa_id: Security Association ID for mapping it to SPD
998         :param policy: bypass(0), discard(1), resolve(2) or protect(3) action
999             (Default value = 0)
1000         :param priority: value for the spd action (Default value = 100)
1001         :param is_outbound: flag for inbound(0) or outbound(1)
1002             (Default value = 1)
1003         :param is_add: (Default value = 1)
1004         """
1005         return self.api(
1006             self.papi.ipsec_spd_entry_add_del_v2,
1007             {
1008                 "is_add": is_add,
1009                 "entry": {
1010                     "spd_id": spd_id,
1011                     "sa_id": sa_id,
1012                     "local_address_start": local_address_start,
1013                     "local_address_stop": local_address_stop,
1014                     "remote_address_start": remote_address_start,
1015                     "remote_address_stop": remote_address_stop,
1016                     "local_port_start": local_port_start,
1017                     "local_port_stop": local_port_stop,
1018                     "remote_port_start": remote_port_start,
1019                     "remote_port_stop": remote_port_stop,
1020                     "protocol": protocol,
1021                     "policy": policy,
1022                     "priority": priority,
1023                     "is_outbound": is_outbound,
1024                 },
1025             },
1026         )
1027
1028     def ipsec_spd_dump(self, spd_id, sa_id=0xFFFFFFFF):
1029         return self.api(self.papi.ipsec_spd_dump, {"spd_id": spd_id, "sa_id": sa_id})
1030
1031     def ipsec_tunnel_if_add_del(
1032         self,
1033         local_ip,
1034         remote_ip,
1035         local_spi,
1036         remote_spi,
1037         crypto_alg,
1038         local_crypto_key,
1039         remote_crypto_key,
1040         integ_alg,
1041         local_integ_key,
1042         remote_integ_key,
1043         is_add=1,
1044         esn=0,
1045         salt=0,
1046         anti_replay=1,
1047         renumber=0,
1048         udp_encap=0,
1049         show_instance=0xFFFFFFFF,
1050     ):
1051         return self.api(
1052             self.papi.ipsec_tunnel_if_add_del,
1053             {
1054                 "local_ip": local_ip,
1055                 "remote_ip": remote_ip,
1056                 "local_spi": local_spi,
1057                 "remote_spi": remote_spi,
1058                 "crypto_alg": crypto_alg,
1059                 "local_crypto_key_len": len(local_crypto_key),
1060                 "local_crypto_key": local_crypto_key,
1061                 "remote_crypto_key_len": len(remote_crypto_key),
1062                 "remote_crypto_key": remote_crypto_key,
1063                 "integ_alg": integ_alg,
1064                 "local_integ_key_len": len(local_integ_key),
1065                 "local_integ_key": local_integ_key,
1066                 "remote_integ_key_len": len(remote_integ_key),
1067                 "remote_integ_key": remote_integ_key,
1068                 "is_add": is_add,
1069                 "esn": esn,
1070                 "anti_replay": anti_replay,
1071                 "renumber": renumber,
1072                 "show_instance": show_instance,
1073                 "udp_encap": udp_encap,
1074                 "salt": salt,
1075             },
1076         )
1077
1078     def ipsec_select_backend(self, protocol, index):
1079         return self.api(
1080             self.papi.ipsec_select_backend, {"protocol": protocol, "index": index}
1081         )
1082
1083     def ipsec_backend_dump(self):
1084         return self.api(self.papi.ipsec_backend_dump, {})
1085
1086     def punt_socket_register(self, reg, pathname, header_version=1):
1087         """Register punt socket"""
1088         return self.api(
1089             self.papi.punt_socket_register,
1090             {"header_version": header_version, "punt": reg, "pathname": pathname},
1091         )
1092
1093     def punt_socket_deregister(self, reg):
1094         """Unregister punt socket"""
1095         return self.api(self.papi.punt_socket_deregister, {"punt": reg})
1096
1097     def igmp_enable_disable(self, sw_if_index, enable, host):
1098         """Enable/disable IGMP on a given interface"""
1099         return self.api(
1100             self.papi.igmp_enable_disable,
1101             {"enable": enable, "mode": host, "sw_if_index": sw_if_index},
1102         )
1103
1104     def igmp_proxy_device_add_del(self, vrf_id, sw_if_index, add):
1105         """Add/del IGMP proxy device"""
1106         return self.api(
1107             self.papi.igmp_proxy_device_add_del,
1108             {"vrf_id": vrf_id, "sw_if_index": sw_if_index, "add": add},
1109         )
1110
1111     def igmp_proxy_device_add_del_interface(self, vrf_id, sw_if_index, add):
1112         """Add/del interface to/from IGMP proxy device"""
1113         return self.api(
1114             self.papi.igmp_proxy_device_add_del_interface,
1115             {"vrf_id": vrf_id, "sw_if_index": sw_if_index, "add": add},
1116         )
1117
1118     def igmp_listen(self, filter, sw_if_index, saddrs, gaddr):
1119         """Listen for new (S,G) on specified interface
1120
1121         :param enable: add/delas
1122         :param sw_if_index: interface sw index
1123         :param saddr: source ip4 addr
1124         :param gaddr: group ip4 addr
1125         """
1126         return self.api(
1127             self.papi.igmp_listen,
1128             {
1129                 "group": {
1130                     "filter": filter,
1131                     "sw_if_index": sw_if_index,
1132                     "n_srcs": len(saddrs),
1133                     "saddrs": saddrs,
1134                     "gaddr": gaddr,
1135                 }
1136             },
1137         )
1138
1139     def igmp_clear_interface(self, sw_if_index):
1140         """Remove all (S,G)s from specified interface
1141         doesn't send IGMP report!
1142         """
1143         return self.api(self.papi.igmp_clear_interface, {"sw_if_index": sw_if_index})
1144
1145     def want_igmp_events(self, enable=1):
1146         return self.api(
1147             self.papi.want_igmp_events, {"enable": enable, "pid": os.getpid()}
1148         )