1 # NB NB NB NB NB NB NB NB NB NB NB
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.
11 from six import moves, iteritems
12 from config import config
13 from vpp_papi import VPPApiClient
15 from vpp_papi_exceptions import (
16 CliFailedCommandError,
18 UnexpectedApiReturnValueError,
22 # Dictionary keyed on message name to override default values for
26 "acl_interface_add_del": {"is_add": 1, "is_input": 1},
27 "bd_ip_mac_add_del": {
30 "bfd_udp_add": {"is_authenticated": False, "bfd_key_id": None, "conf_key_id": None},
31 "bfd_udp_auth_activate": {
36 "bier_disp_entry_add_del": {
37 "next_hop_rpf_id": -1,
41 "bier_disp_table_add_del": {
47 "bier_route_add_del": {
50 "bier_table_add_del": {
53 "bridge_domain_add_del": {
61 "geneve_add_del_tunnel": {
62 "mcast_sw_if_index": 4294967295,
64 "decap_next_index": 4294967295,
66 "input_acl_set_interface": {
67 "ip4_table_index": 4294967295,
68 "ip6_table_index": 4294967295,
69 "l2_table_index": 4294967295,
71 "ip6_add_del_address_using_prefix": {
74 "ip6nd_send_router_solicitation": {
79 "next_hop_sw_if_index": 4294967295,
81 "next_hop_via_label": 1048576,
82 "classify_table_index": 4294967295,
85 "ip_mroute_add_del": {
88 "ip_neighbor_add_del": {
91 "ipsec_interface_add_del_spd": {
94 "ipsec_spd_add_del": {
100 "ipsec_spd_entry_add_del": {
101 "local_port_stop": 65535,
102 "remote_port_stop": 65535,
107 "ipsec_tunnel_if_add_del": {
117 "lisp_add_del_adjacency": {
120 "lisp_add_del_local_eid": {
123 "lisp_add_del_locator": {
128 "lisp_add_del_locator_set": {
131 "lisp_add_del_remote_mapping": {
134 "macip_acl_interface_add_del": {
137 "mpls_ip_bind_unbind": {
141 "mpls_route_add_del": {
142 "mr_next_hop_sw_if_index": 4294967295,
143 "mr_next_hop_weight": 1,
144 "mr_next_hop_via_label": 1048576,
146 "mr_classify_table_index": 4294967295,
148 "mpls_table_add_del": {
151 "mpls_tunnel_add_del": {
152 "next_hop_sw_if_index": 4294967295,
153 "next_hop_weight": 1,
154 "next_hop_via_label": 1048576,
157 "output_acl_set_interface": {
158 "ip4_table_index": 4294967295,
159 "ip6_table_index": 4294967295,
160 "l2_table_index": 4294967295,
162 "pppoe_add_del_session": {
167 "conform_action": {"type": 1},
169 "set_ipfix_exporter": {
170 "collector_port": 4739,
176 "sw_interface_add_del_address": {
179 "sw_interface_ip6nd_ra_prefix": {
180 "val_lifetime": 4294967295,
181 "pref_lifetime": 4294967295,
183 "sw_interface_set_ip_directed_broadcast": {
186 "sw_interface_set_l2_bridge": {
189 "sw_interface_set_mpls_enable": {
192 "sw_interface_set_mtu": {
195 "sw_interface_set_unnumbered": {
198 "sw_interface_span_enable_disable": {
201 "vxlan_add_del_tunnel": {
202 "mcast_sw_if_index": 4294967295,
204 "decap_next_index": 4294967295,
205 "instance": 4294967295,
210 "want_igmp_events": {
213 "want_interface_events": {
216 "want_l2_macs_events": {
220 "want_l2_macs_events2": {
227 def as_fn_signature(d):
228 return ", ".join(f"{k}={v}" for k, v in d.items())
231 class VppPapiProvider(object):
232 """VPP-api provider using vpp-papi
234 @property hook: hook object providing before and after api/cli hooks
237 _zero, _negative = range(2)
239 def __init__(self, name, test_class, read_timeout):
240 self.hook = Hook(test_class)
242 self.test_class = test_class
243 self._expect_api_retval = self._zero
244 self._expect_stack = []
246 # install_dir is a class attribute. We need to set it before
247 # calling the constructor.
248 VPPApiClient.apidir = config.vpp_install_dir
250 self.vpp = VPPApiClient(
251 logger=test_class.logger,
252 read_timeout=read_timeout,
254 server_address=test_class.get_api_sock_path(),
256 self._events = queue.Queue()
261 def assert_negative_api_retval(self):
262 """Expect API failure - used with with, e.g.::
264 with self.vapi.assert_negative_api_retval():
265 self.vapi.<api call expected to fail>
269 self._expect_stack.append(self._expect_api_retval)
270 self._expect_api_retval = self._negative
273 def assert_zero_api_retval(self):
274 """Expect API success - used with with, e.g.::
276 with self.vapi.assert_negative_api_retval():
277 self.vapi.<api call expected to succeed>
279 :note: this is useful only inside another with block
280 as success is the default expected value
282 self._expect_stack.append(self._expect_api_retval)
283 self._expect_api_retval = self._zero
286 def __exit__(self, exc_type, exc_value, traceback):
287 self._expect_api_retval = self._expect_stack.pop()
289 def register_hook(self, hook):
290 """Replace hook registration with new hook
297 def collect_events(self):
298 """Collect all events from the internal queue and clear the queue."""
302 e = self._events.get(block=False)
308 def wait_for_event(self, timeout, name=None):
309 """Wait for and return next event."""
311 self.test_class.logger.debug(
312 "Expecting event '%s' within %ss", name, timeout
315 self.test_class.logger.debug("Expecting event within %ss", timeout)
317 e = self._events.get(timeout=timeout)
319 raise Exception("Event did not occur within timeout")
320 msgname = type(e).__name__
321 if name and msgname != name:
322 raise Exception("Unexpected event received: %s, expected: %s" % msgname)
323 self.test_class.logger.debug("Returning event %s:%s" % (name, e))
326 def __call__(self, name, event):
327 """Enqueue event in the internal event queue."""
328 self.test_class.logger.debug("New event: %s: %s" % (name, event))
329 self._events.put(event)
331 def factory(self, name, apifn):
333 fields = apifn._func.msg.fields
335 # add positional and kw arguments
337 for i, o in enumerate(fields[3:]):
340 except BaseException:
344 if name in defaultmapping:
345 for k, v in iteritems(defaultmapping[name]):
349 return self.api(apifn, d)
353 def __getattribute__(self, name):
355 method = super(VppPapiProvider, self).__getattribute__(name)
356 except AttributeError:
357 method = self.factory(name, getattr(self.papi, name))
358 # lazily load the method so we don't need to call factory
359 # again for this name.
360 setattr(self, name, method)
364 """Connect the API to VPP"""
365 # This might be called before VPP is prepared to listen to the socket
367 while not os.path.exists(self.test_class.get_api_sock_path()):
372 self.vpp.connect(self.name[:63])
373 self.papi = self.vpp.api
374 self.vpp.register_event_callback(self)
376 def disconnect(self):
377 """Disconnect the API from VPP"""
378 self.vpp.disconnect()
380 def api(self, api_fn, api_args, expected_retval=0):
381 """Call API function and check it's return value.
382 Call the appropriate hooks before and after the API call
384 :param api_fn: API function to call
385 :param api_args: tuple of API function arguments
386 :param expected_retval: Expected return value (Default value = 0)
387 :returns: reply from the API
390 self.hook.before_api(api_fn.__name__, api_args)
391 reply = api_fn(**api_args)
392 if self._expect_api_retval == self._negative:
393 if hasattr(reply, "retval") and reply.retval >= 0:
395 "%s(%s) passed unexpectedly: expected negative "
396 "return value instead of %d in %s"
399 as_fn_signature(api_args),
401 moves.reprlib.repr(reply),
404 self.test_class.logger.info(msg)
405 raise UnexpectedApiReturnValueError(reply.retval, msg)
406 elif self._expect_api_retval == self._zero:
407 if hasattr(reply, "retval") and reply.retval != expected_retval:
409 "%s(%s) failed, expected %d return value instead "
413 as_fn_signature(api_args),
419 self.test_class.logger.info(msg)
420 raise UnexpectedApiReturnValueError(reply.retval, msg)
423 "Internal error, unexpected value for "
424 "self._expect_api_retval %s" % self._expect_api_retval
426 self.hook.after_api(api_fn.__name__, api_args)
429 def cli_return_response(self, cli):
430 """Execute a CLI, calling the before/after hooks appropriately.
431 Return the reply without examining it
433 :param cli: CLI to execute
434 :returns: response object
437 self.hook.before_cli(cli)
439 r = self.papi.cli_inband(cmd=cli)
440 self.hook.after_cli(cli)
444 """Execute a CLI, calling the before/after hooks appropriately.
446 :param cli: CLI to execute
450 r = self.cli_return_response(cli)
452 raise CliSyntaxError(r.reply)
454 raise CliFailedCommandError(r.reply)
455 if hasattr(r, "reply"):
458 def ppcli(self, cli):
459 """Helper method to print CLI command in case of info logging level.
461 :param cli: CLI to execute
464 return cli + "\n" + self.cli(cli)
466 def ip6nd_send_router_solicitation(self, sw_if_index, irt=1, mrt=120, mrc=0, mrd=0):
468 self.papi.ip6nd_send_router_solicitation,
474 "sw_if_index": sw_if_index,
478 def want_interface_events(self, enable_disable=1):
480 self.papi.want_interface_events,
482 "enable_disable": enable_disable,
487 def sw_interface_set_mac_address(self, sw_if_index, mac):
489 self.papi.sw_interface_set_mac_address,
490 {"sw_if_index": sw_if_index, "mac_address": mac},
493 def p2p_ethernet_add(self, sw_if_index, remote_mac, subif_id):
494 """Create p2p ethernet subinterface
496 :param sw_if_index: main (parent) interface
497 :param remote_mac: client (remote) mac address
501 self.papi.p2p_ethernet_add,
503 "parent_if_index": sw_if_index,
504 "remote_mac": remote_mac,
505 "subif_id": subif_id,
509 def p2p_ethernet_del(self, sw_if_index, remote_mac):
510 """Delete p2p ethernet subinterface
512 :param sw_if_index: main (parent) interface
513 :param remote_mac: client (remote) mac address
517 self.papi.p2p_ethernet_del,
518 {"parent_if_index": sw_if_index, "remote_mac": remote_mac},
521 def create_vlan_subif(self, sw_if_index, vlan):
529 self.papi.create_vlan_subif, {"sw_if_index": sw_if_index, "vlan_id": vlan}
532 def create_loopback(self, mac=""):
535 :param mac: (Optional)
537 return self.api(self.papi.create_loopback, {"mac_address": mac})
539 def ip_route_dump(self, table_id, is_ip6=False):
541 self.papi.ip_route_dump, {"table": {"table_id": table_id, "is_ip6": is_ip6}}
544 def ip_route_v2_dump(self, table_id, is_ip6=False, src=0):
546 self.papi.ip_route_v2_dump,
547 {"src": src, "table": {"table_id": table_id, "is_ip6": is_ip6}},
550 def ip_neighbor_add_del(
551 self, sw_if_index, mac_address, ip_address, is_add=1, flags=0
553 """Add neighbor MAC to IPv4 or IPv6 address.
558 :param is_add: (Default value = 1)
559 :param flags: (Default value = 0/NONE)
562 self.papi.ip_neighbor_add_del,
566 "sw_if_index": sw_if_index,
568 "mac_address": mac_address,
569 "ip_address": ip_address,
574 def udp_encap_add(self, src_ip, dst_ip, src_port, dst_port, table_id=0):
580 :param outer_fib_id: (Default value = 0)
584 self.papi.udp_encap_add,
589 "src_port": src_port,
590 "dst_port": dst_port,
591 "table_id": table_id,
596 def udp_encap_del(self, id):
597 return self.api(self.papi.udp_encap_del, {"id": id})
599 def udp_encap_dump(self):
600 return self.api(self.papi.udp_encap_dump, {})
602 def want_udp_encap_stats(self, enable=1):
604 self.papi.want_udp_encap_stats, {"enable": enable, "pid": os.getpid()}
607 def mpls_route_dump(self, table_id):
608 return self.api(self.papi.mpls_route_dump, {"table": {"mt_table_id": table_id}})
610 def mpls_table_dump(self):
611 return self.api(self.papi.mpls_table_dump, {})
613 def mpls_table_add_del(self, table_id, is_add=1):
617 :param is_add: (Default value = 1)
622 self.papi.mpls_table_add_del,
625 "mt_table_id": table_id,
631 def mpls_route_add_del(
632 self, table_id, label, eos, eos_proto, is_multicast, paths, is_add, is_multipath
634 """MPLS Route add/del"""
636 self.papi.mpls_route_add_del,
639 "mr_table_id": table_id,
642 "mr_eos_proto": eos_proto,
643 "mr_is_multicast": is_multicast,
644 "mr_n_paths": len(paths),
648 "mr_is_multipath": is_multipath,
652 def mpls_ip_bind_unbind(self, label, prefix, table_id=0, ip_table_id=0, is_bind=1):
655 self.papi.mpls_ip_bind_unbind,
657 "mb_mpls_table_id": table_id,
659 "mb_ip_table_id": ip_table_id,
660 "mb_is_bind": is_bind,
665 def mpls_tunnel_add_del(
666 self, tun_sw_if_index, paths, is_add=1, l2_only=0, is_multicast=0
670 self.papi.mpls_tunnel_add_del,
674 "mt_sw_if_index": tun_sw_if_index,
675 "mt_l2_only": l2_only,
676 "mt_is_multicast": is_multicast,
677 "mt_n_paths": len(paths),
683 def input_acl_set_interface(
687 ip4_table_index=0xFFFFFFFF,
688 ip6_table_index=0xFFFFFFFF,
689 l2_table_index=0xFFFFFFFF,
694 :param ip4_table_index: (Default value = 0xFFFFFFFF)
695 :param ip6_table_index: (Default value = 0xFFFFFFFF)
696 :param l2_table_index: (Default value = 0xFFFFFFFF)
700 self.papi.input_acl_set_interface,
702 "sw_if_index": sw_if_index,
703 "ip4_table_index": ip4_table_index,
704 "ip6_table_index": ip6_table_index,
705 "l2_table_index": l2_table_index,
710 def output_acl_set_interface(
714 ip4_table_index=0xFFFFFFFF,
715 ip6_table_index=0xFFFFFFFF,
716 l2_table_index=0xFFFFFFFF,
721 :param ip4_table_index: (Default value = 0xFFFFFFFF)
722 :param ip6_table_index: (Default value = 0xFFFFFFFF)
723 :param l2_table_index: (Default value = 0xFFFFFFFF)
727 self.papi.output_acl_set_interface,
729 "sw_if_index": sw_if_index,
730 "ip4_table_index": ip4_table_index,
731 "ip6_table_index": ip6_table_index,
732 "l2_table_index": l2_table_index,
737 def set_ipfix_exporter(
748 self.papi.set_ipfix_exporter,
750 "collector_address": collector_address,
751 "collector_port": collector_port,
752 "src_address": src_address,
754 "path_mtu": path_mtu,
755 "template_interval": template_interval,
756 "udp_checksum": udp_checksum,
760 def mfib_signal_dump(self):
761 return self.api(self.papi.mfib_signal_dump, {})
763 def ip_mroute_dump(self, table_id, is_ip6=False):
765 self.papi.ip_mroute_dump,
766 {"table": {"table_id": table_id, "is_ip6": is_ip6}},
769 def pppoe_add_del_session(
770 self, client_ip, client_mac, session_id=0, is_add=1, decap_vrf_id=0
774 :param is_add: (Default value = 1)
775 :param is_ipv6: (Default value = 0)
777 :param session_id: (Default value = 0)
779 :param decap_vrf_id: (Default value = 0)
783 self.papi.pppoe_add_del_session,
786 "session_id": session_id,
787 "client_ip": client_ip,
788 "decap_vrf_id": decap_vrf_id,
789 "client_mac": client_mac,
793 def sr_mpls_policy_add(self, bsid, weight, type, segments):
795 self.papi.sr_mpls_policy_add,
800 "n_segments": len(segments),
801 "segments": segments,
805 def sr_mpls_policy_del(self, bsid):
806 return self.api(self.papi.sr_mpls_policy_del, {"bsid": bsid})
808 def bier_table_add_del(self, bti, mpls_label, is_add=1):
809 """BIER Table add/del"""
811 self.papi.bier_table_add_del,
814 "bt_set": bti.set_id,
815 "bt_sub_domain": bti.sub_domain_id,
816 "bt_hdr_len_id": bti.hdr_len_id,
818 "bt_label": mpls_label,
823 def bier_table_dump(self):
824 return self.api(self.papi.bier_table_dump, {})
826 def bier_route_add_del(self, bti, bp, paths, is_add=1, is_replace=0):
827 """BIER Route add/del"""
829 self.papi.bier_route_add_del,
833 "bt_set": bti.set_id,
834 "bt_sub_domain": bti.sub_domain_id,
835 "bt_hdr_len_id": bti.hdr_len_id,
838 "br_n_paths": len(paths),
842 "br_is_replace": is_replace,
846 def bier_route_dump(self, bti):
848 self.papi.bier_route_dump,
851 "bt_set": bti.set_id,
852 "bt_sub_domain": bti.sub_domain_id,
853 "bt_hdr_len_id": bti.hdr_len_id,
858 def bier_imp_add(self, bti, src, ibytes, is_add=1):
859 """BIER Imposition Add"""
861 self.papi.bier_imp_add,
864 "bt_set": bti.set_id,
865 "bt_sub_domain": bti.sub_domain_id,
866 "bt_hdr_len_id": bti.hdr_len_id,
869 "bi_n_bytes": len(ibytes),
874 def bier_imp_del(self, bi_index):
875 """BIER Imposition del"""
876 return self.api(self.papi.bier_imp_del, {"bi_index": bi_index})
878 def bier_imp_dump(self):
879 return self.api(self.papi.bier_imp_dump, {})
881 def bier_disp_table_add_del(self, bdti, is_add=1):
882 """BIER Disposition Table add/del"""
884 self.papi.bier_disp_table_add_del,
885 {"bdt_tbl_id": bdti, "bdt_is_add": is_add},
888 def bier_disp_table_dump(self):
889 return self.api(self.papi.bier_disp_table_dump, {})
891 def bier_disp_entry_add_del(
903 """BIER Route add/del"""
905 while len(lstack) < 16:
908 self.papi.bier_disp_entry_add_del,
912 "bde_payload_proto": payload_proto,
916 "table_id": next_hop_tbl_id,
917 "rpf_id": next_hop_rpf_id,
919 "label_stack": lstack,
922 "bde_is_add": is_add,
926 def bier_disp_entry_dump(self, bdti):
927 return self.api(self.papi.bier_disp_entry_dump, {"bde_tbl_id": bdti})
929 def ipsec_spd_add_del(self, spd_id, is_add=1):
930 """SPD add/del - Wrapper to add or del ipsec SPD
931 Sample CLI : 'ipsec spd add 1'
933 :param spd_id - SPD ID to be created in the vpp . mandatory
934 :param is_add - create (1) or delete(0) SPD (Default 1 - add) .
936 :returns: reply from the API
939 self.papi.ipsec_spd_add_del, {"spd_id": spd_id, "is_add": is_add}
942 def ipsec_spds_dump(self):
943 return self.api(self.papi.ipsec_spds_dump, {})
945 def ipsec_interface_add_del_spd(self, spd_id, sw_if_index, is_add=1):
946 """ IPSEC interface SPD add/del - \
947 Wrapper to associate/disassociate SPD to interface in VPP
948 Sample CLI : 'set interface ipsec spd GigabitEthernet0/6/0 1'
950 :param spd_id - SPD ID to associate with the interface . mandatory
951 :param sw_if_index - Interface Index which needs to ipsec \
952 association mandatory
953 :param is_add - add(1) or del(0) association with interface \
954 (Default 1 - add) . optional
955 :returns: reply from the API
958 self.papi.ipsec_interface_add_del_spd,
959 {"spd_id": spd_id, "sw_if_index": sw_if_index, "is_add": is_add},
962 def ipsec_spd_interface_dump(self, spd_index=None):
964 self.papi.ipsec_spd_interface_dump,
966 "spd_index": spd_index if spd_index else 0,
967 "spd_index_valid": 1 if spd_index else 0,
971 def ipsec_spd_entry_add_del(
977 remote_address_start,
980 local_port_stop=65535,
982 remote_port_stop=65535,
983 protocol=socket.IPPROTO_RAW,
991 """IPSEC policy SPD add/del -
992 Wrapper to configure ipsec SPD policy entries in VPP
994 :param spd_id: SPD ID for the policy
995 :param local_address_start: local-ip-range start address
996 :param local_address_stop: local-ip-range stop address
997 :param remote_address_start: remote-ip-range start address
998 :param remote_address_stop: remote-ip-range stop address
999 :param local_port_start: (Default value = 0)
1000 :param local_port_stop: (Default value = 65535)
1001 :param remote_port_start: (Default value = 0)
1002 :param remote_port_stop: (Default value = 65535)
1003 :param protocol: Any(0), AH(51) & ESP(50) protocol (Default value = 0)
1004 :param sa_id: Security Association ID for mapping it to SPD
1005 :param policy: bypass(0), discard(1), resolve(2) or protect(3) action
1007 :param priority: value for the spd action (Default value = 100)
1008 :param is_outbound: flag for inbound(0) or outbound(1)
1010 :param is_add: (Default value = 1)
1013 self.papi.ipsec_spd_entry_add_del_v2,
1019 "local_address_start": local_address_start,
1020 "local_address_stop": local_address_stop,
1021 "remote_address_start": remote_address_start,
1022 "remote_address_stop": remote_address_stop,
1023 "local_port_start": local_port_start,
1024 "local_port_stop": local_port_stop,
1025 "remote_port_start": remote_port_start,
1026 "remote_port_stop": remote_port_stop,
1027 "protocol": protocol,
1029 "priority": priority,
1030 "is_outbound": is_outbound,
1035 def ipsec_spd_dump(self, spd_id, sa_id=0xFFFFFFFF):
1036 return self.api(self.papi.ipsec_spd_dump, {"spd_id": spd_id, "sa_id": sa_id})
1038 def ipsec_tunnel_if_add_del(
1056 show_instance=0xFFFFFFFF,
1059 self.papi.ipsec_tunnel_if_add_del,
1061 "local_ip": local_ip,
1062 "remote_ip": remote_ip,
1063 "local_spi": local_spi,
1064 "remote_spi": remote_spi,
1065 "crypto_alg": crypto_alg,
1066 "local_crypto_key_len": len(local_crypto_key),
1067 "local_crypto_key": local_crypto_key,
1068 "remote_crypto_key_len": len(remote_crypto_key),
1069 "remote_crypto_key": remote_crypto_key,
1070 "integ_alg": integ_alg,
1071 "local_integ_key_len": len(local_integ_key),
1072 "local_integ_key": local_integ_key,
1073 "remote_integ_key_len": len(remote_integ_key),
1074 "remote_integ_key": remote_integ_key,
1077 "anti_replay": anti_replay,
1078 "renumber": renumber,
1079 "show_instance": show_instance,
1080 "udp_encap": udp_encap,
1085 def ipsec_select_backend(self, protocol, index):
1087 self.papi.ipsec_select_backend, {"protocol": protocol, "index": index}
1090 def ipsec_backend_dump(self):
1091 return self.api(self.papi.ipsec_backend_dump, {})
1093 def punt_socket_register(self, reg, pathname, header_version=1):
1094 """Register punt socket"""
1096 self.papi.punt_socket_register,
1097 {"header_version": header_version, "punt": reg, "pathname": pathname},
1100 def punt_socket_deregister(self, reg):
1101 """Unregister punt socket"""
1102 return self.api(self.papi.punt_socket_deregister, {"punt": reg})
1104 def igmp_enable_disable(self, sw_if_index, enable, host):
1105 """Enable/disable IGMP on a given interface"""
1107 self.papi.igmp_enable_disable,
1108 {"enable": enable, "mode": host, "sw_if_index": sw_if_index},
1111 def igmp_proxy_device_add_del(self, vrf_id, sw_if_index, add):
1112 """Add/del IGMP proxy device"""
1114 self.papi.igmp_proxy_device_add_del,
1115 {"vrf_id": vrf_id, "sw_if_index": sw_if_index, "add": add},
1118 def igmp_proxy_device_add_del_interface(self, vrf_id, sw_if_index, add):
1119 """Add/del interface to/from IGMP proxy device"""
1121 self.papi.igmp_proxy_device_add_del_interface,
1122 {"vrf_id": vrf_id, "sw_if_index": sw_if_index, "add": add},
1125 def igmp_listen(self, filter, sw_if_index, saddrs, gaddr):
1126 """Listen for new (S,G) on specified interface
1128 :param enable: add/delas
1129 :param sw_if_index: interface sw index
1130 :param saddr: source ip4 addr
1131 :param gaddr: group ip4 addr
1134 self.papi.igmp_listen,
1138 "sw_if_index": sw_if_index,
1139 "n_srcs": len(saddrs),
1146 def igmp_clear_interface(self, sw_if_index):
1147 """Remove all (S,G)s from specified interface
1148 doesn't send IGMP report!
1150 return self.api(self.papi.igmp_clear_interface, {"sw_if_index": sw_if_index})
1152 def want_igmp_events(self, enable=1):
1154 self.papi.want_igmp_events, {"enable": enable, "pid": os.getpid()}