diff options
29 files changed, 4686 insertions, 184 deletions
diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 60753c1227..1c579a8e72 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -8,18 +8,17 @@ arch="x86_64" license="GPL-2.0" depends="json-c c-ares ipsec-tools iproute2 python py-ipaddr bash" makedepends="ncurses-dev net-snmp-dev gawk texinfo perl - acct autoconf automake bash - binutils bison bsd-compat-headers build-base - c-ares c-ares-dev ca-certificates cryptsetup-libs curl - device-mapper-libs expat fakeroot flex fortify-headers gdbm - git gmp isl json-c-dev kmod lddtree libacl libatomic libattr - libblkid libburn libbz2 libc-dev libcap-dev libcurl libedit libffi libgcc - libgomp libisoburn libisofs libltdl libressl libssh2 - libstdc++ libtool libuuid libyang-dev linux-headers lzip lzo m4 make mkinitfs mpc1 - mpfr3 mtools musl-dev ncurses-libs ncurses-terminfo ncurses-terminfo-base - patch pax-utils pcre perl pkgconf python2 python2-dev readline - readline-dev sqlite-libs squashfs-tools sudo tar texinfo xorriso xz-libs - py-pip py-sphinx rtrlib rtrlib-dev" + acct autoconf automake bash binutils bison bsd-compat-headers build-base + c-ares c-ares-dev ca-certificates cryptsetup-libs curl device-mapper-libs + expat fakeroot flex fortify-headers gdbm git gmp isl json-c-dev kmod + lddtree libacl libatomic libattr libblkid libburn libbz2 libc-dev + libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs + libltdl libressl libssh2 libstdc++ libtool libuuid libyang-dev + linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev + ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre + perl pkgconf python2 python2-dev readline readline-dev sqlite-libs + squashfs-tools sudo tar texinfo xorriso xz-libs py-pip py-sphinx rtrlib + rtrlib-dev" checkdepends="pytest py-setuptools" install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall" subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg" diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index f8cd3de68c..bc0b98517e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2394,8 +2394,9 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return 0; } -static void bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, - struct bgp_node *rn) +static struct bgp_path_info * +bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, + struct bgp_node *rn) { struct attr *attr_new; struct bgp_path_info *pi; @@ -2416,6 +2417,8 @@ static void bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, pi->extra->num_labels = parent_pi->extra->num_labels; } bgp_path_info_add(rn, pi); + + return pi; } /* Install EVPN route entry in ES */ @@ -2542,7 +2545,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, break; if (!pi) - bgp_create_evpn_bgp_path_info(parent_pi, rn); + pi = bgp_create_evpn_bgp_path_info(parent_pi, rn); else { if (attrhash_cmp(pi->attr, &attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { @@ -2606,7 +2609,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, break; if (!pi) - bgp_create_evpn_bgp_path_info(parent_pi, rn); + pi = bgp_create_evpn_bgp_path_info(parent_pi, rn); else { if (attrhash_cmp(pi->attr, parent_pi->attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index c8b7e5155f..9e1c89b71c 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -22,6 +22,7 @@ /* clang-format off */ #include <zebra.h> #include <pthread.h> // for pthread_mutex_unlock, pthread_mutex_lock +#include <sys/uio.h> // for writev #include "frr_pthread.h" #include "linklist.h" // for list_delete, list_delete_all_node, lis... @@ -275,35 +276,96 @@ static uint16_t bgp_write(struct peer *peer) { uint8_t type; struct stream *s; - int num; int update_last_write = 0; - unsigned int count = 0; + unsigned int count; uint32_t uo = 0; uint16_t status = 0; uint32_t wpkt_quanta_old; + int writenum = 0; + int num; + unsigned int iovsz; + unsigned int strmsz; + unsigned int total_written; + wpkt_quanta_old = atomic_load_explicit(&peer->bgp->wpkt_quanta, memory_order_relaxed); + struct stream *ostreams[wpkt_quanta_old]; + struct stream **streams = ostreams; + struct iovec iov[wpkt_quanta_old]; + + s = stream_fifo_head(peer->obuf); + + if (!s) + goto done; + + count = iovsz = 0; + while (count < wpkt_quanta_old && iovsz < array_size(iov) && s) { + ostreams[iovsz] = s; + iov[iovsz].iov_base = stream_pnt(s); + iov[iovsz].iov_len = STREAM_READABLE(s); + writenum += STREAM_READABLE(s); + s = s->next; + ++iovsz; + ++count; + } + + strmsz = iovsz; + total_written = 0; - while (count < wpkt_quanta_old && (s = stream_fifo_head(peer->obuf))) { - int writenum; - do { - writenum = stream_get_endp(s) - stream_get_getp(s); - num = write(peer->fd, stream_pnt(s), writenum); + do { + num = writev(peer->fd, iov, iovsz); - if (num < 0) { - if (!ERRNO_IO_RETRY(errno)) { - BGP_EVENT_ADD(peer, TCP_fatal_error); - SET_FLAG(status, BGP_IO_FATAL_ERR); - } else { - SET_FLAG(status, BGP_IO_TRANS_ERR); - } + if (num < 0) { + if (!ERRNO_IO_RETRY(errno)) { + BGP_EVENT_ADD(peer, TCP_fatal_error); + SET_FLAG(status, BGP_IO_FATAL_ERR); + } else { + SET_FLAG(status, BGP_IO_TRANS_ERR); + } + + break; + } else if (num != writenum) { + unsigned int msg_written = 0; + unsigned int ic = iovsz; - goto done; - } else if (num != writenum) - stream_forward_getp(s, num); + for (unsigned int i = 0; i < ic; i++) { + size_t ss = iov[i].iov_len; - } while (num != writenum); + if (ss > (unsigned int) num) + break; + + msg_written++; + iovsz--; + writenum -= ss; + num -= ss; + } + + total_written += msg_written; + + assert(total_written < count); + + memmove(&iov, &iov[msg_written], + sizeof(iov[0]) * iovsz); + streams = &streams[msg_written]; + stream_forward_getp(streams[0], num); + iov[0].iov_base = stream_pnt(streams[0]); + iov[0].iov_len = STREAM_READABLE(streams[0]); + + writenum -= num; + num = 0; + assert(writenum > 0); + } else { + total_written = strmsz; + } + + } while (num != writenum); + + /* Handle statistics */ + for (unsigned int i = 0; i < total_written; i++) { + s = stream_fifo_pop(peer->obuf); + + assert(s == ostreams[i]); /* Retrieve BGP packet type. */ stream_set_getp(s, BGP_MARKER_SIZE + 2); @@ -351,9 +413,8 @@ static uint16_t bgp_write(struct peer *peer) break; } - count++; - - stream_free(stream_fifo_pop(peer->obuf)); + stream_free(s); + ostreams[i] = NULL; update_last_write = 1; } diff --git a/bgpd/bgp_io.h b/bgpd/bgp_io.h index 14a12d3705..75d014f38e 100644 --- a/bgpd/bgp_io.h +++ b/bgpd/bgp_io.h @@ -22,7 +22,7 @@ #ifndef _FRR_BGP_IO_H #define _FRR_BGP_IO_H -#define BGP_WRITE_PACKET_MAX 10U +#define BGP_WRITE_PACKET_MAX 64U #define BGP_READ_PACKET_MAX 10U #include "bgpd/bgpd.h" diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index dd880768b8..eae9db5a61 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1586,36 +1586,24 @@ DEFUN (no_bgp_update_delay, } -static int bgp_wpkt_quanta_config_vty(struct vty *vty, const char *num, - char set) +static int bgp_wpkt_quanta_config_vty(struct vty *vty, uint32_t quanta, + bool set) { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (set) { - uint32_t quanta = strtoul(num, NULL, 10); - atomic_store_explicit(&bgp->wpkt_quanta, quanta, - memory_order_relaxed); - } else { - atomic_store_explicit(&bgp->wpkt_quanta, BGP_WRITE_PACKET_MAX, - memory_order_relaxed); - } + quanta = set ? quanta : BGP_WRITE_PACKET_MAX; + atomic_store_explicit(&bgp->wpkt_quanta, quanta, memory_order_relaxed); return CMD_SUCCESS; } -static int bgp_rpkt_quanta_config_vty(struct vty *vty, const char *num, - char set) +static int bgp_rpkt_quanta_config_vty(struct vty *vty, uint32_t quanta, + bool set) { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (set) { - uint32_t quanta = strtoul(num, NULL, 10); - atomic_store_explicit(&bgp->rpkt_quanta, quanta, - memory_order_relaxed); - } else { - atomic_store_explicit(&bgp->rpkt_quanta, BGP_READ_PACKET_MAX, - memory_order_relaxed); - } + quanta = set ? quanta : BGP_READ_PACKET_MAX; + atomic_store_explicit(&bgp->rpkt_quanta, quanta, memory_order_relaxed); return CMD_SUCCESS; } @@ -1636,47 +1624,32 @@ void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp) vty_out(vty, " read-quanta %d\n", quanta); } -/* Packet quanta configuration */ -DEFUN (bgp_wpkt_quanta, +/* Packet quanta configuration + * + * XXX: The value set here controls the size of a stack buffer in the IO + * thread. When changing these limits be careful to prevent stack overflow. + * + * Furthermore, the maximums used here should correspond to + * BGP_WRITE_PACKET_MAX and BGP_READ_PACKET_MAX. + */ +DEFPY (bgp_wpkt_quanta, bgp_wpkt_quanta_cmd, - "write-quanta (1-10)", - "How many packets to write to peer socket per run\n" - "Number of packets\n") -{ - int idx_number = 1; - return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 1); -} - -DEFUN (no_bgp_wpkt_quanta, - no_bgp_wpkt_quanta_cmd, - "no write-quanta (1-10)", + "[no] write-quanta (1-64)$quanta", NO_STR - "How many packets to write to peer socket per I/O cycle\n" + "How many packets to write to peer socket per run\n" "Number of packets\n") { - int idx_number = 2; - return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); + return bgp_wpkt_quanta_config_vty(vty, quanta, !no); } -DEFUN (bgp_rpkt_quanta, +DEFPY (bgp_rpkt_quanta, bgp_rpkt_quanta_cmd, - "read-quanta (1-10)", - "How many packets to read from peer socket per I/O cycle\n" - "Number of packets\n") -{ - int idx_number = 1; - return bgp_rpkt_quanta_config_vty(vty, argv[idx_number]->arg, 1); -} - -DEFUN (no_bgp_rpkt_quanta, - no_bgp_rpkt_quanta_cmd, - "no read-quanta (1-10)", + "[no] read-quanta (1-10)$quanta", NO_STR "How many packets to read from peer socket per I/O cycle\n" "Number of packets\n") { - int idx_number = 2; - return bgp_rpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); + return bgp_rpkt_quanta_config_vty(vty, quanta, !no); } void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp) @@ -13072,9 +13045,7 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_update_delay_establish_wait_cmd); install_element(BGP_NODE, &bgp_wpkt_quanta_cmd); - install_element(BGP_NODE, &no_bgp_wpkt_quanta_cmd); install_element(BGP_NODE, &bgp_rpkt_quanta_cmd); - install_element(BGP_NODE, &no_bgp_rpkt_quanta_cmd); install_element(BGP_NODE, &bgp_coalesce_time_cmd); install_element(BGP_NODE, &no_bgp_coalesce_time_cmd); diff --git a/configure.ac b/configure.ac index 730e2ae6f0..6147ebf0d8 100755 --- a/configure.ac +++ b/configure.ac @@ -536,6 +536,8 @@ AC_ARG_ENABLE([backtrace], AS_HELP_STRING([--disable-backtrace,], [disable crash backtraces (default autodetect)])) AC_ARG_ENABLE([time-check], AS_HELP_STRING([--disable-time-check], [disable slow thread warning messages])) +AC_ARG_ENABLE([cpu-time], + AS_HELP_STRING([--disable-cpu-time], [disable cpu usage data gathering])) AC_ARG_ENABLE([pcreposix], AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions])) AC_ARG_ENABLE([fpm], @@ -614,6 +616,14 @@ if test x"${enable_time_check}" != x"no" ; then fi fi +case "${enable_cpu_time}" in + "no") + AC_DEFINE([EXCLUDE_CPU_TIME], [1], [Exclude getrusage data gathering]) + ;; + "*") + ;; +esac + case "${enable_systemd}" in "no") ;; "yes") diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 5509fd5f0d..c7d722164a 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -451,7 +451,8 @@ Terminal Mode Commands This command displays system run statistics for all the different event types. If no options is specified all different run types are displayed together. Additionally you can ask to look at (r)ead, (w)rite, (t)imer, - (e)vent and e(x)ecute thread event types. + (e)vent and e(x)ecute thread event types. If you have compiled with + disable-cpu-time then this command will not show up. .. index:: show thread poll .. clicmd:: show thread poll diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index c99a5c49a3..bb60bad947 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2167,6 +2167,8 @@ Dumping Messages and Routing Tables Other BGP Commands ------------------ +The following are available in the top level *enable* mode: + .. index:: clear bgp \* .. clicmd:: clear bgp \* @@ -2202,6 +2204,24 @@ Other BGP Commands Clear peer using soft reconfiguration in this address-family and sub-address-family. +The following are available in the ``router bgp`` mode: + +.. index:: write-quanta (1-64) +.. clicmd:: write-quanta (1-64) + + BGP message Tx I/O is vectored. This means that multiple packets are written + to the peer socket at the same time each I/O cycle, in order to minimize + system call overhead. This value controls how many are written at a time. + Under certain load conditions, reducing this value could make peer traffic + less 'bursty'. In practice, leave this settings on the default (64) unless + you truly know what you are doing. + +.. index:: read-quanta (1-10) +.. index:: read-quanta (1-10) + + Unlike Tx, BGP Rx traffic is not vectored. Packets are read off the wire one + at a time in a loop. This setting controls how many iterations the loop runs + for. As with write-quanta, it is best to leave this setting on the default. .. _bgp-displaying-bgp-information: diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 45549dccad..392a2dd784 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -302,6 +302,23 @@ options from the list below. Build the Sysrepo northbound plugin. +.. option:: --enable-time-check XXX + + When this is enabled with a XXX value in microseconds, any thread that + runs for over this value will cause a warning to be issued to the log. + If you do not specify any value or don't include this option then + the default time is 5 seconds. If --disable-time-check is specified + then no warning is issued for any thread run length. + +.. option:: --disable-cpu-time + + Disable cpu process accounting, this command also disables the `show thread cpu` + command. If this option is disabled, --enable-time-check is ignored. This + disabling of cpu time effectively means that the getrusage call is skipped. + Since this is a process switch into the kernel, systems with high FRR + load might see improvement in behavior. Be aware that `show thread cpu` + is considered a good data gathering tool from the perspective of developers. + You may specify any combination of the above options to the configure script. By default, the executables are placed in :file:`/usr/local/sbin` and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/` diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 61919f0229..d3e788d5d3 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -40,6 +40,7 @@ struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"}; struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"}; struct debug nb_dbg_notif = {0, "Northbound notifications"}; struct debug nb_dbg_events = {0, "Northbound events"}; +struct debug nb_dbg_libyang = {0, "libyang debugging"}; struct nb_config *vty_shared_candidate_config; static struct thread_master *master; @@ -1581,7 +1582,7 @@ DEFPY (rollback_config, /* Debug CLI commands. */ static struct debug *nb_debugs[] = { &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc, - &nb_dbg_notif, &nb_dbg_events, + &nb_dbg_notif, &nb_dbg_events, &nb_dbg_libyang, }; static const char *const nb_debugs_conflines[] = { @@ -1590,6 +1591,7 @@ static const char *const nb_debugs_conflines[] = { "debug northbound callbacks rpc", "debug northbound notifications", "debug northbound events", + "debug northbound libyang", }; DEFINE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set)); @@ -1614,6 +1616,7 @@ DEFPY (debug_nb, callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\ |notifications$notifications\ |events$events\ + |libyang$libyang\ >]", NO_STR DEBUG_STR @@ -1623,7 +1626,8 @@ DEFPY (debug_nb, "State\n" "RPC\n" "Notifications\n" - "Events\n") + "Events\n" + "libyang debugging\n") { uint32_t mode = DEBUG_NODE2MODE(vty->node); @@ -1641,10 +1645,16 @@ DEFPY (debug_nb, DEBUG_MODE_SET(&nb_dbg_notif, mode, !no); if (events) DEBUG_MODE_SET(&nb_dbg_events, mode, !no); + if (libyang) { + DEBUG_MODE_SET(&nb_dbg_libyang, mode, !no); + yang_debugging_set(!no); + } /* no specific debug --> act on all of them */ - if (strmatch(argv[argc - 1]->text, "northbound")) + if (strmatch(argv[argc - 1]->text, "northbound")) { nb_debug_set_all(mode, !no); + yang_debugging_set(!no); + } return CMD_SUCCESS; } diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 218cae4e74..089899368d 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -616,6 +616,11 @@ class NorthboundImpl final : public frr::Northbound::Service return LYD_JSON; case frr::XML: return LYD_XML; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown data encoding format (%u)", + __func__, encoding); + exit(1); } } @@ -90,7 +90,7 @@ struct pbr_rule { uint32_t unique; struct pbr_filter filter; struct pbr_action action; - uint32_t ifindex; + ifindex_t ifindex; }; /* TCP flags value shared diff --git a/lib/thread.c b/lib/thread.c index 6669952ff4..649fe500cd 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -109,6 +109,7 @@ static void cpu_record_hash_free(void *a) XFREE(MTYPE_THREAD_STATS, hist); } +#ifndef EXCLUDE_CPU_TIME static void vty_out_cpu_thread_history(struct vty *vty, struct cpu_thread_history *a) { @@ -219,6 +220,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); } +#endif static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) { @@ -288,6 +290,7 @@ static uint8_t parse_filter(const char *filterstr) return filter; } +#ifndef EXCLUDE_CPU_TIME DEFUN (show_thread_cpu, show_thread_cpu_cmd, "show thread cpu [FILTER]", @@ -313,6 +316,7 @@ DEFUN (show_thread_cpu, cpu_record_print(vty, filter); return CMD_SUCCESS; } +#endif static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) { @@ -403,7 +407,9 @@ DEFUN (clear_thread_cpu, void thread_cmd_init(void) { +#ifndef EXCLUDE_CPU_TIME install_element(VIEW_NODE, &show_thread_cpu_cmd); +#endif install_element(VIEW_NODE, &show_thread_poll_cmd); install_element(ENABLE_NODE, &clear_thread_cpu_cmd); } @@ -1511,7 +1517,9 @@ void thread_getrusage(RUSAGE_T *r) #define FRR_RUSAGE RUSAGE_SELF #endif monotime(&r->real); +#ifndef EXCLUDE_CPU_TIME getrusage(FRR_RUSAGE, &(r->cpu)); +#endif } /* @@ -1527,9 +1535,11 @@ void thread_getrusage(RUSAGE_T *r) */ void thread_call(struct thread *thread) { +#ifndef EXCLUDE_CPU_TIME _Atomic unsigned long realtime, cputime; unsigned long exp; unsigned long helper; +#endif RUSAGE_T before, after; GETRUSAGE(&before); @@ -1541,6 +1551,7 @@ void thread_call(struct thread *thread) GETRUSAGE(&after); +#ifndef EXCLUDE_CPU_TIME realtime = thread_consumed_time(&after, &before, &helper); cputime = helper; @@ -1585,6 +1596,7 @@ void thread_call(struct thread *thread) realtime / 1000, cputime / 1000); } #endif /* CONSUMED_TIME_CHECK */ +#endif /* Exclude CPU Time */ } /* Execute thread */ diff --git a/lib/yang.c b/lib/yang.c index 5470762ea6..d153f75530 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -616,6 +616,17 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) zlog(priority, "libyang: %s", msg); } +void yang_debugging_set(bool enable) +{ + if (enable) { + ly_verb(LY_LLDBG); + ly_verb_dbg(0xFF); + } else { + ly_verb(LY_LLERR); + ly_verb_dbg(0); + } +} + struct ly_ctx *yang_ctx_new_setup(void) { struct ly_ctx *ctx; @@ -645,10 +656,6 @@ void yang_init(void) ly_set_log_clb(ly_log_cb, 1); ly_log_options(LY_LOLOG | LY_LOSTORE); - /* Let libyang log everything possible. */ - ly_verb(LY_LLDBG); - ly_verb_dbg(0xFF); - /* Initialize libyang container for native models. */ ly_native_ctx = yang_ctx_new_setup(); if (!ly_native_ctx) { diff --git a/lib/yang.h b/lib/yang.h index 322c74c76a..6892e36019 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -486,6 +486,14 @@ extern struct yang_data *yang_data_list_find(const struct list *list, extern struct ly_ctx *yang_ctx_new_setup(void); /* + * Enable or disable libyang verbose debugging. + * + * enable + * When set to true, enable libyang verbose debugging, otherwise disable it. + */ +extern void yang_debugging_set(bool enable); + +/* * Initialize the YANG subsystem. Should be called only once during the * daemon initialization process. */ diff --git a/nhrpd/list.h b/nhrpd/list.h index ee7f1c4403..590d92560c 100644 --- a/nhrpd/list.h +++ b/nhrpd/list.h @@ -199,7 +199,8 @@ static inline int list_empty(const struct list_head *n) #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ + n = (&pos->member != (head) ? \ + list_entry(pos->member.next, typeof(*pos), member) : NULL); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 50746d9ad5..ad38cad83b 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -76,8 +76,10 @@ static inline void notifier_del(struct notifier_block *n) static inline void notifier_call(struct notifier_list *l, int cmd) { struct notifier_block *n, *nn; - list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry) - n->action(n, cmd); + list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry) { + if (n) + n->action(n, cmd); + } } static inline int notifier_active(struct notifier_list *l) diff --git a/tests/topotests/bgp_large_community/__init__.py b/tests/topotests/bgp_large_community/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_large_community/__init__.py diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json new file mode 100644 index 0000000000..902c01bcbe --- /dev/null +++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json @@ -0,0 +1,262 @@ +{ + "ipv4base": "192.168.1.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.1.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "4000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r5": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r5": { + "dest_link": { + "r4-link1": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "6000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r5-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r5-link1": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json new file mode 100644 index 0000000000..6f1ca90afb --- /dev/null +++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json @@ -0,0 +1,344 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r6": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "4000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r6": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "5000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r6": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r6": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "r6": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "6000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r5": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r5": { + "dest_link": { + "r6": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py new file mode 100755 index 0000000000..83ec1e784d --- /dev/null +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py @@ -0,0 +1,1281 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test large-community/community functionality: +1. Verify if large community attribute can be configured only in correct + canonical format. +2. Verify that the community attribute value, which we have advertised are + received in correct format and values, at the receiving end. +3. Verify BGP Large Community attribute"s transitive property attribute. +4. Verify that BGP Large Communities attribute are malformed, if the length of + the BGP Large Communities Attribute value, expressed in octets, + is not a non-zero multiple of 12. +5. Verify if overriding large community values works fine. +6. Verify that large community values" aggregation works fine. +7. Standard community also work fine in conjunction with large-community. +8. Matching prefixes based on attributes other than prefix list and make use + of set clause (IPV6). +9. Matching prefixes based on attributes other than prefix list and make use + of set clause (IPV4). +10. Verify community and large-community list operations in route-map with all + clause (exact, all, any, regex) works. +11. Verify that any value in BGP Large communities for boundary values. +12. Clear BGP neighbor-ship and check if large community and community + attributes are getting re-populated. + +""" + +import pytest +import time +from os import path as os_path +import sys +from json import load as json_load + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, + write_test_footer, reset_config_on_routers, + create_route_maps, create_bgp_community_lists, + create_prefix_lists, verify_bgp_community, step, + check_address_types +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + clear_bgp_and_verify +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Save the Current Working Directory to find configuration files. +CWD = os_path.dirname(os_path.realpath(__file__)) +sys.path.append(os_path.join(CWD, "../")) +sys.path.append(os_path.join(CWD, "../lib/")) + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_large_community_topo_1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json_load(topoJson) +except IOError: + logger.info("Could not read file:", jsonFile) + +# Global variables +bgp_convergence = False +NETWORK = { + "ipv4": ["200.50.2.0", "200.50.2.1", "200.50.2.0"], + "ipv6": ["1::1", "1::2", "1::0"] +} +MASK = {"ipv4": "32", "ipv6": "128"} +NET_MASK = {"ipv4": "24", "ipv6": "120"} +IPV4_NET = ["200.50.2.0"] +IPV6_NET = ["1::0"] +CONFIG_ROUTER_R1 = False +CONFIG_ROUTER_R2 = False +CONFIG_ROUTER_ADDITIVE = False +ADDR_TYPES = [] +LARGE_COMM = { + "r1": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1", + "r2": "2:1:1 2:2:1 2:3:1 2:4:1 2:5:1", + "mal_1": "1:1 1:2 1:3 1:4 1:5", + "pf_list_1": "0:0:1 0:0:10 0:0:100", + "pf_list_2": "0:0:2 0:0:20 0:0:200", + "agg_1": "0:0:1 0:0:2 0:0:10 0:0:20 0:0:100 0:0:200 2:1:1 " + "2:2:1 2:3:1 2:4:1 2:5:1", + "agg_2": "0:0:2 0:0:20 0:0:200 2:1:1 " + "2:2:1 2:3:1 2:4:1 2:5:1" +} +STANDARD_COMM = { + "r1": "1:1 1:2 1:3 1:4 1:5", + "r2": "2:1 2:2 2:3 2:4 2:5", + "mal_1": "1 2 3 4 5", + "pf_list_1": "0:1 0:10 0:100", + "pf_list_2": "0:2 0:20 0:200", + "agg_1": "0:1 0:2 0:10 0:20 0:100 0:200 2:1 2:2 2:3 2:4 2:5", + "agg_2": "0:2 0:20 0:200 2:1 2:2 2:3 2:4 2:5" +} + + +class CreateTopo(Topo): + """ + Test topology builder + + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global ADDR_TYPES + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ##tgen.mininet_cli() + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, ("setup_module :Failed \n Error:" + " {}".format(bgp_convergence)) + + ADDR_TYPES = check_address_types() + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + + +def config_router_r1(tgen, topo, tc_name): + global CONFIG_ROUTER_R1 + + input_dict_1 = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": LARGE_COMM["r1"] + }, + "community": { + "num": STANDARD_COMM["r1"] + } + } + } + ] + } + } + } + + step("Configuring LC1 on r1") + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + # Configure neighbor for route map + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" % ( + NETWORK["ipv4"][0], MASK["ipv4"]), + "no_of_network": 4 + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "LC1", + "direction": "out" + }] + } + } + }, + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "LC1", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" % ( + NETWORK["ipv6"][0], MASK["ipv6"]), + "no_of_network": 4 + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "LC1", + "direction": "out" + }] + } + } + }, + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "LC1", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + + step("Applying LC1 on r1 neighbors and advertising networks") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + CONFIG_ROUTER_R1 = True + + +def config_router_r2(tgen, topo, tc_name): + global CONFIG_ROUTER_R2 + + input_dict = { + "r2": { + "route_maps": { + "LC2": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": LARGE_COMM["r2"] + }, + "community": { + "num": STANDARD_COMM["r2"] + } + } + } + ] + } + } + } + + step("Configuring route-maps LC2 on r2") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "LC2", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "LC2", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + + step("Applying LC2 on r2 neighbors in out direction") + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + CONFIG_ROUTER_R2 = True + + +def config_router_additive(tgen, topo, tc_name): + global CONFIG_ROUTER_ADDITIVE + + input_dict = { + "r2": { + "route_maps": { + "LC2": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": LARGE_COMM["r2"], + "action": "additive" + }, + "community": { + "num": STANDARD_COMM["r2"], + "action": "additive" + } + } + } + ] + } + } + } + + step("Configuring LC2 with community attributes as additive") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + # tgen.mininet_cli() + CONFIG_ROUTER_ADDITIVE = True + + +def config_for_as_path(tgen, topo, tc_name): + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + # Create ipv6 prefix list + input_dict_1 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv4"][0], + MASK["ipv4"]), + "action": "permit" + } + ], + "pf_list_2": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv4"][1], + MASK["ipv4"]), + "action": "permit" + } + ] + }, + "ipv6": { + "pf_list_3": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv6"][0], + MASK["ipv6"]), + "action": "permit" + } + ], + "pf_list_4": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv6"][1], + MASK["ipv6"]), + "action": "permit" + } + ] + } + + } + } + } + + step("Configuring prefix-lists on r1 to filter networks") + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_2 = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": 10, + "match": { + "ipv4": { + "prefix_lists": "pf_list_1" + } + }, + "set": { + "large_community": { + "num": LARGE_COMM["pf_list_1"] + }, + "community": { + "num": STANDARD_COMM["pf_list_1"] + } + } + }, + { + "action": "permit", + "seq_id": 20, + "match": { + "ipv6": { + "prefix_lists": "pf_list_3" + } + }, + "set": { + "large_community": { + "num": LARGE_COMM["pf_list_1"] + }, + "community": { + "num": STANDARD_COMM["pf_list_1"] + } + } + }, + { + "action": "permit", + "seq_id": 30, + "match": { + "ipv4": { + "prefix_lists": "pf_list_2" + } + }, + "set": { + "large_community": { + "num": LARGE_COMM["pf_list_2"] + }, + "community": { + "num": STANDARD_COMM["pf_list_2"] + } + } + }, + { + "action": "permit", + "seq_id": 40, + "match": { + "ipv6": { + "prefix_lists": "pf_list_4" + } + }, + "set": { + "large_community": { + "num": LARGE_COMM["pf_list_2"] + }, + "community": { + "num": STANDARD_COMM["pf_list_2"] + } + } + } + ] + } + } + } + + step("Applying prefix-lists match in route-map LC1 on r1. Setting" + " community attritbute for filtered networks") + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + config_router_additive(tgen, topo, tc_name) + + input_dict_3 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": LARGE_COMM["pf_list_1"], + "large": True + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": STANDARD_COMM["pf_list_1"], + } + ] + } + } + + step("Configuring bgp community lists on r4") + result = create_bgp_community_lists(tgen, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_4 = { + "r4": { + "route_maps": { + "LC4": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large_community_list": {"id": "ANY"}, + "community_list": {"id": "ANY"} + }, + "set": { + "aspath": { + "as_num": "4000000", + "as_action": "prepend" + } + } + } + ] + } + } + } + + step("Applying community list on route-map on r4") + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "LC4", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "LC4", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + + step("Applying route-map LC4 out from r4 to r5 ") + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + +##################################################### +# +# Test cases +# +##################################################### +def test_large_community_set(request): + """ + Verify if large community attribute can be configured only in correct + canonical format. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # API call to modify router id + # input_dict dictionary to be provided to configure route_map + input_dict = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r1"]}, + "community": {"num": STANDARD_COMM["r1"]} + } + } + ] + } + } + } + + step("Trying to set bgp communities") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_advertise(request): + """ + Verify that the community attribute value, which we have advertised are + received in correct format and values, at the receiving end. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], + input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_bgp_community(tgen, adt, "r3", [NETWORK[adt][0]], + input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_transitive(request): + """ + Verify BGP Large Community attribute"s transitive property attribute. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + config_router_r1(tgen, topo, tc_name) + + input_dict_1 = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"] + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], + input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_override(request): + """ + Verify if overriding large community values works fine. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + input_dict_3 = { + "largeCommunity": LARGE_COMM["r2"], + "community": STANDARD_COMM["r2"] + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][1]], + input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_additive(request): + """ + Verify that large community values" aggregation works fine. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + config_router_additive(tgen, topo, tc_name) + + input_dict_1 = { + "largeCommunity": "%s %s" % (LARGE_COMM["r1"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["r1"], STANDARD_COMM["r2"]) + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], + input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_match_as_path(request): + """ + Matching prefixes based on attributes other than prefix list and make use + of set clause. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_for_as_path(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": "%s %s" % ( + LARGE_COMM["pf_list_1"], LARGE_COMM["r2"]), + "community": "%s %s" % ( + STANDARD_COMM["pf_list_1"], STANDARD_COMM["r2"]), + } + + input_dict_1 = { + "largeCommunity": "%s %s" % ( + LARGE_COMM["pf_list_2"], LARGE_COMM["r2"]), + "community": "%s %s" % ( + STANDARD_COMM["pf_list_2"], STANDARD_COMM["r2"]), + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r5", [NETWORK[adt][0]], + input_dict) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + result = verify_bgp_community(tgen, adt, "r5", [NETWORK[adt][1]], + input_dict_1, expected=False) + + assert result is not True, "Test case {} : Should fail \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_match_all(request): + """ + Verify community and large-community list operations in route-map with all + clause (exact, all, any, regex) works. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + config_router_additive(tgen, topo, tc_name) + + input_dict_1 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "1:1:1", + "large": True + }, + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1", + "large": True + }, + { + "community_type": "expanded", + "action": "permit", + "name": "EXP_ALL", + "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:[1-5]:1", + "large": True + } + ] + } + } + + step("Create bgp community lists for ANY, EXACT and EXP_ALL match") + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_2 = { + "r4": { + "route_maps": { + "LC4": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ANY"}} + }, + { + "action": "permit", + "seq_id": "20", + "match": {"large-community-list": {"id": "EXACT"}} + }, + { + "action": "permit", + "seq_id": "30", + "match": {"large-community-list": {"id": "EXP_ALL"}} + } + ] + } + } + } + + step("Applying bgp community lits on LC4 route-map") + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_3 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "LC4", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "LC4", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + + step("Apply route-mpa LC4 on r4 for r2 neighbor, direction 'in'") + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_4 = { + "largeCommunity": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1 2:3:1 " + "2:4:1 2:5:1" + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], + input_dict_4) + assert result is True, "Test case {} : Should fail \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +#@pytest.mark.skip(reason="as-set not working for ipv6") +def test_large_community_aggregate_network(request): + """ + Restart router and check if large community and community + attributes are getting re-populated. + """ + + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + config_for_as_path(tgen, topo, tc_name) + + input_dict = { + "community": STANDARD_COMM["agg_1"], + "largeCommunity": LARGE_COMM["agg_1"] + } + + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "aggregate_address": [ + { + "network": "%s/%s" % ( + NETWORK["ipv4"][2], NET_MASK["ipv4"]), + "as_set": True + } + ] + } + }, + "ipv6": { + "unicast": { + "aggregate_address": [ + { + "network": "%s/%s" % ( + NETWORK["ipv6"][2], NET_MASK["ipv6"]), + "as_set": True + } + ] + } + } + } + } + } + } + + step("Configuring aggregate address as-set on r2") + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", + ["%s/%s" % (NETWORK[adt][2], + NET_MASK[adt])], + input_dict) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" % ( + NETWORK["ipv4"][0], MASK["ipv4"]), + "no_of_network": 1, + "delete": True + } + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" % ( + NETWORK["ipv6"][0], MASK["ipv6"]), + "no_of_network": 1, + "delete": True + } + ] + } + } + } + } + } + } + + step("Stop advertising one of the networks") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_3 = { + "community": STANDARD_COMM["agg_2"], + "largeCommunity": LARGE_COMM["agg_2"] + } + + for adt in ADDR_TYPES: + step("Verifying bgp community values on r5 is also modified") + result = verify_bgp_community(tgen, adt, "r4", + ["%s/%s" % (NETWORK[adt][2], + NET_MASK[adt])], + input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_boundary_values(request): + """ + Verify that any value in BGP Large communities for boundary values. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:-1" + } + ] + } + } + + step("Checking boundary value for community 0:-1") + result = create_bgp_community_lists(tgen, input_dict) + assert result is not True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Checking community attribute 0:65536") + input_dict_2 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:65536" + } + ] + } + } + + step("Checking boundary value for community 0:65536") + result = create_bgp_community_lists(tgen, input_dict_2) + assert result is not True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Checking boundary value for community 0:4294967296") + input_dict_3 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:4294967296", + "large": True + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_3) + assert result is not True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + step("Checking boundary value for community 0:-1:1") + + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:-1:1", + "large": True + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is not True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + +def test_large_community_after_clear_bgp(request): + """ + Clear BGP neighbor-ship and check if large community and community + attributes are getting re-populated. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"] + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], + input_dict) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Clearing BGP on r1") + clear_bgp_and_verify(tgen, topo, "r1") + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], + input_dict) + assert result is True, "Test case {} : Failed \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py new file mode 100755 index 0000000000..cba20551cd --- /dev/null +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py @@ -0,0 +1,2408 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +#Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_large_community_topo_1.py: Test BGP large community. + +Following tests are covered: +1. Verify the standard large-community-lists can permit or deny + large community attribute only in the correct canonical format. +2. Verify the expanded large-community-lists can permit or deny + large community attribute both in the correct canonical format + as well as REG_EX. +3. Verify that we can modify a large-community-list is in use, + to add/remove attribute value and it takes immediate effect. +4. Verify that large community attribute gets advertised when + route-map is applied to a neighbor and cleared when route-map + is removed. +5. Verify that duplicate BGP Large Community values are NOT be transmitted. +6. Verify if we want to remove all the large-community attributes from a + set of prefix we can set the value as NONE. +7. Redistribute connected and static routes in BGP process with a route-map + appending/removing L-comm attributes. +8. Verify if we want to remove specific large-community values from + a set of prefix we can make use of DELETE operation based on L-comm list. +9. Verify that if community values are NOT be advertised to a specific + neighbour, we negate send-community command. + (Send-community all is enabled by default for all neighbors) +10. Verify that large-community lists can not be configured without providing + specific L-community values(for match/delete operation in a route-map). +11. Verify that Match_EXACT clause should pass only if all of the L-comm + values configured (horizontally) in the community list is present in + the prefix. There must be no additional L-communities in the prefix. +12. Verify that Match_ALL clause should pass only if ALL of the L-comm values + configured (horizontally) in the community list is present in the prefix. + There could be additional L-communities in the prefix that are not present + in the L-comm list. +13. Verify that Match_ANY clause should pass only if at-least any one L-comm + value configured(vertically) in large-community list, is present in prefixes. +14. Verify large-community lists operation in a route-map with match RegEx + statements. +""" + +import os +import sys +import json +import pytest +import time + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +# Import topoJson from lib, to create topology and initial configuration +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, + write_test_footer, reset_config_on_routers, + create_route_maps, create_bgp_community_lists, + create_prefix_lists, verify_bgp_community, step, + verify_create_community_list, delete_route_maps, + verify_route_maps, create_static_routes, + check_address_types +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + clear_bgp_and_verify +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_large_community_topo_2.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False + +NETWORKS = {"ipv4": ["200.50.2.0/32"], "ipv6": ["1::1/128"]} + + +class GenerateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("="*40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(GenerateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence, ADDR_TYPES + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + # Ipv4 + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, ("setup_module :Failed \n Error:" + " {}".format(bgp_convergence)) + ADDR_TYPES = check_address_types() + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}".\ + format(time.asctime(time.localtime(time.time())))) + logger.info("="*40) + +##################################################### +# +# Testcases +# +##################################################### + + +def test_create_bgp_standard_large_community_list(request): + """ + Create standard large-community-list and verify it can permit + or deny large community attribute only in the correct canonical + format. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("Create srtandard large community list") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_1_STD", + "value": "2:1:1 2:1:2 1:2:3", + "large": True + }, + { + "community_type": "standard", + "action": "permit", + "name": "LC_2_STD", + "value": "3:1:1 3:1:2", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create srtandard large community list with in-correct values") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_1_STD_ERR", + "value": "0:0:0", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ## TODO should fail + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_create_bgp_expanded_large_community_list(request): + """ + Create expanded large-community-list and verify it can permit + or deny large community attribute both in the correct canonical + format as well as REG_EX + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create expanded large community list") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "LC_1_EXP", + "value": "1:1:200 1:2:* 3:2:1", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_large_community_lists_referenced_by_rmap(request): + """ + This test is to verify that we can modify a large-community-list + is in use, add/remove attribute value and it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create standard large community list") + input_dict_1 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_DEL", + "value": "1:2:1 1:3:1 2:1:1 2:2:2 3:3:3", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_2 = { + "r1": { + "route_maps": { + "RM_R2_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:2:1 1:3:1 2:10:1 3:3:3 4:4:4 5:5:5", + "action": "additive" + } + } + } + ] + } + }, + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_comm_list": { + "id": "LC_DEL", + "delete": True + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "RM_R2_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "RM_R2_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify Community-list") + dut = "r4" + input_dict_4 = { + "largeCommunity": "2:10:1 4:4:4 5:5:5" + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_apply_and_remove(request): + """ + This test is to verify that large community attribute gets advertised when + route-map is applied to a neighbor and cleared when route-map is removed + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "200:200:1 200:200:10 200:200:20000", + "action": "additive" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_LC1", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_LC1", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = { + "largeCommunity": "200:200:1 200:200:10 200:200:20000" + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Delete route map reference by community-list") + input_dict_3 = { + "r4": { + "route_maps": ["RM_LC1"] + } + } + result = delete_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify route map is deleted") + result = verify_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_duplicate_large_community_list_attributes_not_transitive(request): + """ + This test is to verify that duplicate BGP Large Community values + are NOT be transmitted. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3" + " 2:0:4 2:0:5", + "action": "additive" + } + } + } + ], + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:10000 2:0:1 2:0:2", + "action": "additive" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + }, + "r6": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + }, + "r6": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = { + "largeCommunity": + "0:0:1 0:0:10 0:0:100 0:0:10000 2:0:1 2:0:2 2:0:3 2:0:4 2:0:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_set_none(request): + """ + This test is to verify if we want to remove all the large-community + attributes from a set of prefix we can set the value as NONE. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3" + " 2:0:4", + "action": "additive" + } + } + } + ] + } + }, + "r6": { + "route_maps": { + "RM_R6_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "none" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [{ + "name": "RM_R6_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [{ + "name": "RM_R6_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify Community-list") + dut = "r6" + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_lcomm_lists_with_redistribute_static_connected_rmap(request): + """ + This test is to verify redistribute connected and static ipv4 routes + in BGP process with a route-map appending/removing L-comm attributes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("create static routes") + input_dict = { + "r1": { + "static_routes": [ + { + "network": "200.50.2.0/32", + "next_hop": "10.0.0.6" + }, + { + "network": "1::1/128", + "next_hop": "fd00:0:0:1::2" + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("redistribute static routes") + input_dict_1 = { + "r1":{ + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": "route-map RM_R2_OUT" + }, + { + "redist_type": "connected", + "attribute": "route-map RM_R2_OUT" + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": "route-map RM_R2_OUT" + }, + { + "redist_type": "connected", + "attribute": "route-map RM_R2_OUT" + } + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_3 = { + "r1": { + "route_maps": { + "RM_R2_OUT": [{ + "action": "permit", + "set": { + "large_community": {"num":"55:55:55 555:555:555"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list for static and connected ipv4 route on" + " r2") + + input_dict_5 = { + "largeCommunity": "55:55:55 555:555:555" + } + + if "ipv4" in ADDR_TYPES: + dut = "r2" + networks = ["200.50.2.0/32", "1.0.1.17/32"] + result = verify_bgp_community(tgen, "ipv4", dut, networks, + input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list for static and connected ipv4 route" + " on r4") + dut = "r4" + networks = ["200.50.2.0/32", "1.0.1.17/32"] + result = verify_bgp_community(tgen, "ipv4", dut, networks, + input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + if "ipv6" in ADDR_TYPES: + step("Verify large-community-list for static and connected ipv6 route" + " on r2") + dut = "r2" + networks = ["1::1/128", "2001:db8:f::1:17/128"] + result = verify_bgp_community(tgen, "ipv6", dut, networks, + input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list for static and connected ipv6 route" + " on r4") + dut = "r4" + networks = ["1::1/128", "2001:db8:f::1:17/128"] + result = verify_bgp_community(tgen, "ipv6", dut, networks, + input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_set_delete(request): + """ + This test is to verify if we want to remove specific large-community + values from a set of prefix we can make use of DELETE operation based + on L-comm list + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("configure route_map") + input_dict_2 = { + "r6": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "Test", + "value": "1:2:1 1:1:10 1:3:100", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_3 = { + "r6": { + "route_maps": { + "RM_R6_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_comm_list": { + "id": "Test", + "delete": True + } + } + } + ] + } + }, + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:2:1 1:1:10 1:3:100 2:1:1 2:2:2 2:3:3" + " 2:4:4 2:5:5", + "action": "additive" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [{ + "name": "RM_R6_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [{ + "name": "RM_R6_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_5 = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_no_send_community(request): + """ + This test is to verify if we want to remove specific large-community + values from a set of prefix we can make use of DELETE operation based + on L-comm list + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r5": { + "route_maps": { + "RM_R6_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "route_maps": [{ + "name": "RM_R6_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "route_maps": [{ + "name": "RM_R6_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for no-send-community") + input_dict_5 = { + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "no_send_community": "large" + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "no_send_community": "large" + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify Community-list") + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_create_large_community_lists_with_no_attribute_values(request): + """ + This test is to verify that large-community lists can not be + configured without providing specific L-community values + (for match/delete operation in a route-map). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create standard large commumity-list") + input_dict_1 = { + "r5": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "Test1", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_exact(request): + """ + This test is to verify that Match_EXACT clause should pass + only if all of the L-comm values configured (horizontally) + in the community list is present in the prefix. There must + be no additional L-communities in the prefix. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "EXACT", + "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large-community-list": ["EXACT"], + "match_exact": True + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_4 = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_all(request): + """ + This test is to verify that Match_ALL clause should pass + only if ALL of the L-comm values configured (horizontally) + in the community list are present in the prefix. There + could be additional L-communities in the prefix that are + not present in the L-comm list. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [{ + "action": "permit", + "set": { + "large_community": { + "num": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large-community-list": { + "id": "ALL" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_4 = { + "largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_any(request): + """ + This test is to verify that Match_ANY clause should pass + only if at-least any one L-comm value configured(vertically) + in large-community list, is present in prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:1:1", + "large": True + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:2:1", + "large": True + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:3:1", + "large": True + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:4:1", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large-community-list": { + "id": "ANY" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_regex(request): + """ + This test is to verify large-community lists" operation in a route-map + with match RegEx statements. Match clause should pass only if the + complete string of L-comm values are matched + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5", + }, + "community": { + "num": "1:1 1:2 1:3 1:4 1:5" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "1::1/128"} + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "RM_R4_OUT", + "direction": "out" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo,input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "1:1:1 2:1:3 2:1:4 2:1:5", + "large": True + }, + { + "community_type": "expanded", + "action": "permit", + "name": "EXP_ALL", + "value": "1:1:1 2:1:[3-5]", + "large": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large_community_list": { + "id": "ALL", + }, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "RM_R4_IN", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = { + "largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Delete route map reference by community-list") + input_dict_3 = { + "r4": { + "route_maps": ["RM_R4_IN"] + } + } + result = delete_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "20", + "match": { + "large_community_list": { + "id": "EXP_ALL", + }, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("clear ip bgp") + result = clear_bgp_and_verify(tgen, topo, 'r4') + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = { + "largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], + input_dict_7, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".\ + format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 41d6a326cf..7ec584bf5f 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -200,29 +200,6 @@ def __create_bgp_global(tgen, input_dict, router, build=False): config_data.append("bgp router-id {}".format( router_id)) - aggregate_address = bgp_data.setdefault("aggregate_address", - {}) - if aggregate_address: - network = aggregate_address.setdefault("network", None) - if not network: - logger.error("Router %s: 'network' not present in " - "input_dict for BGP", router) - else: - cmd = "aggregate-address {}".format(network) - - as_set = aggregate_address.setdefault("as_set", False) - summary = aggregate_address.setdefault("summary", False) - del_action = aggregate_address.setdefault("delete", False) - if as_set: - cmd = "{} {}".format(cmd, "as-set") - if summary: - cmd = "{} {}".format(cmd, "summary") - - if del_action: - cmd = "no {}".format(cmd) - - config_data.append(cmd) - return config_data @@ -300,15 +277,25 @@ def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router, ebgp )) - aggregate_address = addr_data.setdefault("aggregate_address", - {}) - if aggregate_address: - ip = aggregate_address("network", None) - attribute = aggregate_address("attribute", None) - if ip: - cmd = "aggregate-address {}".format(ip) - if attribute: - cmd = "{} {}".format(cmd, attribute) + aggregate_addresses = addr_data.setdefault("aggregate_address", []) + for aggregate_address in aggregate_addresses: + network = aggregate_address.setdefault("network", None) + if not network: + logger.debug("Router %s: 'network' not present in " + "input_dict for BGP", router) + else: + cmd = "aggregate-address {}".format(network) + + as_set = aggregate_address.setdefault("as_set", False) + summary = aggregate_address.setdefault("summary", False) + del_action = aggregate_address.setdefault("delete", False) + if as_set: + cmd = "{} as-set".format(cmd) + if summary: + cmd = "{} summary".format(cmd) + + if del_action: + cmd = "no {}".format(cmd) config_data.append(cmd) @@ -481,14 +468,20 @@ def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type, send_community = peer.setdefault("send_community", None) prefix_lists = peer.setdefault("prefix_lists", {}) route_maps = peer.setdefault("route_maps", {}) + no_send_community = peer.setdefault("no_send_community", None) # next-hop-self if next_hop_self: config_data.append("{} next-hop-self".format(neigh_cxt)) - # no_send_community + # send_community if send_community: config_data.append("{} send-community".format(neigh_cxt)) + # no_send_community + if no_send_community: + config_data.append("no {} send-community {}".format( + neigh_cxt, no_send_community)) + if prefix_lists: for prefix_list in prefix_lists: name = prefix_list.setdefault("name", {}) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index c413bf45c7..38b97cba2d 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -44,7 +44,7 @@ from lib.topotest import interface_set_status FRRCFG_FILE = "frr_json.conf" FRRCFG_BKUP_FILE = "frr_json_initial.conf" -ERROR_LIST = ["Malformed", "Failure", "Unknown"] +ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"] ROUTER_LIST = [] #### @@ -617,7 +617,7 @@ def write_test_header(tc_name): """ Display message at beginning of test case""" count = 20 logger.info("*"*(len(tc_name)+count)) - logger.info("START -> Testcase : %s" % tc_name) + step("START -> Testcase : %s" % tc_name, reset=True) logger.info("*"*(len(tc_name)+count)) @@ -709,9 +709,9 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0): kwargs.pop('expected') ret = func(*args, **kwargs) logger.debug("Function returned %s" % ret) - if return_is_str and isinstance(ret, bool): + if return_is_str and isinstance(ret, bool) and _expected: return ret - elif return_is_str and _expected is False: + if isinstance(ret, str) and _expected is False: return ret if _attempts == i: @@ -732,6 +732,31 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0): return _retry +class Stepper: + """ + Prints step number for the test case step being executed + """ + count = 1 + + def __call__(self, msg, reset): + if reset: + Stepper.count = 1 + logger.info(msg) + else: + logger.info("STEP %s: '%s'", Stepper.count, msg) + Stepper.count += 1 + + +def step(msg, reset=False): + """ + Call Stepper to print test steps. Need to reset at the beginning of test. + * ` msg` : Step message body. + * `reset` : Reset step count to 1 when set to True. + """ + _step = Stepper() + _step(msg, reset) + + ############################################# # These APIs, will used by testcase ############################################# @@ -1949,3 +1974,61 @@ def verify_bgp_community(tgen, addr_type, router, network, input_dict=None): logger.debug("Exiting lib API: verify_bgp_community()") return True + + +def verify_create_community_list(tgen, input_dict): + """ + API is to verify if large community list is created for any given DUT in + input_dict by running "sh bgp large-community-list {"comm_name"} detail" + command. + Parameters + ---------- + * `tgen`: topogen object + * `input_dict`: having details like - for which router, large community + needs to be verified + Usage + ----- + input_dict = { + "r1": { + "large-community-list": { + "standard": { + "Test1": [{"action": "PERMIT", "attribute":\ + ""}] + }}}} + result = verify_create_community_list(tgen, input_dict) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_create_community_list()") + + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + + logger.info("Verifying large-community is created for dut %s:", + router) + + for comm_data in input_dict[router]["bgp_community_lists"]: + comm_name = comm_data["name"] + comm_type = comm_data["community_type"] + show_bgp_community = \ + run_frr_cmd(rnode, + "show bgp large-community-list {} detail". + format(comm_name)) + + # Verify community list and type + if comm_name in show_bgp_community and comm_type in \ + show_bgp_community: + logger.info("BGP %s large-community-list %s is" + " created", comm_type, comm_name) + else: + errormsg = "BGP {} large-community-list {} is not" \ + " created".format(comm_type, comm_name) + return errormsg + + logger.debug("Exiting lib API: verify_create_community_list()") + return True diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 0c7a7471b1..643dcb7edc 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1339,7 +1339,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_end_all, vtysh_end_all_cmd, "end", } DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, - "router bgp [(1-4294967295)$instasn [<view|vrf> WORD]]", + "router bgp [(1-4294967295) [<view|vrf> WORD]]", ROUTER_STR BGP_STR AS_STR "BGP view\nBGP VRF\n" "View/VRF name\n") @@ -2255,6 +2255,7 @@ DEFUN (vtysh_show_poll, return ret; } +#ifndef EXCLUDE_CPU_TIME DEFUN (vtysh_show_thread, vtysh_show_thread_cmd, "show thread cpu [FILTER]", @@ -2281,6 +2282,7 @@ DEFUN (vtysh_show_thread, } return ret; } +#endif DEFUN (vtysh_show_work_queues, vtysh_show_work_queues_cmd, @@ -2427,10 +2429,10 @@ DEFUN (vtysh_show_error_code, /* Northbound. */ DEFUN (show_yang_operational_data, show_yang_operational_data_cmd, - "show yang operational-data XPATH$xpath\ + "show yang operational-data XPATH\ [{\ - format <json$json|xml$xml>\ - |translate WORD$translator_family\ + format <json|xml>\ + |translate WORD\ }]" DAEMONS_LIST, SHOW_STR "YANG information\n" @@ -2454,9 +2456,10 @@ DEFUNSH(VTYSH_ALL, debug_nb, debug_nb_cmd, "[no] debug northbound\ [<\ - callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\ - |notifications$notifications\ - |events$events\ + callbacks [{configuration|state|rpc}]\ + |notifications\ + |events\ + |libyang\ >]", NO_STR DEBUG_STR @@ -2466,7 +2469,8 @@ DEFUNSH(VTYSH_ALL, debug_nb, "State\n" "RPC\n" "Notifications\n" - "Events\n") + "Events\n" + "libyang debugging\n") { return CMD_SUCCESS; } @@ -4077,7 +4081,9 @@ void vtysh_init_vty(void) install_element(VIEW_NODE, &vtysh_show_modules_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); +#ifndef EXCLUDE_CPU_TIME install_element(VIEW_NODE, &vtysh_show_thread_cmd); +#endif install_element(VIEW_NODE, &vtysh_show_poll_cmd); /* Logging */ diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 711c4e0877..2fdb215128 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -86,9 +86,8 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); /* interface on which applied */ - if (rule->ifp) - addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifp->name, - strlen(rule->ifp->name) + 1); + addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifname, + strlen(rule->ifname) + 1); /* source IP, if specified */ if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { @@ -122,8 +121,7 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) zlog_debug( "Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u", nl_msg_type_to_str(cmd), nl_family_to_str(family), - rule->ifp ? rule->ifp->name : "Unknown", - rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, + rule->ifname, rule->rule.ifindex, rule->rule.priority, rule->rule.filter.fwmark, prefix2str(&rule->rule.filter.src_ip, buf1, sizeof(buf1)), @@ -227,13 +225,15 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[FRA_IFNAME] == NULL) return 0; - /* If we don't know the interface, we don't care. */ ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); zns = zebra_ns_lookup(ns_id); - rule.ifp = if_lookup_by_name_per_ns(zns, ifname); - if (!rule.ifp) + + /* If we don't know the interface, we don't care. */ + if (!if_lookup_by_name_per_ns(zns, ifname)) return 0; + strlcpy(rule.ifname, ifname, sizeof(rule.ifname)); + if (tb[FRA_PRIORITY]) rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); @@ -268,8 +268,8 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) zlog_debug( "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(frh->family), rule.ifp->name, - rule.ifp->ifindex, rule.rule.priority, + nl_family_to_str(frh->family), rule.ifname, + rule.rule.ifindex, rule.rule.priority, prefix2str(&rule.rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&rule.rule.filter.dst_ip, buf2, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index b0488b7559..e61e68b7fe 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -783,10 +783,7 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, stream_putl(s, rule->rule.seq); stream_putl(s, rule->rule.priority); stream_putl(s, rule->rule.unique); - if (rule->ifp) - stream_putl(s, rule->ifp->ifindex); - else - stream_putl(s, 0); + stream_putl(s, rule->rule.ifindex); stream_putw_at(s, 0, stream_get_endp(s)); @@ -2294,7 +2291,6 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) struct zebra_pbr_rule zpr; struct stream *s; uint32_t total, i; - ifindex_t ifindex; s = msg; STREAM_GETL(s, total); @@ -2319,17 +2315,20 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) STREAM_GETW(s, zpr.rule.filter.dst_port); STREAM_GETL(s, zpr.rule.filter.fwmark); STREAM_GETL(s, zpr.rule.action.table); - STREAM_GETL(s, ifindex); + STREAM_GETL(s, zpr.rule.ifindex); - if (ifindex) { - zpr.ifp = if_lookup_by_index_per_ns( - zvrf->zns, - ifindex); - if (!zpr.ifp) { + if (zpr.rule.ifindex) { + struct interface *ifp; + + ifp = if_lookup_by_index_per_ns(zvrf->zns, + zpr.rule.ifindex); + if (!ifp) { zlog_debug("Failed to lookup ifindex: %u", - ifindex); + zpr.rule.ifindex); return; } + + strlcpy(zpr.ifname, ifp->name, sizeof(zpr.ifname)); } if (!is_default_prefix(&zpr.rule.filter.src_ip)) diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index f95a4ff950..e24d2e2b42 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -144,17 +144,12 @@ uint32_t zebra_pbr_rules_hash_key(const void *arg) key = jhash_3words(rule->rule.seq, rule->rule.priority, rule->rule.action.table, prefix_hash_key(&rule->rule.filter.src_ip)); - if (rule->ifp) - key = jhash_1word(rule->ifp->ifindex, key); - else - key = jhash_1word(0, key); if (rule->rule.filter.fwmark) - key = jhash_1word(rule->rule.filter.fwmark, key); + key = jhash_3words(rule->rule.filter.fwmark, rule->vrf_id, + rule->rule.ifindex, key); else - key = jhash_1word(0, key); - - key = jhash_1word(rule->vrf_id, key); + key = jhash_2words(rule->vrf_id, rule->rule.ifindex, key); return jhash_3words(rule->rule.filter.src_port, rule->rule.filter.dst_port, @@ -196,7 +191,7 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip)) return false; - if (r1->ifp != r2->ifp) + if (r1->rule.ifindex != r2->rule.ifindex) return false; if (r1->vrf_id != r2->vrf_id) @@ -208,7 +203,7 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) struct pbr_rule_unique_lookup { struct zebra_pbr_rule *rule; uint32_t unique; - struct interface *ifp; + ifindex_t ifindex; vrf_id_t vrf_id; }; @@ -218,7 +213,7 @@ static int pbr_rule_lookup_unique_walker(struct hash_bucket *b, void *data) struct zebra_pbr_rule *rule = b->data; if (pul->unique == rule->rule.unique - && pul->ifp == rule->ifp + && pul->ifindex == rule->rule.ifindex && pul->vrf_id == rule->vrf_id) { pul->rule = rule; return HASHWALK_ABORT; @@ -233,7 +228,7 @@ pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule) struct pbr_rule_unique_lookup pul; pul.unique = zrule->rule.unique; - pul.ifp = zrule->ifp; + pul.ifindex = zrule->rule.ifindex; pul.rule = NULL; pul.vrf_id = zrule->vrf_id; hash_walk(zrouter.rules_hash, &pbr_rule_lookup_unique_walker, &pul); @@ -471,8 +466,12 @@ static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data) if (rule->sock == *sock) { (void)kernel_del_pbr_rule(rule); - hash_release(zrouter.rules_hash, rule); - XFREE(MTYPE_TMP, rule); + if (hash_release(zrouter.rules_hash, rule)) + XFREE(MTYPE_TMP, rule); + else + zlog_debug( + "%s: Rule seq: %u is being cleaned but we can't find it in our tables", + __func__, rule->rule.seq); } } diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index fcc9c5c39a..b7fbc9b7d5 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -41,7 +41,7 @@ struct zebra_pbr_rule { struct pbr_rule rule; - struct interface *ifp; + char ifname[INTERFACE_NAMSIZ]; vrf_id_t vrf_id; }; |
