diff options
Diffstat (limited to 'extras')
42 files changed, 1502 insertions, 2426 deletions
diff --git a/extras/deprecated/netmap/FEATURE.yaml b/extras/deprecated/netmap/FEATURE.yaml deleted file mode 100644 index e23e5c243e7..00000000000 --- a/extras/deprecated/netmap/FEATURE.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: Netmap Device -maintainer: Damjan Marion <damarion@cisco.com> -features: - - L4 checksum offload -description: "Create a netmap interface, which is a high speed user-space - interface that allows VPP to patch into a linux namespace, - a linux container, or a physical NIC without the use of DPDK." -missing: - - API dump -state: production -properties: [API, CLI, STATS, MULTITHREAD] diff --git a/extras/deprecated/netmap/cli.c b/extras/deprecated/netmap/cli.c deleted file mode 100644 index 713632947a1..00000000000 --- a/extras/deprecated/netmap/cli.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ -#include <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> - -#include <vnet/devices/netmap/net_netmap.h> -#include <vnet/devices/netmap/netmap.h> - -static clib_error_t * -netmap_create_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u8 *host_if_name = NULL; - u8 hwaddr[6]; - u8 *hw_addr_ptr = 0; - int r; - u8 is_pipe = 0; - u8 is_master = 0; - u32 sw_if_index = ~0; - clib_error_t *error = NULL; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "name %s", &host_if_name)) - ; - else - if (unformat - (line_input, "hw-addr %U", unformat_ethernet_address, hwaddr)) - hw_addr_ptr = hwaddr; - else if (unformat (line_input, "pipe")) - is_pipe = 1; - else if (unformat (line_input, "master")) - is_master = 1; - else if (unformat (line_input, "slave")) - is_master = 0; - else - { - error = clib_error_return (0, "unknown input `%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (host_if_name == NULL) - { - error = clib_error_return (0, "missing host interface name"); - goto done; - } - - r = - netmap_create_if (vm, host_if_name, hw_addr_ptr, is_pipe, is_master, - &sw_if_index); - - if (r == VNET_API_ERROR_SYSCALL_ERROR_1) - { - error = clib_error_return (0, "%s (errno %d)", strerror (errno), errno); - goto done; - } - - if (r == VNET_API_ERROR_INVALID_INTERFACE) - { - error = clib_error_return (0, "Invalid interface name"); - goto done; - } - - if (r == VNET_API_ERROR_SUBIF_ALREADY_EXISTS) - { - error = clib_error_return (0, "Interface already exists"); - goto done; - } - - vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (), - sw_if_index); - -done: - unformat_free (line_input); - - return error; -} - -/*? - * '<em>netmap</em>' is a framework for very fast packet I/O from userspace. - * '<em>VALE</em>' is an equally fast in-kernel software switch using the - * netmap API. '<em>netmap</em>' includes '<em>netmap pipes</em>', a shared - * memory packet transport channel. Together, they provide a high speed - * user-space interface that allows VPP to patch into a linux namespace, a - * linux container, or a physical NIC without the use of DPDK. Netmap/VALE - * generates the '<em>netmap.ko</em>' kernel module that needs to be loaded - * before netmap interfaces can be created. - * - https://github.com/luigirizzo/netmap - Netmap/VALE repo. - * - https://github.com/vpp-dev/netmap - VPP development package for Netmap/VALE, - * which is a snapshot of the Netmap/VALE repo with minor changes to work - * with containers and modified kernel drivers to work with NICs. - * - * Create a netmap interface that will attach to a linux interface. - * The interface must already exist. Once created, a new netmap interface - * will exist in VPP with the name '<em>netmap-<ifname></em>', where - * '<em><ifname></em>' takes one of two forms: - * - <b>ifname</b> - Linux interface to bind too. - * - <b>valeXXX:YYY</b> - - * - Where '<em>valeXXX</em>' is an arbitrary name for a VALE - * interface that must start with '<em>vale</em>' and is less - * than 16 characters. - * - Where '<em>YYY</em>' is an existing linux namespace. - * - * This command has the following optional parameters: - * - * - <b>hw-addr <mac-addr></b> - Optional ethernet address, can be in either - * X:X:X:X:X:X unix or X.X.X cisco format. - * - * - <b>pipe</b> - Optional flag to indicate that a '<em>netmap pipe</em>' - * instance should be created. - * - * - <b>master | slave</b> - Optional flag to indicate whether VPP should - * be the master or slave of the '<em>netmap pipe</em>'. Only considered - * if '<em>pipe</em>' is entered. Defaults to '<em>slave</em>' if not entered. - * - * @cliexpar - * Example of how to create a netmap interface tied to the linux - * namespace '<em>vpp1</em>': - * @cliexstart{create netmap name vale00:vpp1 hw-addr 02:FE:3F:34:15:9B pipe master} - * netmap-vale00:vpp1 - * @cliexend - * Once the netmap interface is created, enable the interface using: - * @cliexcmd{set interface state netmap-vale00:vpp1 up} -?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (netmap_create_command, static) = { - .path = "create netmap", - .short_help = "create netmap name <ifname>|valeXXX:YYY " - "[hw-addr <mac-addr>] [pipe] [master|slave]", - .function = netmap_create_command_fn, -}; -/* *INDENT-ON* */ - -static clib_error_t * -netmap_delete_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u8 *host_if_name = NULL; - clib_error_t *error = NULL; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "name %s", &host_if_name)) - ; - else - { - error = clib_error_return (0, "unknown input `%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (host_if_name == NULL) - { - error = clib_error_return (0, "missing host interface name"); - goto done; - } - - netmap_delete_if (vm, host_if_name); - -done: - unformat_free (line_input); - - return error; -} - -/*? - * Delete a netmap interface. Use the '<em><ifname></em>' to identify - * the netmap interface to be deleted. In VPP, netmap interfaces are - * named as '<em>netmap-<ifname></em>', where '<em><ifname></em>' - * takes one of two forms: - * - <b>ifname</b> - Linux interface to bind too. - * - <b>valeXXX:YYY</b> - - * - Where '<em>valeXXX</em>' is an arbitrary name for a VALE - * interface that must start with '<em>vale</em>' and is less - * than 16 characters. - * - Where '<em>YYY</em>' is an existing linux namespace. - * - * @cliexpar - * Example of how to delete a netmap interface named '<em>netmap-vale00:vpp1</em>': - * @cliexcmd{delete netmap name vale00:vpp1} -?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (netmap_delete_command, static) = { - .path = "delete netmap", - .short_help = "delete netmap name <ifname>|valeXXX:YYY", - .function = netmap_delete_command_fn, -}; -/* *INDENT-ON* */ - -clib_error_t * -netmap_cli_init (vlib_main_t * vm) -{ - return 0; -} - -VLIB_INIT_FUNCTION (netmap_cli_init); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/netmap/device.c b/extras/deprecated/netmap/device.c deleted file mode 100644 index 47407aaaa26..00000000000 --- a/extras/deprecated/netmap/device.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#include <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> - -#include <vnet/devices/netmap/net_netmap.h> -#include <vnet/devices/netmap/netmap.h> - -#define foreach_netmap_tx_func_error \ -_(NO_FREE_SLOTS, "no free tx slots") \ -_(PENDING_MSGS, "pending msgs in tx ring") - -typedef enum -{ -#define _(f,s) NETMAP_TX_ERROR_##f, - foreach_netmap_tx_func_error -#undef _ - NETMAP_TX_N_ERROR, -} netmap_tx_func_error_t; - -static char *netmap_tx_func_error_strings[] = { -#define _(n,s) s, - foreach_netmap_tx_func_error -#undef _ -}; - - -static u8 * -format_netmap_device_name (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - netmap_main_t *apm = &netmap_main; - netmap_if_t *nif = pool_elt_at_index (apm->interfaces, i); - - s = format (s, "netmap-%s", nif->host_if_name); - return s; -} - -static u8 * -format_netmap_device (u8 * s, va_list * args) -{ - u32 dev_instance = va_arg (*args, u32); - int verbose = va_arg (*args, int); - netmap_main_t *nm = &netmap_main; - netmap_if_t *nif = vec_elt_at_index (nm->interfaces, dev_instance); - u32 indent = format_get_indent (s); - - s = format (s, "NETMAP interface"); - if (verbose) - { - s = format (s, "\n%U version %d flags 0x%x" - "\n%U region %u memsize 0x%x offset 0x%x" - "\n%U tx_slots %u rx_slots %u tx_rings %u rx_rings %u", - format_white_space, indent + 2, - nif->req->nr_version, - nif->req->nr_flags, - format_white_space, indent + 2, - nif->mem_region, - nif->req->nr_memsize, - nif->req->nr_offset, - format_white_space, indent + 2, - nif->req->nr_tx_slots, - nif->req->nr_rx_slots, - nif->req->nr_tx_rings, nif->req->nr_rx_rings); - } - return s; -} - -static u8 * -format_netmap_tx_trace (u8 * s, va_list * args) -{ - s = format (s, "Unimplemented..."); - return s; -} - -VNET_DEVICE_CLASS_TX_FN (netmap_device_class) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - netmap_main_t *nm = &netmap_main; - u32 *buffers = vlib_frame_vector_args (frame); - u32 n_left = frame->n_vectors; - f64 const time_constant = 1e3; - vnet_interface_output_runtime_t *rd = (void *) node->runtime_data; - netmap_if_t *nif = pool_elt_at_index (nm->interfaces, rd->dev_instance); - int cur_ring; - - clib_spinlock_lock_if_init (&nif->lockp); - - cur_ring = nif->first_tx_ring; - - while (n_left && cur_ring <= nif->last_tx_ring) - { - struct netmap_ring *ring = NETMAP_TXRING (nif->nifp, cur_ring); - int n_free_slots = nm_ring_space (ring); - uint cur = ring->cur; - - if (nm_tx_pending (ring)) - { - if (ioctl (nif->fd, NIOCTXSYNC, NULL) < 0) - clib_unix_warning ("NIOCTXSYNC"); - clib_cpu_time_wait (time_constant); - - if (nm_tx_pending (ring) && !n_free_slots) - { - cur_ring++; - continue; - } - } - - while (n_left && n_free_slots) - { - vlib_buffer_t *b0 = 0; - u32 bi = buffers[0]; - u32 len; - u32 offset = 0; - buffers++; - - struct netmap_slot *slot = &ring->slot[cur]; - - do - { - b0 = vlib_get_buffer (vm, bi); - len = b0->current_length; - /* memcpy */ - clib_memcpy_fast ((u8 *) NETMAP_BUF (ring, slot->buf_idx) + - offset, vlib_buffer_get_current (b0), len); - offset += len; - } - while ((bi = b0->next_buffer)); - - slot->len = offset; - cur = (cur + 1) % ring->num_slots; - n_free_slots--; - n_left--; - } - CLIB_MEMORY_BARRIER (); - ring->head = ring->cur = cur; - } - - if (n_left < frame->n_vectors) - ioctl (nif->fd, NIOCTXSYNC, NULL); - - clib_spinlock_unlock_if_init (&nif->lockp); - - if (n_left) - vlib_error_count (vm, node->node_index, - (n_left == - frame->n_vectors ? NETMAP_TX_ERROR_PENDING_MSGS : - NETMAP_TX_ERROR_NO_FREE_SLOTS), n_left); - - vlib_buffer_free (vm, vlib_frame_vector_args (frame), frame->n_vectors); - return frame->n_vectors; -} - -static void -netmap_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index, - u32 node_index) -{ - netmap_main_t *apm = &netmap_main; - vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); - netmap_if_t *nif = pool_elt_at_index (apm->interfaces, hw->dev_instance); - - /* Shut off redirection */ - if (node_index == ~0) - { - nif->per_interface_next_index = node_index; - return; - } - - nif->per_interface_next_index = - vlib_node_add_next (vlib_get_main (), netmap_input_node.index, - node_index); -} - -static void -netmap_clear_hw_interface_counters (u32 instance) -{ - /* Nothing for now */ -} - -static clib_error_t * -netmap_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) -{ - netmap_main_t *apm = &netmap_main; - vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); - netmap_if_t *nif = pool_elt_at_index (apm->interfaces, hw->dev_instance); - u32 hw_flags; - - nif->is_admin_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0; - - if (nif->is_admin_up) - hw_flags = VNET_HW_INTERFACE_FLAG_LINK_UP; - else - hw_flags = 0; - - vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); - - return 0; -} - -static clib_error_t * -netmap_subif_add_del_function (vnet_main_t * vnm, - u32 hw_if_index, - struct vnet_sw_interface_t *st, int is_add) -{ - /* Nothing for now */ - return 0; -} - -/* *INDENT-OFF* */ -VNET_DEVICE_CLASS (netmap_device_class) = { - .name = "netmap", - .format_device_name = format_netmap_device_name, - .format_device = format_netmap_device, - .format_tx_trace = format_netmap_tx_trace, - .tx_function_n_errors = NETMAP_TX_N_ERROR, - .tx_function_error_strings = netmap_tx_func_error_strings, - .rx_redirect_to_node = netmap_set_interface_next_node, - .clear_counters = netmap_clear_hw_interface_counters, - .admin_up_down_function = netmap_interface_admin_up_down, - .subif_add_del_function = netmap_subif_add_del_function, -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/netmap/dir.dox b/extras/deprecated/netmap/dir.dox deleted file mode 100644 index 7ddbf947c29..00000000000 --- a/extras/deprecated/netmap/dir.dox +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Doxygen directory documentation */ - -/** -@dir -@brief netmap Interface Implementation. - -This directory contains the source code for the netmap driver. - -*/ -/*? %%clicmd:group_label netmap %% ?*/ -/*? %%syscfg:group_label netmap %% ?*/ diff --git a/extras/deprecated/netmap/net_netmap.h b/extras/deprecated/netmap/net_netmap.h deleted file mode 100644 index fd4253b7c0c..00000000000 --- a/extras/deprecated/netmap/net_netmap.h +++ /dev/null @@ -1,650 +0,0 @@ -/* - * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``S IS''AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * $FreeBSD: head/sys/net/netmap.h 251139 2013-05-30 14:07:14Z luigi $ - * - * Definitions of constants and the structures used by the netmap - * framework, for the part visible to both kernel and userspace. - * Detailed info on netmap is available with "man netmap" or at - * - * http://info.iet.unipi.it/~luigi/netmap/ - * - * This API is also used to communicate with the VALE software switch - */ - -#ifndef _NET_NETMAP_H_ -#define _NET_NETMAP_H_ - -#define NETMAP_API 11 /* current API version */ - -#define NETMAP_MIN_API 11 /* min and max versions accepted */ -#define NETMAP_MAX_API 15 -/* - * Some fields should be cache-aligned to reduce contention. - * The alignment is architecture and OS dependent, but rather than - * digging into OS headers to find the exact value we use an estimate - * that should cover most architectures. - */ -#define NM_CACHE_ALIGN 128 - -/* - * --- Netmap data structures --- - * - * The userspace data structures used by netmap are shown below. - * They are allocated by the kernel and mmap()ed by userspace threads. - * Pointers are implemented as memory offsets or indexes, - * so that they can be easily dereferenced in kernel and userspace. - - KERNEL (opaque, obviously) - - ==================================================================== - | - USERSPACE | struct netmap_ring - +---->+---------------+ - / | head,cur,tail | - struct netmap_if (nifp, 1 per fd) / | buf_ofs | - +---------------+ / | other fields | - | ni_tx_rings | / +===============+ - | ni_rx_rings | / | buf_idx, len | slot[0] - | | / | flags, ptr | - | | / +---------------+ - +===============+ / | buf_idx, len | slot[1] - | txring_ofs[0] | (rel.to nifp)--' | flags, ptr | - | txring_ofs[1] | +---------------+ - (tx+1 entries) (num_slots entries) - | txring_ofs[t] | | buf_idx, len | slot[n-1] - +---------------+ | flags, ptr | - | rxring_ofs[0] | +---------------+ - | rxring_ofs[1] | - (rx+1 entries) - | rxring_ofs[r] | - +---------------+ - - * For each "interface" (NIC, host stack, PIPE, VALE switch port) bound to - * a file descriptor, the mmap()ed region contains a (logically readonly) - * struct netmap_if pointing to struct netmap_ring's. - * - * There is one netmap_ring per physical NIC ring, plus one tx/rx ring - * pair attached to the host stack (this pair is unused for non-NIC ports). - * - * All physical/host stack ports share the same memory region, - * so that zero-copy can be implemented between them. - * VALE switch ports instead have separate memory regions. - * - * The netmap_ring is the userspace-visible replica of the NIC ring. - * Each slot has the index of a buffer (MTU-sized and residing in the - * mmapped region), its length and some flags. An extra 64-bit pointer - * is provided for user-supplied buffers in the tx path. - * - * In user space, the buffer address is computed as - * (char *)ring + buf_ofs + index * NETMAP_BUF_SIZE - * - * Added in NETMAP_API 11: - * - * + NIOCREGIF can request the allocation of extra spare buffers from - * the same memory pool. The desired number of buffers must be in - * nr_arg3. The ioctl may return fewer buffers, depending on memory - * availability. nr_arg3 will return the actual value, and, once - * mapped, nifp->ni_bufs_head will be the index of the first buffer. - * - * The buffers are linked to each other using the first uint32_t - * as the index. On close, ni_bufs_head must point to the list of - * buffers to be released. - * - * + NIOCREGIF can request space for extra rings (and buffers) - * allocated in the same memory space. The number of extra rings - * is in nr_arg1, and is advisory. This is a no-op on NICs where - * the size of the memory space is fixed. - * - * + NIOCREGIF can attach to PIPE rings sharing the same memory - * space with a parent device. The ifname indicates the parent device, - * which must already exist. Flags in nr_flags indicate if we want to - * bind the master or slave side, the index (from nr_ringid) - * is just a cookie and does not need to be sequential. - * - * + NIOCREGIF can also attach to 'monitor' rings that replicate - * the content of specific rings, also from the same memory space. - * - * Extra flags in nr_flags support the above functions. - * Application libraries may use the following naming scheme: - * netmap:foo all NIC ring pairs - * netmap:foo^ only host ring pair - * netmap:foo+ all NIC ring + host ring pairs - * netmap:foo-k the k-th NIC ring pair - * netmap:foo{k PIPE ring pair k, master side - * netmap:foo}k PIPE ring pair k, slave side - */ - -/* - * struct netmap_slot is a buffer descriptor - */ -struct netmap_slot { - uint32_t buf_idx; /* buffer index */ - uint16_t len; /* length for this slot */ - uint16_t flags; /* buf changed, etc. */ - uint64_t ptr; /* pointer for indirect buffers */ -}; - -/* - * The following flags control how the slot is used - */ - -#define NS_BUF_CHANGED 0x0001 /* buf_idx changed */ - /* - * must be set whenever buf_idx is changed (as it might be - * necessary to recompute the physical address and mapping) - * - * It is also set by the kernel whenever the buf_idx is - * changed internally (e.g., by pipes). Applications may - * use this information to know when they can reuse the - * contents of previously prepared buffers. - */ - -#define NS_REPORT 0x0002 /* ask the hardware to report results */ - /* - * Request notification when slot is used by the hardware. - * Normally transmit completions are handled lazily and - * may be unreported. This flag lets us know when a slot - * has been sent (e.g. to terminate the sender). - */ - -#define NS_FORWARD 0x0004 /* pass packet 'forward' */ - /* - * (Only for physical ports, rx rings with NR_FORWARD set). - * Slot released to the kernel (i.e. before ring->head) with - * this flag set are passed to the peer ring (host/NIC), - * thus restoring the host-NIC connection for these slots. - * This supports efficient traffic monitoring or firewalling. - */ - -#define NS_NO_LEARN 0x0008 /* disable bridge learning */ - /* - * On a VALE switch, do not 'learn' the source port for - * this buffer. - */ - -#define NS_INDIRECT 0x0010 /* userspace buffer */ - /* - * (VALE tx rings only) data is in a userspace buffer, - * whose address is in the 'ptr' field in the slot. - */ - -#define NS_MOREFRAG 0x0020 /* packet has more fragments */ - /* - * (VALE ports only) - * Set on all but the last slot of a multi-segment packet. - * The 'len' field refers to the individual fragment. - */ - -#define NS_PORT_SHIFT 8 -#define NS_PORT_MASK (0xff << NS_PORT_SHIFT) - /* - * The high 8 bits of the flag, if not zero, indicate the - * destination port for the VALE switch, overriding - * the lookup table. - */ - -#define NS_RFRAGS(_slot) ( ((_slot)->flags >> 8) & 0xff) - /* - * (VALE rx rings only) the high 8 bits - * are the number of fragments. - */ - - -/* - * struct netmap_ring - * - * Netmap representation of a TX or RX ring (also known as "queue"). - * This is a queue implemented as a fixed-size circular array. - * At the software level the important fields are: head, cur, tail. - * - * In TX rings: - * - * head first slot available for transmission. - * cur wakeup point. select() and poll() will unblock - * when 'tail' moves past 'cur' - * tail (readonly) first slot reserved to the kernel - * - * [head .. tail-1] can be used for new packets to send; - * 'head' and 'cur' must be incremented as slots are filled - * with new packets to be sent; - * 'cur' can be moved further ahead if we need more space - * for new transmissions. XXX todo (2014-03-12) - * - * In RX rings: - * - * head first valid received packet - * cur wakeup point. select() and poll() will unblock - * when 'tail' moves past 'cur' - * tail (readonly) first slot reserved to the kernel - * - * [head .. tail-1] contain received packets; - * 'head' and 'cur' must be incremented as slots are consumed - * and can be returned to the kernel; - * 'cur' can be moved further ahead if we want to wait for - * new packets without returning the previous ones. - * - * DATA OWNERSHIP/LOCKING: - * The netmap_ring, and all slots and buffers in the range - * [head .. tail-1] are owned by the user program; - * the kernel only accesses them during a netmap system call - * and in the user thread context. - * - * Other slots and buffers are reserved for use by the kernel - */ -struct netmap_ring { - /* - * buf_ofs is meant to be used through macros. - * It contains the offset of the buffer region from this - * descriptor. - */ - const int64_t buf_ofs; - const uint32_t num_slots; /* number of slots in the ring. */ - const uint32_t nr_buf_size; - const uint16_t ringid; - const uint16_t dir; /* 0: tx, 1: rx */ - - uint32_t head; /* (u) first user slot */ - uint32_t cur; /* (u) wakeup point */ - uint32_t tail; /* (k) first kernel slot */ - - uint32_t flags; - - struct timeval ts; /* (k) time of last *sync() */ - - /* opaque room for a mutex or similar object */ -#if !defined(_WIN32) || defined(__CYGWIN__) - uint8_t __attribute__((__aligned__(NM_CACHE_ALIGN))) sem[128]; -#else - uint8_t __declspec(align(NM_CACHE_ALIGN)) sem[128]; -#endif - - /* the slots follow. This struct has variable size */ - struct netmap_slot slot[0]; /* array of slots. */ -}; - - -/* - * RING FLAGS - */ -#define NR_TIMESTAMP 0x0002 /* set timestamp on *sync() */ - /* - * updates the 'ts' field on each netmap syscall. This saves - * saves a separate gettimeofday(), and is not much worse than - * software timestamps generated in the interrupt handler. - */ - -#define NR_FORWARD 0x0004 /* enable NS_FORWARD for ring */ - /* - * Enables the NS_FORWARD slot flag for the ring. - */ - - -/* - * Netmap representation of an interface and its queue(s). - * This is initialized by the kernel when binding a file - * descriptor to a port, and should be considered as readonly - * by user programs. The kernel never uses it. - * - * There is one netmap_if for each file descriptor on which we want - * to select/poll. - * select/poll operates on one or all pairs depending on the value of - * nmr_queueid passed on the ioctl. - */ -struct netmap_if { - char ni_name[IFNAMSIZ]; /* name of the interface. */ - const uint32_t ni_version; /* API version, currently unused */ - const uint32_t ni_flags; /* properties */ -#define NI_PRIV_MEM 0x1 /* private memory region */ - - /* - * The number of packet rings available in netmap mode. - * Physical NICs can have different numbers of tx and rx rings. - * Physical NICs also have a 'host' ring pair. - * Additionally, clients can request additional ring pairs to - * be used for internal communication. - */ - const uint32_t ni_tx_rings; /* number of HW tx rings */ - const uint32_t ni_rx_rings; /* number of HW rx rings */ - - uint32_t ni_bufs_head; /* head index for extra bufs */ - uint32_t ni_spare1[5]; - /* - * The following array contains the offset of each netmap ring - * from this structure, in the following order: - * NIC tx rings (ni_tx_rings); host tx ring (1); extra tx rings; - * NIC rx rings (ni_rx_rings); host tx ring (1); extra rx rings. - * - * The area is filled up by the kernel on NIOCREGIF, - * and then only read by userspace code. - */ - const ssize_t ring_ofs[0]; -}; - - -#ifndef NIOCREGIF -/* - * ioctl names and related fields - * - * NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues, - * whose identity is set in NIOCREGIF through nr_ringid. - * These are non blocking and take no argument. - * - * NIOCGINFO takes a struct ifreq, the interface name is the input, - * the outputs are number of queues and number of descriptor - * for each queue (useful to set number of threads etc.). - * The info returned is only advisory and may change before - * the interface is bound to a file descriptor. - * - * NIOCREGIF takes an interface name within a struct nmre, - * and activates netmap mode on the interface (if possible). - * - * The argument to NIOCGINFO/NIOCREGIF overlays struct ifreq so we - * can pass it down to other NIC-related ioctls. - * - * The actual argument (struct nmreq) has a number of options to request - * different functions. - * The following are used in NIOCREGIF when nr_cmd == 0: - * - * nr_name (in) - * The name of the port (em0, valeXXX:YYY, etc.) - * limited to IFNAMSIZ for backward compatibility. - * - * nr_version (in/out) - * Must match NETMAP_API as used in the kernel, error otherwise. - * Always returns the desired value on output. - * - * nr_tx_slots, nr_tx_slots, nr_tx_rings, nr_rx_rings (in/out) - * On input, non-zero values may be used to reconfigure the port - * according to the requested values, but this is not guaranteed. - * On output the actual values in use are reported. - * - * nr_ringid (in) - * Indicates how rings should be bound to the file descriptors. - * If nr_flags != 0, then the low bits (in NETMAP_RING_MASK) - * are used to indicate the ring number, and nr_flags specifies - * the actual rings to bind. NETMAP_NO_TX_POLL is unaffected. - * - * NOTE: THE FOLLOWING (nr_flags == 0) IS DEPRECATED: - * If nr_flags == 0, NETMAP_HW_RING and NETMAP_SW_RING control - * the binding as follows: - * 0 (default) binds all physical rings - * NETMAP_HW_RING | ring number binds a single ring pair - * NETMAP_SW_RING binds only the host tx/rx rings - * - * NETMAP_NO_TX_POLL can be OR-ed to make select()/poll() push - * packets on tx rings only if POLLOUT is set. - * The default is to push any pending packet. - * - * NETMAP_DO_RX_POLL can be OR-ed to make select()/poll() release - * packets on rx rings also when POLLIN is NOT set. - * The default is to touch the rx ring only with POLLIN. - * Note that this is the opposite of TX because it - * reflects the common usage. - * - * NOTE: NETMAP_PRIV_MEM IS DEPRECATED, use nr_arg2 instead. - * NETMAP_PRIV_MEM is set on return for ports that do not use - * the global memory allocator. - * This information is not significant and applications - * should look at the region id in nr_arg2 - * - * nr_flags is the recommended mode to indicate which rings should - * be bound to a file descriptor. Values are NR_REG_* - * - * nr_arg1 (in) The number of extra rings to be reserved. - * Especially when allocating a VALE port the system only - * allocates the amount of memory needed for the port. - * If more shared memory rings are desired (e.g. for pipes), - * the first invocation for the same basename/allocator - * should specify a suitable number. Memory cannot be - * extended after the first allocation without closing - * all ports on the same region. - * - * nr_arg2 (in/out) The identity of the memory region used. - * On input, 0 means the system decides autonomously, - * other values may try to select a specific region. - * On return the actual value is reported. - * Region '1' is the global allocator, normally shared - * by all interfaces. Other values are private regions. - * If two ports the same region zero-copy is possible. - * - * nr_arg3 (in/out) number of extra buffers to be allocated. - * - * - * - * nr_cmd (in) if non-zero indicates a special command: - * NETMAP_BDG_ATTACH and nr_name = vale*:ifname - * attaches the NIC to the switch; nr_ringid specifies - * which rings to use. Used by vale-ctl -a ... - * nr_arg1 = NETMAP_BDG_HOST also attaches the host port - * as in vale-ctl -h ... - * - * NETMAP_BDG_DETACH and nr_name = vale*:ifname - * disconnects a previously attached NIC. - * Used by vale-ctl -d ... - * - * NETMAP_BDG_LIST - * list the configuration of VALE switches. - * - * NETMAP_BDG_VNET_HDR - * Set the virtio-net header length used by the client - * of a VALE switch port. - * - * NETMAP_BDG_NEWIF - * create a persistent VALE port with name nr_name. - * Used by vale-ctl -n ... - * - * NETMAP_BDG_DELIF - * delete a persistent VALE port. Used by vale-ctl -d ... - * - * nr_arg1, nr_arg2, nr_arg3 (in/out) command specific - * - * - * - */ - - -/* - * struct nmreq overlays a struct ifreq (just the name) - */ -struct nmreq { - char nr_name[IFNAMSIZ]; - uint32_t nr_version; /* API version */ - uint32_t nr_offset; /* nifp offset in the shared region */ - uint32_t nr_memsize; /* size of the shared region */ - uint32_t nr_tx_slots; /* slots in tx rings */ - uint32_t nr_rx_slots; /* slots in rx rings */ - uint16_t nr_tx_rings; /* number of tx rings */ - uint16_t nr_rx_rings; /* number of rx rings */ - - uint16_t nr_ringid; /* ring(s) we care about */ -#define NETMAP_HW_RING 0x4000 /* single NIC ring pair */ -#define NETMAP_SW_RING 0x2000 /* only host ring pair */ - -#define NETMAP_RING_MASK 0x0fff /* the ring number */ - -#define NETMAP_NO_TX_POLL 0x1000 /* no automatic txsync on poll */ - -#define NETMAP_DO_RX_POLL 0x8000 /* DO automatic rxsync on poll */ - - uint16_t nr_cmd; -#define NETMAP_BDG_ATTACH 1 /* attach the NIC */ -#define NETMAP_BDG_DETACH 2 /* detach the NIC */ -#define NETMAP_BDG_REGOPS 3 /* register bridge callbacks */ -#define NETMAP_BDG_LIST 4 /* get bridge's info */ -#define NETMAP_BDG_VNET_HDR 5 /* set the port virtio-net-hdr length */ -#define NETMAP_BDG_OFFSET NETMAP_BDG_VNET_HDR /* deprecated alias */ -#define NETMAP_BDG_NEWIF 6 /* create a virtual port */ -#define NETMAP_BDG_DELIF 7 /* destroy a virtual port */ -#define NETMAP_PT_HOST_CREATE 8 /* create ptnetmap kthreads */ -#define NETMAP_PT_HOST_DELETE 9 /* delete ptnetmap kthreads */ -#define NETMAP_BDG_POLLING_ON 10 /* delete polling kthread */ -#define NETMAP_BDG_POLLING_OFF 11 /* delete polling kthread */ -#define NETMAP_VNET_HDR_GET 12 /* get the port virtio-net-hdr length */ - uint16_t nr_arg1; /* reserve extra rings in NIOCREGIF */ -#define NETMAP_BDG_HOST 1 /* attach the host stack on ATTACH */ - - uint16_t nr_arg2; - uint32_t nr_arg3; /* req. extra buffers in NIOCREGIF */ - uint32_t nr_flags; - /* various modes, extends nr_ringid */ - uint32_t spare2[1]; -}; - -#define NR_REG_MASK 0xf /* values for nr_flags */ -enum { NR_REG_DEFAULT = 0, /* backward compat, should not be used. */ - NR_REG_ALL_NIC = 1, - NR_REG_SW = 2, - NR_REG_NIC_SW = 3, - NR_REG_ONE_NIC = 4, - NR_REG_PIPE_MASTER = 5, - NR_REG_PIPE_SLAVE = 6, -}; -/* monitor uses the NR_REG to select the rings to monitor */ -#define NR_MONITOR_TX 0x100 -#define NR_MONITOR_RX 0x200 -#define NR_ZCOPY_MON 0x400 -/* request exclusive access to the selected rings */ -#define NR_EXCLUSIVE 0x800 -/* request ptnetmap host support */ -#define NR_PASSTHROUGH_HOST NR_PTNETMAP_HOST /* deprecated */ -#define NR_PTNETMAP_HOST 0x1000 -#define NR_RX_RINGS_ONLY 0x2000 -#define NR_TX_RINGS_ONLY 0x4000 -/* Applications set this flag if they are able to deal with virtio-net headers, - * that is send/receive frames that start with a virtio-net header. - * If not set, NIOCREGIF will fail with netmap ports that require applications - * to use those headers. If the flag is set, the application can use the - * NETMAP_VNET_HDR_GET command to figure out the header length. */ -#define NR_ACCEPT_VNET_HDR 0x8000 - - -/* - * Windows does not have _IOWR(). _IO(), _IOW() and _IOR() are defined - * in ws2def.h but not sure if they are in the form we need. - * XXX so we redefine them - * in a convenient way to use for DeviceIoControl signatures - */ -#ifdef _WIN32 -#undef _IO // ws2def.h -#define _WIN_NM_IOCTL_TYPE 40000 -#define _IO(_c, _n) CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \ - METHOD_BUFFERED, FILE_ANY_ACCESS ) -#define _IO_direct(_c, _n) CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \ - METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) - -#define _IOWR(_c, _n, _s) _IO(_c, _n) - -/* We havesome internal sysctl in addition to the externally visible ones */ -#define NETMAP_MMAP _IO_direct('i', 160) // note METHOD_OUT_DIRECT -#define NETMAP_POLL _IO('i', 162) - -/* and also two setsockopt for sysctl emulation */ -#define NETMAP_SETSOCKOPT _IO('i', 140) -#define NETMAP_GETSOCKOPT _IO('i', 141) - - -//These linknames are for the Netmap Core Driver -#define NETMAP_NT_DEVICE_NAME L"\\Device\\NETMAP" -#define NETMAP_DOS_DEVICE_NAME L"\\DosDevices\\netmap" - -//Definition of a structure used to pass a virtual address within an IOCTL -typedef struct _MEMORY_ENTRY { - PVOID pUsermodeVirtualAddress; -} MEMORY_ENTRY, *PMEMORY_ENTRY; - -typedef struct _POLL_REQUEST_DATA { - int events; - int timeout; - int revents; -} POLL_REQUEST_DATA; - -#endif /* _WIN32 */ - -/* - * FreeBSD uses the size value embedded in the _IOWR to determine - * how much to copy in/out. So we need it to match the actual - * data structure we pass. We put some spares in the structure - * to ease compatibility with other versions - */ -#define NIOCGINFO _IOWR('i', 145, struct nmreq) /* return IF info */ -#define NIOCREGIF _IOWR('i', 146, struct nmreq) /* interface register */ -#define NIOCTXSYNC _IO('i', 148) /* sync tx queues */ -#define NIOCRXSYNC _IO('i', 149) /* sync rx queues */ -#define NIOCCONFIG _IOWR('i',150, struct nm_ifreq) /* for ext. modules */ -#endif /* !NIOCREGIF */ - - -/* - * Helper functions for kernel and userspace - */ - -/* - * check if space is available in the ring. - */ -static inline int -nm_ring_empty(struct netmap_ring *ring) -{ - return (ring->cur == ring->tail); -} - -/* - * Opaque structure that is passed to an external kernel - * module via ioctl(fd, NIOCCONFIG, req) for a user-owned - * bridge port (at this point ephemeral VALE interface). - */ -#define NM_IFRDATA_LEN 256 -struct nm_ifreq { - char nifr_name[IFNAMSIZ]; - char data[NM_IFRDATA_LEN]; -}; - -/* - * netmap kernel thread configuration - */ -/* bhyve/vmm.ko MSIX parameters for IOCTL */ -struct ptn_vmm_ioctl_msix { - uint64_t msg; - uint64_t addr; -}; - -/* IOCTL parameters */ -struct nm_kth_ioctl { - u_long com; - /* TODO: use union */ - union { - struct ptn_vmm_ioctl_msix msix; - } data; -}; - -/* Configuration of a ptnetmap ring */ -struct ptnet_ring_cfg { - uint64_t ioeventfd; /* eventfd in linux, tsleep() parameter in FreeBSD */ - uint64_t irqfd; /* eventfd in linux, ioctl fd in FreeBSD */ - struct nm_kth_ioctl ioctl; /* ioctl parameter to send irq (only used in bhyve/FreeBSD) */ -}; -#endif /* _NET_NETMAP_H_ */ diff --git a/extras/deprecated/netmap/netmap.api b/extras/deprecated/netmap/netmap.api deleted file mode 100644 index a14753cad9c..00000000000 --- a/extras/deprecated/netmap/netmap.api +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2015-2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -option version = "1.0.0"; - -/** \brief Create netmap - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param netmap_if_name - interface name - @param hw_addr - interface MAC - @param use_random_hw_addr - use random generated MAC - @param is_pipe - is pipe - @param is_master - 0=slave, 1=master -*/ -autoreply define netmap_create -{ - u32 client_index; - u32 context; - - u8 netmap_if_name[64]; - u8 hw_addr[6]; - u8 use_random_hw_addr; - u8 is_pipe; - u8 is_master; -}; - -/** \brief Delete netmap - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param netmap_if_name - interface name -*/ -autoreply define netmap_delete -{ - u32 client_index; - u32 context; - - u8 netmap_if_name[64]; -}; - -/* - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/netmap/netmap.c b/extras/deprecated/netmap/netmap.c deleted file mode 100644 index 7cab6bb0289..00000000000 --- a/extras/deprecated/netmap/netmap.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#include <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <fcntl.h> -#include <vnet/devices/netmap/net_netmap.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> -#include <vnet/devices/netmap/netmap.h> - -netmap_main_t netmap_main; - -static u32 -netmap_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, - u32 flags) -{ - /* nothing for now */ - return 0; -} - -static clib_error_t * -netmap_fd_read_ready (clib_file_t * uf) -{ - vlib_main_t *vm = vlib_get_main (); - netmap_main_t *nm = &netmap_main; - u32 idx = uf->private_data; - - nm->pending_input_bitmap = - clib_bitmap_set (nm->pending_input_bitmap, idx, 1); - - /* Schedule the rx node */ - vlib_node_set_interrupt_pending (vm, netmap_input_node.index); - - return 0; -} - -static void -close_netmap_if (netmap_main_t * nm, netmap_if_t * nif) -{ - if (nif->clib_file_index != ~0) - { - clib_file_del (&file_main, file_main.file_pool + nif->clib_file_index); - nif->clib_file_index = ~0; - } - else if (nif->fd > -1) - close (nif->fd); - - if (nif->mem_region) - { - netmap_mem_region_t *reg = &nm->mem_regions[nif->mem_region]; - if (--reg->refcnt == 0) - { - munmap (reg->mem, reg->region_size); - reg->region_size = 0; - } - } - - - mhash_unset (&nm->if_index_by_host_if_name, nif->host_if_name, - &nif->if_index); - vec_free (nif->host_if_name); - vec_free (nif->req); - - clib_memset (nif, 0, sizeof (*nif)); - pool_put (nm->interfaces, nif); -} - -int -netmap_worker_thread_enable () -{ - /* if worker threads are enabled, switch to polling mode */ - foreach_vlib_main (( - { - vlib_node_set_state (this_vlib_main, - netmap_input_node.index, - VLIB_NODE_STATE_POLLING); - })); - - return 0; -} - -int -netmap_worker_thread_disable () -{ - foreach_vlib_main (( - { - vlib_node_set_state (this_vlib_main, - netmap_input_node.index, - VLIB_NODE_STATE_INTERRUPT); - })); - - return 0; -} - -int -netmap_create_if (vlib_main_t * vm, u8 * if_name, u8 * hw_addr_set, - u8 is_pipe, u8 is_master, u32 * sw_if_index) -{ - netmap_main_t *nm = &netmap_main; - int ret = 0; - netmap_if_t *nif = 0; - u8 hw_addr[6]; - clib_error_t *error = 0; - vnet_sw_interface_t *sw; - vnet_main_t *vnm = vnet_get_main (); - uword *p; - struct nmreq *req = 0; - netmap_mem_region_t *reg; - vlib_thread_main_t *tm = vlib_get_thread_main (); - int fd; - - p = mhash_get (&nm->if_index_by_host_if_name, if_name); - if (p) - return VNET_API_ERROR_SUBIF_ALREADY_EXISTS; - - fd = open ("/dev/netmap", O_RDWR); - if (fd < 0) - return VNET_API_ERROR_SUBIF_ALREADY_EXISTS; - - pool_get (nm->interfaces, nif); - nif->if_index = nif - nm->interfaces; - nif->fd = fd; - nif->clib_file_index = ~0; - - vec_validate (req, 0); - nif->req = req; - req->nr_version = NETMAP_API; - req->nr_flags = NR_REG_ALL_NIC; - - if (is_pipe) - req->nr_flags = is_master ? NR_REG_PIPE_MASTER : NR_REG_PIPE_SLAVE; - else - req->nr_flags = NR_REG_ALL_NIC; - - req->nr_flags |= NR_ACCEPT_VNET_HDR; - snprintf (req->nr_name, IFNAMSIZ, "%s", if_name); - req->nr_name[IFNAMSIZ - 1] = 0; - - if (ioctl (nif->fd, NIOCREGIF, req)) - { - ret = VNET_API_ERROR_NOT_CONNECTED; - goto error; - } - - nif->mem_region = req->nr_arg2; - vec_validate (nm->mem_regions, nif->mem_region); - reg = &nm->mem_regions[nif->mem_region]; - if (reg->region_size == 0) - { - reg->mem = mmap (NULL, req->nr_memsize, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - clib_warning ("mem %p", reg->mem); - if (reg->mem == MAP_FAILED) - { - ret = VNET_API_ERROR_NOT_CONNECTED; - goto error; - } - reg->region_size = req->nr_memsize; - } - reg->refcnt++; - - nif->nifp = NETMAP_IF (reg->mem, req->nr_offset); - nif->first_rx_ring = 0; - nif->last_rx_ring = 0; - nif->first_tx_ring = 0; - nif->last_tx_ring = 0; - nif->host_if_name = if_name; - nif->per_interface_next_index = ~0; - - if (tm->n_vlib_mains > 1) - clib_spinlock_init (&nif->lockp); - - { - clib_file_t template = { 0 }; - template.read_function = netmap_fd_read_ready; - template.file_descriptor = nif->fd; - template.private_data = nif->if_index; - template.description = format (0, "netmap socket"); - nif->clib_file_index = clib_file_add (&file_main, &template); - } - - /*use configured or generate random MAC address */ - if (hw_addr_set) - memcpy (hw_addr, hw_addr_set, 6); - else - { - f64 now = vlib_time_now (vm); - u32 rnd; - rnd = (u32) (now * 1e6); - rnd = random_u32 (&rnd); - - memcpy (hw_addr + 2, &rnd, sizeof (rnd)); - hw_addr[0] = 2; - hw_addr[1] = 0xfe; - } - - error = ethernet_register_interface (vnm, netmap_device_class.index, - nif->if_index, hw_addr, - &nif->hw_if_index, - netmap_eth_flag_change); - - if (error) - { - clib_error_report (error); - ret = VNET_API_ERROR_SYSCALL_ERROR_1; - goto error; - } - - sw = vnet_get_hw_sw_interface (vnm, nif->hw_if_index); - nif->sw_if_index = sw->sw_if_index; - - mhash_set_mem (&nm->if_index_by_host_if_name, if_name, &nif->if_index, 0); - - if (sw_if_index) - *sw_if_index = nif->sw_if_index; - - if (tm->n_vlib_mains > 1 && pool_elts (nm->interfaces) == 1) - netmap_worker_thread_enable (); - - return 0; - -error: - close_netmap_if (nm, nif); - return ret; -} - -int -netmap_delete_if (vlib_main_t * vm, u8 * host_if_name) -{ - vnet_main_t *vnm = vnet_get_main (); - netmap_main_t *nm = &netmap_main; - netmap_if_t *nif; - uword *p; - vlib_thread_main_t *tm = vlib_get_thread_main (); - - p = mhash_get (&nm->if_index_by_host_if_name, host_if_name); - if (p == NULL) - { - clib_warning ("Host interface %s does not exist", host_if_name); - return VNET_API_ERROR_SYSCALL_ERROR_1; - } - nif = pool_elt_at_index (nm->interfaces, p[0]); - - /* bring down the interface */ - vnet_hw_interface_set_flags (vnm, nif->hw_if_index, 0); - - ethernet_delete_interface (vnm, nif->hw_if_index); - - close_netmap_if (nm, nif); - - if (tm->n_vlib_mains > 1 && pool_elts (nm->interfaces) == 0) - netmap_worker_thread_disable (); - - return 0; -} - -static clib_error_t * -netmap_init (vlib_main_t * vm) -{ - netmap_main_t *nm = &netmap_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - vlib_thread_registration_t *tr; - uword *p; - - clib_memset (nm, 0, sizeof (netmap_main_t)); - - nm->input_cpu_first_index = 0; - nm->input_cpu_count = 1; - - /* find out which cpus will be used for input */ - p = hash_get_mem (tm->thread_registrations_by_name, "workers"); - tr = p ? (vlib_thread_registration_t *) p[0] : 0; - - if (tr && tr->count > 0) - { - nm->input_cpu_first_index = tr->first_index; - nm->input_cpu_count = tr->count; - } - - mhash_init_vec_string (&nm->if_index_by_host_if_name, sizeof (uword)); - - vec_validate_aligned (nm->rx_buffers, tm->n_vlib_mains - 1, - CLIB_CACHE_LINE_BYTES); - - return 0; -} - -VLIB_INIT_FUNCTION (netmap_init); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/netmap/netmap.h b/extras/deprecated/netmap/netmap.h deleted file mode 100644 index 29f855fda8e..00000000000 --- a/extras/deprecated/netmap/netmap.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ -/* - * Copyright (C) 2011-2014 Universita` di Pisa. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <vppinfra/lock.h> - -typedef struct -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - clib_spinlock_t lockp; - u8 *host_if_name; - uword if_index; - u32 hw_if_index; - u32 sw_if_index; - u32 clib_file_index; - - u32 per_interface_next_index; - u8 is_admin_up; - - /* netmap */ - struct nmreq *req; - u16 mem_region; - int fd; - struct netmap_if *nifp; - u16 first_tx_ring; - u16 last_tx_ring; - u16 first_rx_ring; - u16 last_rx_ring; - -} netmap_if_t; - -typedef struct -{ - char *mem; - u32 region_size; - int refcnt; -} netmap_mem_region_t; - -typedef struct -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - netmap_if_t *interfaces; - - /* bitmap of pending rx interfaces */ - uword *pending_input_bitmap; - - /* rx buffer cache */ - u32 **rx_buffers; - - /* hash of host interface names */ - mhash_t if_index_by_host_if_name; - - /* vector of memory regions */ - netmap_mem_region_t *mem_regions; - - /* first cpu index */ - u32 input_cpu_first_index; - - /* total cpu count */ - u32 input_cpu_count; -} netmap_main_t; - -extern netmap_main_t netmap_main; -extern vnet_device_class_t netmap_device_class; -extern vlib_node_registration_t netmap_input_node; - -int netmap_create_if (vlib_main_t * vm, u8 * host_if_name, u8 * hw_addr_set, - u8 is_pipe, u8 is_master, u32 * sw_if_index); -int netmap_delete_if (vlib_main_t * vm, u8 * host_if_name); - - -/* Macros and helper functions from sys/net/netmap_user.h */ - -#ifdef _NET_NETMAP_H_ - -#define _NETMAP_OFFSET(type, ptr, offset) \ - ((type)(void *)((char *)(ptr) + (offset))) - -#define NETMAP_IF(_base, _ofs) _NETMAP_OFFSET(struct netmap_if *, _base, _ofs) - -#define NETMAP_TXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \ - nifp, (nifp)->ring_ofs[index] ) - -#define NETMAP_RXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \ - nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] ) - -#define NETMAP_BUF(ring, index) \ - ((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size)) - -#define NETMAP_BUF_IDX(ring, buf) \ - ( ((char *)(buf) - ((char *)(ring) + (ring)->buf_ofs) ) / \ - (ring)->nr_buf_size ) - -static inline uint32_t -nm_ring_next (struct netmap_ring *ring, uint32_t i) -{ - return (PREDICT_FALSE (i + 1 == ring->num_slots) ? 0 : i + 1); -} - - -/* - * Return 1 if we have pending transmissions in the tx ring. - * When everything is complete ring->head = ring->tail + 1 (modulo ring size) - */ -static inline int -nm_tx_pending (struct netmap_ring *ring) -{ - return nm_ring_next (ring, ring->tail) != ring->head; -} - -static inline uint32_t -nm_ring_space (struct netmap_ring *ring) -{ - int ret = ring->tail - ring->cur; - if (ret < 0) - ret += ring->num_slots; - return ret; -} -#endif - - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/netmap/netmap_api.c b/extras/deprecated/netmap/netmap_api.c deleted file mode 100644 index ee05ec22d25..00000000000 --- a/extras/deprecated/netmap/netmap_api.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - *------------------------------------------------------------------ - * netmap_api.c - netmap api - * - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#include <vnet/vnet.h> -#include <vlibmemory/api.h> - -#include <vnet/interface.h> -#include <vnet/api_errno.h> -#include <vnet/devices/netmap/netmap.h> - -#include <vnet/vnet_msg_enum.h> - -#define vl_typedefs /* define message structures */ -#include <vnet/vnet_all_api_h.h> -#undef vl_typedefs - -#define vl_endianfun /* define message structures */ -#include <vnet/vnet_all_api_h.h> -#undef vl_endianfun - -/* instantiate all the print functions we know about */ -#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) -#define vl_printfun -#include <vnet/vnet_all_api_h.h> -#undef vl_printfun - -#include <vlibapi/api_helper_macros.h> - -#define foreach_vpe_api_msg \ -_(NETMAP_CREATE, netmap_create) \ -_(NETMAP_DELETE, netmap_delete) \ - -static void -vl_api_netmap_create_t_handler (vl_api_netmap_create_t * mp) -{ - vlib_main_t *vm = vlib_get_main (); - vl_api_netmap_create_reply_t *rmp; - int rv = 0; - u8 *if_name = NULL; - - if_name = format (0, "%s", mp->netmap_if_name); - vec_add1 (if_name, 0); - - rv = - netmap_create_if (vm, if_name, mp->use_random_hw_addr ? 0 : mp->hw_addr, - mp->is_pipe, mp->is_master, 0); - - vec_free (if_name); - - REPLY_MACRO (VL_API_NETMAP_CREATE_REPLY); -} - -static void -vl_api_netmap_delete_t_handler (vl_api_netmap_delete_t * mp) -{ - vlib_main_t *vm = vlib_get_main (); - vl_api_netmap_delete_reply_t *rmp; - int rv = 0; - u8 *if_name = NULL; - - if_name = format (0, "%s", mp->netmap_if_name); - vec_add1 (if_name, 0); - - rv = netmap_delete_if (vm, if_name); - - vec_free (if_name); - - REPLY_MACRO (VL_API_NETMAP_DELETE_REPLY); -} - -/* - * netmap_api_hookup - * Add vpe's API message handlers to the table. - * vlib has already mapped shared memory and - * added the client registration handlers. - * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process() - */ -#define vl_msg_name_crc_list -#include <vnet/vnet_all_api_h.h> -#undef vl_msg_name_crc_list - -static void -setup_message_id_table (api_main_t * am) -{ -#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); - foreach_vl_msg_name_crc_netmap; -#undef _ -} - -static clib_error_t * -netmap_api_hookup (vlib_main_t * vm) -{ - api_main_t *am = vlibapi_get_main (); - -#define _(N,n) \ - vl_msg_api_set_handlers(VL_API_##N, #n, \ - vl_api_##n##_t_handler, \ - vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_vpe_api_msg; -#undef _ - - /* - * Set up the (msg_name, crc, message-id) table - */ - setup_message_id_table (am); - - return 0; -} - -VLIB_API_INIT_FUNCTION (netmap_api_hookup); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/netmap/node.c b/extras/deprecated/netmap/node.c deleted file mode 100644 index b0a68824b9c..00000000000 --- a/extras/deprecated/netmap/node.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#include <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> -#include <vnet/devices/devices.h> -#include <vnet/feature/feature.h> - -#include <vnet/devices/netmap/net_netmap.h> -#include <vnet/devices/netmap/netmap.h> - -#define foreach_netmap_input_error - -typedef enum -{ -#define _(f,s) NETMAP_INPUT_ERROR_##f, - foreach_netmap_input_error -#undef _ - NETMAP_INPUT_N_ERROR, -} netmap_input_error_t; - -static char *netmap_input_error_strings[] = { -#define _(n,s) s, - foreach_netmap_input_error -#undef _ -}; - -typedef struct -{ - u32 next_index; - u32 hw_if_index; - struct netmap_slot slot; -} netmap_input_trace_t; - -static u8 * -format_netmap_input_trace (u8 * s, va_list * args) -{ - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); - netmap_input_trace_t *t = va_arg (*args, netmap_input_trace_t *); - u32 indent = format_get_indent (s); - - s = format (s, "netmap: hw_if_index %d next-index %d", - t->hw_if_index, t->next_index); - s = format (s, "\n%Uslot: flags 0x%x len %u buf_idx %u", - format_white_space, indent + 2, - t->slot.flags, t->slot.len, t->slot.buf_idx); - return s; -} - -always_inline void -buffer_add_to_chain (vlib_main_t * vm, u32 bi, u32 first_bi, u32 prev_bi) -{ - vlib_buffer_t *b = vlib_get_buffer (vm, bi); - vlib_buffer_t *first_b = vlib_get_buffer (vm, first_bi); - vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_bi); - - /* update first buffer */ - first_b->total_length_not_including_first_buffer += b->current_length; - - /* update previous buffer */ - prev_b->next_buffer = bi; - prev_b->flags |= VLIB_BUFFER_NEXT_PRESENT; - - /* update current buffer */ - b->next_buffer = 0; -} - -always_inline uword -netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame, netmap_if_t * nif) -{ - u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT; - uword n_trace = vlib_get_trace_count (vm, node); - netmap_main_t *nm = &netmap_main; - u32 n_rx_packets = 0; - u32 n_rx_bytes = 0; - u32 *to_next = 0; - u32 n_free_bufs; - struct netmap_ring *ring; - int cur_ring; - u32 thread_index = vm->thread_index; - u32 n_buffer_bytes = vlib_buffer_get_default_data_size (vm); - - if (nif->per_interface_next_index != ~0) - next_index = nif->per_interface_next_index; - - n_free_bufs = vec_len (nm->rx_buffers[thread_index]); - if (PREDICT_FALSE (n_free_bufs < VLIB_FRAME_SIZE)) - { - vec_validate (nm->rx_buffers[thread_index], - VLIB_FRAME_SIZE + n_free_bufs - 1); - n_free_bufs += - vlib_buffer_alloc (vm, &nm->rx_buffers[thread_index][n_free_bufs], - VLIB_FRAME_SIZE); - _vec_len (nm->rx_buffers[thread_index]) = n_free_bufs; - } - - cur_ring = nif->first_rx_ring; - while (cur_ring <= nif->last_rx_ring && n_free_bufs) - { - int r = 0; - u32 cur_slot_index; - ring = NETMAP_RXRING (nif->nifp, cur_ring); - r = nm_ring_space (ring); - - if (!r) - { - cur_ring++; - continue; - } - - if (r > n_free_bufs) - r = n_free_bufs; - - cur_slot_index = ring->cur; - while (r) - { - u32 n_left_to_next; - u32 next0 = next_index; - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (r && n_left_to_next) - { - vlib_buffer_t *first_b0 = 0; - u32 offset = 0; - u32 bi0 = 0, first_bi0 = 0, prev_bi0; - u32 next_slot_index = (cur_slot_index + 1) % ring->num_slots; - u32 next2_slot_index = (cur_slot_index + 2) % ring->num_slots; - struct netmap_slot *slot = &ring->slot[cur_slot_index]; - u32 data_len = slot->len; - - /* prefetch 2 slots in advance */ - CLIB_PREFETCH (&ring->slot[next2_slot_index], - CLIB_CACHE_LINE_BYTES, LOAD); - /* prefetch start of next packet */ - CLIB_PREFETCH (NETMAP_BUF - (ring, ring->slot[next_slot_index].buf_idx), - CLIB_CACHE_LINE_BYTES, LOAD); - - while (data_len && n_free_bufs) - { - vlib_buffer_t *b0; - /* grab free buffer */ - u32 last_empty_buffer = - vec_len (nm->rx_buffers[thread_index]) - 1; - prev_bi0 = bi0; - bi0 = nm->rx_buffers[thread_index][last_empty_buffer]; - b0 = vlib_get_buffer (vm, bi0); - _vec_len (nm->rx_buffers[thread_index]) = last_empty_buffer; - n_free_bufs--; - - /* copy data */ - u32 bytes_to_copy = - data_len > n_buffer_bytes ? n_buffer_bytes : data_len; - b0->current_data = 0; - clib_memcpy_fast (vlib_buffer_get_current (b0), - (u8 *) NETMAP_BUF (ring, slot->buf_idx) + - offset, bytes_to_copy); - - /* fill buffer header */ - b0->current_length = bytes_to_copy; - - if (offset == 0) - { - b0->total_length_not_including_first_buffer = 0; - b0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID; - vnet_buffer (b0)->sw_if_index[VLIB_RX] = - nif->sw_if_index; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - first_bi0 = bi0; - first_b0 = vlib_get_buffer (vm, first_bi0); - } - else - buffer_add_to_chain (vm, bi0, first_bi0, prev_bi0); - - offset += bytes_to_copy; - data_len -= bytes_to_copy; - } - - /* trace */ - if (PREDICT_FALSE (n_trace > 0)) - { - if (PREDICT_TRUE (first_b0 != 0)) - { - netmap_input_trace_t *tr; - vlib_trace_buffer (vm, node, next0, first_b0, - /* follow_chain */ 0); - vlib_set_trace_count (vm, node, --n_trace); - tr = vlib_add_trace (vm, node, first_b0, sizeof (*tr)); - tr->next_index = next0; - tr->hw_if_index = nif->hw_if_index; - memcpy (&tr->slot, slot, sizeof (struct netmap_slot)); - } - } - - /* redirect if feature path enabled */ - vnet_feature_start_device_input_x1 (nif->sw_if_index, &next0, - first_b0); - - /* enque and take next packet */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, first_bi0, - next0); - - /* next packet */ - n_rx_packets++; - n_rx_bytes += slot->len; - to_next[0] = first_bi0; - to_next += 1; - n_left_to_next--; - cur_slot_index = next_slot_index; - - r--; - } - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - ring->head = ring->cur = cur_slot_index; - cur_ring++; - } - - if (n_rx_packets) - ioctl (nif->fd, NIOCRXSYNC, NULL); - - vlib_increment_combined_counter - (vnet_get_main ()->interface_main.combined_sw_if_counters - + VNET_INTERFACE_COUNTER_RX, - vlib_get_thread_index (), nif->hw_if_index, n_rx_packets, n_rx_bytes); - - vnet_device_increment_rx_packets (thread_index, n_rx_packets); - - return n_rx_packets; -} - -VLIB_NODE_FN (netmap_input_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - int i; - u32 n_rx_packets = 0; - u32 thread_index = vm->thread_index; - netmap_main_t *nm = &netmap_main; - netmap_if_t *nmi; - - for (i = 0; i < vec_len (nm->interfaces); i++) - { - nmi = vec_elt_at_index (nm->interfaces, i); - if (nmi->is_admin_up && - (i % nm->input_cpu_count) == - (thread_index - nm->input_cpu_first_index)) - n_rx_packets += netmap_device_input_fn (vm, node, frame, nmi); - } - - return n_rx_packets; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (netmap_input_node) = { - .name = "netmap-input", - .sibling_of = "device-input", - .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED, - .format_trace = format_netmap_input_trace, - .type = VLIB_NODE_TYPE_INPUT, - /* default state is INTERRUPT mode, switch to POLLING if worker threads are enabled */ - .state = VLIB_NODE_STATE_INTERRUPT, - .n_errors = NETMAP_INPUT_N_ERROR, - .error_strings = netmap_input_error_strings, -}; -/* *INDENT-ON* */ - - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/extras/deprecated/plugins/gbp/test_gbp.py b/extras/deprecated/plugins/gbp/test_gbp.py index 84dda76af19..6a4fe0dae42 100644 --- a/extras/deprecated/plugins/gbp/test_gbp.py +++ b/extras/deprecated/plugins/gbp/test_gbp.py @@ -391,9 +391,9 @@ class VppGbpEndpointGroup(VppObject): def encode(self) -> dict: return { - "uplink_sw_if_index": self.uplink.sw_if_index - if self.uplink - else INDEX_INVALID, + "uplink_sw_if_index": ( + self.uplink.sw_if_index if self.uplink else INDEX_INVALID + ), "bd_id": self.bd.bd.bd_id, "rd_id": self.rd.rd_id, "vnid": self.vnid, @@ -460,12 +460,12 @@ class VppGbpBridgeDomain(VppObject): return { "flags": self.flags, "bvi_sw_if_index": self.bvi.sw_if_index, - "uu_fwd_sw_if_index": self.uu_fwd.sw_if_index - if self.uu_fwd - else INDEX_INVALID, - "bm_flood_sw_if_index": self.bm_flood.sw_if_index - if self.bm_flood - else INDEX_INVALID, + "uu_fwd_sw_if_index": ( + self.uu_fwd.sw_if_index if self.uu_fwd else INDEX_INVALID + ), + "bm_flood_sw_if_index": ( + self.bm_flood.sw_if_index if self.bm_flood else INDEX_INVALID + ), "bd_id": self.bd.bd_id, "rd_id": self.rd.rd_id, } @@ -510,12 +510,12 @@ class VppGbpRouteDomain(VppObject): "scope": self.scope, "ip4_table_id": self.t4.table_id, "ip6_table_id": self.t6.table_id, - "ip4_uu_sw_if_index": self.ip4_uu.sw_if_index - if self.ip4_uu - else INDEX_INVALID, - "ip6_uu_sw_if_index": self.ip6_uu.sw_if_index - if self.ip6_uu - else INDEX_INVALID, + "ip4_uu_sw_if_index": ( + self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID + ), + "ip6_uu_sw_if_index": ( + self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID + ), } def add_vpp_config(self): diff --git a/extras/deprecated/vnet/lawful-intercept/lawful_intercept.c b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.c new file mode 100644 index 00000000000..61b1a6165f4 --- /dev/null +++ b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vnet/lawful-intercept/lawful_intercept.h> + +li_main_t li_main; + +static clib_error_t * +set_li_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + li_main_t *lm = &li_main; + ip4_address_t collector; + u8 collector_set = 0; + ip4_address_t src; + u8 src_set = 0; + u32 tmp; + u16 udp_port = 0; + u8 is_add = 1; + int i; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "collector %U", unformat_ip4_address, &collector)) + collector_set = 1; + if (unformat (input, "src %U", unformat_ip4_address, &src)) + src_set = 1; + else if (unformat (input, "udp-port %d", &tmp)) + udp_port = tmp; + else if (unformat (input, "del")) + is_add = 0; + else + break; + } + + if (collector_set == 0) + return clib_error_return (0, "collector must be set..."); + if (src_set == 0) + return clib_error_return (0, "src must be set..."); + if (udp_port == 0) + return clib_error_return (0, "udp-port must be set..."); + + if (is_add == 1) + { + for (i = 0; i < vec_len (lm->collectors); i++) + { + if (lm->collectors[i].as_u32 == collector.as_u32) + { + if (lm->ports[i] == udp_port) + return clib_error_return ( + 0, "collector %U:%d already configured", format_ip4_address, + &collector, udp_port); + else + return clib_error_return ( + 0, "collector %U already configured with port %d", + format_ip4_address, &collector, (int) (lm->ports[i])); + } + } + vec_add1 (lm->collectors, collector); + vec_add1 (lm->ports, udp_port); + vec_add1 (lm->src_addrs, src); + return 0; + } + else + { + for (i = 0; i < vec_len (lm->collectors); i++) + { + if ((lm->collectors[i].as_u32 == collector.as_u32) + && lm->ports[i] == udp_port) + { + vec_delete (lm->collectors, 1, i); + vec_delete (lm->ports, 1, i); + vec_delete (lm->src_addrs, 1, i); + return 0; + } + } + return clib_error_return (0, "collector %U:%d not configured", + &collector, udp_port); + } + return 0; +} + +VLIB_CLI_COMMAND (set_li_command, static) = { + .path = "set li", + .short_help = + "set li src <ip4-address> collector <ip4-address> udp-port <nnnn>", + .function = set_li_command_fn, +}; + +static clib_error_t * +li_init (vlib_main_t * vm) +{ + li_main_t *lm = &li_main; + + lm->vlib_main = vm; + lm->vnet_main = vnet_get_main (); + lm->hit_node_index = li_hit_node.index; + return 0; +} + +VLIB_INIT_FUNCTION (li_init); + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/lawful-intercept/lawful_intercept.h b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.h new file mode 100644 index 00000000000..ba74204fb9e --- /dev/null +++ b/extras/deprecated/vnet/lawful-intercept/lawful_intercept.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __lawful_intercept_h__ +#define __lawful_intercept_h__ + +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> + +typedef struct +{ + /* LI collector info */ + ip4_address_t *src_addrs; + ip4_address_t *collectors; + u16 *ports; + + /* Hit node index */ + u32 hit_node_index; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; +} li_main_t; + +extern li_main_t li_main; + +typedef CLIB_PACKED(struct { + ip4_header_t ip4; + udp_header_t udp; +}) ip4_udp_header_t; + +extern vlib_node_registration_t li_hit_node; + +#endif /* __lawful_intercept_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/lawful-intercept/node.c b/extras/deprecated/vnet/lawful-intercept/node.c new file mode 100644 index 00000000000..86f135b9ea1 --- /dev/null +++ b/extras/deprecated/vnet/lawful-intercept/node.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vppinfra/error.h> + +#include <vnet/lawful-intercept/lawful_intercept.h> + +#include <vppinfra/error.h> +#include <vppinfra/elog.h> + +extern vlib_node_registration_t li_hit_node; + +typedef struct +{ + u32 next_index; +} li_hit_trace_t; + +/* packet trace format function */ +static u8 * +format_li_hit_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + li_hit_trace_t *t = va_arg (*args, li_hit_trace_t *); + + s = format (s, "LI_HIT: next index %d", t->next_index); + + return s; +} + +#define foreach_li_hit_error \ +_(HITS, "LI packets processed") \ +_(NO_COLLECTOR, "No collector configured") \ +_(BUFFER_ALLOCATION_FAILURE, "Buffer allocation failure") + +typedef enum +{ +#define _(sym,str) LI_HIT_ERROR_##sym, + foreach_li_hit_error +#undef _ + LI_HIT_N_ERROR, +} li_hit_error_t; + +static char *li_hit_error_strings[] = { +#define _(sym,string) string, + foreach_li_hit_error +#undef _ +}; + +typedef enum +{ + LI_HIT_NEXT_ETHERNET, + LI_HIT_N_NEXT, +} li_hit_next_t; + +VLIB_NODE_FN (li_hit_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + li_hit_next_t next_index; + vlib_frame_t *int_frame = 0; + u32 *to_int_next = 0; + li_main_t *lm = &li_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + if (PREDICT_FALSE (vec_len (lm->collectors) == 0)) + { + vlib_node_increment_counter (vm, li_hit_node.index, + LI_HIT_ERROR_NO_COLLECTOR, n_left_from); + } + else + { + /* The intercept frame... */ + int_frame = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + to_int_next = vlib_frame_vector_args (int_frame); + } + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + +#if 0 + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 next0 = LI_HIT_NEXT_INTERFACE_OUTPUT; + u32 next1 = LI_HIT_NEXT_INTERFACE_OUTPUT; + u32 sw_if_index0, sw_if_index1; + u8 tmp0[6], tmp1[6]; + ethernet_header_t *en0, *en1; + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + clib_prefetch_store (p2->data); + clib_prefetch_store (p3->data); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* $$$$$ Dual loop: process 2 x packets here $$$$$ */ + ASSERT (b0->current_data == 0); + ASSERT (b1->current_data == 0); + + en0 = vlib_buffer_get_current (b0); + en1 = vlib_buffer_get_current (b1); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + /* Send pkt back out the RX interface */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = sw_if_index1; + + /* $$$$$ End of processing 2 x packets $$$$$ */ + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + li_hit_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + li_hit_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = sw_if_index1; + t->next_index = next1; + } + } + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } +#endif /* $$$ dual-loop off */ + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + vlib_buffer_t *c0; + ip4_udp_header_t *iu0; + ip4_header_t *ip0; + udp_header_t *udp0; + u32 next0 = LI_HIT_NEXT_ETHERNET; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + if (PREDICT_TRUE (to_int_next != 0)) + { + /* Make an intercept copy. This can fail. */ + c0 = vlib_buffer_copy (vm, b0); + + if (PREDICT_FALSE (c0 == 0)) + { + vlib_node_increment_counter + (vm, node->node_index, + LI_HIT_ERROR_BUFFER_ALLOCATION_FAILURE, 1); + goto skip; + } + + vlib_buffer_advance (c0, -sizeof (*iu0)); + + iu0 = vlib_buffer_get_current (c0); + ip0 = &iu0->ip4; + + ip0->ip_version_and_header_length = 0x45; + ip0->ttl = 254; + ip0->protocol = IP_PROTOCOL_UDP; + + ip0->src_address.as_u32 = lm->src_addrs[0].as_u32; + ip0->dst_address.as_u32 = lm->collectors[0].as_u32; + ip0->length = vlib_buffer_length_in_chain (vm, c0); + ip0->checksum = ip4_header_checksum (ip0); + + udp0 = &iu0->udp; + udp0->src_port = udp0->dst_port = + clib_host_to_net_u16 (lm->ports[0]); + udp0->checksum = 0; + udp0->length = + clib_net_to_host_u16 (vlib_buffer_length_in_chain (vm, b0)); + + to_int_next[0] = vlib_get_buffer_index (vm, c0); + to_int_next++; + } + + skip: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + li_hit_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + if (int_frame) + { + int_frame->n_vectors = frame->n_vectors; + vlib_put_frame_to_node (vm, ip4_lookup_node.index, int_frame); + } + + vlib_node_increment_counter (vm, li_hit_node.index, + LI_HIT_ERROR_HITS, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (li_hit_node) = { + .name = "li-hit", + .vector_size = sizeof (u32), + .format_trace = format_li_hit_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(li_hit_error_strings), + .error_strings = li_hit_error_strings, + + .n_next_nodes = LI_HIT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [LI_HIT_NEXT_ETHERNET] = "ethernet-input-not-l2", + }, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py b/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py index 1b6b4e731ca..2a373f8e56e 100644 --- a/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py +++ b/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py @@ -56,9 +56,9 @@ class VppVxlanGbpTunnel(VppInterface): "dst": self.dst, "mode": self.mode, "vni": self.vni, - "mcast_sw_if_index": self.mcast_itf.sw_if_index - if self.mcast_itf - else INDEX_INVALID, + "mcast_sw_if_index": ( + self.mcast_itf.sw_if_index if self.mcast_itf else INDEX_INVALID + ), "encap_table_id": self.encap_table_id, "instance": self.instance, } diff --git a/extras/hs-test/Makefile b/extras/hs-test/Makefile index f0ec755afb2..6ee89bc2770 100644 --- a/extras/hs-test/Makefile +++ b/extras/hs-test/Makefile @@ -23,6 +23,14 @@ ifeq ($(CPUS),) CPUS=1 endif +ifeq ($(PARALLEL),) +PARALLEL=1 +endif + +ifeq ($(REPEAT),) +REPEAT=0 +endif + ifeq ($(VPPSRC),) VPPSRC=$(shell pwd)/../.. endif @@ -35,13 +43,14 @@ ifeq ($(ARCH),) ARCH=$(shell dpkg --print-architecture) endif -list_tests = @(grep -r ') Test' *_test.go | cut -d '*' -f2 | cut -d '(' -f1 | \ - tr -d ' ' | tr ')' '/' | sed 's/Suite//') +list_tests = @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color --seed=2 | head -n -1 | grep 'Test' | \ + sed 's/^/* /; s/\(Suite\) /\1\//g' .PHONY: help help: @echo "Make targets:" @echo " test - run tests" + @echo " test-debug - run tests (vpp debug image)" @echo " build - build test infra" @echo " build-debug - build test infra (vpp debug image)" @echo " build-go - just build golang files" @@ -60,6 +69,8 @@ help: @echo " TEST=[test-name] - specific test to run" @echo " CPUS=[n-cpus] - number of cpus to run with vpp" @echo " VPPSRC=[path-to-vpp-src] - path to vpp source files (for gdb)" + @echo " PARALLEL=[n-cpus] - number of test processes to spawn to run in parallel" + @echo " REPEAT=[n] - repeat tests up to N times or until a failure occurs" @echo @echo "List of all tests:" $(call list_tests) @@ -68,30 +79,49 @@ help: list-tests: $(call list_tests) +.PHONY: build-vpp-release build-vpp-release: @make -C ../.. build-release +.PHONY: build-vpp-debug build-vpp-debug: @make -C ../.. build +.build.ok: build + @touch .build.ok + +.build_debug.ok: build-debug + @touch .build.ok + .PHONY: test -test: .deps.ok .build.vpp +test: .deps.ok .build.ok + -bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ + --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \ + --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) + $(call jq-summary) + @bash ./script/compress.sh + +.PHONY: test-debug +test-debug: .deps.ok .build_debug.ok @bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \ - --vppsrc=$(VPPSRC) + --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) +.PHONY: build-go build-go: go build ./tools/http_server +.PHONY: build build: .deps.ok build-vpp-release build-go - @rm -f .build.vpp + @rm -f .build.ok bash ./script/build_hst.sh release - @touch .build.vpp + @touch .build.ok +.PHONY: build-debug build-debug: .deps.ok build-vpp-debug build-go - @rm -f .build.vpp + @rm -f .build.ok bash ./script/build_hst.sh debug - @touch .build.vpp + @touch .build.ok .deps.ok: @sudo make install-deps @@ -101,17 +131,20 @@ install-deps: @rm -f .deps.ok @apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl software-properties-common \ - && apt-get install -y golang apache2-utils wrk bridge-utils + apache2-utils wrk bridge-utils @if [ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ] ; then \ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \ echo "deb [arch=$(ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(UBUNTU_CODENAME) stable" \ | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ; \ apt-get update; \ fi - @apt-get install -y docker-ce @touch .deps.ok .PHONY: fixstyle fixstyle: @gofmt -w . @go mod tidy + +# splitting this into multiple lines breaks the command +jq-summary = @jq -r '.[0] | .SpecReports[] | select(.State == "failed") | select(.Failure != null) | "TestName: \(.LeafNodeText)\nMessage:\n\(.Failure.Message)\n Full Stack Trace:\n\(.Failure.Location.FullStackTrace)\n"' summary/report.json > summary/failed-summary.log \ + && echo "Summary generated -> failed-summary.log" diff --git a/extras/hs-test/README.rst b/extras/hs-test/README.rst index 6db832b7fbe..1dc1039b33f 100644 --- a/extras/hs-test/README.rst +++ b/extras/hs-test/README.rst @@ -9,7 +9,10 @@ End-to-end tests often want multiple VPP instances, network namespaces, differen and to execute external tools or commands. With such requirements the existing VPP test framework is not sufficient. For this, ``Go`` was chosen as a high level language, allowing rapid development, with ``Docker`` and ``ip`` being the tools for creating required topology. -Go's package `testing`_ together with `go test`_ command form the base framework upon which the *hs-test* is built and run. +`Ginkgo`_ forms the base framework upon which the *hs-test* is built and run. +All tests are technically in a single suite because we are only using ``package main``. We simulate suite behavior by grouping tests by the topology they require. +This allows us to run those mentioned groups in parallel, but not individual tests in parallel. + Anatomy of a test case ---------------------- @@ -24,15 +27,16 @@ Anatomy of a test case **Action flow when running a test case**: #. It starts with running ``make test``. Optional arguments are VERBOSE, PERSIST (topology configuration isn't cleaned up after test run), - and TEST=<test-name> to run specific test. -#. ``make list-tests`` (or ``make help``) shows all test names. -#. ``go test`` compiles package ``main`` along with any files with names matching the file pattern ``*_test.go`` - and then runs the resulting test binaries -#. The go test framework runs each function matching :ref:`naming convention<test-convention>`. Each of these corresponds to a `test suite`_ -#. Testify toolkit's ``suite.Run(t *testing.T, suite TestingSuite)`` function runs the suite and does the following: - + TEST=<test-name> to run a specific test and PARALLEL=[n-cpus]. +#. ``make list-tests`` (or ``make help``) shows all tests. The current `list of tests`_ is at the bottom of this document. +#. ``Ginkgo`` looks for a spec suite in the current directory and then compiles it to a .test binary +#. The Ginkgo test framework runs each function that was registered manually using ``registerMySuiteTest(s *MySuite)``. Each of these functions correspond to a suite +#. Ginkgo's ``RunSpecs(t, "Suite description")`` function is the entry point and does the following: + + #. Ginkgo compiles the spec, builds a spec tree + #. ``Describe`` container nodes in suite\_\*_test.go files are run (in series by default, or in parallel with the argument PARALLEL=[n-cpus]) #. Suite is initialized. The topology is loaded and configured in this step - #. Test suite runs all the tests attached to it + #. Registered tests are run in generated ``It`` subject nodes #. Execute tear-down functions, which currently consists of stopping running containers and clean-up of test topology @@ -43,23 +47,25 @@ This describes adding a new test case to an existing suite. For adding a new suite, please see `Modifying the framework`_ below. #. To write a new test case, create a file whose name ends with ``_test.go`` or pick one that already exists -#. Declare method whose name starts with ``Test`` and specifies its receiver as a pointer to the suite's struct (defined in ``framework_test.go``) +#. Declare method whose name ends with ``Test`` and specifies its parameter as a pointer to the suite's struct (defined in ``suite_*_test.go``) #. Implement test behaviour inside the test method. This typically includes the following: - #. Retrieve a running container in which to run some action. Method ``getContainerByName`` - from ``HstSuite`` struct serves this purpose - #. Interact with VPP through the ``VppInstance`` struct embedded in container. It provides ``vppctl`` method to access debug CLI - #. Run arbitrary commands inside the containers with ``exec`` method - #. Run other external tool with one of the preexisting functions in the ``utils.go`` file. - For example, use ``wget`` with ``startWget`` function - #. Use ``exechelper`` or just plain ``exec`` packages to run whatever else - #. Verify results of your tests using ``assert`` methods provided by the test suite, - implemented by HstSuite struct + #. Retrieve a running container in which to run some action. Method ``getContainerByName`` + from ``HstSuite`` struct serves this purpose + #. Interact with VPP through the ``VppInstance`` struct embedded in container. It provides ``vppctl`` method to access debug CLI + #. Run arbitrary commands inside the containers with ``exec`` method + #. Run other external tool with one of the preexisting functions in the ``utils.go`` file. + For example, use ``wget`` with ``startWget`` function + #. Use ``exechelper`` or just plain ``exec`` packages to run whatever else + #. Verify results of your tests using ``assert`` methods provided by the test suite, implemented by HstSuite struct or use ``Gomega`` assert functions. + +#. Create an ``init()`` function and register the test using ``register*SuiteTests(testCaseFunction)`` + **Example test case** Assumed are two docker containers, each with its own VPP instance running. One VPP then pings the other. -This can be put in file ``extras/hs-test/my_test.go`` and run with command ``./test -run TestMySuite/TestMyCase``. +This can be put in file ``extras/hs-test/my_test.go`` and run with command ``make test TEST=MyTest`` or ``ginkgo -v --trace --focus MyTest``. :: @@ -69,7 +75,11 @@ This can be put in file ``extras/hs-test/my_test.go`` and run with command ``./t "fmt" ) - func (s *MySuite) TestMyCase() { + func init(){ + registerMySuiteTest(MyTest) + } + + func MyTest(s *MySuite) { clientVpp := s.getContainerByName("client-vpp").vppInstance serverVethAddress := s.netInterfaces["server-iface"].AddressString() @@ -86,8 +96,7 @@ Modifying the framework .. _test-convention: -#. Adding a new suite takes place in ``framework_test.go`` and by creating a new file for the suite. - Naming convention for the suite files is ``suite_name_test.go`` where *name* will be replaced +#. To add a new suite, create a new file. Naming convention for the suite files is ``suite_name_test.go`` where *name* will be replaced by the actual name #. Make a ``struct``, in the suite file, with at least ``HstSuite`` struct as its member. @@ -99,7 +108,17 @@ Modifying the framework HstSuite } -#. In suite file, implement ``SetupSuite`` method which testify runs once before starting any of the tests. +#. Create a new slice that will contain test functions with a pointer to the suite's struct: ``var myTests = []func(s *MySuite){}`` + +#. Then create a new function that will append test functions to that slice: + + :: + + func registerMySuiteTests(tests ...func(s *MySuite)) { + nginxTests = append(myTests, tests...) + } + +#. In suite file, implement ``SetupSuite`` method which Ginkgo runs once before starting any of the tests. It's important here to call ``configureNetworkTopology`` method, pass the topology name to the function in a form of file name of one of the *yaml* files in ``topo-network`` folder. Without the extension. In this example, *myTopology* corresponds to file ``extras/hs-test/topo-network/myTopology.yaml`` @@ -111,6 +130,8 @@ Modifying the framework :: func (s *MySuite) SetupSuite() { + s.HstSuite.SetupSuite() + // Add custom setup code here s.configureNetworkTopology("myTopology") @@ -123,19 +144,62 @@ Modifying the framework :: func (s *MySuite) SetupTest() { + s.HstSuite.setupTest() s.SetupVolumes() s.SetupContainers() } -#. In order for ``go test`` to run this suite, we need to create a normal test function and pass our suite to ``suite.Run``. - These functions are placed at the end of ``framework_test.go`` +#. In order for ``Ginkgo`` to run this suite, we need to create a ``Describe`` container node with setup nodes and an ``It`` subject node. + Place them at the end of the suite file + + * Declare a suite struct variable before anything else + * To use ``BeforeAll()`` and ``AfterAll()``, the container has to be marked as ``Ordered`` + * Because the container is now marked as Ordered, if a test fails, all the subsequent tests are skipped. + To override this behavior, decorate the container node with ``ContinueOnFailure`` :: - func TestMySuite(t *testing.T) { - var m MySuite - suite.Run(t, &m) - } + var _ = Describe("MySuite", Ordered, ContinueOnFailure, func() { + var s MySuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + for _, test := range mySuiteTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) { + test(&s) + }, SpecTimeout(time.Minute*5)) + } + }) + +#. Notice the loop - it will generate multiple ``It`` nodes, each running a different test. + ``test := test`` is necessary, otherwise only the last test in a suite will run. + For a more detailed description, check Ginkgo's documentation: https://onsi.github.io/ginkgo/#dynamically-generating-specs\. + +#. ``funcValue.Name()`` returns the full name of a function (e.g. ``fd.io/hs-test.MyTest``), however, we only need the test name (``MyTest``). + +#. To run certain tests solo, create a new slice that will only contain tests that have to run solo and a new register function. + Add a ``Serial`` decorator to the container node and ``Label("SOLO")`` to the ``It`` subject node: + + :: + + var _ = Describe("MySuiteSolo", Ordered, ContinueOnFailure, Serial, func() { + ... + It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) { + test(&s) + }, SpecTimeout(time.Minute*5)) + }) #. Next step is to add test cases to the suite. For that, see section `Adding a test case`_ above @@ -186,14 +250,9 @@ Alternatively copy the executable from host system to the Docker image, similarl **Skipping tests** ``HstSuite`` provides several methods that can be called in tests for skipping it conditionally or unconditionally such as: -``skip()``, ``SkipIfMultiWorker()``, ``SkipUnlessExtendedTestsBuilt()``. +``skip()``, ``SkipIfMultiWorker()``, ``SkipUnlessExtendedTestsBuilt()``. You can also use Ginkgo's ``Skip()``. However the tests currently run under test suites which set up topology and containers before actual test is run. For the reason of saving -test run time it is not advisable to use aforementioned skip methods and instead prefix test name with ``Skip``: - -:: - - func (s *MySuite) SkipTest(){ - +test run time it is not advisable to use aforementioned skip methods and instead, just don't register the test. **Debugging a test** @@ -201,11 +260,11 @@ It is possible to debug VPP by attaching ``gdb`` before test execution by adding :: - $ make test TEST=TestVeths/TestLDPreloadIperfVpp DEBUG=true + $ make test TEST=LDPreloadIperfVppTest DEBUG=true ... run following command in different terminal: - docker exec -it server-vpp gdb -ex "attach $(docker exec server-vpp pidof vpp)" - Afterwards press CTRL+C to continue + docker exec -it server-vpp2456109 gdb -ex "attach $(docker exec server-vpp2456109 pidof vpp)" + Afterwards press CTRL+\ to continue If a test consists of more VPP instances then this is done for each of them. @@ -223,8 +282,38 @@ Generally, these will be updated on a per-need basis, for example when a bug is or a new version incompatibility issue occurs. -.. _testing: https://pkg.go.dev/testing -.. _go test: https://pkg.go.dev/cmd/go#hdr-Test_packages -.. _test suite: https://github.com/stretchr/testify#suite-package +.. _ginkgo: https://onsi.github.io/ginkgo/ .. _volumes: https://docs.docker.com/storage/volumes/ +**List of tests** + +.. _list of tests: + +Please update this list whenever you add a new test by pasting the output below. + +* NsSuite/HttpTpsTest +* NsSuite/VppProxyHttpTcpTest +* NsSuite/VppProxyHttpTlsTest +* NsSuite/EnvoyProxyHttpTcpTest +* NginxSuite/MirroringTest +* VethsSuiteSolo TcpWithLossTest [SOLO] +* NoTopoSuiteSolo HttpStaticPromTest [SOLO] +* TapSuite/LinuxIperfTest +* NoTopoSuite/NginxHttp3Test +* NoTopoSuite/NginxAsServerTest +* NoTopoSuite/NginxPerfCpsTest +* NoTopoSuite/NginxPerfRpsTest +* NoTopoSuite/NginxPerfWrkTest +* VethsSuite/EchoBuiltinTest +* VethsSuite/HttpCliTest +* VethsSuite/LDPreloadIperfVppTest +* VethsSuite/VppEchoQuicTest +* VethsSuite/VppEchoTcpTest +* VethsSuite/VppEchoUdpTest +* VethsSuite/XEchoVclClientUdpTest +* VethsSuite/XEchoVclClientTcpTest +* VethsSuite/XEchoVclServerUdpTest +* VethsSuite/XEchoVclServerTcpTest +* VethsSuite/VclEchoTcpTest +* VethsSuite/VclEchoUdpTest +* VethsSuite/VclRetryAttachTest diff --git a/extras/hs-test/address_allocator.go b/extras/hs-test/address_allocator.go index 72bc298fd3f..e05ea76b9bb 100644 --- a/extras/hs-test/address_allocator.go +++ b/extras/hs-test/address_allocator.go @@ -12,7 +12,7 @@ import ( type AddressCounter = int type Ip4AddressAllocator struct { - networks map[int]AddressCounter + networks map[int]AddressCounter chosenOctet int assignedIps []string } @@ -47,7 +47,7 @@ func (a *Ip4AddressAllocator) NewIp4InterfaceAddress(inputNetworkNumber ...int) // Creates a file every time an IP is assigned: used to keep track of addresses in use. // If an address is not in use, 'counter' is then copied to 'chosenOctet' and it is used for the remaining tests. // Also checks host IP addresses. -func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddresses int) (string, error){ +func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddresses int) (string, error) { hostIps, _ := exechelper.CombinedOutput("ip a") counter := 10 var address string @@ -56,7 +56,7 @@ func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddress if a.chosenOctet != 0 { address = fmt.Sprintf("10.%v.%v.%v", a.chosenOctet, networkNumber, numberOfAddresses) file, err := os.Create(address) - if err != nil{ + if err != nil { return "", errors.New("unable to create file: " + fmt.Sprint(err)) } file.Close() @@ -68,14 +68,14 @@ func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddress counter++ } else if os.IsNotExist(err) { file, err := os.Create(address) - if err != nil{ + if err != nil { return "", errors.New("unable to create file: " + fmt.Sprint(err)) - } + } file.Close() a.chosenOctet = counter break } else { - return "", errors.New("an error occured while checking if a file exists: " + fmt.Sprint(err)) + return "", errors.New("an error occurred while checking if a file exists: " + fmt.Sprint(err)) } } } @@ -84,8 +84,8 @@ func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddress return address, nil } -func (a *Ip4AddressAllocator) deleteIpAddresses(){ - for ip := range a.assignedIps{ +func (a *Ip4AddressAllocator) deleteIpAddresses() { + for ip := range a.assignedIps { os.Remove(a.assignedIps[ip]) } } diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go index 87e8aa38e98..45f41d26a87 100644 --- a/extras/hs-test/container.go +++ b/extras/hs-test/container.go @@ -9,6 +9,7 @@ import ( "time" "github.com/edwarnicke/exechelper" + . "github.com/onsi/ginkgo/v2" ) const ( @@ -38,7 +39,7 @@ type Container struct { vppInstance *VppInstance } -func newContainer(suite *HstSuite, yamlInput ContainerConfig, pid string) (*Container, error) { +func newContainer(suite *HstSuite, yamlInput ContainerConfig) (*Container, error) { containerName := yamlInput["name"].(string) if len(containerName) == 0 { err := fmt.Errorf("container name must not be blank") @@ -48,7 +49,7 @@ func newContainer(suite *HstSuite, yamlInput ContainerConfig, pid string) (*Cont var container = new(Container) container.volumes = make(map[string]Volume) container.envVars = make(map[string]string) - container.name = containerName + pid + container.name = containerName container.suite = suite if image, ok := yamlInput["image"]; ok { @@ -76,7 +77,7 @@ func newContainer(suite *HstSuite, yamlInput ContainerConfig, pid string) (*Cont } if _, ok := yamlInput["volumes"]; ok { - workingVolumeDir := logDir + container.suite.T().Name() + pid + volumeDir + workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + volumeDir workDirReplacer := strings.NewReplacer("$HST_DIR", workDir) volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir) for _, volu := range yamlInput["volumes"].([]interface{}) { @@ -249,7 +250,7 @@ func (c *Container) copy(sourceFileName string, targetFileName string) error { } func (c *Container) createFile(destFileName string, content string) error { - f, err := os.CreateTemp("/tmp", "hst-config" + c.suite.pid) + f, err := os.CreateTemp("/tmp", "hst-config"+c.suite.pid) if err != nil { return err } @@ -273,7 +274,7 @@ func (c *Container) execServer(command string, arguments ...any) { serverCommand := fmt.Sprintf(command, arguments...) containerExecCommand := "docker exec -d" + c.getEnvVarsAsCliOption() + " " + c.name + " " + serverCommand - c.suite.T().Helper() + GinkgoHelper() c.suite.log(containerExecCommand) c.suite.assertNil(exechelper.Run(containerExecCommand)) } @@ -282,7 +283,7 @@ func (c *Container) exec(command string, arguments ...any) string { cliCommand := fmt.Sprintf(command, arguments...) containerExecCommand := "docker exec" + c.getEnvVarsAsCliOption() + " " + c.name + " " + cliCommand - c.suite.T().Helper() + GinkgoHelper() c.suite.log(containerExecCommand) byteOutput, err := exechelper.CombinedOutput(containerExecCommand) c.suite.assertNil(err, err) @@ -291,12 +292,12 @@ func (c *Container) exec(command string, arguments ...any) string { func (c *Container) getLogDirPath() string { testId := c.suite.getTestId() - testName := c.suite.T().Name() + testName := CurrentSpecReport().LeafNodeText logDirPath := logDir + testName + "/" + testId + "/" cmd := exec.Command("mkdir", "-p", logDirPath) if err := cmd.Run(); err != nil { - c.suite.T().Fatalf("mkdir error: %v", err) + Fail("mkdir error: " + fmt.Sprint(err)) } return logDirPath @@ -313,12 +314,12 @@ func (c *Container) saveLogs() { cmd = exec.Command("docker", "logs", "--details", "-t", c.name) output, err := cmd.CombinedOutput() if err != nil { - c.suite.T().Fatalf("fetching logs error: %v", err) + Fail("fetching logs error: " + fmt.Sprint(err)) } f, err := os.Create(testLogFilePath) if err != nil { - c.suite.T().Fatalf("file create error: %v", err) + Fail("file create error: " + fmt.Sprint(err)) } fmt.Fprint(f, string(output)) f.Close() diff --git a/extras/hs-test/cpu.go b/extras/hs-test/cpu.go index e17bc11fbe0..a976f47d8a5 100644 --- a/extras/hs-test/cpu.go +++ b/extras/hs-test/cpu.go @@ -2,11 +2,14 @@ package main import ( "bufio" + "errors" "fmt" "os" + "os/exec" + "strings" ) -var CPU_PATH = "/sys/fs/cgroup/cpuset.cpus.effective" +var CgroupPath = "/sys/fs/cgroup/" type CpuContext struct { cpuAllocator *CpuAllocatorT @@ -36,9 +39,27 @@ func (c *CpuAllocatorT) Allocate(nCpus int) (*CpuContext, error) { return &cpuCtx, nil } -func (c *CpuAllocatorT) readCpus(fname string) error { +func (c *CpuAllocatorT) readCpus() error { var first, last int - file, err := os.Open(CPU_PATH) + + // Path depends on cgroup version. We need to check which version is in use. + // For that following command can be used: 'stat -fc %T /sys/fs/cgroup/' + // In case the output states 'cgroup2fs' then cgroups v2 is used, 'tmpfs' in case cgroups v1. + cmd := exec.Command("stat", "-fc", "%T", "/sys/fs/cgroup/") + byteOutput, err := cmd.CombinedOutput() + if err != nil { + return err + } + CpuPath := CgroupPath + if strings.Contains(string(byteOutput), "tmpfs") { + CpuPath += "cpuset/cpuset.effective_cpus" + } else if strings.Contains(string(byteOutput), "cgroup2fs") { + CpuPath += "cpuset.cpus.effective" + } else { + return errors.New("cgroup unknown fs: " + string(byteOutput)) + } + + file, err := os.Open(CpuPath) if err != nil { return err } @@ -60,7 +81,7 @@ func (c *CpuAllocatorT) readCpus(fname string) error { func CpuAllocator() (*CpuAllocatorT, error) { if cpuAllocator == nil { cpuAllocator = new(CpuAllocatorT) - err := cpuAllocator.readCpus(CPU_PATH) + err := cpuAllocator.readCpus() if err != nil { return nil, err } diff --git a/extras/hs-test/docker/Dockerfile.vpp b/extras/hs-test/docker/Dockerfile.vpp index 6b057581d4b..ace83c593c6 100644 --- a/extras/hs-test/docker/Dockerfile.vpp +++ b/extras/hs-test/docker/Dockerfile.vpp @@ -16,6 +16,8 @@ COPY \ $DIR/unittest_plugin.so \ $DIR/quic_plugin.so \ $DIR/http_static_plugin.so \ + $DIR/ping_plugin.so \ + $DIR/nsim_plugin.so \ $DIR/prom_plugin.so \ $DIR/tlsopenssl_plugin.so \ /usr/lib/x86_64-linux-gnu/vpp_plugins/ diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go index 690f6d1378e..0515b5e0411 100644 --- a/extras/hs-test/echo_test.go +++ b/extras/hs-test/echo_test.go @@ -1,6 +1,11 @@ package main -func (s *VethsSuite) TestEchoBuiltin() { +func init() { + registerVethTests(EchoBuiltinTest) + registerSoloVethTests(TcpWithLossTest) +} + +func EchoBuiltinTest(s *VethsSuite) { serverVpp := s.getContainerByName("server-vpp").vppInstance serverVeth := s.getInterfaceByName(serverInterfaceName) @@ -16,7 +21,7 @@ func (s *VethsSuite) TestEchoBuiltin() { s.assertNotContains(o, "failed:") } -func (s *VethsSuite) TestTcpWithLoss() { +func TcpWithLossTest(s *VethsSuite) { serverVpp := s.getContainerByName("server-vpp").vppInstance serverVeth := s.getInterfaceByName(serverInterfaceName) @@ -26,14 +31,14 @@ func (s *VethsSuite) TestTcpWithLoss() { clientVpp := s.getContainerByName("client-vpp").vppInstance // Ensure that VPP doesn't abort itself with NSIM enabled - // Warning: Removing this ping will make the test fail! + // Warning: Removing this ping will make VPP crash! clientVpp.vppctl("ping %s", serverVeth.ip4AddressString()) // Add loss of packets with Network Delay Simulator clientVpp.vppctl("set nsim poll-main-thread delay 0.01 ms bandwidth 40 gbit" + " packet-size 1400 packets-per-drop 1000") - clientVpp.vppctl("nsim output-feature enable-disable " + s.getInterfaceByName(clientInterfaceName).name) + clientVpp.vppctl("nsim output-feature enable-disable host-" + s.getInterfaceByName(clientInterfaceName).name) // Do echo test from client-vpp container output := clientVpp.vppctl("test echo client uri tcp://%s/20022 verbose echo-bytes mbytes 50", diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go index 84aa570e681..8773fa2417d 100644 --- a/extras/hs-test/framework_test.go +++ b/extras/hs-test/framework_test.go @@ -3,30 +3,11 @@ package main import ( "testing" - "github.com/stretchr/testify/suite" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -func TestTapSuite(t *testing.T) { - var m TapSuite - suite.Run(t, &m) -} - -func TestNs(t *testing.T) { - var m NsSuite - suite.Run(t, &m) -} - -func TestVeths(t *testing.T) { - var m VethsSuite - suite.Run(t, &m) -} - -func TestNoTopo(t *testing.T) { - var m NoTopoSuite - suite.Run(t, &m) -} - -func TestNginx(t *testing.T) { - var m NginxSuite - suite.Run(t, &m) +func TestHst(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "HST") } diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod index 00e1213079d..0f3854d2148 100644 --- a/extras/hs-test/go.mod +++ b/extras/hs-test/go.mod @@ -4,19 +4,28 @@ go 1.21 require ( github.com/edwarnicke/exechelper v1.0.3 - github.com/stretchr/testify v1.8.4 - go.fd.io/govpp v0.9.0 + go.fd.io/govpp v0.10.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect +) + +require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/kr/text v0.2.0 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect + github.com/onsi/ginkgo/v2 v2.16.0 + github.com/onsi/gomega v1.32.0 github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/vishvananda/netns v0.0.4 // indirect golang.org/x/sys v0.16.0 // indirect diff --git a/extras/hs-test/go.sum b/extras/hs-test/go.sum index df596738622..479b0289814 100644 --- a/extras/hs-test/go.sum +++ b/extras/hs-test/go.sum @@ -1,3 +1,6 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -6,10 +9,17 @@ github.com/edwarnicke/exechelper v1.0.3 h1:OY2ocGAITTqnEDvZk0dRQSeMIQvyH0SyL/4nc github.com/edwarnicke/exechelper v1.0.3/go.mod h1:R65OUPKns4bgeHkCmfSHbmqLBU8aHZxTgLmEyUBUk4U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -18,8 +28,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= +github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -28,22 +40,27 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -go.fd.io/govpp v0.9.0 h1:EHUXhQ+dph2K2An4YMqmd/WBE3Fcqsg97KVmdLJoSoU= -go.fd.io/govpp v0.9.0/go.mod h1:9QoqjEbvfuuXNfjHS0A7YS+7QQVVaQ9cMioOWpSM4rY= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +go.fd.io/govpp v0.10.0 h1:lL93SbqOILjON2pMvazrlHRekGYTRy0Qmj57RuAkxR0= +go.fd.io/govpp v0.10.0/go.mod h1:5m3bZM9ck+2EGC2O3ASmSSJAaoouyOlVWtiwj5BdCv0= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go index c5c8edbb829..725fee73f24 100644 --- a/extras/hs-test/hst_suite.go +++ b/extras/hs-test/hst_suite.go @@ -5,14 +5,16 @@ import ( "errors" "flag" "fmt" + "io" + "log" "os" "os/exec" "strings" "time" "github.com/edwarnicke/exechelper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "gopkg.in/yaml.v3" ) @@ -28,7 +30,6 @@ var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp") var vppSourceFileDir = flag.String("vppsrc", "", "vpp source file directory") type HstSuite struct { - suite.Suite containers map[string]*Container volumes []string netConfigs []NetConfig @@ -38,15 +39,23 @@ type HstSuite struct { cpuAllocator *CpuAllocatorT cpuContexts []*CpuContext cpuPerVpp int - pid string + pid string + logger *log.Logger + logFile *os.File } func (s *HstSuite) SetupSuite() { + s.createLogger() + s.log("Suite Setup") + RegisterFailHandler(func(message string, callerSkip ...int) { + s.hstFail() + Fail(message, callerSkip...) + }) var err error s.pid = fmt.Sprint(os.Getpid()) s.cpuAllocator, err = CpuAllocator() if err != nil { - s.FailNow("failed to init cpu allocator: %v", err) + Fail("failed to init cpu allocator: " + fmt.Sprint(err)) } s.cpuPerVpp = *nConfiguredCpus } @@ -63,10 +72,13 @@ func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) { } func (s *HstSuite) TearDownSuite() { + defer s.logFile.Close() + s.log("Suite Teardown") s.unconfigureNetworkTopology() } func (s *HstSuite) TearDownTest() { + s.log("Test Teardown") if *isPersistent { return } @@ -85,6 +97,7 @@ func (s *HstSuite) skipIfUnconfiguring() { } func (s *HstSuite) SetupTest() { + s.log("Test Setup") s.skipIfUnconfiguring() s.setupVolumes() s.setupContainers() @@ -106,15 +119,15 @@ func (s *HstSuite) setupContainers() { } } -func logVppInstance(container *Container, maxLines int){ - if container.vppInstance == nil{ +func (s *HstSuite) logVppInstance(container *Container, maxLines int) { + if container.vppInstance == nil { return } logSource := container.getHostWorkDir() + defaultLogFilePath file, err := os.Open(logSource) - if err != nil{ + if err != nil { return } defer file.Close() @@ -123,7 +136,7 @@ func logVppInstance(container *Container, maxLines int){ var lines []string var counter int - for scanner.Scan(){ + for scanner.Scan() { lines = append(lines, scanner.Text()) counter++ if counter > maxLines { @@ -132,82 +145,81 @@ func logVppInstance(container *Container, maxLines int){ } } - fmt.Println("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:") - for _, line := range lines{ - fmt.Println(line) + s.log("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:") + for _, line := range lines { + s.log(line) } - fmt.Printf("^^^^^^^^^^^^^^^\n\n") + s.log("^^^^^^^^^^^^^^^\n\n") } func (s *HstSuite) hstFail() { - fmt.Println("Containers: " + fmt.Sprint(s.containers)) - for _, container := range s.containers{ + s.log("Containers: " + fmt.Sprint(s.containers)) + for _, container := range s.containers { out, err := container.log(20) - if err != nil{ + if err != nil { fmt.Printf("An error occured while obtaining '%s' container logs: %s\n", container.name, fmt.Sprint(err)) - break + continue } - fmt.Printf("\nvvvvvvvvvvvvvvv " + - container.name + ":\n" + - out + - "^^^^^^^^^^^^^^^\n\n") - logVppInstance(container, 20) + s.log("\nvvvvvvvvvvvvvvv " + + container.name + ":\n" + + out + + "^^^^^^^^^^^^^^^\n\n") + s.logVppInstance(container, 20) } - s.T().FailNow() } func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) { - if !assert.Nil(s.T(), object, msgAndArgs...) { - s.hstFail() - } + Expect(object).To(BeNil(), msgAndArgs...) } func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) { - if !assert.NotNil(s.T(), object, msgAndArgs...) { - s.hstFail() - } + Expect(object).ToNot(BeNil(), msgAndArgs...) } func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) { - if !assert.Equal(s.T(), expected, actual, msgAndArgs...) { - s.hstFail() - } + Expect(actual).To(Equal(expected), msgAndArgs...) } func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) { - if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) { - s.hstFail() - } + Expect(actual).ToNot(Equal(expected), msgAndArgs...) } func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) { - if !assert.Contains(s.T(), testString, contains, msgAndArgs...) { - s.hstFail() - } + Expect(testString).To(ContainSubstring(fmt.Sprint(contains)), msgAndArgs...) } func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) { - if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) { - s.hstFail() - } + Expect(testString).ToNot(ContainSubstring(fmt.Sprint(contains)), msgAndArgs...) } func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) { - if !assert.NotEmpty(s.T(), object, msgAndArgs...) { - s.hstFail() + Expect(object).ToNot(BeEmpty(), msgAndArgs...) +} + +func (s *HstSuite) createLogger(){ + suiteName := CurrentSpecReport().ContainerHierarchyTexts[0] + var err error + s.logFile, err = os.Create("summary/" + suiteName + ".log") + if err != nil { + Fail("Unable to create log file.") } + s.logger = log.New(io.Writer(s.logFile), "", log.LstdFlags) } -func (s *HstSuite) log(args ...any) { +// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter +// to keep console tidy +func (s *HstSuite) log(arg any) { + logs := strings.Split(fmt.Sprint(arg), "\n") + for _, line := range logs { + s.logger.Println(line) + } if *isVerbose { - s.T().Helper() - s.T().Log(args...) + GinkgoWriter.Println(arg) } } -func (s *HstSuite) skip(args ...any) { - s.log(args...) - s.T().SkipNow() +func (s *HstSuite) skip(args string) { + Skip(args) } func (s *HstSuite) SkipIfMultiWorker(args ...any) { @@ -249,11 +261,11 @@ func (s *HstSuite) getNetNamespaceByName(name string) string { } func (s *HstSuite) getInterfaceByName(name string) *NetInterface { - return s.netInterfaces[name + s.pid] + return s.netInterfaces[name+s.pid] } func (s *HstSuite) getContainerByName(name string) *Container { - return s.containers[name + s.pid] + return s.containers[name+s.pid] } /* @@ -261,25 +273,25 @@ func (s *HstSuite) getContainerByName(name string) *Container { * are not able to modify the original container and affect other tests by doing that */ func (s *HstSuite) getTransientContainerByName(name string) *Container { - containerCopy := *s.containers[name + s.pid] + containerCopy := *s.containers[name+s.pid] return &containerCopy } func (s *HstSuite) loadContainerTopology(topologyName string) { data, err := os.ReadFile(containerTopologyDir + topologyName + ".yaml") if err != nil { - s.T().Fatalf("read error: %v", err) + Fail("read error: " + fmt.Sprint(err)) } var yamlTopo YamlTopology err = yaml.Unmarshal(data, &yamlTopo) if err != nil { - s.T().Fatalf("unmarshal error: %v", err) + Fail("unmarshal error: " + fmt.Sprint(err)) } for _, elem := range yamlTopo.Volumes { volumeMap := elem["volume"].(VolumeConfig) hostDir := volumeMap["host-dir"].(string) - workingVolumeDir := logDir + s.T().Name() + s.pid + volumeDir + workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + volumeDir volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir) hostDir = volDirReplacer.Replace(hostDir) s.volumes = append(s.volumes, hostDir) @@ -287,10 +299,11 @@ func (s *HstSuite) loadContainerTopology(topologyName string) { s.containers = make(map[string]*Container) for _, elem := range yamlTopo.Containers { - newContainer, err := newContainer(s, elem, s.pid) + newContainer, err := newContainer(s, elem) newContainer.suite = s + newContainer.name += newContainer.suite.pid if err != nil { - s.T().Fatalf("container config error: %v", err) + Fail("container config error: " + fmt.Sprint(err)) } s.containers[newContainer.name] = newContainer } @@ -299,12 +312,12 @@ func (s *HstSuite) loadContainerTopology(topologyName string) { func (s *HstSuite) loadNetworkTopology(topologyName string) { data, err := os.ReadFile(networkTopologyDir + topologyName + ".yaml") if err != nil { - s.T().Fatalf("read error: %v", err) + Fail("read error: " + fmt.Sprint(err)) } var yamlTopo YamlTopology err = yaml.Unmarshal(data, &yamlTopo) if err != nil { - s.T().Fatalf("unmarshal error: %v", err) + Fail("unmarshal error: " + fmt.Sprint(err)) } s.ip4AddrAllocator = NewIp4AddressAllocator() @@ -316,10 +329,10 @@ func (s *HstSuite) loadNetworkTopology(topologyName string) { } if peer, ok := elem["peer"].(NetDevConfig); ok { - if peer["name"].(string) != ""{ + if peer["name"].(string) != "" { peer["name"] = peer["name"].(string) + s.pid } - if _, ok := peer["netns"]; ok{ + if _, ok := peer["netns"]; ok { peer["netns"] = peer["netns"].(string) + s.pid } } @@ -341,7 +354,7 @@ func (s *HstSuite) loadNetworkTopology(topologyName string) { if namespace, err := newNetNamespace(elem); err == nil { s.netConfigs = append(s.netConfigs, &namespace) } else { - s.T().Fatalf("network config error: %v", err) + Fail("network config error: " + fmt.Sprint(err)) } } case Veth, Tap: @@ -350,7 +363,7 @@ func (s *HstSuite) loadNetworkTopology(topologyName string) { s.netConfigs = append(s.netConfigs, netIf) s.netInterfaces[netIf.Name()] = netIf } else { - s.T().Fatalf("network config error: %v", err) + Fail("network config error: " + fmt.Sprint(err)) } } case Bridge: @@ -358,7 +371,7 @@ func (s *HstSuite) loadNetworkTopology(topologyName string) { if bridge, err := newBridge(elem); err == nil { s.netConfigs = append(s.netConfigs, &bridge) } else { - s.T().Fatalf("network config error: %v", err) + Fail("network config error: " + fmt.Sprint(err)) } } } @@ -374,7 +387,7 @@ func (s *HstSuite) configureNetworkTopology(topologyName string) { for _, nc := range s.netConfigs { if err := nc.configure(); err != nil { - s.T().Fatalf("network config error: %v", err) + Fail("Network config error: " + fmt.Sprint(err)) } } } @@ -389,7 +402,7 @@ func (s *HstSuite) unconfigureNetworkTopology() { } func (s *HstSuite) getTestId() string { - testName := s.T().Name() + testName := CurrentSpecReport().LeafNodeText if s.testIds == nil { s.testIds = map[string]string{} diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index 943c8a591d4..f52d2816930 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -2,11 +2,25 @@ package main import ( "fmt" + "net/http" "os" "strings" + "time" + + . "github.com/onsi/ginkgo/v2" ) -func (s *NsSuite) TestHttpTps() { +func init() { + registerNsTests(HttpTpsTest) + registerVethTests(HttpCliTest, HttpCliConnectErrorTest) + registerNoTopoTests(NginxHttp3Test, NginxAsServerTest, + NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest, HeaderServerTest, + HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest, + HttpCliBadRequestTest) + registerNoTopoSoloTests(HttpStaticPromTest) +} + +func HttpTpsTest(s *NsSuite) { iface := s.getInterfaceByName(clientInterface) client_ip := iface.ip4AddressString() port := "8080" @@ -18,13 +32,16 @@ func (s *NsSuite) TestHttpTps() { // configure vpp in the container container.vppInstance.vppctl("http tps uri tcp://0.0.0.0/8080") - go s.startWget(finished, client_ip, port, "test_file_10M", clientNetns) + go func() { + defer GinkgoRecover() + s.startWget(finished, client_ip, port, "test_file_10M", clientNetns) + }() // wait for client err := <-finished - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) } -func (s *VethsSuite) TestHttpCli() { +func HttpCliTest(s *VethsSuite) { serverContainer := s.getContainerByName("server-vpp") clientContainer := s.getContainerByName("client-vpp") @@ -35,13 +52,27 @@ func (s *VethsSuite) TestHttpCli() { uri := "http://" + serverVeth.ip4AddressString() + "/80" o := clientContainer.vppInstance.vppctl("http cli client" + - " uri " + uri + " query /show/version") + " uri " + uri + " query /show/vlib/graph") s.log(o) s.assertContains(o, "<html>", "<html> not found in the result!") } -func (s *NoTopoSuite) TestNginxHttp3() { +func HttpCliConnectErrorTest(s *VethsSuite) { + clientContainer := s.getContainerByName("client-vpp") + + serverVeth := s.getInterfaceByName(serverInterfaceName) + + uri := "http://" + serverVeth.ip4AddressString() + "/80" + + o := clientContainer.vppInstance.vppctl("http cli client" + + " uri " + uri + " query /show/vlib/graph") + + s.log(o) + s.assertContains(o, "failed to connect") +} + +func NginxHttp3Test(s *NoTopoSuite) { s.SkipUnlessExtendedTestsBuilt() query := "index.html" @@ -57,23 +88,102 @@ func (s *NoTopoSuite) TestNginxHttp3() { args := fmt.Sprintf("curl --noproxy '*' --local-port 55444 --http3-only -k https://%s:8443/%s", serverAddress, query) curlCont.extraRunningArgs = args o, err := curlCont.combinedOutput() - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.assertContains(o, "<http>", "<http> not found in the result!") } -func (s *NoTopoSuite) TestHttpStaticProm() { +func HttpStaticPromTest(s *NoTopoSuite) { finished := make(chan error, 1) query := "stats.prom" vpp := s.getContainerByName("vpp").vppInstance serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() s.log(vpp.vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers")) s.log(vpp.vppctl("prom enable")) - go s.startWget(finished, serverAddress, "80", query, "") + time.Sleep(time.Second * 5) + go func() { + defer GinkgoRecover() + s.startWget(finished, serverAddress, "80", query, "") + }() err := <-finished - s.assertNil(err, err) + s.assertNil(err) +} + +func HttpStaticMovedTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + vpp.container.exec("mkdir -p /tmp/tmp.aaa") + vpp.container.createFile("/tmp/tmp.aaa/index.html", "<http><body><p>Hello</p></body></http>") + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server www-root /tmp uri tcp://" + serverAddress + "/80 debug")) + + client := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/tmp.aaa", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(301, resp.StatusCode) + s.assertNotEqual("", resp.Header.Get("Location")) +} + +func HttpStaticNotFoundTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server www-root /tmp uri tcp://" + serverAddress + "/80 debug")) + + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/notfound.html", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := http.DefaultClient.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(404, resp.StatusCode) +} + +func HttpCliMethodNotAllowedTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := http.DefaultClient.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(405, resp.StatusCode) + // TODO: need to be fixed in http code + //s.assertNotEqual("", resp.Header.Get("Allow")) +} + +func HttpCliBadRequestTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + req, err := http.NewRequest("GET", "http://"+serverAddress+":80", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := http.DefaultClient.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(400, resp.StatusCode) +} + +func HeaderServerTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + vpp.vppctl("http cli server") + + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/show/version", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := http.DefaultClient.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual("http_cli_server", resp.Header.Get("Server")) } -func (s *NoTopoSuite) TestNginxAsServer() { +func NginxAsServerTest(s *NoTopoSuite) { query := "return_ok" finished := make(chan error, 1) @@ -86,7 +196,10 @@ func (s *NoTopoSuite) TestNginxAsServer() { serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() defer func() { os.Remove(query) }() - go s.startWget(finished, serverAddress, "80", query, "") + go func() { + defer GinkgoRecover() + s.startWget(finished, serverAddress, "80", query, "") + }() s.assertNil(<-finished) } @@ -124,9 +237,11 @@ func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error { args += " -r" args += " http://" + serverAddress + ":80/64B.json" abCont.extraRunningArgs = args + time.Sleep(time.Second * 10) o, err := abCont.combinedOutput() rps := parseString(o, "Requests per second:") - s.log(rps, err) + s.log(rps) + s.log(err) s.assertNil(err, "err: '%s', output: '%s'", err, o) } else { wrkCont := s.getContainerByName("wrk") @@ -135,20 +250,21 @@ func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error { wrkCont.extraRunningArgs = args o, err := wrkCont.combinedOutput() rps := parseString(o, "requests") - s.log(rps, err) + s.log(rps) + s.log(err) s.assertNil(err, "err: '%s', output: '%s'", err, o) } return nil } -func (s *NoTopoSuite) TestNginxPerfCps() { +func NginxPerfCpsTest(s *NoTopoSuite) { s.assertNil(runNginxPerf(s, "cps", "ab")) } -func (s *NoTopoSuite) TestNginxPerfRps() { +func NginxPerfRpsTest(s *NoTopoSuite) { s.assertNil(runNginxPerf(s, "rps", "ab")) } -func (s *NoTopoSuite) TestNginxPerfWrk() { +func NginxPerfWrkTest(s *NoTopoSuite) { s.assertNil(runNginxPerf(s, "", "wrk")) } diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go index 8d9168d3d5d..24d2de39485 100644 --- a/extras/hs-test/ldp_test.go +++ b/extras/hs-test/ldp_test.go @@ -3,9 +3,15 @@ package main import ( "fmt" "os" + + . "github.com/onsi/ginkgo/v2" ) -func (s *VethsSuite) TestLDPreloadIperfVpp() { +func init() { + registerVethTests(LDPreloadIperfVppTest) +} + +func LDPreloadIperfVppTest(s *VethsSuite) { var clnVclConf, srvVclConf Stanza serverContainer := s.getContainerByName("server-vpp") @@ -14,10 +20,7 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() { clientContainer := s.getContainerByName("client-vpp") clientVclFileName := clientContainer.getHostWorkDir() + "/vcl_cln.conf" - ldpreload := os.Getenv("HST_LDPRELOAD") - s.assertNotEqual("", ldpreload) - - ldpreload = "LD_PRELOAD=" + ldpreload + ldpreload := "LD_PRELOAD=../../build-root/build-vpp-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so" stopServerCh := make(chan struct{}, 1) srvCh := make(chan error, 1) @@ -36,7 +39,7 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() { append("use-mq-eventfd"). append(clientAppSocketApi).close(). saveToFile(clientVclFileName) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default", serverContainer.getHostWorkDir()) @@ -49,26 +52,32 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() { append("use-mq-eventfd"). append(serverAppSocketApi).close(). saveToFile(serverVclFileName) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.log("attaching server to vpp") srvEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+serverVclFileName) - go s.startServerApp(srvCh, stopServerCh, srvEnv) + go func() { + defer GinkgoRecover() + s.startServerApp(srvCh, stopServerCh, srvEnv) + }() err = <-srvCh - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.log("attaching client to vpp") var clnRes = make(chan string, 1) clnEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+clientVclFileName) serverVethAddress := s.getInterfaceByName(serverInterfaceName).ip4AddressString() - go s.startClientApp(serverVethAddress, clnEnv, clnCh, clnRes) + go func() { + defer GinkgoRecover() + s.startClientApp(serverVethAddress, clnEnv, clnCh, clnRes) + }() s.log(<-clnRes) // wait for client's result err = <-clnCh - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) // stop server stopServerCh <- struct{}{} diff --git a/extras/hs-test/linux_iperf_test.go b/extras/hs-test/linux_iperf_test.go index 06247e45240..e323f7fb721 100644 --- a/extras/hs-test/linux_iperf_test.go +++ b/extras/hs-test/linux_iperf_test.go @@ -1,6 +1,16 @@ package main -func (s *TapSuite) TestLinuxIperf() { +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" +) + +func init() { + registerTapTests(LinuxIperfTest) +} + +func LinuxIperfTest(s *TapSuite) { clnCh := make(chan error) stopServerCh := make(chan struct{}) srvCh := make(chan error, 1) @@ -9,13 +19,19 @@ func (s *TapSuite) TestLinuxIperf() { stopServerCh <- struct{}{} }() - go s.startServerApp(srvCh, stopServerCh, nil) + go func() { + defer GinkgoRecover() + s.startServerApp(srvCh, stopServerCh, nil) + }() err := <-srvCh - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.log("server running") ipAddress := s.getInterfaceByName(tapInterfaceName).ip4AddressString() - go s.startClientApp(ipAddress, nil, clnCh, clnRes) + go func() { + defer GinkgoRecover() + s.startClientApp(ipAddress, nil, clnCh, clnRes) + }() s.log("client running") s.log(<-clnRes) err = <-clnCh diff --git a/extras/hs-test/mirroring_test.go b/extras/hs-test/mirroring_test.go index 91f43f45682..6c5a860b01c 100644 --- a/extras/hs-test/mirroring_test.go +++ b/extras/hs-test/mirroring_test.go @@ -4,7 +4,11 @@ import ( "github.com/edwarnicke/exechelper" ) -func (s *NginxSuite) TestMirroring() { +func init() { + registerNginxTests(MirroringTest) +} + +func MirroringTest(s *NginxSuite) { proxyAddress := s.getInterfaceByName(mirroringClientInterfaceName).peer.ip4AddressString() path := "/64B.json" diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go index c2f9b6f2825..ac5f94c8535 100644 --- a/extras/hs-test/proxy_test.go +++ b/extras/hs-test/proxy_test.go @@ -5,8 +5,13 @@ import ( "os" "github.com/edwarnicke/exechelper" + . "github.com/onsi/ginkgo/v2" ) +func init() { + registerNsTests(VppProxyHttpTcpTest, VppProxyHttpTlsTest, EnvoyProxyHttpTcpTest) +} + func testProxyHttpTcp(s *NsSuite, proto string) error { var outputFile string = "test" + s.pid + ".data" var srcFilePid string = "httpTestFile" + s.pid @@ -19,12 +24,15 @@ func testProxyHttpTcp(s *NsSuite, proto string) error { // create test file err := exechelper.Run(fmt.Sprintf("ip netns exec %s truncate -s %s %s", serverNetns, fileSize, srcFilePid)) - s.assertNil(err, "failed to run truncate command: " + fmt.Sprint(err)) + s.assertNil(err, "failed to run truncate command: "+fmt.Sprint(err)) defer func() { os.Remove(srcFilePid) }() s.log("test file created...") - go s.startHttpServer(serverRunning, stopServer, ":666", serverNetns) + go func() { + defer GinkgoRecover() + s.startHttpServer(serverRunning, stopServer, ":666", serverNetns) + }() // TODO better error handling and recovery <-serverRunning @@ -64,21 +72,21 @@ func configureVppProxy(s *NsSuite, proto string) { clientVeth.ip4AddressString(), serverVeth.peer.ip4AddressString(), ) - s.log("proxy configured...", output) + s.log("proxy configured: " + output) } -func (s *NsSuite) TestVppProxyHttpTcp() { +func VppProxyHttpTcpTest(s *NsSuite) { proto := "tcp" configureVppProxy(s, proto) err := testProxyHttpTcp(s, proto) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) } -func (s *NsSuite) TestVppProxyHttpTls() { +func VppProxyHttpTlsTest(s *NsSuite) { proto := "tls" configureVppProxy(s, proto) err := testProxyHttpTcp(s, proto) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) } func configureEnvoyProxy(s *NsSuite) { @@ -100,8 +108,8 @@ func configureEnvoyProxy(s *NsSuite) { s.assertNil(envoyContainer.start()) } -func (s *NsSuite) TestEnvoyProxyHttpTcp() { +func EnvoyProxyHttpTcpTest(s *NsSuite) { configureEnvoyProxy(s) err := testProxyHttpTcp(s, "tcp") - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) } diff --git a/extras/hs-test/raw_session_test.go b/extras/hs-test/raw_session_test.go index 670ed582522..5c66df0b1ce 100644 --- a/extras/hs-test/raw_session_test.go +++ b/extras/hs-test/raw_session_test.go @@ -1,15 +1,19 @@ package main -func (s *VethsSuite) TestVppEchoQuic() { +func init() { + registerVethTests(VppEchoQuicTest, VppEchoTcpTest) +} + +func VppEchoQuicTest(s *VethsSuite) { s.testVppEcho("quic") } -// udp echo currently broken in vpp, skipping -func (s *VethsSuite) SkipTestVppEchoUdp() { +// TODO: udp echo currently broken in vpp +func VppEchoUdpTest(s *VethsSuite) { s.testVppEcho("udp") } -func (s *VethsSuite) TestVppEchoTcp() { +func VppEchoTcpTest(s *VethsSuite) { s.testVppEcho("tcp") } diff --git a/extras/hs-test/script/compress.sh b/extras/hs-test/script/compress.sh new file mode 100644 index 00000000000..92a2fbd6789 --- /dev/null +++ b/extras/hs-test/script/compress.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +if [ "${COMPRESS_FAILED_TEST_LOGS}" == "yes" -a -s "${HS_SUMMARY}/failed-summary.log" ] +then + echo -n "Copying docker logs..." + dirs=$(jq -r '.[0] | .SpecReports[] | select(.State == "failed") | .LeafNodeText' ${HS_SUMMARY}/report.json) + for dirName in $dirs; do + logDir=/tmp/hs-test/$dirName + if [ -d "$logDir" ]; then + mkdir -p $WORKSPACE/archives/summary + cp -r $logDir $WORKSPACE/archives/summary/ + fi + done + echo "Done." + + if [ -n "$WORKSPACE" ] + then + echo -n "Copying failed test logs into build log archive directory ($WORKSPACE/archives)... " + mkdir -p $WORKSPACE/archives/summary + cp -a ${HS_SUMMARY}/* $WORKSPACE/archives/summary + echo "Done." + fi + + echo -n "Compressing files in $WORKSPACE/archives from test runs... " + cd $WORKSPACE/archives + find . -type f \( -name "*.json" -o -name "*.log" \) -exec gzip {} \; + echo "Done." + +else + echo "Not compressing files in temporary directories from test runs." + exit 0 +fi + +exit 1
\ No newline at end of file diff --git a/extras/hs-test/suite_nginx_test.go b/extras/hs-test/suite_nginx_test.go index 8f40590d1f2..c559496e71b 100644 --- a/extras/hs-test/suite_nginx_test.go +++ b/extras/hs-test/suite_nginx_test.go @@ -1,18 +1,37 @@ package main +import ( + "reflect" + "runtime" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" +) + // These correspond to names used in yaml config const ( - vppProxyContainerName = "vpp-proxy" - nginxProxyContainerName = "nginx-proxy" - nginxServerContainerName = "nginx-server" + vppProxyContainerName = "vpp-proxy" + nginxProxyContainerName = "nginx-proxy" + nginxServerContainerName = "nginx-server" mirroringClientInterfaceName = "hstcln" mirroringServerInterfaceName = "hstsrv" ) +var nginxTests = []func(s *NginxSuite){} +var nginxSoloTests = []func(s *NginxSuite){} + type NginxSuite struct { HstSuite } +func registerNginxTests(tests ...func(s *NginxSuite)) { + nginxTests = append(nginxTests, tests...) +} +func registerNginxSoloTests(tests ...func(s *NginxSuite)) { + nginxSoloTests = append(nginxSoloTests, tests...) +} + func (s *NginxSuite) SetupSuite() { s.HstSuite.SetupSuite() s.loadNetworkTopology("2taps") @@ -60,3 +79,56 @@ func (s *NginxSuite) SetupTest() { proxyVpp.waitForApp("nginx-", 5) } + +var _ = Describe("NginxSuite", Ordered, ContinueOnFailure, func() { + var s NginxSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + for _, test := range nginxTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) + +var _ = Describe("NginxSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { + var s NginxSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range nginxSoloTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) diff --git a/extras/hs-test/suite_no_topo_test.go b/extras/hs-test/suite_no_topo_test.go index bbf0cfda685..625dca9f3cf 100644 --- a/extras/hs-test/suite_no_topo_test.go +++ b/extras/hs-test/suite_no_topo_test.go @@ -1,15 +1,34 @@ package main +import ( + "reflect" + "runtime" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" +) + const ( - singleTopoContainerVpp = "vpp" - singleTopoContainerNginx = "nginx" - tapInterfaceName = "htaphost" + singleTopoContainerVpp = "vpp" + singleTopoContainerNginx = "nginx" + tapInterfaceName = "htaphost" ) +var noTopoTests = []func(s *NoTopoSuite){} +var noTopoSoloTests = []func(s *NoTopoSuite){} + type NoTopoSuite struct { HstSuite } +func registerNoTopoTests(tests ...func(s *NoTopoSuite)) { + noTopoTests = append(noTopoTests, tests...) +} +func registerNoTopoSoloTests(tests ...func(s *NoTopoSuite)) { + noTopoSoloTests = append(noTopoSoloTests, tests...) +} + func (s *NoTopoSuite) SetupSuite() { s.HstSuite.SetupSuite() s.loadNetworkTopology("tap") @@ -35,3 +54,57 @@ func (s *NoTopoSuite) SetupTest() { s.assertNil(vpp.createTap(tapInterface), "failed to create tap interface") } + +var _ = Describe("NoTopoSuite", Ordered, ContinueOnFailure, func() { + var s NoTopoSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range noTopoTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) + +var _ = Describe("NoTopoSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { + var s NoTopoSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range noTopoSoloTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) diff --git a/extras/hs-test/suite_ns_test.go b/extras/hs-test/suite_ns_test.go index 46d5bef92ad..85b90911c2f 100644 --- a/extras/hs-test/suite_ns_test.go +++ b/extras/hs-test/suite_ns_test.go @@ -1,15 +1,35 @@ package main +import ( + "fmt" + "reflect" + "runtime" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" +) + // These correspond to names used in yaml config const ( clientInterface = "hclnvpp" serverInterface = "hsrvvpp" ) +var nsTests = []func(s *NsSuite){} +var nsSoloTests = []func(s *NsSuite){} + type NsSuite struct { HstSuite } +func registerNsTests(tests ...func(s *NsSuite)) { + nsTests = append(nsTests, tests...) +} +func registerNsSoloTests(tests ...func(s *NsSuite)) { + nsSoloTests = append(nsSoloTests, tests...) +} + func (s *NsSuite) SetupSuite() { s.HstSuite.SetupSuite() s.configureNetworkTopology("ns") @@ -34,12 +54,66 @@ func (s *NsSuite) SetupTest() { s.assertNil(vpp.start()) idx, err := vpp.createAfPacket(s.getInterfaceByName(serverInterface)) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.assertNotEqual(0, idx) idx, err = vpp.createAfPacket(s.getInterfaceByName(clientInterface)) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.assertNotEqual(0, idx) container.exec("chmod 777 -R %s", container.getContainerWorkDir()) } + +var _ = Describe("NsSuite", Ordered, ContinueOnFailure, func() { + var s NsSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range nsTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) + +var _ = Describe("NsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { + var s NsSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range nsSoloTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) diff --git a/extras/hs-test/suite_tap_test.go b/extras/hs-test/suite_tap_test.go index 8b0950a797e..ebf0f9b3cbc 100644 --- a/extras/hs-test/suite_tap_test.go +++ b/extras/hs-test/suite_tap_test.go @@ -1,15 +1,84 @@ package main import ( + "reflect" + "runtime" + "strings" "time" + + . "github.com/onsi/ginkgo/v2" ) type TapSuite struct { HstSuite } +var tapTests = []func(s *TapSuite){} +var tapSoloTests = []func(s *TapSuite){} + +func registerTapTests(tests ...func(s *TapSuite)) { + tapTests = append(tapTests, tests...) +} +func registerTapSoloTests(tests ...func(s *TapSuite)) { + tapSoloTests = append(tapSoloTests, tests...) +} + func (s *TapSuite) SetupSuite() { time.Sleep(1 * time.Second) s.HstSuite.SetupSuite() s.configureNetworkTopology("tap") } + +var _ = Describe("TapSuite", Ordered, ContinueOnFailure, func() { + var s TapSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range tapTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) + +var _ = Describe("TapSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { + var s TapSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for _, test := range tapSoloTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) diff --git a/extras/hs-test/suite_veth_test.go b/extras/hs-test/suite_veth_test.go index 061eee07d1f..d47bf8c52a9 100644 --- a/extras/hs-test/suite_veth_test.go +++ b/extras/hs-test/suite_veth_test.go @@ -1,7 +1,13 @@ package main import ( + "fmt" + "reflect" + "runtime" + "strings" "time" + + . "github.com/onsi/ginkgo/v2" ) // These correspond to names used in yaml config @@ -10,10 +16,20 @@ const ( clientInterfaceName = "cln" ) +var vethTests = []func(s *VethsSuite){} +var vethSoloTests = []func(s *VethsSuite){} + type VethsSuite struct { HstSuite } +func registerVethTests(tests ...func(s *VethsSuite)) { + vethTests = append(vethTests, tests...) +} +func registerSoloVethTests(tests ...func(s *VethsSuite)) { + vethSoloTests = append(vethSoloTests, tests...) +} + func (s *VethsSuite) SetupSuite() { time.Sleep(1 * time.Second) s.HstSuite.SetupSuite() @@ -36,7 +52,7 @@ func (s *VethsSuite) SetupTest() { cpus := s.AllocateCpus() serverVpp, err := serverContainer.newVppInstance(cpus, sessionConfig) - s.assertNotNil(serverVpp, err) + s.assertNotNil(serverVpp, fmt.Sprint(err)) s.setupServerVpp() @@ -45,7 +61,7 @@ func (s *VethsSuite) SetupTest() { cpus = s.AllocateCpus() clientVpp, err := clientContainer.newVppInstance(cpus, sessionConfig) - s.assertNotNil(clientVpp, err) + s.assertNotNil(clientVpp, fmt.Sprint(err)) s.setupClientVpp() } @@ -56,7 +72,7 @@ func (s *VethsSuite) setupServerVpp() { serverVeth := s.getInterfaceByName(serverInterfaceName) idx, err := serverVpp.createAfPacket(serverVeth) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.assertNotEqual(0, idx) } @@ -66,6 +82,63 @@ func (s *VethsSuite) setupClientVpp() { clientVeth := s.getInterfaceByName(clientInterfaceName) idx, err := clientVpp.createAfPacket(clientVeth) - s.assertNil(err, err) + s.assertNil(err, fmt.Sprint(err)) s.assertNotEqual(0, idx) } + +var _ = Describe("VethsSuite", Ordered, ContinueOnFailure, func() { + var s VethsSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + + }) + AfterEach(func() { + s.TearDownTest() + }) + + // https://onsi.github.io/ginkgo/#dynamically-generating-specs + for _, test := range vethTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) + +var _ = Describe("VethsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { + var s VethsSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + // https://onsi.github.io/ginkgo/#dynamically-generating-specs + for _, test := range vethSoloTests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(time.Minute*5)) + } +}) diff --git a/extras/hs-test/test b/extras/hs-test/test index c3b9eaef145..fd17feb2c50 100755 --- a/extras/hs-test/test +++ b/extras/hs-test/test @@ -8,6 +8,8 @@ persist_set=0 unconfigure_set=0 debug_set=0 vppsrc= +ginkgo_args= +parallel= for i in "$@" do @@ -49,8 +51,18 @@ case "${i}" in tc_name="${i#*=}" if [ $tc_name != "all" ]; then single_test=1 - args="$args -run $tc_name -verbose" + ginkgo_args="$ginkgo_args --focus $tc_name -vv" + args="$args -verbose" + else + ginkgo_args="$ginkgo_args -v" fi + ;; + --parallel=*) + ginkgo_args="$ginkgo_args -procs=${i#*=}" + ;; + --repeat=*) + ginkgo_args="$ginkgo_args --repeat=${i#*=}" + ;; esac done @@ -74,4 +86,6 @@ if [ $single_test -eq 0 ] && [ $debug_set -eq 1 ]; then exit 1 fi -sudo -E go test -timeout=20m -buildvcs=false -v $args +mkdir -p summary + +sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --no-color --trace --json-report=summary/report.json $ginkgo_args -- $args diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go index cb6aaa4adc0..fdcd60ad503 100644 --- a/extras/hs-test/vcl_test.go +++ b/extras/hs-test/vcl_test.go @@ -5,6 +5,11 @@ import ( "time" ) +func init() { + registerVethTests(XEchoVclClientUdpTest, XEchoVclClientTcpTest, XEchoVclServerUdpTest, + XEchoVclServerTcpTest, VclEchoTcpTest, VclEchoUdpTest, VclRetryAttachTest) +} + func getVclConfig(c *Container, ns_id_optional ...string) string { var s Stanza ns_id := "default" @@ -23,11 +28,11 @@ func getVclConfig(c *Container, ns_id_optional ...string) string { return s.close().toString() } -func (s *VethsSuite) TestXEchoVclClientUdp() { +func XEchoVclClientUdpTest(s *VethsSuite) { s.testXEchoVclClient("udp") } -func (s *VethsSuite) TestXEchoVclClientTcp() { +func XEchoVclClientTcpTest(s *VethsSuite) { s.testXEchoVclClient("tcp") } @@ -49,11 +54,11 @@ func (s *VethsSuite) testXEchoVclClient(proto string) { s.assertContains(o, "CLIENT RESULTS") } -func (s *VethsSuite) TestXEchoVclServerUdp() { +func XEchoVclServerUdpTest(s *VethsSuite) { s.testXEchoVclServer("udp") } -func (s *VethsSuite) TestXEchoVclServerTcp() { +func XEchoVclServerTcpTest(s *VethsSuite) { s.testXEchoVclServer("tcp") } @@ -97,16 +102,15 @@ func (s *VethsSuite) testVclEcho(proto string) { s.log(o) } -func (s *VethsSuite) TestVclEchoTcp() { +func VclEchoTcpTest(s *VethsSuite) { s.testVclEcho("tcp") } -func (s *VethsSuite) TestVclEchoUdp() { +func VclEchoUdpTest(s *VethsSuite) { s.testVclEcho("udp") } -// this test takes too long, for now it's being skipped -func (s *VethsSuite) SkipTestVclRetryAttach() { +func VclRetryAttachTest(s *VethsSuite) { s.testRetryAttach("tcp") } diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go index 1a058c8e0e3..9b400cfcb77 100644 --- a/extras/hs-test/vppinstance.go +++ b/extras/hs-test/vppinstance.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io" "os" "os/exec" "os/signal" @@ -10,7 +11,9 @@ import ( "syscall" "time" + "github.com/sirupsen/logrus" "github.com/edwarnicke/exechelper" + . "github.com/onsi/ginkgo/v2" "go.fd.io/govpp" "go.fd.io/govpp/api" @@ -59,6 +62,8 @@ plugins { plugin http_static_plugin.so { enable } plugin prom_plugin.so { enable } plugin tlsopenssl_plugin.so { enable } + plugin ping_plugin.so { enable } + plugin nsim_plugin.so { enable } } logging { @@ -103,6 +108,10 @@ func (vpp *VppInstance) getEtcDir() string { } func (vpp *VppInstance) start() error { + // Replace default logger in govpp with our own + govppLogger := logrus.New() + govppLogger.SetOutput(io.MultiWriter(vpp.getSuite().logger.Writer(), GinkgoWriter)) + core.SetLogger(govppLogger) // Create folders containerWorkDir := vpp.container.getContainerWorkDir() @@ -131,9 +140,10 @@ func (vpp *VppInstance) start() error { vpp.container.createFile(vppcliFileName, cliContent) vpp.container.exec("chmod 0755 " + vppcliFileName) + vpp.getSuite().log("starting vpp") if *isVppDebug { sig := make(chan os.Signal, 1) - signal.Notify(sig, syscall.SIGINT) + signal.Notify(sig, syscall.SIGQUIT) cont := make(chan bool, 1) go func() { <-sig @@ -143,7 +153,7 @@ func (vpp *VppInstance) start() error { vpp.container.execServer("su -c \"vpp -c " + startupFileName + " &> /proc/1/fd/1\"") fmt.Println("run following command in different terminal:") fmt.Println("docker exec -it " + vpp.container.name + " gdb -ex \"attach $(docker exec " + vpp.container.name + " pidof vpp)\"") - fmt.Println("Afterwards press CTRL+C to continue") + fmt.Println("Afterwards press CTRL+\\ to continue") <-cont fmt.Println("continuing...") } else { @@ -151,6 +161,7 @@ func (vpp *VppInstance) start() error { vpp.container.execServer("su -c \"vpp -c " + startupFileName + " &> /proc/1/fd/1\"") } + vpp.getSuite().log("connecting to vpp") // Connect to VPP and store the connection sockAddress := vpp.container.getHostWorkDir() + defaultApiSocketFilePath conn, connEv, err := govpp.AsyncConnect( @@ -207,7 +218,7 @@ func (vpp *VppInstance) GetSessionStat(stat string) int { tokens := strings.Split(strings.TrimSpace(line), " ") val, err := strconv.Atoi(tokens[0]) if err != nil { - vpp.getSuite().FailNow("failed to parse stat value %s", err) + Fail("failed to parse stat value %s" + fmt.Sprint(err)) return 0 } return val @@ -217,6 +228,7 @@ func (vpp *VppInstance) GetSessionStat(stat string) int { } func (vpp *VppInstance) waitForApp(appName string, timeout int) { + vpp.getSuite().log("waiting for app " + appName) for i := 0; i < timeout; i++ { o := vpp.vppctl("show app") if strings.Contains(o, appName) { @@ -240,6 +252,7 @@ func (vpp *VppInstance) createAfPacket( } createReply := &af_packet.AfPacketCreateV2Reply{} + vpp.getSuite().log("create af-packet interface " + veth.Name()) if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil { return 0, err } @@ -252,6 +265,7 @@ func (vpp *VppInstance) createAfPacket( } upReply := &interfaces.SwInterfaceSetFlagsReply{} + vpp.getSuite().log("set af-packet interface " + veth.Name() + " up") if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil { return 0, err } @@ -273,6 +287,7 @@ func (vpp *VppInstance) createAfPacket( } addressReply := &interfaces.SwInterfaceAddDelAddressReply{} + vpp.getSuite().log("af-packet interface " + veth.Name() + " add address " + veth.ip4Address) if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil { return 0, err } @@ -292,6 +307,7 @@ func (vpp *VppInstance) addAppNamespace( } reply := &session.AppNamespaceAddDelV2Reply{} + vpp.getSuite().log("add app namespace " + namespaceId) if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil { return err } @@ -301,6 +317,7 @@ func (vpp *VppInstance) addAppNamespace( } sessionReply := &session.SessionEnableDisableReply{} + vpp.getSuite().log("enable app namespace " + namespaceId) if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil { return err } @@ -325,6 +342,7 @@ func (vpp *VppInstance) createTap( } createTapReply := &tapv2.TapCreateV2Reply{} + vpp.getSuite().log("create tap interface " + tap.Name()) // Create tap interface if err := vpp.apiChannel.SendRequest(createTapReq).ReceiveReply(createTapReply); err != nil { return err @@ -338,6 +356,7 @@ func (vpp *VppInstance) createTap( } addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{} + vpp.getSuite().log("tap interface " + tap.Name() + " add address " + tap.peer.ip4Address) if err := vpp.apiChannel.SendRequest(addAddressReq).ReceiveReply(addAddressReply); err != nil { return err } @@ -349,6 +368,7 @@ func (vpp *VppInstance) createTap( } upReply := &interfaces.SwInterfaceSetFlagsReply{} + vpp.getSuite().log("set tap interface " + tap.Name() + " up") if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil { return err } @@ -360,7 +380,6 @@ func (vpp *VppInstance) saveLogs() { logTarget := vpp.container.getLogDirPath() + "vppinstance-" + vpp.container.name + ".log" logSource := vpp.container.getHostWorkDir() + defaultLogFilePath cmd := exec.Command("cp", logSource, logTarget) - vpp.getSuite().T().Helper() vpp.getSuite().log(cmd.String()) cmd.Run() } diff --git a/extras/scripts/crcchecker.py b/extras/scripts/crcchecker.py index 01cb02523d0..7dcdb681e18 100755 --- a/extras/scripts/crcchecker.py +++ b/extras/scripts/crcchecker.py @@ -82,13 +82,15 @@ def filelist_from_git_ls(): def is_uncommitted_changes(): """Returns true if there are uncommitted changes in the repo""" - git_status = "git status --porcelain -uno" - returncode = run(git_status.split(), stdout=PIPE, stderr=PIPE) - if returncode.returncode != 0: - sys.exit(returncode.returncode) - - if returncode.stdout: - return True + # Don't run this check in the Jenkins CI + if os.getenv("FDIOTOOLS_IMAGE") is None: + git_status = "git status --porcelain -uno" + returncode = run(git_status.split(), stdout=PIPE, stderr=PIPE) + if returncode.returncode != 0: + sys.exit(returncode.returncode) + + if returncode.stdout: + return True return False |