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