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