summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorKlement Sekera <ksekera@cisco.com>2016-09-29 14:43:44 +0200
committerDave Barach <openvpp@barachs.net>2016-12-07 19:54:12 +0000
commit0e3c0de1ed87f3cdf16e26e05e39ea6eebeafb18 (patch)
treedd5f11c08311a95ce56eb8c33d4e8681940bf877 /test
parentd171d48edc1672564db8bb920586b8ea220df14c (diff)
BFD: basic asynchronous session up/down
This is a work-in-progress basic BFD session handling. Only asynchronous mode is supported at the moment. Setting the session flags doesn't work. Change-Id: Idba27f721b5c35be5a66a6d202a63d23ff7ecf6f Signed-off-by: Klement Sekera <ksekera@cisco.com>
Diffstat (limited to 'test')
-rw-r--r--test/bfd.py216
-rw-r--r--test/framework.py28
-rw-r--r--test/hook.py20
-rw-r--r--test/template_bd.py3
-rw-r--r--test/test_bfd.py273
-rw-r--r--test/util.py22
-rw-r--r--test/vpp_object.py79
-rw-r--r--test/vpp_papi_provider.py435
-rw-r--r--test/vpp_pg_interface.py40
9 files changed, 926 insertions, 190 deletions
diff --git a/test/bfd.py b/test/bfd.py
new file mode 100644
index 00000000000..beacd80f687
--- /dev/null
+++ b/test/bfd.py
@@ -0,0 +1,216 @@
+from socket import AF_INET, AF_INET6
+from scapy.all import *
+from scapy.packet import *
+from scapy.fields import *
+from framework import *
+from vpp_object import *
+from util import NumericConstant
+
+
+class BFDDiagCode(NumericConstant):
+ """ BFD Diagnostic Code """
+ no_diagnostic = 0
+ control_detection_time_expired = 1
+ echo_function_failed = 2
+ neighbor_signaled_session_down = 3
+ forwarding_plane_reset = 4
+ path_down = 5
+ concatenated_path_down = 6
+ administratively_down = 7
+ reverse_concatenated_path_down = 8
+
+ desc_dict = {
+ no_diagnostic: "No diagnostic",
+ control_detection_time_expired: "Control Detection Time Expired",
+ echo_function_failed: "Echo Function Failed",
+ neighbor_signaled_session_down: "Neighbor Signaled Session Down",
+ forwarding_plane_reset: "Forwarding Plane Reset",
+ path_down: "Path Down",
+ concatenated_path_down: "Concatenated Path Down",
+ administratively_down: "Administratively Down",
+ reverse_concatenated_path_down: "Reverse Concatenated Path Down",
+ }
+
+ def __init__(self, value):
+ NumericConstant.__init__(self, value)
+
+
+class BFDState(NumericConstant):
+ """ BFD State """
+ admin_down = 0
+ down = 1
+ init = 2
+ up = 3
+
+ desc_dict = {
+ admin_down: "AdminDown",
+ down: "Down",
+ init: "Init",
+ up: "Up",
+ }
+
+ def __init__(self, value):
+ NumericConstant.__init__(self, value)
+
+
+class BFD(Packet):
+
+ udp_dport = 3784 #: BFD destination port per RFC 5881
+ udp_sport_min = 49152 #: BFD source port min value per RFC 5881
+ udp_sport_max = 65535 #: BFD source port max value per RFC 5881
+
+ name = "BFD"
+
+ fields_desc = [
+ BitField("version", 1, 3),
+ BitEnumField("diag", 0, 5, BFDDiagCode.desc_dict),
+ BitEnumField("state", 0, 2, BFDState.desc_dict),
+ FlagsField("flags", 0, 6, ['P', 'F', 'C', 'A', 'D', 'M']),
+ XByteField("detect_mult", 0),
+ XByteField("length", 24),
+ BitField("my_discriminator", 0, 32),
+ BitField("your_discriminator", 0, 32),
+ BitField("desired_min_tx_interval", 0, 32),
+ BitField("required_min_rx_interval", 0, 32),
+ BitField("required_min_echo_rx_interval", 0, 32)]
+
+ def mysummary(self):
+ return self.sprintf("BFD(my_disc=%BFD.my_discriminator%,"
+ "your_disc=%BFD.your_discriminator%)")
+
+# glue the BFD packet class to scapy parser
+bind_layers(UDP, BFD, dport=BFD.udp_dport)
+
+
+class VppBFDUDPSession(VppObject):
+ """ Represents BFD UDP session in VPP """
+
+ @property
+ def test(self):
+ """ Test which created this session """
+ return self._test
+
+ @property
+ def interface(self):
+ """ Interface on which this session lives """
+ return self._interface
+
+ @property
+ def af(self):
+ """ Address family - AF_INET or AF_INET6 """
+ return self._af
+
+ @property
+ def bs_index(self):
+ """ BFD session index from VPP """
+ if self._bs_index is not None:
+ return self._bs_index
+ raise NotConfiguredException("not configured")
+
+ @property
+ def local_addr(self):
+ """ BFD session local address (VPP address) """
+ if self._local_addr is None:
+ return self._interface.local_ip4
+ return self._local_addr
+
+ @property
+ def local_addr_n(self):
+ """ BFD session local address (VPP address) - raw, suitable for API """
+ if self._local_addr is None:
+ return self._interface.local_ip4n
+ return self._local_addr_n
+
+ @property
+ def peer_addr(self):
+ """ BFD session peer address """
+ return self._peer_addr
+
+ @property
+ def peer_addr_n(self):
+ """ BFD session peer address - raw, suitable for API """
+ return self._peer_addr_n
+
+ @property
+ def state(self):
+ """ BFD session state """
+ result = self.test.vapi.bfd_udp_session_dump()
+ session = None
+ for s in result:
+ if s.sw_if_index == self.interface.sw_if_index:
+ if self.af == AF_INET \
+ and s.is_ipv6 == 0 \
+ and self.interface.local_ip4n == s.local_addr[:4] \
+ and self.interface.remote_ip4n == s.peer_addr[:4]:
+ session = s
+ break
+ if session is None:
+ raise Exception(
+ "Could not find BFD session in VPP response: %s" % repr(result))
+ return session.state
+
+ @property
+ def desired_min_tx(self):
+ return self._desired_min_tx
+
+ @property
+ def required_min_rx(self):
+ return self._required_min_rx
+
+ @property
+ def detect_mult(self):
+ return self._detect_mult
+
+ def __init__(self, test, interface, peer_addr, local_addr=None, af=AF_INET):
+ self._test = test
+ self._interface = interface
+ self._af = af
+ self._local_addr = local_addr
+ self._peer_addr = peer_addr
+ self._peer_addr_n = socket.inet_pton(af, peer_addr)
+ self._bs_index = None
+ self._desired_min_tx = 200000 # 0.2s
+ self._required_min_rx = 200000 # 0.2s
+ self._detect_mult = 3 # 3 packets need to be missed
+
+ def add_vpp_config(self):
+ is_ipv6 = 1 if AF_INET6 == self.af else 0
+ result = self.test.vapi.bfd_udp_add(
+ self._interface.sw_if_index,
+ self.desired_min_tx,
+ self.required_min_rx,
+ self.detect_mult,
+ self.local_addr_n,
+ self.peer_addr_n,
+ is_ipv6=is_ipv6)
+ self._bs_index = result.bs_index
+
+ def query_vpp_config(self):
+ result = self.test.vapi.bfd_udp_session_dump()
+ session = None
+ for s in result:
+ if s.sw_if_index == self.interface.sw_if_index:
+ if self.af == AF_INET \
+ and s.is_ipv6 == 0 \
+ and self.interface.local_ip4n == s.local_addr[:4] \
+ and self.interface.remote_ip4n == s.peer_addr[:4]:
+ session = s
+ break
+ if session is None:
+ return False
+ return True
+
+ def remove_vpp_config(self):
+ if hasattr(self, '_bs_index'):
+ is_ipv6 = 1 if AF_INET6 == self._af else 0
+ self.test.vapi.bfd_udp_del(
+ self._interface.sw_if_index,
+ self.local_addr_n,
+ self.peer_addr_n,
+ is_ipv6=is_ipv6)
+
+ def object_id(self):
+ return "bfd-udp-%d" % self.bs_index
+
+ def admin_up(self):
+ self.test.vapi.bfd_session_set_flags(self.bs_index, 1)
diff --git a/test/framework.py b/test/framework.py
index b3cbb08a4b5..aa4f2fdf8fc 100644
--- a/test/framework.py
+++ b/test/framework.py
@@ -462,6 +462,34 @@ class VppTestCase(unittest.TestCase):
if info.dst == dst_index:
return info
+ def assert_equal(self, real_value, expected_value, name_or_class=None):
+ if name_or_class is None:
+ self.assertEqual(real_value, expected_value, msg)
+ return
+ try:
+ msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
+ msg = msg % (getdoc(name_or_class).strip(),
+ real_value, str(name_or_class(real_value)),
+ expected_value, str(name_or_class(expected_value)))
+ except:
+ msg = "Invalid %s: %s does not match expected value %s" % (
+ name_or_class, real_value, expected_value)
+
+ self.assertEqual(real_value, expected_value, msg)
+
+ def assert_in_range(
+ self,
+ real_value,
+ expected_min,
+ expected_max,
+ name=None):
+ if name is None:
+ msg = None
+ else:
+ msg = "Invalid %s: %s out of range <%s,%s>" % (
+ name, real_value, expected_min, expected_max)
+ self.assertTrue(expected_min <= real_value <= expected_max, msg)
+
class VppTestResult(unittest.TestResult):
"""
diff --git a/test/hook.py b/test/hook.py
index 90e9bbf56b0..f3e5f8808bd 100644
--- a/test/hook.py
+++ b/test/hook.py
@@ -100,9 +100,13 @@ class PollHook(Hook):
signaldict = dict(
(k, v) for v, k in reversed(sorted(signal.__dict__.items()))
if v.startswith('SIG') and not v.startswith('SIG_'))
+
+ if self.testcase.vpp.returncode in signaldict:
+ s = signaldict[abs(self.testcase.vpp.returncode)]
+ else:
+ s = "unknown"
msg = "VPP subprocess died unexpectedly with returncode %d [%s]" % (
- self.testcase.vpp.returncode,
- signaldict[abs(self.testcase.vpp.returncode)])
+ self.testcase.vpp.returncode, s)
self.logger.critical(msg)
core_path = self.testcase.tempdir + '/core'
if os.path.isfile(core_path):
@@ -110,27 +114,27 @@ class PollHook(Hook):
self.testcase.vpp_dead = True
raise VppDiedError(msg)
- def after_api(self, api_name, api_args):
+ def before_api(self, api_name, api_args):
"""
- Check if VPP died after executing an API
+ Check if VPP died before executing an API
:param api_name: name of the API
:param api_args: tuple containing the API arguments
:raises VppDiedError: exception if VPP is not running anymore
"""
- super(PollHook, self).after_api(api_name, api_args)
+ super(PollHook, self).before_api(api_name, api_args)
self.poll_vpp()
- def after_cli(self, cli):
+ def before_cli(self, cli):
"""
- Check if VPP died after executing a CLI
+ Check if VPP died before executing a CLI
:param cli: CLI string
:raises Exception: exception if VPP is not running anymore
"""
- super(PollHook, self).after_cli(cli)
+ super(PollHook, self).before_cli(cli)
self.poll_vpp()
diff --git a/test/template_bd.py b/test/template_bd.py
index 6c6fb3da200..01e8b855004 100644
--- a/test/template_bd.py
+++ b/test/template_bd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-from abc import abstractmethod
+from abc import abstractmethod, ABCMeta
from scapy.layers.l2 import Ether, Raw
from scapy.layers.inet import IP, UDP
@@ -8,6 +8,7 @@ from scapy.layers.inet import IP, UDP
class BridgeDomain(object):
""" Bridge domain abstraction """
+ __metaclass__ = ABCMeta
@property
def frame_pg0_to_pg1(self):
diff --git a/test/test_bfd.py b/test/test_bfd.py
new file mode 100644
index 00000000000..20d3aea5e0c
--- /dev/null
+++ b/test/test_bfd.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python
+
+import unittest
+import time
+from random import randint
+from bfd import *
+from framework import *
+from util import ppp
+
+
+class BFDCLITestCase(VppTestCase):
+ """Bidirectional Forwarding Detection (BFD) - CLI"""
+
+ @classmethod
+ def setUpClass(cls):
+ super(BFDCLITestCase, cls).setUpClass()
+
+ try:
+ cls.create_pg_interfaces([0])
+ cls.pg0.config_ip4()
+ cls.pg0.resolve_arp()
+
+ except Exception:
+ super(BFDCLITestCase, cls).tearDownClass()
+ raise
+
+ def test_add_bfd(self):
+ """ create a BFD session """
+ session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
+ session.add_vpp_config()
+ self.logger.debug("Session state is %s" % str(session.state))
+ session.remove_vpp_config()
+ session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
+ session.add_vpp_config()
+ self.logger.debug("Session state is %s" % str(session.state))
+ session.remove_vpp_config()
+
+ def test_double_add(self):
+ """ create the same BFD session twice (negative case) """
+ session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
+ session.add_vpp_config()
+ try:
+ session.add_vpp_config()
+ except:
+ session.remove_vpp_config()
+ return
+ session.remove_vpp_config()
+ raise Exception("Expected failure while adding duplicate "
+ "configuration")
+
+
+def create_packet(interface, ttl=255, src_port=50000, **kwargs):
+ p = (Ether(src=interface.remote_mac, dst=interface.local_mac) /
+ IP(src=interface.remote_ip4, dst=interface.local_ip4, ttl=ttl) /
+ UDP(sport=src_port, dport=BFD.udp_dport) /
+ BFD(*kwargs))
+ return p
+
+
+def verify_ip(test, packet, local_ip, remote_ip):
+ """ Verify correctness of IP layer. """
+ ip = packet[IP]
+ test.assert_equal(ip.src, local_ip, "IP source address")
+ test.assert_equal(ip.dst, remote_ip, "IP destination address")
+ test.assert_equal(ip.ttl, 255, "IP TTL")
+
+
+def verify_udp(test, packet):
+ """ Verify correctness of UDP layer. """
+ udp = packet[UDP]
+ test.assert_equal(udp.dport, BFD.udp_dport, "UDP destination port")
+ test.assert_in_range(udp.sport, BFD.udp_sport_min, BFD.udp_sport_max,
+ "UDP source port")
+
+
+class BFDTestSession(object):
+
+ def __init__(self, test, interface, detect_mult=3):
+ self.test = test
+ self.interface = interface
+ self.bfd_values = {
+ 'my_discriminator': 0,
+ 'desired_min_tx_interval': 500000,
+ 'detect_mult': detect_mult,
+ 'diag': BFDDiagCode.no_diagnostic,
+ }
+
+ def update(self, **kwargs):
+ self.bfd_values.update(kwargs)
+
+ def create_packet(self):
+ packet = create_packet(self.interface)
+ for name, value in self.bfd_values.iteritems():
+ packet[BFD].setfieldval(name, value)
+ return packet
+
+ def send_packet(self):
+ p = self.create_packet()
+ self.test.logger.debug(ppp("Sending packet:", p))
+ self.test.pg0.add_stream([p])
+ self.test.pg_start()
+
+ def verify_packet(self, packet):
+ """ Verify correctness of BFD layer. """
+ bfd = packet[BFD]
+ self.test.assert_equal(bfd.version, 1, "BFD version")
+ self.test.assert_equal(bfd.your_discriminator,
+ self.bfd_values['my_discriminator'],
+ "BFD - your discriminator")
+
+
+class BFDTestCase(VppTestCase):
+ """Bidirectional Forwarding Detection (BFD)"""
+
+ @classmethod
+ def setUpClass(cls):
+ super(BFDTestCase, cls).setUpClass()
+ try:
+ cls.create_pg_interfaces([0, 1])
+ cls.pg0.config_ip4()
+ cls.pg0.generate_remote_hosts()
+ cls.pg0.configure_ipv4_neighbors()
+ cls.pg0.admin_up()
+ cls.pg0.resolve_arp()
+
+ except Exception:
+ super(BFDTestCase, cls).tearDownClass()
+ raise
+
+ def setUp(self):
+ self.vapi.want_bfd_events()
+ self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
+ self.vpp_session.add_vpp_config()
+ self.vpp_session.admin_up()
+ self.test_session = BFDTestSession(self, self.pg0)
+
+ def tearDown(self):
+ self.vapi.want_bfd_events(enable_disable=0)
+ if not self.vpp_dead:
+ self.vpp_session.remove_vpp_config()
+ super(BFDTestCase, self).tearDown()
+
+ def verify_event(self, event, expected_state):
+ """ Verify correctness of event values. """
+ e = event
+ self.logger.debug("Event: %s" % repr(e))
+ self.assert_equal(e.bs_index, self.vpp_session.bs_index,
+ "BFD session index")
+ self.assert_equal(e.sw_if_index, self.vpp_session.interface.sw_if_index,
+ "BFD interface index")
+ is_ipv6 = 0
+ if self.vpp_session.af == AF_INET6:
+ is_ipv6 = 1
+ self.assert_equal(e.is_ipv6, is_ipv6, "is_ipv6")
+ if self.vpp_session.af == AF_INET:
+ self.assert_equal(e.local_addr[:4], self.vpp_session.local_addr_n,
+ "Local IPv4 address")
+ self.assert_equal(e.peer_addr[:4], self.vpp_session.peer_addr_n,
+ "Peer IPv4 address")
+ else:
+ self.assert_equal(e.local_addr, self.vpp_session.local_addr_n,
+ "Local IPv6 address")
+ self.assert_equal(e.peer_addr, self.vpp_session.peer_addr_n,
+ "Peer IPv6 address")
+ self.assert_equal(e.state, expected_state, BFDState)
+
+ def wait_for_bfd_packet(self, timeout=1):
+ p = self.pg0.wait_for_packet(timeout=timeout)
+ bfd = p[BFD]
+ if bfd is None:
+ raise Exception(ppp("Unexpected or invalid BFD packet:", p))
+ if bfd.payload:
+ raise Exception(ppp("Unexpected payload in BFD packet:", bfd))
+ verify_ip(self, p, self.pg0.local_ip4, self.pg0.remote_ip4)
+ verify_udp(self, p)
+ self.test_session.verify_packet(p)
+ return p
+
+ def test_slow_timer(self):
+ """ Slow timer """
+
+ self.pg_enable_capture([self.pg0])
+ expected_packets = 10
+ self.logger.info("Waiting for %d BFD packets" % expected_packets)
+ self.wait_for_bfd_packet()
+ for i in range(expected_packets):
+ before = time.time()
+ self.wait_for_bfd_packet()
+ after = time.time()
+ self.assert_in_range(
+ after - before, 0.75, 1, "time between slow packets")
+ before = after
+
+ def test_zero_remote_min_rx(self):
+ """ Zero RemoteMinRxInterval """
+ self.pg_enable_capture([self.pg0])
+ p = self.wait_for_bfd_packet()
+ self.test_session.update(my_discriminator=randint(0, 40000000),
+ your_discriminator=p[BFD].my_discriminator,
+ state=BFDState.init,
+ required_min_rx_interval=0)
+ self.test_session.send_packet()
+ e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+ self.verify_event(e, expected_state=BFDState.up)
+
+ try:
+ p = self.pg0.wait_for_packet(timeout=1)
+ except:
+ return
+ raise Exception(ppp("Received unexpected BFD packet:", p))
+
+ def bfd_conn_up(self):
+ self.pg_enable_capture([self.pg0])
+ self.logger.info("Waiting for slow hello")
+ p = self.wait_for_bfd_packet()
+ self.logger.info("Sending Init")
+ self.test_session.update(my_discriminator=randint(0, 40000000),
+ your_discriminator=p[BFD].my_discriminator,
+ state=BFDState.init,
+ required_min_rx_interval=500000)
+ self.test_session.send_packet()
+ self.logger.info("Waiting for event")
+ e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+ self.verify_event(e, expected_state=BFDState.up)
+ self.logger.info("Session is Up")
+ self.test_session.update(state=BFDState.up)
+
+ def test_conn_up(self):
+ """ Basic connection up """
+ self.bfd_conn_up()
+
+ def test_hold_up(self):
+ """ Hold BFD up """
+ self.bfd_conn_up()
+ for i in range(5):
+ self.wait_for_bfd_packet()
+ self.test_session.send_packet()
+
+ def test_conn_down(self):
+ """ Session down after inactivity """
+ self.bfd_conn_up()
+ self.wait_for_bfd_packet()
+ self.assert_equal(
+ 0, len(self.vapi.collect_events()),
+ "number of bfd events")
+ self.wait_for_bfd_packet()
+ self.assert_equal(
+ 0, len(self.vapi.collect_events()),
+ "number of bfd events")
+ e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+ self.verify_event(e, expected_state=BFDState.down)
+
+ @unittest.skip("this test is not working yet")
+ def test_large_required_min_rx(self):
+ self.bfd_conn_up()
+ interval = 5000000
+ self.test_session.update(required_min_rx_interval=interval)
+ self.test_session.send_packet()
+ now = time.time()
+ count = 1
+ while time.time() < now + interval / 1000000:
+ try:
+ p = self.wait_for_bfd_packet()
+ if count > 1:
+ self.logger.error(ppp("Received unexpected packet:", p))
+ count += 1
+ except:
+ pass
+ self.assert_equal(count, 1, "number of packets received")
+
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)
diff --git a/test/util.py b/test/util.py
index 643377f5740..f6c6acd41e9 100644
--- a/test/util.py
+++ b/test/util.py
@@ -1,5 +1,6 @@
import socket
import sys
+from abc import abstractmethod, ABCMeta
from cStringIO import StringIO
@@ -14,6 +15,27 @@ def ppp(headline, packet):
return o.getvalue()
+class NumericConstant(object):
+ __metaclass__ = ABCMeta
+
+ desc_dict = {}
+
+ @abstractmethod
+ def __init__(self, value):
+ self._value = value
+
+ def __int__(self):
+ return self._value
+
+ def __long__(self):
+ return self._value
+
+ def __str__(self):
+ if self._value in self.desc_dict:
+ return self.desc_dict[self._value]
+ return ""
+
+
class Host(object):
""" Generic test host "connected" to VPPs interface. """
diff --git a/test/vpp_object.py b/test/vpp_object.py
new file mode 100644
index 00000000000..2b71fc1fd39
--- /dev/null
+++ b/test/vpp_object.py
@@ -0,0 +1,79 @@
+from abc import ABCMeta, abstractmethod
+
+
+class VppObject(object):
+ """ Abstract vpp object """
+ __metaclass__ = ABCMeta
+
+ def __init__(self):
+ VppObjectRegistry().register(self)
+
+ @abstractmethod
+ def add_vpp_config(self):
+ """ Add the configuration for this object to vpp. """
+ pass
+
+ @abstractmethod
+ def query_vpp_config(self):
+ """Query the vpp configuration.
+
+ :return: True if the object is configured"""
+ pass
+
+ @abstractmethod
+ def remove_vpp_config(self):
+ """ Remove the configuration for this object from vpp. """
+ pass
+
+ @abstractmethod
+ def object_id(self):
+ """ Return a unique string representing this object. """
+ pass
+
+
+class VppObjectRegistry(object):
+ """ Class which handles automatic configuration cleanup. """
+ _shared_state = {}
+
+ def __init__(self):
+ self.__dict__ = self._shared_state
+ if not hasattr(self, "_object_registry"):
+ self._object_registry = []
+ if not hasattr(self, "_object_dict"):
+ self._object_dict = dict()
+
+ def register(self, o):
+ """ Register an object in the registry. """
+ if not o.unique_id() in self._object_dict:
+ self._object_registry.append(o)
+ self._object_dict[o.unique_id()] = o
+ else:
+ print "not adding duplicate %s" % o
+
+ def remove_vpp_config(self, logger):
+ """
+ Remove configuration (if present) from vpp and then remove all objects
+ from the registry.
+ """
+ if not self._object_registry:
+ logger.info("No objects registered for auto-cleanup.")
+ return
+ logger.info("Removing VPP configuration for registered objects")
+ for o in reversed(self._object_registry):
+ if o.query_vpp_config():
+ logger.info("Removing %s", o)
+ o.remove_vpp_config()
+ else:
+ logger.info("Skipping %s, configuration not present", o)
+ failed = []
+ for o in self._object_registry:
+ if o.query_vpp_config():
+ failed.append(o)
+ self._object_registry = []
+ self._object_dict = dict()
+ if failed:
+ logger.error("Couldn't remove configuration for object(s):")
+ for x in failed:
+ logger.error(repr(x))
+ raise Exception("Couldn't remove configuration for object(s): %s" %
+ (", ".join(str(x) for x in failed)))
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 2f931803e14..5e80a03e2c5 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -1,7 +1,12 @@
-import os, fnmatch
-import array
+import os
+import fnmatch
+import time
from hook import Hook
+# Sphinx creates auto-generated documentation by importing the python source
+# files and collecting the docstrings from them. The NO_VPP_PAPI flag allows the
+# vpp_papi_provider.py file to be importable without having to build the whole
+# vpp api if the user only wishes to generate the test documentation.
do_import = True
try:
no_vpp_papi = os.getenv("NO_VPP_PAPI")
@@ -17,9 +22,11 @@ if do_import:
MPLS_IETF_MAX_LABEL = 0xfffff
MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1
+
class L2_VTR_OP:
L2_POP_1 = 3
+
class VppPapiProvider(object):
"""VPP-api provider using vpp-papi
@@ -35,12 +42,13 @@ class VppPapiProvider(object):
self.test_class = test_class
jsonfiles = []
- install_dir=os.getenv('VPP_TEST_INSTALL_PATH')
+ install_dir = os.getenv('VPP_TEST_INSTALL_PATH')
for root, dirnames, filenames in os.walk(install_dir):
for filename in fnmatch.filter(filenames, '*.api.json'):
jsonfiles.append(os.path.join(root, filename))
self.papi = VPP(jsonfiles)
+ self._events = list()
def register_hook(self, hook):
"""Replace hook registration with new hook
@@ -50,9 +58,35 @@ class VppPapiProvider(object):
"""
self.hook = hook
+ def collect_events(self):
+ e = self._events
+ self._events = list()
+ return e
+
+ def wait_for_event(self, timeout, name=None):
+ limit = time.time() + timeout
+ while time.time() < limit:
+ if self._events:
+ e = self._events.pop(0)
+ if name and type(e).__name__ != name:
+ raise Exception(
+ "Unexpected event received: %s, expected: %s" %
+ (type(e).__name__, name))
+ return e
+ time.sleep(0) # yield
+ if name is not None:
+ raise Exception("Event %s did not occur within timeout" % name)
+ raise Exception("Event did not occur within timeout")
+
+ def __call__(self, name, event):
+ # FIXME use the name instead of relying on type(e).__name__ ?
+ # FIXME #2 if this throws, it is eaten silently, Ole?
+ self._events.append(event)
+
def connect(self):
"""Connect the API to VPP"""
self.papi.connect(self.name, self.shm_prefix)
+ self.papi.register_event_callback(self)
def disconnect(self):
"""Disconnect the API from VPP"""
@@ -73,7 +107,7 @@ class VppPapiProvider(object):
if hasattr(reply, 'retval') and reply.retval != expected_retval:
msg = "API call failed, expected retval == %d, got %s" % (
expected_retval, repr(reply))
- self.test_class.test_instance.logger.error(msg)
+ self.test_class.logger.error(msg)
raise Exception(msg)
self.hook.after_api(api_fn.__name__, api_args)
return reply
@@ -116,7 +150,7 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.pg_create_interface,
- { "interface_id" : pg_index })
+ {"interface_id": pg_index})
def sw_interface_dump(self, filter=None):
"""
@@ -125,7 +159,7 @@ class VppPapiProvider(object):
"""
if filter is not None:
- args = {"name_filter_valid" : 1, "name_filter" : filter}
+ args = {"name_filter_valid": 1, "name_filter": filter}
else:
args = {}
return self.api(self.papi.sw_interface_dump, args)
@@ -140,8 +174,8 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.sw_interface_set_table,
- { 'sw_if_index' : sw_if_index, 'is_ipv6' : is_ipv6,
- 'vrf_id' : table_id})
+ {'sw_if_index': sw_if_index, 'is_ipv6': is_ipv6,
+ 'vrf_id': table_id})
def sw_interface_add_del_address(self, sw_if_index, addr, addr_len,
is_ipv6=0, is_add=1, del_all=0):
@@ -156,12 +190,12 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.sw_interface_add_del_address,
- { 'sw_if_index' : sw_if_index,
- 'is_add' : is_add,
- 'is_ipv6' : is_ipv6,
- 'del_all' : del_all,
- 'address_length' : addr_len,
- 'address' : addr})
+ {'sw_if_index': sw_if_index,
+ 'is_add': is_add,
+ 'is_ipv6': is_ipv6,
+ 'del_all': del_all,
+ 'address_length': addr_len,
+ 'address': addr})
def sw_interface_enable_disable_mpls(self, sw_if_index,
is_enable=1):
@@ -172,12 +206,12 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.sw_interface_set_mpls_enable,
- {'sw_if_index' : sw_if_index,
- 'enable' : is_enable })
+ {'sw_if_index': sw_if_index,
+ 'enable': is_enable})
def sw_interface_ra_suppress(self, sw_if_index):
return self.api(self.papi.sw_interface_ip6nd_ra_config,
- {'sw_if_index' : sw_if_index })
+ {'sw_if_index': sw_if_index})
def vxlan_add_del_tunnel(
self,
@@ -202,14 +236,14 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.vxlan_add_del_tunnel,
- {'is_add' : is_add,
- 'is_ipv6' : is_ipv6,
- 'src_address' : src_addr,
- 'dst_address' : dst_addr,
- 'mcast_sw_if_index' : mcast_sw_if_index,
- 'encap_vrf_id' : encap_vrf_id,
- 'decap_next_index' : decap_next_index,
- 'vni' : vni})
+ {'is_add': is_add,
+ 'is_ipv6': is_ipv6,
+ 'src_address': src_addr,
+ 'dst_address': dst_addr,
+ 'mcast_sw_if_index': mcast_sw_if_index,
+ 'encap_vrf_id': encap_vrf_id,
+ 'decap_next_index': decap_next_index,
+ 'vni': vni})
def bridge_domain_add_del(self, bd_id, flood=1, uu_flood=1, forward=1,
learn=1, arp_term=0, is_add=1):
@@ -229,13 +263,13 @@ class VppPapiProvider(object):
:param int is_add: Add or delete flag. (Default value = 1)
"""
return self.api(self.papi.bridge_domain_add_del,
- { 'bd_id' : bd_id,
- 'flood' : flood,
- 'uu_flood' : uu_flood,
- 'forward' : forward,
- 'learn' : learn,
- 'arp_term' : arp_term,
- 'is_add' : is_add})
+ {'bd_id': bd_id,
+ 'flood': flood,
+ 'uu_flood': uu_flood,
+ 'forward': forward,
+ 'learn': learn,
+ 'arp_term': arp_term,
+ 'is_add': is_add})
def l2fib_add_del(self, mac, bd_id, sw_if_index, is_add=1, static_mac=0,
filter_mac=0, bvi_mac=0):
@@ -254,13 +288,13 @@ class VppPapiProvider(object):
interface. (Default value = 0)
"""
return self.api(self.papi.l2fib_add_del,
- { 'mac' : self._convert_mac(mac),
- 'bd_id' : bd_id,
- 'sw_if_index' : sw_if_index,
- 'is_add' : is_add,
- 'static_mac' : static_mac,
- 'filter_mac' : filter_mac,
- 'bvi_mac' : bvi_mac })
+ {'mac': self._convert_mac(mac),
+ 'bd_id': bd_id,
+ 'sw_if_index': sw_if_index,
+ 'is_add': is_add,
+ 'static_mac': static_mac,
+ 'filter_mac': filter_mac,
+ 'bvi_mac': bvi_mac})
def sw_interface_set_l2_bridge(self, sw_if_index, bd_id,
shg=0, bvi=0, enable=1):
@@ -274,11 +308,11 @@ class VppPapiProvider(object):
:param int enable: Add or remove interface. (Default value = 1)
"""
return self.api(self.papi.sw_interface_set_l2_bridge,
- { 'rx_sw_if_index' : sw_if_index,
- 'bd_id' : bd_id,
- 'shg' : shg,
- 'bvi' : bvi,
- 'enable' : enable })
+ {'rx_sw_if_index': sw_if_index,
+ 'bd_id': bd_id,
+ 'shg': shg,
+ 'bvi': bvi,
+ 'enable': enable})
def bridge_flags(self, bd_id, is_set, feature_bitmap):
"""Enable/disable required feature of the bridge domain with defined ID.
@@ -293,9 +327,9 @@ class VppPapiProvider(object):
- arp-term (1 << 4).
"""
return self.api(self.papi.bridge_flags,
- {'bd_id' : bd_id,
- 'is_set' : is_set,
- 'feature_bitmap' : feature_bitmap })
+ {'bd_id': bd_id,
+ 'is_set': is_set,
+ 'feature_bitmap': feature_bitmap})
def bridge_domain_dump(self, bd_id=0):
"""
@@ -305,7 +339,7 @@ class VppPapiProvider(object):
:return: Dictionary of bridge domain(s) data.
"""
return self.api(self.papi.bridge_domain_dump,
- {'bd_id' : bd_id })
+ {'bd_id': bd_id})
def sw_interface_set_l2_xconnect(self, rx_sw_if_index, tx_sw_if_index,
enable):
@@ -319,11 +353,17 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.sw_interface_set_l2_xconnect,
- { 'rx_sw_if_index' : rx_sw_if_index,
- 'tx_sw_if_index' : tx_sw_if_index,
- 'enable' : enable })
+ {'rx_sw_if_index': rx_sw_if_index,
+ 'tx_sw_if_index': tx_sw_if_index,
+ 'enable': enable})
- def sw_interface_set_l2_tag_rewrite(self, sw_if_index, vtr_oper, push=0, tag1=0, tag2=0):
+ def sw_interface_set_l2_tag_rewrite(
+ self,
+ sw_if_index,
+ vtr_oper,
+ push=0,
+ tag1=0,
+ tag2=0):
"""L2 interface vlan tag rewrite configure request
:param client_index - opaque cookie to identify the sender
:param context - sender context, to match reply w/ request
@@ -335,11 +375,11 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.l2_interface_vlan_tag_rewrite,
- { 'sw_if_index' : sw_if_index,
- 'vtr_op' : vtr_oper,
- 'push_dot1q' : push,
- 'tag1' : tag1,
- 'tag2' : tag2 })
+ {'sw_if_index': sw_if_index,
+ 'vtr_op': vtr_oper,
+ 'push_dot1q': push,
+ 'tag1': tag1,
+ 'tag2': tag2})
def sw_interface_set_flags(self, sw_if_index, admin_up_down,
link_up_down=0, deleted=0):
@@ -352,10 +392,10 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.sw_interface_set_flags,
- { 'sw_if_index' : sw_if_index,
- 'admin_up_down' : admin_up_down,
- 'link_up_down' : link_up_down,
- 'deleted' : deleted })
+ {'sw_if_index': sw_if_index,
+ 'admin_up_down': admin_up_down,
+ 'link_up_down': link_up_down,
+ 'deleted': deleted})
def create_subif(self, sw_if_index, sub_id, outer_vlan, inner_vlan,
no_tags=0, one_tag=0, two_tags=0, dot1ad=0, exact_match=0,
@@ -379,18 +419,18 @@ class VppPapiProvider(object):
"""
return self.api(
self.papi.create_subif,
- { 'sw_if_index' : sw_if_index,
- 'sub_id' : sub_id,
- 'no_tags' : no_tags,
- 'one_tag' : one_tag,
- 'two_tags' : two_tags,
- 'dot1ad' : dot1ad,
- 'exact_match' : exact_match,
- 'default_sub' : default_sub,
- 'outer_vlan_id_any' : outer_vlan_id_any,
- 'inner_vlan_id_any' : inner_vlan_id_any,
- 'outer_vlan_id' : outer_vlan,
- 'inner_vlan_id' : inner_vlan })
+ {'sw_if_index': sw_if_index,
+ 'sub_id': sub_id,
+ 'no_tags': no_tags,
+ 'one_tag': one_tag,
+ 'two_tags': two_tags,
+ 'dot1ad': dot1ad,
+ 'exact_match': exact_match,
+ 'default_sub': default_sub,
+ 'outer_vlan_id_any': outer_vlan_id_any,
+ 'inner_vlan_id_any': inner_vlan_id_any,
+ 'outer_vlan_id': outer_vlan,
+ 'inner_vlan_id': inner_vlan})
def delete_subif(self, sw_if_index):
"""Delete subinterface
@@ -398,7 +438,7 @@ class VppPapiProvider(object):
:param sw_if_index:
"""
return self.api(self.papi.delete_subif,
- { 'sw_if_index' : sw_if_index })
+ {'sw_if_index': sw_if_index})
def create_vlan_subif(self, sw_if_index, vlan):
"""
@@ -408,8 +448,8 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.create_vlan_subif,
- {'sw_if_index' : sw_if_index,
- 'vlan_id' : vlan })
+ {'sw_if_index': sw_if_index,
+ 'vlan_id': vlan})
def create_loopback(self, mac=''):
"""
@@ -417,7 +457,7 @@ class VppPapiProvider(object):
:param mac: (Optional)
"""
return self.api(self.papi.create_loopback,
- { 'mac_address' : mac })
+ {'mac_address': mac})
def ip_add_del_route(
self,
@@ -428,9 +468,9 @@ class VppPapiProvider(object):
table_id=0,
next_hop_table_id=0,
next_hop_weight=1,
- next_hop_n_out_labels = 0,
- next_hop_out_label_stack = [],
- next_hop_via_label = MPLS_LABEL_INVALID,
+ next_hop_n_out_labels=0,
+ next_hop_out_label_stack=[],
+ next_hop_via_label=MPLS_LABEL_INVALID,
create_vrf_if_needed=0,
is_resolve_host=0,
is_resolve_attached=0,
@@ -470,29 +510,29 @@ class VppPapiProvider(object):
return self.api(
self.papi.ip_add_del_route,
- { 'next_hop_sw_if_index' : next_hop_sw_if_index,
- 'table_id' : table_id,
- 'classify_table_index' : classify_table_index,
- 'next_hop_table_id' : next_hop_table_id,
- 'create_vrf_if_needed' : create_vrf_if_needed,
- 'is_add' : is_add,
- 'is_drop' : is_drop,
- 'is_unreach' : is_unreach,
- 'is_prohibit' : is_prohibit,
- 'is_ipv6' : is_ipv6,
- 'is_local' : is_local,
- 'is_classify' : is_classify,
- 'is_multipath' : is_multipath,
- 'is_resolve_host' : is_resolve_host,
- 'is_resolve_attached' : is_resolve_attached,
- 'not_last' : not_last,
- 'next_hop_weight' : next_hop_weight,
- 'dst_address_length' : dst_address_length,
- 'dst_address' : dst_address,
- 'next_hop_address' : next_hop_address,
- 'next_hop_n_out_labels' : next_hop_n_out_labels,
- 'next_hop_via_label' : next_hop_via_label,
- 'next_hop_out_label_stack' : next_hop_out_label_stack })
+ {'next_hop_sw_if_index': next_hop_sw_if_index,
+ 'table_id': table_id,
+ 'classify_table_index': classify_table_index,
+ 'next_hop_table_id': next_hop_table_id,
+ 'create_vrf_if_needed': create_vrf_if_needed,
+ 'is_add': is_add,
+ 'is_drop': is_drop,
+ 'is_unreach': is_unreach,
+ 'is_prohibit': is_prohibit,
+ 'is_ipv6': is_ipv6,
+ 'is_local': is_local,
+ 'is_classify': is_classify,
+ 'is_multipath': is_multipath,
+ 'is_resolve_host': is_resolve_host,
+ 'is_resolve_attached': is_resolve_attached,
+ 'not_last': not_last,
+ 'next_hop_weight': next_hop_weight,
+ 'dst_address_length': dst_address_length,
+ 'dst_address': dst_address,
+ 'next_hop_address': next_hop_address,
+ 'next_hop_n_out_labels': next_hop_n_out_labels,
+ 'next_hop_via_label': next_hop_via_label,
+ 'next_hop_out_label_stack': next_hop_out_label_stack})
def ip_neighbor_add_del(self,
sw_if_index,
@@ -516,13 +556,13 @@ class VppPapiProvider(object):
return self.api(
self.papi.ip_neighbor_add_del,
- { 'vrf_id' : vrf_id,
- 'sw_if_index' : sw_if_index,
- 'is_add' : is_add,
- 'is_ipv6' : is_ipv6,
- 'is_static' : is_static,
- 'mac_address' : mac_address,
- 'dst_address' : dst_address
+ {'vrf_id': vrf_id,
+ 'sw_if_index': sw_if_index,
+ 'is_add': is_add,
+ 'is_ipv6': is_ipv6,
+ 'is_static': is_static,
+ 'mac_address': mac_address,
+ 'dst_address': dst_address
}
)
@@ -536,9 +576,9 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.sw_interface_span_enable_disable,
- { 'sw_if_index_from' : sw_if_index_from,
- 'sw_if_index_to' : sw_if_index_to,
- 'state' : state })
+ {'sw_if_index_from': sw_if_index_from,
+ 'sw_if_index_to': sw_if_index_to,
+ 'state': state})
def gre_tunnel_add_del(self,
src_address,
@@ -559,12 +599,12 @@ class VppPapiProvider(object):
return self.api(
self.papi.gre_add_del_tunnel,
- { 'is_add' : is_add,
- 'is_ipv6' : is_ip6,
- 'teb' : is_teb,
- 'src_address' : src_address,
- 'dst_address' : dst_address,
- 'outer_fib_id' : outer_fib_id }
+ {'is_add': is_add,
+ 'is_ipv6': is_ip6,
+ 'teb': is_teb,
+ 'src_address': src_address,
+ 'dst_address': dst_address,
+ 'outer_fib_id': outer_fib_id}
)
def mpls_route_add_del(
@@ -577,9 +617,9 @@ class VppPapiProvider(object):
table_id=0,
next_hop_table_id=0,
next_hop_weight=1,
- next_hop_n_out_labels = 0,
- next_hop_out_label_stack = [],
- next_hop_via_label = MPLS_LABEL_INVALID,
+ next_hop_n_out_labels=0,
+ next_hop_out_label_stack=[],
+ next_hop_via_label=MPLS_LABEL_INVALID,
create_vrf_if_needed=0,
is_resolve_host=0,
is_resolve_attached=0,
@@ -615,24 +655,24 @@ class VppPapiProvider(object):
return self.api(
self.papi.mpls_route_add_del,
- { 'mr_label' : label,
- 'mr_eos' : eos,
- 'mr_table_id' : table_id,
- 'mr_classify_table_index' : classify_table_index,
- 'mr_create_table_if_needed' : create_vrf_if_needed,
- 'mr_is_add' : is_add,
- 'mr_is_classify' : is_classify,
- 'mr_is_multipath' : is_multipath,
- 'mr_is_resolve_host' : is_resolve_host,
- 'mr_is_resolve_attached' : is_resolve_attached,
- 'mr_next_hop_proto_is_ip4' : next_hop_proto_is_ip4,
- 'mr_next_hop_weight' : next_hop_weight,
- 'mr_next_hop' : next_hop_address,
- 'mr_next_hop_n_out_labels' : next_hop_n_out_labels,
- 'mr_next_hop_sw_if_index' : next_hop_sw_if_index,
- 'mr_next_hop_table_id' : next_hop_table_id,
- 'mr_next_hop_via_label' : next_hop_via_label,
- 'mr_next_hop_out_label_stack' : next_hop_out_label_stack })
+ {'mr_label': label,
+ 'mr_eos': eos,
+ 'mr_table_id': table_id,
+ 'mr_classify_table_index': classify_table_index,
+ 'mr_create_table_if_needed': create_vrf_if_needed,
+ 'mr_is_add': is_add,
+ 'mr_is_classify': is_classify,
+ 'mr_is_multipath': is_multipath,
+ 'mr_is_resolve_host': is_resolve_host,
+ 'mr_is_resolve_attached': is_resolve_attached,
+ 'mr_next_hop_proto_is_ip4': next_hop_proto_is_ip4,
+ 'mr_next_hop_weight': next_hop_weight,
+ 'mr_next_hop': next_hop_address,
+ 'mr_next_hop_n_out_labels': next_hop_n_out_labels,
+ 'mr_next_hop_sw_if_index': next_hop_sw_if_index,
+ 'mr_next_hop_table_id': next_hop_table_id,
+ 'mr_next_hop_via_label': next_hop_via_label,
+ 'mr_next_hop_out_label_stack': next_hop_out_label_stack})
def mpls_ip_bind_unbind(
self,
@@ -648,14 +688,14 @@ class VppPapiProvider(object):
"""
return self.api(
self.papi.mpls_ip_bind_unbind,
- {'mb_mpls_table_id' : table_id,
- 'mb_label' : label,
- 'mb_ip_table_id' : ip_table_id,
- 'mb_create_table_if_needed' : create_vrf_if_needed,
- 'mb_is_bind' : is_bind,
- 'mb_is_ip4' : is_ip4,
- 'mb_address_length' : dst_address_length,
- 'mb_address' : dst_address})
+ {'mb_mpls_table_id': table_id,
+ 'mb_label': label,
+ 'mb_ip_table_id': ip_table_id,
+ 'mb_create_table_if_needed': create_vrf_if_needed,
+ 'mb_is_bind': is_bind,
+ 'mb_is_ip4': is_ip4,
+ 'mb_address_length': dst_address_length,
+ 'mb_address': dst_address})
def mpls_tunnel_add_del(
self,
@@ -665,9 +705,9 @@ class VppPapiProvider(object):
next_hop_sw_if_index=0xFFFFFFFF,
next_hop_table_id=0,
next_hop_weight=1,
- next_hop_n_out_labels = 0,
- next_hop_out_label_stack = [],
- next_hop_via_label = MPLS_LABEL_INVALID,
+ next_hop_n_out_labels=0,
+ next_hop_out_label_stack=[],
+ next_hop_via_label=MPLS_LABEL_INVALID,
create_vrf_if_needed=0,
is_add=1,
l2_only=0):
@@ -696,19 +736,16 @@ class VppPapiProvider(object):
"""
return self.api(
self.papi.mpls_tunnel_add_del,
- {'mt_sw_if_index' : tun_sw_if_index,
- 'mt_is_add' : is_add,
- 'mt_l2_only' : l2_only,
- 'mt_next_hop_proto_is_ip4' : next_hop_proto_is_ip4,
- 'mt_next_hop_weight' : next_hop_weight,
- 'mt_next_hop' : next_hop_address,
- 'mt_next_hop_n_out_labels' : next_hop_n_out_labels,
- 'mt_next_hop_sw_if_index' :next_hop_sw_if_index,
- 'mt_next_hop_table_id' : next_hop_table_id,
- 'mt_next_hop_out_label_stack' : next_hop_out_label_stack })
-
- return self.api(vpp_papi.sw_interface_span_enable_disable,
- (sw_if_index_from, sw_if_index_to, enable))
+ {'mt_sw_if_index': tun_sw_if_index,
+ 'mt_is_add': is_add,
+ 'mt_l2_only': l2_only,
+ 'mt_next_hop_proto_is_ip4': next_hop_proto_is_ip4,
+ 'mt_next_hop_weight': next_hop_weight,
+ 'mt_next_hop': next_hop_address,
+ 'mt_next_hop_n_out_labels': next_hop_n_out_labels,
+ 'mt_next_hop_sw_if_index': next_hop_sw_if_index,
+ 'mt_next_hop_table_id': next_hop_table_id,
+ 'mt_next_hop_out_label_stack': next_hop_out_label_stack})
def snat_interface_add_del_feature(
self,
@@ -723,9 +760,9 @@ class VppPapiProvider(object):
"""
return self.api(
self.papi.snat_interface_add_del_feature,
- {'is_add' : is_add,
- 'is_inside' : is_inside,
- 'sw_if_index' : sw_if_index})
+ {'is_add': is_add,
+ 'is_inside': is_inside,
+ 'sw_if_index': sw_if_index})
def snat_add_static_mapping(
self,
@@ -750,14 +787,14 @@ class VppPapiProvider(object):
"""
return self.api(
self.papi.snat_add_static_mapping,
- {'is_add' : is_add,
- 'is_ip4' : is_ip4,
- 'addr_only' : addr_only,
- 'local_ip_address' : local_ip,
- 'external_ip_address' : external_ip,
- 'local_port' : local_port,
- 'external_port' : external_port,
- 'vrf_id' : vrf_id})
+ {'is_add': is_add,
+ 'is_ip4': is_ip4,
+ 'addr_only': addr_only,
+ 'local_ip_address': local_ip,
+ 'external_ip_address': external_ip,
+ 'local_port': local_port,
+ 'external_port': external_port,
+ 'vrf_id': vrf_id})
def snat_add_address_range(
self,
@@ -774,10 +811,10 @@ class VppPapiProvider(object):
"""
return self.api(
self.papi.snat_add_address_range,
- {'is_ip4' : is_ip4,
- 'first_ip_address' : first_ip_address,
- 'last_ip_address' : last_ip_address,
- 'is_add' : is_add})
+ {'is_ip4': is_ip4,
+ 'first_ip_address': first_ip_address,
+ 'last_ip_address': last_ip_address,
+ 'is_add': is_add})
def snat_address_dump(self):
"""Dump S-NAT addresses
@@ -796,3 +833,43 @@ class VppPapiProvider(object):
:return: Dictionary of S-NAT static mappings
"""
return self.api(self.papi.snat_static_mapping_dump, {})
+
+ def control_ping(self):
+ self.api(self.papi.control_ping)
+
+ def bfd_udp_add(self, sw_if_index, desired_min_tx, required_min_rx,
+ detect_mult, local_addr, peer_addr, is_ipv6=0):
+ return self.api(self.papi.bfd_udp_add,
+ {
+ 'sw_if_index': sw_if_index,
+ 'desired_min_tx': desired_min_tx,
+ 'required_min_rx': required_min_rx,
+ 'local_addr': local_addr,
+ 'peer_addr': peer_addr,
+ 'is_ipv6': is_ipv6,
+ 'detect_mult': detect_mult,
+ })
+
+ def bfd_udp_del(self, sw_if_index, local_addr, peer_addr, is_ipv6=0):
+ return self.api(self.papi.bfd_udp_del,
+ {
+ 'sw_if_index': sw_if_index,
+ 'local_addr': local_addr,
+ 'peer_addr': peer_addr,
+ 'is_ipv6': is_ipv6,
+ })
+
+ def bfd_udp_session_dump(self):
+ return self.api(self.papi.bfd_udp_session_dump, {})
+
+ def bfd_session_set_flags(self, bs_idx, admin_up_down):
+ return self.api(self.papi.bfd_session_set_flags, {
+ 'bs_index': bs_idx,
+ 'admin_up_down': admin_up_down,
+ })
+
+ def want_bfd_events(self, enable_disable=1):
+ return self.api(self.papi.want_bfd_events, {
+ 'enable_disable': enable_disable,
+ 'pid': os.getpid(),
+ })
diff --git a/test/vpp_pg_interface.py b/test/vpp_pg_interface.py
index 533c4603a2e..012f5768353 100644
--- a/test/vpp_pg_interface.py
+++ b/test/vpp_pg_interface.py
@@ -1,10 +1,10 @@
import os
import time
-from scapy.utils import wrpcap, rdpcap
+from scapy.utils import wrpcap, rdpcap, PcapReader
from vpp_interface import VppInterface
from scapy.layers.l2 import Ether, ARP
-from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA, \
+from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA,\
ICMPv6NDOptSrcLLAddr, ICMPv6NDOptDstLLAddr
from util import ppp
@@ -93,6 +93,7 @@ class VppPGInterface(VppInterface):
pass
# FIXME this should be an API, but no such exists atm
self.test.vapi.cli(self.capture_cli)
+ self._pcap_reader = None
def add_stream(self, pkts):
"""
@@ -132,6 +133,41 @@ class VppPGInterface(VppInterface):
return []
return output
+ def wait_for_packet(self, timeout):
+ """
+ Wait for next packet captured with a timeout
+
+ :param timeout: How long to wait for the packet
+
+ :returns: Captured packet if no packet arrived within timeout
+ :raises Exception: if no packet arrives within timeout
+ """
+ limit = time.time() + timeout
+ if self._pcap_reader is None:
+ self.test.logger.debug("Waiting for the capture file to appear")
+ while time.time() < limit:
+ if os.path.isfile(self.out_path):
+ break
+ time.sleep(0) # yield
+ if os.path.isfile(self.out_path):
+ self.test.logger.debug("Capture file appeared after %fs" %
+ (time.time() - (limit - timeout)))
+ self._pcap_reader = PcapReader(self.out_path)
+ else:
+ self.test.logger.debug("Timeout - capture file still nowhere")
+ raise Exception("Packet didn't arrive within timeout")
+
+ self.test.logger.debug("Waiting for packet")
+ while time.time() < limit:
+ p = self._pcap_reader.recv()
+ if p is not None:
+ self.test.logger.debug("Packet received after %fs",
+ (time.time() - (limit - timeout)))
+ return p
+ time.sleep(0) # yield
+ self.test.logger.debug("Timeout - no packets received")
+ raise Exception("Packet didn't arrive within timeout")
+
def create_arp_req(self):
"""Create ARP request applicable for this interface"""
return (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.remote_mac) /