X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Fvpp_papi_provider.py;h=90c954dc0b354e9313c43c007891ffd4fd0c2bb5;hb=32e1c010b0c34fd0984f7fc45fae648a182025c5;hp=5e80a03e2c54be1b6f1b20d7e3a64de6bfb4e4ac;hpb=0e3c0de1ed87f3cdf16e26e05e39ea6eebeafb18;p=vpp.git diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 5e80a03e2c5..90c954dc0b3 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2,11 +2,12 @@ import os import fnmatch import time from hook import Hook +from collections import deque # 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. +# 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") @@ -31,15 +32,17 @@ class VppPapiProvider(object): """VPP-api provider using vpp-papi @property hook: hook object providing before and after api/cli hooks - - """ + _zero, _negative = range(2) + def __init__(self, name, shm_prefix, test_class): self.hook = Hook("vpp-papi-provider") self.name = name self.shm_prefix = shm_prefix self.test_class = test_class + self._expect_api_retval = self._zero + self._expect_stack = [] jsonfiles = [] install_dir = os.getenv('VPP_TEST_INSTALL_PATH') @@ -48,7 +51,25 @@ class VppPapiProvider(object): jsonfiles.append(os.path.join(root, filename)) self.papi = VPP(jsonfiles) - self._events = list() + self._events = deque() + + def __enter__(self): + return self + + def expect_negative_api_retval(self): + """ Expect API failure """ + self._expect_stack.append(self._expect_api_retval) + self._expect_api_retval = self._negative + return self + + def expect_zero_api_retval(self): + """ Expect API success """ + self._expect_stack.append(self._expect_api_retval) + self._expect_api_retval = self._zero + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._expect_api_retval = self._expect_stack.pop() def register_hook(self, hook): """Replace hook registration with new hook @@ -59,19 +80,25 @@ class VppPapiProvider(object): self.hook = hook def collect_events(self): + """ Collect all events from the internal queue and clear the queue. """ e = self._events - self._events = list() + self._events = deque() return e def wait_for_event(self, timeout, name=None): + """ Wait for and return next event. """ + if self._events: + self.test_class.logger.debug("Not waiting, event already queued") limit = time.time() + timeout while time.time() < limit: if self._events: - e = self._events.pop(0) + e = self._events.popleft() if name and type(e).__name__ != name: raise Exception( "Unexpected event received: %s, expected: %s" % (type(e).__name__, name)) + self.test_class.logger.debug("Returning event %s:%s" % + (name, e)) return e time.sleep(0) # yield if name is not None: @@ -79,8 +106,10 @@ class VppPapiProvider(object): raise Exception("Event did not occur within timeout") def __call__(self, name, event): + """ Enqueue event in the internal event queue. """ # FIXME use the name instead of relying on type(e).__name__ ? # FIXME #2 if this throws, it is eaten silently, Ole? + self.test_class.logger.debug("New event: %s: %s" % (name, event)) self._events.append(event) def connect(self): @@ -93,7 +122,7 @@ class VppPapiProvider(object): self.papi.disconnect() def api(self, api_fn, api_args, expected_retval=0): - """Call API function and check it's return value + """ Call API function and check it's return value. Call the appropriate hooks before and after the API call :param api_fn: API function to call @@ -104,17 +133,28 @@ class VppPapiProvider(object): """ self.hook.before_api(api_fn.__name__, api_args) reply = api_fn(**api_args) - 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.logger.error(msg) - raise Exception(msg) + if self._expect_api_retval == self._negative: + if hasattr(reply, 'retval') and reply.retval >= 0: + msg = "API call passed unexpectedly: expected negative "\ + "return value instead of %d in %s" % \ + (reply.retval, repr(reply)) + self.test_class.logger.info(msg) + raise Exception(msg) + elif self._expect_api_retval == self._zero: + if hasattr(reply, 'retval') and reply.retval != expected_retval: + msg = "API call failed, expected zero return value instead "\ + "of %d in %s" % (expected_retval, repr(reply)) + self.test_class.logger.info(msg) + raise Exception(msg) + else: + raise Exception("Internal error, unexpected value for " + "self._expect_api_retval %s" % + self._expect_api_retval) self.hook.after_api(api_fn.__name__, api_args) return reply def cli(self, cli): - """ - Execute a CLI, calling the before/after hooks appropriately. + """ Execute a CLI, calling the before/after hooks appropriately. :param cli: CLI to execute :returns: CLI output @@ -128,8 +168,7 @@ class VppPapiProvider(object): return r.reply.decode().rstrip('\x00') def ppcli(self, cli): - """ - Helping method to print CLI command in case of info logging level. + """ Helper method to print CLI command in case of info logging level. :param cli: CLI to execute :returns: CLI output @@ -165,8 +204,7 @@ class VppPapiProvider(object): return self.api(self.papi.sw_interface_dump, args) def sw_interface_set_table(self, sw_if_index, is_ipv6, table_id): - """ - Set the IPvX Table-id for the Interface + """ Set the IPvX Table-id for the Interface :param sw_if_index: :param is_ipv6: @@ -213,6 +251,24 @@ class VppPapiProvider(object): return self.api(self.papi.sw_interface_ip6nd_ra_config, {'sw_if_index': sw_if_index}) + def ip6_sw_interface_ra_config(self, sw_if_index, + no, + suppress, + send_unicast): + return self.api(self.papi.sw_interface_ip6nd_ra_config, + {'sw_if_index': sw_if_index, + 'is_no': no, + 'suppress': suppress, + 'send_unicast': send_unicast}) + + def ip6_sw_interface_enable_disable(self, sw_if_index, enable): + """ + Enable/Disable An interface for IPv6 + """ + return self.api(self.papi.sw_interface_ip6_enable_disable, + {'sw_if_index': sw_if_index, + 'enable': enable}) + def vxlan_add_del_tunnel( self, src_addr, @@ -459,6 +515,10 @@ class VppPapiProvider(object): return self.api(self.papi.create_loopback, {'mac_address': mac}) + def delete_loopback(self, sw_if_index): + return self.api(self.papi.delete_loopback, + {'sw_if_index': sw_if_index, }) + def ip_add_del_route( self, dst_address, @@ -534,6 +594,9 @@ class VppPapiProvider(object): 'next_hop_via_label': next_hop_via_label, 'next_hop_out_label_stack': next_hop_out_label_stack}) + def ip_fib_dump(self): + return self.api(self.papi.ip_fib_dump, {}) + def ip_neighbor_add_del(self, sw_if_index, mac_address, @@ -566,14 +629,61 @@ class VppPapiProvider(object): } ) + def reset_vrf(self, + vrf_id, + is_ipv6=0, + ): + """ Reset VRF (remove all routes etc.) request. + + :param int vrf_id: ID of the FIB table / VRF to reset. + :param int is_ipv6: 1 for IPv6 neighbor, 0 for IPv4. (Default = 0) + """ + + return self.api( + self.papi.reset_vrf, + {'vrf_id': vrf_id, + 'is_ipv6': is_ipv6, + } + ) + + def reset_fib(self, + vrf_id, + is_ipv6=0, + ): + """ Reset VRF (remove all routes etc.) request. + + :param int vrf_id: ID of the FIB table / VRF to reset. + :param int is_ipv6: 1 for IPv6 neighbor, 0 for IPv4. (Default = 0) + """ + + return self.api( + self.papi.reset_fib, + {'vrf_id': vrf_id, + 'is_ipv6': is_ipv6, + } + ) + + def ip_dump(self, + is_ipv6=0, + ): + """ Return IP dump. + + :param int is_ipv6: 1 for IPv6 neighbor, 0 for IPv4. (Default = 0) + """ + + return self.api( + self.papi.ip_dump, + {'is_ipv6': is_ipv6, + } + ) + def sw_interface_span_enable_disable( self, sw_if_index_from, sw_if_index_to, state=1): """ :param sw_if_index_from: :param sw_if_index_to: - :param enable - + :param state: """ return self.api(self.papi.sw_interface_span_enable_disable, {'sw_if_index_from': sw_if_index_from, @@ -767,7 +877,8 @@ class VppPapiProvider(object): def snat_add_static_mapping( self, local_ip, - external_ip, + external_ip=0, + external_sw_if_index=0xFFFFFFFF, local_port=0, external_port=0, addr_only=1, @@ -778,6 +889,7 @@ class VppPapiProvider(object): :param local_ip: Local IP address :param external_ip: External IP address + :param external_sw_if_index: External interface instead of IP address :param local_port: Local port number (Default value = 0) :param external_port: External port number (Default value = 0) :param addr_only: 1 if address only mapping, 0 if address and port @@ -794,6 +906,7 @@ class VppPapiProvider(object): 'external_ip_address': external_ip, 'local_port': local_port, 'external_port': external_port, + 'external_sw_if_index': external_sw_if_index, 'vrf_id': vrf_id}) def snat_add_address_range( @@ -834,6 +947,47 @@ class VppPapiProvider(object): """ return self.api(self.papi.snat_static_mapping_dump, {}) + def snat_show_config(self): + """Show S-NAT config + :return: S-NAT config parameters + """ + return self.api(self.papi.snat_show_config, {}) + + def snat_add_interface_addr( + self, + sw_if_index, + is_add=1): + """Add/del S-NAT address from interface + + :param sw_if_index: Software index of the interface + :param is_add: 1 if add, 0 if delete (Default value = 1) + """ + return self.api(self.papi.snat_add_del_interface_addr, + {'is_add': is_add, 'sw_if_index': sw_if_index}) + + def snat_interface_addr_dump(self): + """Dump S-NAT addresses interfaces + :return: Dictionary of S-NAT addresses interfaces + """ + return self.api(self.papi.snat_interface_addr_dump, {}) + + def snat_ipfix( + self, + domain_id=1, + src_port=4739, + enable=1): + """Enable/disable S-NAT IPFIX logging + + :param domain_id: Observation domain ID (Default value = 1) + :param src_port: Source port number (Default value = 4739) + :param enable: 1 if enable, 0 if disable (Default value = 1) + """ + return self.api( + self.papi.snat_ipfix_enable_disable, + {'domain_id': domain_id, + 'src_port': src_port, + 'enable': enable}) + def control_ping(self): self.api(self.papi.control_ping) @@ -873,3 +1027,186 @@ class VppPapiProvider(object): 'enable_disable': enable_disable, 'pid': os.getpid(), }) + + def classify_add_del_table( + self, + is_add, + mask, + match_n_vectors=1, + table_index=0xFFFFFFFF, + nbuckets=2, + memory_size=2097152, + skip_n_vectors=0, + next_table_index=0xFFFFFFFF, + miss_next_index=0xFFFFFFFF, + current_data_flag=0, + current_data_offset=0): + """ + :param is_add: + :param mask: + :param match_n_vectors: (Default value = 1) + :param table_index: (Default value = 0xFFFFFFFF) + :param nbuckets: (Default value = 2) + :param memory_size: (Default value = 2097152) + :param skip_n_vectors: (Default value = 0) + :param next_table_index: (Default value = 0xFFFFFFFF) + :param miss_next_index: (Default value = 0xFFFFFFFF) + :param current_data_flag: (Default value = 0) + :param current_data_offset: (Default value = 0) + """ + + return self.api( + self.papi.classify_add_del_table, + {'is_add': is_add, + 'table_index': table_index, + 'nbuckets': nbuckets, + 'memory_size': memory_size, + 'skip_n_vectors': skip_n_vectors, + 'match_n_vectors': match_n_vectors, + 'next_table_index': next_table_index, + 'miss_next_index': miss_next_index, + 'current_data_flag': current_data_flag, + 'current_data_offset': current_data_offset, + 'mask': mask}) + + def classify_add_del_session( + self, + is_add, + table_index, + match, + opaque_index=0xFFFFFFFF, + hit_next_index=0xFFFFFFFF, + advance=0, + action=0, + metadata=0): + """ + :param is_add: + :param table_index: + :param match: + :param opaque_index: (Default value = 0xFFFFFFFF) + :param hit_next_index: (Default value = 0xFFFFFFFF) + :param advance: (Default value = 0) + :param action: (Default value = 0) + :param metadata: (Default value = 0) + """ + + return self.api( + self.papi.classify_add_del_session, + {'is_add': is_add, + 'table_index': table_index, + 'hit_next_index': hit_next_index, + 'opaque_index': opaque_index, + 'advance': advance, + 'action': action, + 'metadata': metadata, + 'match': match}) + + def input_acl_set_interface( + self, + is_add, + sw_if_index, + ip4_table_index=0xFFFFFFFF, + ip6_table_index=0xFFFFFFFF, + l2_table_index=0xFFFFFFFF): + """ + :param is_add: + :param sw_if_index: + :param ip4_table_index: (Default value = 0xFFFFFFFF) + :param ip6_table_index: (Default value = 0xFFFFFFFF) + :param l2_table_index: (Default value = 0xFFFFFFFF) + """ + + return self.api( + self.papi.input_acl_set_interface, + {'sw_if_index': sw_if_index, + 'ip4_table_index': ip4_table_index, + 'ip6_table_index': ip6_table_index, + 'l2_table_index': l2_table_index, + 'is_add': is_add}) + + def set_ipfix_exporter( + self, + collector_address, + src_address, + path_mtu, + template_interval, + vrf_id=0, + collector_port=4739, + udp_checksum=0): + return self.api( + self.papi.set_ipfix_exporter, + { + 'collector_address': collector_address, + 'collector_port': collector_port, + 'src_address': src_address, + 'vrf_id': vrf_id, + 'path_mtu': path_mtu, + 'template_interval': template_interval, + 'udp_checksum': udp_checksum, + }) + + def dhcp_proxy_config(self, + dhcp_server, + dhcp_src_address, + rx_table_id=0, + server_table_id=0, + is_add=1, + is_ipv6=0, + insert_circuit_id=0): + return self.api( + self.papi.dhcp_proxy_config_2, + { + 'rx_vrf_id': rx_table_id, + 'server_vrf_id': server_table_id, + 'is_ipv6': is_ipv6, + 'is_add': is_add, + 'insert_circuit_id': insert_circuit_id, + 'dhcp_server': dhcp_server, + 'dhcp_src_address': dhcp_src_address, + }) + + def dhcp_proxy_set_vss(self, + table_id, + fib_id, + oui, + is_add=1, + is_ip6=0): + return self.api( + self.papi.dhcp_proxy_set_vss, + { + 'tbl_id': table_id, + 'fib_id': fib_id, + 'is_ipv6': is_ip6, + 'is_add': is_add, + 'oui': oui, + }) + + def ip_mroute_add_del(self, + src_address, + grp_address, + grp_address_length, + e_flags, + next_hop_sw_if_index, + i_flags, + table_id=0, + create_vrf_if_needed=0, + is_add=1, + is_ipv6=0, + is_local=0): + """ + """ + return self.api( + self.papi.ip_mroute_add_del, + {'next_hop_sw_if_index': next_hop_sw_if_index, + 'entry_flags': e_flags, + 'itf_flags': i_flags, + 'create_vrf_if_needed': create_vrf_if_needed, + 'is_add': is_add, + 'is_ipv6': is_ipv6, + 'is_local': is_local, + 'grp_address_length': grp_address_length, + 'grp_address': grp_address, + 'src_address': src_address}) + + def mfib_signal_dump(self): + return self.api(self.papi.mfib_signal_dump, {})