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