diff options
43 files changed, 663 insertions, 516 deletions
diff --git a/babeld/message.c b/babeld/message.c index 794b6e9976..d88790824c 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -1115,7 +1115,9 @@ really_send_update(struct interface *ifp, if(channels_len >= 0) { accumulate_byte(ifp, 2); accumulate_byte(ifp, channels_len); - accumulate_bytes(ifp, channels, channels_len); + + if (channels && channels_len > 0) + accumulate_bytes(ifp, channels, channels_len); } end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + channels_size); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index f3acfa4167..d68a1ad5fd 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -435,16 +435,18 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ *ifindex = pi6->ipi6_ifindex; + + /* Set scope ID for link local addresses. */ + if (IN6_IS_ADDR_LINKLOCAL( + &peer->sa_sin6.sin6_addr)) + peer->sa_sin6.sin6_scope_id = *ifindex; + if (IN6_IS_ADDR_LINKLOCAL( + &local->sa_sin6.sin6_addr)) + local->sa_sin6.sin6_scope_id = *ifindex; } } } - /* Set scope ID for link local addresses. */ - if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr)) - peer->sa_sin6.sin6_scope_id = *ifindex; - if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr)) - local->sa_sin6.sin6_scope_id = *ifindex; - return mlen; } diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index d5b3d6b197..648c3be47e 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -294,6 +294,10 @@ static struct bgp_path_info_mpath * bgp_path_info_mpath_get(struct bgp_path_info *path) { struct bgp_path_info_mpath *mpath; + + if (!path) + return NULL; + if (!path->mpath) { mpath = bgp_path_info_mpath_new(); if (!mpath) @@ -523,6 +527,7 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, list_delete_node(mp_list, mp_node); bgp_path_info_mpath_dequeue(cur_mpath); if ((mpath_count < maxpaths) + && prev_mpath && bgp_path_info_nexthop_cmp(prev_mpath, cur_mpath)) { bgp_path_info_mpath_enqueue(prev_mpath, diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index b6d32d36ea..583adcda77 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -1143,6 +1143,9 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, break; } + memset(&pfx_un1, 0, sizeof(pfx_un1)); + memset(&pfx_un2, 0, sizeof(pfx_un2)); + /* * UN address comparisons */ @@ -1184,7 +1187,7 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, } } - if (!pfx_un1.family || !pfx_un2.family) + if (pfx_un1.family == 0 || pfx_un2.family == 0) return 0; if (pfx_un1.family != pfx_un2.family) diff --git a/configure.ac b/configure.ac index e94cb541ec..77d5ee1555 100755 --- a/configure.ac +++ b/configure.ac @@ -183,15 +183,17 @@ AC_DEFUN([AC_LINK_IFELSE_FLAGS], [{ AC_LINK_IFELSE( [$3], [ - AC_MSG_RESULT([yes]) CFLAGS="$ac_cflags_save" LIBS="$ac_libs_save" - $5 + m4_default([$5], [ + AC_MSG_RESULT([yes]) + ]) ], [ - AC_MSG_RESULT([no]) CFLAGS="$ac_cflags_save" LIBS="$ac_libs_save" - $4 + m4_default([$4], [ + AC_MSG_RESULT([no]) + ]) ]) AC_LANG_POP([C]) }]) @@ -609,92 +611,30 @@ AM_CONDITIONAL([FPM], [test "x$enable_fpm" = "xyes"]) # Python for clippy # -AC_DEFUN([FRR_PYTHON_CHECK_WORKING], [ - AC_MSG_CHECKING([whether we found a working Python version]) - AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ -#include <Python.h> -#if PY_VERSION_HEX < 0x02070000 -#error python too old -#endif -int main(void); -], -[ -{ - Py_Initialize(); - return 0; -} -])], [ - # some python installs are missing the zlib dependency... - PYTHON_LIBS="${PYTHON_LIBS} -lz" - AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ -#include <Python.h> -#if PY_VERSION_HEX < 0x02070000 -#error python too old -#endif -int main(void); -], -[ -{ - Py_Initialize(); - return 0; -} -])], [ - m4_if([$1], [], [ - PYTHONCONFIG="" - unset PYTHON_LIBS - unset PYTHON_CFLAGS - ], [$1]) - ]) - ]) -]) - AS_IF([test "$host" = "$build"], [ - PYTHONCONFIG="" - - # ordering: - # 1. try python3, but respect the user's preference on which minor ver - # 2. try python, which might be py3 or py2 again on the user's preference - # 3. try python2 (can really only be 2.7 but eh) - # 4. try 3.6 > 3.5 > 3.4 > 3.3 > 3.2 > 2.7 through pkg-config (no user pref) - # - # (AX_PYTHON_DEVEL has no clue about py3 vs py2) - # (AX_PYTHON does not do what we need) - - AC_CHECK_TOOLS([PYTHONCONFIG], [ \ - python3-config \ - python-config \ - python2-config \ - python3.6-config \ - python3.5-config \ - python3.4-config \ - python3.3-config \ - python3.2-config \ - python2.7-config ]) - if test -n "$PYTHONCONFIG"; then - PYTHON_CFLAGS="`\"${PYTHONCONFIG}\" --includes`" - PYTHON_LIBS="`\"${PYTHONCONFIG}\" --ldflags`" - - FRR_PYTHON_CHECK_WORKING([]) - fi + FRR_PYTHON_DEV +], [ + FRR_PYTHON +]) - if test -z "$PYTHONCONFIG"; then - PKG_CHECK_MODULES([PYTHON], [python-3.6], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.5], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.4], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.3], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.2], [], [ - PKG_CHECK_MODULES([PYTHON], [python-2.7], [], [ - AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar]) - ])])])])])]) +FRR_PYTHON_MODULES([pytest]) +if test "${enable_doc}" != "no"; then + FRR_PYTHON_MODULES([sphinx], , [ + if test "${enable_doc}" = "yes"; then + AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx is not available for $PYTHON. Please disable docs or install sphinx.]) + fi + ]) +fi +AM_CONDITIONAL([DOC], [test "${enable_doc}" != "no" -a "$frr_py_mod_sphinx" != "false"]) +AM_CONDITIONAL([DOC_HTML], [test "${enable_doc_html}" = "yes"]) - FRR_PYTHON_CHECK_WORKING([ - AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar]) - ]) - fi +FRR_PYTHON_MOD_EXEC([sphinx], [--version], [ + PYSPHINX="-m sphinx" +], [ + PYSPHINX="-c 'import sys; from sphinx import main; sys.exit(main(sys.argv))'" ]) -AC_SUBST([PYTHON_CFLAGS]) -AC_SUBST([PYTHON_LIBS]) +AC_SUBST([PYSPHINX]) # # Logic for protobuf support. @@ -1507,16 +1447,6 @@ FRR_INCLUDES #endif ])dnl -dnl disable doc check -AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build sphinx-build3 sphinx-build2], [/bin/false]) -if test "$SPHINXBUILD" = "/bin/false"; then - if test "${enable_doc}" = "yes"; then - AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx-build is not available. Please disable docs or install sphinx.]) - fi -fi -AM_CONDITIONAL([DOC], [test "${enable_doc}" != "no" -a "$SPHINXBUILD" != "/bin/false"]) -AM_CONDITIONAL([DOC_HTML], [test "${enable_doc_html}" = "yes"]) - dnl -------------------- dnl Daemon disable check dnl -------------------- @@ -1666,6 +1596,7 @@ int main(void); return 0; } ])], [ + AC_MSG_RESULT([no]) AC_MSG_ERROR([--enable-snmp given but not usable])]) case "${enable_snmp}" in yes) @@ -2356,7 +2287,9 @@ zebra protobuf enabled : ${enable_protobuf:-no} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." -if test "${enable_doc}" != "no";then - AS_IF([test "$SPHINXBUILD" = /bin/false], - AC_MSG_WARN([sphinx-build is missing but required to build documentation])) +if test "${enable_doc}" != "no" -a "$frr_py_mod_sphinx" = false; then + AC_MSG_WARN([sphinx is missing but required to build documentation]) +fi +if test "$frr_py_mod_pytest" = false; then + AC_MSG_WARN([pytest is missing, unit tests cannot be performed]) fi diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index f1ec2ad3ea..ee0ffc2bf7 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -163,10 +163,9 @@ an example.) --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion \ - SPHINXBUILD=sphinx-build2.7 + --with-pkg-extra-version=-MyOwnFRRVersion make - make check PYTHON=/usr/bin/python2.7 + make check sudo make install Create empty FRR configuration files diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index a26d055bc2..76f927853d 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -16,7 +16,7 @@ Add packages: :: sudo apt-get install git autoconf automake libtool make \ - libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \ + libreadline-dev texinfo libjson-c-dev pkg-config bison flex python3-pip \ libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev \ libsnmp-dev @@ -24,7 +24,7 @@ Install newer pytest (>3.0) from pip :: - sudo pip install pytest + sudo pip3 install pytest .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index 2c5a9681af..e58c59f451 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -9,8 +9,8 @@ Add packages: :: sudo apt-get install git autoconf automake libtool make \ - libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \ - libc-ares-dev python3-dev python-pytest python3-sphinx build-essential \ + libreadline-dev texinfo libjson-c-dev pkg-config bison flex \ + libc-ares-dev python3-dev python3-pytest python3-sphinx build-essential \ libsnmp-dev libsystemd-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst index 204c185f56..d11da2d647 100644 --- a/doc/developer/building-frr-for-fedora.rst +++ b/doc/developer/building-frr-for-fedora.rst @@ -13,8 +13,8 @@ Installing Dependencies sudo dnf install git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig json-c-devel \ - pam-devel pytest bison flex c-ares-devel python3-devel python2-sphinx \ - perl-core patch + pam-devel python3-pytest bison flex c-ares-devel python3-devel \ + python3-sphinx perl-core patch .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-freebsd10.rst b/doc/developer/building-frr-for-freebsd10.rst index 86c44f4d90..e85cb80053 100644 --- a/doc/developer/building-frr-for-freebsd10.rst +++ b/doc/developer/building-frr-for-freebsd10.rst @@ -17,7 +17,7 @@ is first package install and asked) :: pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py27-pytest c-ares python3 py-sphinx + bison flex py36-pytest c-ares python3.6 py36-sphinx Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-freebsd11.rst b/doc/developer/building-frr-for-freebsd11.rst index 5e56c8cd7a..b97538b763 100644 --- a/doc/developer/building-frr-for-freebsd11.rst +++ b/doc/developer/building-frr-for-freebsd11.rst @@ -17,7 +17,7 @@ is first package install and asked) .. code-block:: shell pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py27-pytest c-ares python3 py36-sphinx texinfo + bison flex py36-pytest c-ares python3.6 py36-sphinx texinfo Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-freebsd9.rst b/doc/developer/building-frr-for-freebsd9.rst index 59241d1d13..1e97749795 100644 --- a/doc/developer/building-frr-for-freebsd9.rst +++ b/doc/developer/building-frr-for-freebsd9.rst @@ -17,8 +17,8 @@ is first package install and asked) :: pkg install -y git autoconf automake libtool gmake \ - pkgconf texinfo json-c bison flex py27-pytest c-ares \ - python3 py-sphinx libexecinfo + pkgconf texinfo json-c bison flex py36-pytest c-ares \ + python3 py36-sphinx libexecinfo Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst index 49091c49b4..e50d11130a 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -23,7 +23,7 @@ Add packages: :: sudo pkg_add git autoconf automake libtool gmake openssl \ - pkg-config json-c python27 py27-test python35 py-sphinx + pkg-config json-c py36-test python36 py36-sphinx Install SSL Root Certificates (for git https access): @@ -33,13 +33,6 @@ Install SSL Root Certificates (for git https access): sudo touch /etc/openssl/openssl.cnf sudo mozilla-rootcerts install -Select default Python and py.test - -:: - - sudo ln -s /usr/pkg/bin/python2.7 /usr/bin/python - sudo ln -s /usr/pkg/bin/py.test-2.7 /usr/bin/py.test - .. include:: building-libyang.rst Get FRR, compile it and install it (from Git) diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index 64c462a5c8..32d1145edc 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -14,7 +14,7 @@ Install required packages :: sudo pkgin install git autoconf automake libtool gmake openssl \ - pkg-config json-c python27 py27-test python35 py-sphinx + pkg-config json-c python36 py36-test py36-sphinx Install SSL Root Certificates (for git https access): @@ -24,13 +24,6 @@ Install SSL Root Certificates (for git https access): sudo touch /etc/openssl/openssl.cnf sudo mozilla-rootcerts install -Select default Python and py.test - -:: - - sudo ln -s /usr/pkg/bin/python2.7 /usr/bin/python - sudo ln -s /usr/pkg/bin/py.test-2.7 /usr/bin/py.test - .. include:: building-libyang.rst Get FRR, compile it and install it (from Git) diff --git a/doc/developer/building-frr-for-ubuntu1404.rst b/doc/developer/building-frr-for-ubuntu1404.rst index 6e2765c1c8..569b3bded1 100644 --- a/doc/developer/building-frr-for-ubuntu1404.rst +++ b/doc/developer/building-frr-for-ubuntu1404.rst @@ -12,7 +12,7 @@ Installing Dependencies apt-get update apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev python3-sphinx install-info build-essential \ libsnmp-dev perl diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst index a9a0a2f733..03852a62aa 100644 --- a/doc/developer/building-frr-for-ubuntu1604.rst +++ b/doc/developer/building-frr-for-ubuntu1604.rst @@ -12,7 +12,7 @@ Installing Dependencies apt-get update apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ install-info build-essential libsystemd-dev libsnmp-dev perl diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst index 8bdc2b9c76..96c0efe02a 100644 --- a/doc/developer/building-frr-for-ubuntu1804.rst +++ b/doc/developer/building-frr-for-ubuntu1804.rst @@ -12,7 +12,7 @@ Installing Dependencies sudo apt update sudo apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ install-info build-essential libsystemd-dev libsnmp-dev perl diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index f6b9931156..d344046148 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -32,7 +32,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. cd frr ./bootstrap.sh - ./configure --with-pkg-extra-version=-MyRPMVersion SPHINXBUILD=sphinx-build2.7 + ./configure --with-pkg-extra-version=-MyRPMVersion make dist .. note:: diff --git a/doc/subdir.am b/doc/subdir.am index 7d3792bf2b..a1297a4f81 100644 --- a/doc/subdir.am +++ b/doc/subdir.am @@ -4,7 +4,6 @@ # You can set these variables from the command line. SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build PAPER ?= # Internal variables. @@ -32,20 +31,20 @@ am__v_MAKEINFO_1 = doc/%/_build/.doctrees/environment.pickle: $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -a -q -b text -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -a -q -b text -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/text" \ ) doc/%/_build/html/.buildinfo: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -q -b html -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -q -b html -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/html" \ ) .PRECIOUS: doc/%/_build/texinfo/frr.texi doc/%/_build/texinfo/frr.texi: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -q -b texinfo -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -q -b texinfo -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/texinfo" \ ) doc/%/_build/texinfo/frr.info: doc/%/_build/texinfo/frr.texi @@ -54,7 +53,7 @@ doc/%/_build/man/man.stamp: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ $(MKDIR_P) "$${subdoc}/_build/man"; touch $@.tmp; \ - $(SPHINXBUILD) -a -q -b man -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -a -q -b man -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/man" && \ mv -f $@.tmp $@ \ ) @@ -80,7 +79,7 @@ $(M_SPHINXTARGETS): doc/%/_build/.doctrees/environment.pickle builder="$${target##*/}"; \ subdoc="$${target#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ rm -rf "$@"; \ - $(SPHINXBUILD) -q -b $${builder} -d $${subdoc}/_build/.doctrees \ + $(PYTHON) $(PYSPHINX) -q -b $${builder} -d $${subdoc}/_build/.doctrees \ $(ALLSPHINXOPTS) $(top_srcdir)/$${subdoc} $@ \ ) diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 964297292f..6438c11413 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -347,6 +347,27 @@ compile directory: ./configure --with-libyang-pluginsdir="`pwd`/yang/libyang_plugins/.libs" \ --with-yangmodelsdir="`pwd`/yang" +Python dependency, documentation and tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +FRR's documentation and basic unit tests heavily use code written in Python. +Additionally, FRR ships Python extensions written in C which are used during +its build process. + +To this extent, FRR needs the following: + +* an installation of CPython, preferably version 3.2 or newer (2.7 works but + is end of life and will stop working at some point.) +* development files (mostly headers) for that version of CPython +* an installation of `sphinx` for that version of CPython, to build the + documentation +* an installation of `pytest` for that version of CPython, to run the unit + tests + +The `sphinx` and `pytest` dependencies can be avoided by not building +documentation / not running ``make check``, but the CPython dependency is a +hard dependency of the FRR build process (for the `clippy` tool.) + .. _least-privilege-support: Least-Privilege Support diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index 0a00497d5a..f080ba4876 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -764,7 +764,7 @@ static struct route_map_rule_cmd route_set_tag_cmd = { DEFUN (match_metric, match_metric_cmd, - "match metric <0-4294967295>", + "match metric (0-4294967295)", MATCH_STR "Match metric of route\n" "Metric value\n") @@ -787,7 +787,7 @@ DEFUN (no_match_metric, } ALIAS(no_match_metric, no_match_metric_val_cmd, - "no match metric <0-4294967295>", NO_STR MATCH_STR + "no match metric (0-4294967295)", NO_STR MATCH_STR "Match metric of route\n" "Metric value\n") @@ -822,7 +822,7 @@ ALIAS(no_match_interface, no_match_interface_val_cmd, "no match interface WORD", DEFUN (match_ip_next_hop, match_ip_next_hop_cmd, - "match ip next-hop (<1-199>|<1300-2699>|WORD)", + "match ip next-hop ((1-199)|(1300-2699)|WORD)", MATCH_STR IP_STR "Match next-hop address of route\n" @@ -850,7 +850,7 @@ DEFUN (no_match_ip_next_hop, } ALIAS(no_match_ip_next_hop, no_match_ip_next_hop_val_cmd, - "no match ip next-hop (<1-199>|<1300-2699>|WORD)", NO_STR MATCH_STR IP_STR + "no match ip next-hop ((1-199)|(1300-2699)|WORD)", NO_STR MATCH_STR IP_STR "Match next-hop address of route\n" "IP access-list number\n" "IP access-list number (expanded range)\n" @@ -895,7 +895,7 @@ ALIAS(no_match_ip_next_hop_prefix_list, DEFUN (match_ip_address, match_ip_address_cmd, - "match ip address (<1-199>|<1300-2699>|WORD)", + "match ip address ((1-199)|(1300-2699)|WORD)", MATCH_STR IP_STR "Match address of route\n" @@ -922,7 +922,7 @@ DEFUN (no_match_ip_address, } ALIAS(no_match_ip_address, no_match_ip_address_val_cmd, - "no match ip address (<1-199>|<1300-2699>|WORD)", NO_STR MATCH_STR IP_STR + "no match ip address ((1-199)|(1300-2699)|WORD)", NO_STR MATCH_STR IP_STR "Match address of route\n" "IP access-list number\n" "IP access-list number (expanded range)\n" @@ -966,7 +966,7 @@ ALIAS(no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_val_cmd, DEFUN (match_tag, match_tag_cmd, - "match tag <0-65535>", + "match tag (0-65535)", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -987,7 +987,7 @@ DEFUN (no_match_tag, return eigrp_route_match_delete(vty, vty->index, "tag", argv[0]); } -ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag <0-65535>", +ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag (0-65535)", NO_STR MATCH_STR "Match tag of route\n" "Metric value\n") @@ -996,7 +996,7 @@ ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag <0-65535>", DEFUN (set_metric, set_metric_cmd, - "set metric <0-4294967295>", + "set metric (0-4294967295)", SET_STR "Metric value for destination routing protocol\n" "Metric value\n") @@ -1022,7 +1022,7 @@ DEFUN (no_set_metric, } ALIAS(no_set_metric, no_set_metric_val_cmd, - "no set metric (<0-4294967295>|<+/-metric>)", NO_STR SET_STR + "no set metric ((0-4294967295)|<+/-metric>)", NO_STR SET_STR "Metric value for destination routing protocol\n" "Metric value\n" "Add or subtract metric\n") @@ -1069,7 +1069,7 @@ ALIAS(no_set_ip_nexthop, no_set_ip_nexthop_val_cmd, DEFUN (set_tag, set_tag_cmd, - "set tag <0-65535>", + "set tag (0-65535)", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -1090,7 +1090,7 @@ DEFUN (no_set_tag, return eigrp_route_set_delete(vty, vty->index, "tag", argv[0]); } -ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag <0-65535>", NO_STR SET_STR +ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag (0-65535)", NO_STR SET_STR "Tag value for routing protocol\n" "Tag value\n") diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index fc5bdbdbc5..1a1634ca91 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -370,7 +370,7 @@ DEFUN (eigrp_metric_weights, DEFUN (no_eigrp_metric_weights, no_eigrp_metric_weights_cmd, - "no metric weights <0-255> <0-255> <0-255> <0-255> <0-255>", + "no metric weights (0-255) (0-255) (0-255) (0-255) (0-255)", NO_STR "Modify metrics and parameters for advertisement\n" "Modify metric coefficients\n" @@ -1209,7 +1209,7 @@ DEFUN (eigrp_maximum_paths, DEFUN (no_eigrp_maximum_paths, no_eigrp_maximum_paths_cmd, - "no maximum-paths <1-32>", + "no maximum-paths (1-32)", NO_STR "Forward packets over multiple paths\n" "Number of paths\n") @@ -151,7 +151,7 @@ int vty_out(struct vty *vty, const char *format, ...) va_list args; ssize_t len; char buf[1024]; - char *p = buf; + char *p = NULL; char *filtered; if (vty->frame_pos) { diff --git a/m4/.gitignore b/m4/.gitignore index 2c04163659..cc778b9e99 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -4,6 +4,7 @@ !ax_compare_version.m4 !ax_prog_perl_modules.m4 !ax_pthread.m4 +!ax_python.m4 !ax_sys_weak_alias.m4 !ax_sys_weak_alias.m4 !pkg.m4 diff --git a/m4/ax_python.m4 b/m4/ax_python.m4 new file mode 100644 index 0000000000..32043c81ae --- /dev/null +++ b/m4/ax_python.m4 @@ -0,0 +1,284 @@ +dnl FRR Python autoconf magic +dnl 2019 David Lamparter for NetDEF, Inc. +dnl SPDX-License-Identifier: GPL-2.0-or-later + +dnl the _ at the beginning will be cut off (to support the empty version string) +m4_define_default([_FRR_PY_VERS], [_3 _ _2 _3.7 _3.6 _3.5 _3.4 _3.3 _3.2 _2.7]) + +dnl check basic interpreter properties (py2/py3) +dnl doubles as simple check whether the interpreter actually works +dnl also swaps in the full path to the interpreter +dnl arg1: if-true, arg2: if-false +AC_DEFUN([_FRR_PYTHON_INTERP], [dnl +AC_ARG_VAR([PYTHON], [Python interpreter to use])dnl + AC_MSG_CHECKING([python interpreter $PYTHON]) + AC_RUN_LOG(["$PYTHON" -c 'import sys; open("conftest.pyver", "w").write(sys.executable or ""); sys.exit(not (sys.version_info.major == 2 and sys.version_info.minor >= 7))']) + py2=$ac_status + _py2_full="`cat conftest.pyver 2>/dev/null`" + rm -f "conftest.pyver" >/dev/null 2>/dev/null + + AC_RUN_LOG(["$PYTHON" -c 'import sys; open("conftest.pyver", "w").write(sys.executable or ""); sys.exit(not ((sys.version_info.major == 3 and sys.version_info.minor >= 2) or sys.version_info.major > 3))']) + py3=$ac_status + _py3_full="`cat conftest.pyver 2>/dev/null`" + rm -f "conftest.pyver" >/dev/null 2>/dev/null + + case "p${py2}p${py3}" in + p0p1) frr_cv_python=python2 + _python_full="$_py2_full" ;; + p1p0) frr_cv_python=python3 + _python_full="$_py3_full" ;; + *) frr_cv_python=none ;; + esac + + if test "$frr_cv_python" = none; then + AC_MSG_RESULT([not working]) + $2 + else + test -n "$_python_full" -a -x "$_python_full" && PYTHON="$_python_full" + AC_MSG_RESULT([$PYTHON ($frr_cv_python)]) + $1 + fi + + dnl return value + test "$frr_cv_python" != none +]) + +dnl check whether $PYTHON has modules available +dnl arg1: list of modules (space separated) +dnl arg2: if all true, arg3: if any missing +dnl also sets frr_py_mod_<name> to "true" or "false" +AC_DEFUN([FRR_PYTHON_MODULES], [ + result=true + for pymod in $1; do + AC_MSG_CHECKING([whether $PYTHON module $pymod is available]) + AC_RUN_LOG(["$PYTHON" -c "import $pymod"]) + sane="`echo \"$pymod\" | tr -c '[a-zA-Z0-9\n]' '_'`" + if test "$ac_status" -eq 0; then + AC_MSG_RESULT([yes]) + eval frr_py_mod_$sane=true + else + AC_MSG_RESULT([no]) + eval frr_py_mod_$sane=false + result=false + fi + done + if $result; then + m4_default([$2], [:]) + else + m4_default([$3], [:]) + fi + $result +]) + +dnl check whether $PYTHON has modules available +dnl arg1: list of modules (space separated) +dnl arg2: command line parameters for executing +dnl arg3: if all true, arg4: if any missing +dnl also sets frr_py_modexec_<name> to "true" or "false" +AC_DEFUN([FRR_PYTHON_MOD_EXEC], [ + result=true + for pymod in $1; do + AC_MSG_CHECKING([whether $PYTHON module $pymod is executable]) + AC_RUN_LOG(["$PYTHON" -m "$pymod" $2 > /dev/null]) + sane="`echo \"$pymod\" | tr -c '[a-zA-Z0-9\n]' '_'`" + if test "$ac_status" -eq 0; then + AC_MSG_RESULT([yes]) + eval frr_py_modexec_$sane=true + else + AC_MSG_RESULT([no]) + eval frr_py_modexec_$sane=false + result=false + fi + done + if $result; then + m4_default([$3], [:]) + else + m4_default([$4], [:]) + fi + $result +]) + +dnl check whether we can build & link python bits +dnl input: PYTHON_CFLAGS and PYTHON_LIBS +AC_DEFUN([_FRR_PYTHON_DEVENV], [ + result=true + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include <Python.h> +#if PY_VERSION_HEX < 0x02070000 +#error python too old +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + # some python installs are missing the zlib dependency... + PYTHON_LIBS="${PYTHON_LIBS} -lz" + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include <Python.h> +#if PY_VERSION_HEX < 0x02070000 +#error python too old +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + result=false + AC_MSG_RESULT([no]) + ], [:]) + ], [:]) + + if $result; then + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include <Python.h> +#if PY_VERSION_HEX != $1 +#error python version mismatch +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + result=false + AC_MSG_RESULT([version mismatch]) + ], [ + AC_MSG_RESULT([yes]) + ]) + fi + + if $result; then + m4_default([$2], [:]) + else + m4_default([$3], [ + unset PYTHON_LIBS + unset PYTHON_CFLAGS + ]) + fi +]) + +AC_DEFUN([_FRR_PYTHON_GETDEV], [dnl +AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl + + py_abi="` \"$1\" -c \"import sys; print(getattr(sys, 'abiflags', ''))\"`" + py_hex="` \"$1\" -c \"import sys; print(hex(sys.hexversion))\"`" + py_ldver="` \"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('LDVERSION') or '')\"`" + py_ver="` \"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('VERSION') or '')\"`" + py_bindir="`\"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('BINDIR') or '')\"`" + test -z "$py_bindir" || py_bindir="$py_bindir/" + echo "py_abi=${py_abi} py_ldver=${py_ldver} py_ver=${py_ver} py_bindir=${py_bindir}" >&AS_MESSAGE_LOG_FD + + py_found=false + + for tryver in "${py_ldver}" "${py_ver}"; do + pycfg="${py_bindir}python${tryver}-config" + AC_MSG_CHECKING([whether ${pycfg} is available]) + if "$pycfg" --configdir >/dev/null 2>/dev/null; then + AC_MSG_RESULT([yes]) + + PYTHON_CFLAGS="`\"$pycfg\" --includes`" + PYTHON_LIBS="`\"$pycfg\" --ldflags`" + + AC_MSG_CHECKING([whether ${pycfg} provides a working build environment]) + _FRR_PYTHON_DEVENV([$py_hex], [ + py_found=true + break + ]) + else + AC_MSG_RESULT([no]) + fi + + pkg_failed=no + AC_MSG_CHECKING([whether pkg-config python-${tryver} is available]) + unset PYTHON_CFLAGS + unset PYTHON_LIBS + pkg="python-${tryver}" + pkg="${pkg%-}" + _PKG_CONFIG([PYTHON_CFLAGS], [cflags], [${pkg}]) + _PKG_CONFIG([PYTHON_LIBS], [libs], [${pkg}]) + if test $pkg_failed = no; then + AC_MSG_RESULT([yes]) + + PYTHON_CFLAGS=$pkg_cv_PYTHON_CFLAGS + PYTHON_LIBS=$pkg_cv_PYTHON_LIBS + + AC_MSG_CHECKING([whether pkg-config python-${tryver} provides a working build environment]) + _FRR_PYTHON_DEVENV([$py_hex], [ + py_found=true + break + ]) + else + AC_MSG_RESULT([no]) + fi + done + + if $py_found; then + m4_default([$2], [:]) + else + unset PYTHON_CFLAGS + unset PYTHON_LIBS + m4_default([$3], [:]) + fi +]) + +dnl just find python without checking headers/libs +AC_DEFUN([FRR_PYTHON], [ + dnl user override + if test "x$PYTHON" != "x"; then + _FRR_PYTHON_INTERP([], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but not working]) + ]) + else + for frr_pyver in _FRR_PY_VERS; do + PYTHON="python${frr_pyver#_}" + _FRR_PYTHON_INTERP([break]) + PYTHON=":" + done + if test "$PYTHON" = ":"; then + AC_MSG_ERROR([no working python version found]) + fi + fi + AC_SUBST([PYTHON]) +]) + +dnl find python with checking headers/libs +AC_DEFUN([FRR_PYTHON_DEV], [dnl +AC_ARG_VAR([PYTHON_CFLAGS], [C compiler flags for Python])dnl +AC_ARG_VAR([PYTHON_LIBS], [linker flags for Python])dnl + + dnl user override + if test "x$PYTHON" != "x"; then + _FRR_PYTHON_INTERP([], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but not working]) + ]) + _FRR_PYTHON_GETDEV([$PYTHON], [], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but development environment not working]) + ]) + else + for frr_pyver in _FRR_PY_VERS; do + PYTHON="python${frr_pyver#_}" + _FRR_PYTHON_INTERP([ + _FRR_PYTHON_GETDEV([$PYTHON], [ + break + ]) + ]) + PYTHON=":" + done + if test "$PYTHON" = ":"; then + AC_MSG_ERROR([no working python version found]) + fi + fi + + AC_SUBST([PYTHON_CFLAGS]) + AC_SUBST([PYTHON_LIBS]) + AC_SUBST([PYTHON]) +]) diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 3de4609a2b..d6105b71d4 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -207,7 +207,7 @@ static void parse_sa_message(struct vici_message_ctx *ctx, } break; default: - if (!key) + if (!key || !key->ptr) break; switch (key->ptr[0]) { @@ -550,7 +550,7 @@ int sock_open_unix(const char *path) memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(addr.sun_path)); diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index ebd9ac3f47..27042e197c 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -44,12 +44,6 @@ # defines for configure %define rundir %{_localstatedir}/run/%{name} -# define for sphinx-build binary -%if 0%{?rhel} && 0%{?rhel} < 7 - %define sphinx sphinx-build2.7 -%else - %define sphinx sphinx-build -%endif ############################################################################ #### Version String tweak @@ -360,7 +354,7 @@ developing OSPF-API and frr applications. %else --disable-bfdd \ %endif - SPHINXBUILD=%{sphinx} + # end make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 5a4087b177..d83f4d2791 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -184,7 +184,8 @@ static int ripng_if_down(struct interface *ifp) zlog_debug("turn off %s", ifp->name); /* Leave from multicast group. */ - ripng_multicast_leave(ifp, ripng->sock); + if (ripng) + ripng_multicast_leave(ifp, ripng->sock); ri->running = 0; } diff --git a/tests/subdir.am b/tests/subdir.am index 99f5f87fac..41f1a4873b 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -2,8 +2,6 @@ # tests # -PYTHON ?= python - if BGPD TESTS_BGPD = \ tests/bgpd/test_aspath \ diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index e0da20e07f..2acb04fb0e 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -429,6 +429,33 @@ def ip4_route_zebra(node, vrf_name=None): lines = lines[1:] return '\n'.join(lines) +def ip6_route_zebra(node, vrf_name=None): + """ + Retrieves the output of 'show ipv6 route [vrf vrf_name]', then + canonicalizes it by eliding link-locals. + """ + + if vrf_name == None: + tmp = node.vtysh_cmd('show ipv6 route') + else: + tmp = node.vtysh_cmd('show ipv6 route vrf {0}'.format(vrf_name)) + + # Mask out timestamp + output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp) + + # Mask out the link-local addresses + output = re.sub(r'fe80::[^ ]+,', 'fe80::XXXX:XXXX:XXXX:XXXX,', output) + + lines = output.splitlines() + header_found = False + while lines and (not lines[0].strip() or not header_found): + if '> - selected route' in lines[0]: + header_found = True + lines = lines[1:] + + return '\n'.join(lines) + + def proto_name_to_number(protocol): return { 'bgp': '186', diff --git a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 deleted file mode 100644 index c4b382171d..0000000000 --- a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r1 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r1-stubnet - ipv6 ospf6 network broadcast -! -interface r1-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.1 - log-adjacency-changes detail - redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref index c961512bd9..2db6f620f9 100644 --- a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 +O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r1/zebra.conf b/tests/topotests/ospf6-topo1/r1/zebra.conf index de298f40e1..dfbcea8d21 100644 --- a/tests/topotests/ospf6-topo1/r1/zebra.conf +++ b/tests/topotests/ospf6-topo1/r1/zebra.conf @@ -2,6 +2,9 @@ hostname r1 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r1-stubnet ipv6 address fc00:1:1:1::1/64 ! diff --git a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 deleted file mode 100644 index bb9958d173..0000000000 --- a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r2 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r2-stubnet - ipv6 ospf6 network broadcast -! -interface r2-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.2 - log-adjacency-changes detail - redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref index 014eddbcb6..9060b0739f 100644 --- a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r2/zebra.conf b/tests/topotests/ospf6-topo1/r2/zebra.conf index d5345fede6..f05d1a60ff 100644 --- a/tests/topotests/ospf6-topo1/r2/zebra.conf +++ b/tests/topotests/ospf6-topo1/r2/zebra.conf @@ -2,6 +2,9 @@ hostname r2 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r2-stubnet ipv6 address fc00:2:2:2::2/64 ! diff --git a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 deleted file mode 100644 index d2dbc4a41c..0000000000 --- a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,31 +0,0 @@ -hostname r3 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r3-stubnet - ipv6 ospf6 network broadcast -! -interface r3-sw5 - ipv6 ospf6 network broadcast -! -interface r3-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.3 - log-adjacency-changes detail - redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref index 1ac7cbd6b4..9406f41e94 100644 --- a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet -O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 -O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5 -O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, XX:XX:XX +O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r3/zebra.conf b/tests/topotests/ospf6-topo1/r3/zebra.conf index 11f1ff59f2..d8051c350d 100644 --- a/tests/topotests/ospf6-topo1/r3/zebra.conf +++ b/tests/topotests/ospf6-topo1/r3/zebra.conf @@ -2,6 +2,9 @@ hostname r3 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r3-stubnet ipv6 address fc00:3:3:3::3/64 ! diff --git a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 deleted file mode 100644 index 6f9c30d75a..0000000000 --- a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r4 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r4-stubnet - ipv6 ospf6 network broadcast -! -interface r4-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.4 - log-adjacency-changes detail - redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref index 698dea6c7b..9bf032b5e7 100644 --- a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet -O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6 -O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 +O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, XX:XX:XX +O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r4/zebra.conf b/tests/topotests/ospf6-topo1/r4/zebra.conf index 4b0a8a1f92..cada58bd01 100644 --- a/tests/topotests/ospf6-topo1/r4/zebra.conf +++ b/tests/topotests/ospf6-topo1/r4/zebra.conf @@ -2,6 +2,9 @@ hostname r4 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r4-stubnet ipv6 address fc00:4:4:4::4/64 ! diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 5da04b6449..b70ae02266 100755 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -76,20 +76,19 @@ import sys import pytest from time import sleep -from mininet.topo import Topo -from mininet.net import Mininet -from mininet.node import Node, OVSSwitch, Host -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import Intf - from functools import partial -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from lib import topotest +from mininet.topo import Topo +# Save the Current Working Directory to find configuration files later. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) -fatal_error = "" +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger ##################################################### @@ -102,34 +101,45 @@ class NetworkTopo(Topo): "OSPFv3 (IPv6) Test Topology 1" def build(self, **_opts): - # - # Define Switches first - # - switch = {} - for i in range(1, 7): - switch[i] = self.addSwitch('SW%s' % i, - dpid=topotest.int2dpid(i), - cls=topotest.LegacySwitch) - # - # Define FRR/Quagga Routers - # - router = {} - for i in range(1, 5): - router[i] = topotest.addRouter(self, 'r%s' % i) + "Build function" + + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router('r{}'.format(routern)) # # Wire up the switches and routers + # Note that we specify the link names so we match the config files # - # Stub nets - for i in range(1, 5): - self.addLink(switch[i], router[i], intfName2='r%s-stubnet' % i) - # Switch 5 - self.addLink(switch[5], router[1], intfName2='r1-sw5') - self.addLink(switch[5], router[2], intfName2='r2-sw5') - self.addLink(switch[5], router[3], intfName2='r3-sw5') - # Switch 6 - self.addLink(switch[6], router[3], intfName2='r3-sw6') - self.addLink(switch[6], router[4], intfName2='r4-sw6') + + # Create a empty network for router 1 + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1'], nodeif='r1-stubnet') + + # Create a empty network for router 2 + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2'], nodeif='r2-stubnet') + + # Create a empty network for router 3 + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r3'], nodeif='r3-stubnet') + + # Create a empty network for router 4 + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['r4'], nodeif='r4-stubnet') + + # Interconnect routers 1, 2, and 3 + switch = tgen.add_switch('s5') + switch.add_link(tgen.gears['r1'], nodeif='r1-sw5') + switch.add_link(tgen.gears['r2'], nodeif='r2-sw5') + switch.add_link(tgen.gears['r3'], nodeif='r3-sw5') + + # Interconnect routers 3 and 4 + switch = tgen.add_switch('s6') + switch.add_link(tgen.gears['r3'], nodeif='r3-sw6') + switch.add_link(tgen.gears['r4'], nodeif='r4-sw6') ##################################################### @@ -138,192 +148,178 @@ class NetworkTopo(Topo): ## ##################################################### -def setup_module(module): - global topo, net +def setup_module(mod): + "Sets up the pytest environment" - print("\n\n** %s: Setup Topology" % module.__name__) - print("******************************************\n") + tgen = Topogen(NetworkTopo, mod.__name__) + tgen.start_topology() - print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + logger.info("** %s: Setup Topology" % mod.__name__) + logger.info("******************************************") - thisDir = os.path.dirname(os.path.realpath(__file__)) - topo = NetworkTopo() + # For debugging after starting net, but before starting FRR, + # uncomment the next line + # tgen.mininet_cli() - net = Mininet(controller=None, topo=topo) - net.start() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, + os.path.join(CWD, '{}/ospf6d.conf'.format(rname)) + ) - # For debugging after starting net, but before starting FRR/Quagga, uncomment the next line - # CLI(net) + # Initialize all routers. + tgen.start_router() - ospf_config = 'ospf6d.conf' - if net['r1'].checkRouterVersion('<', '4.0'): - ospf_config = 'ospf6d.conf-pre-v4' + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() - # Starting Routers - for i in range(1, 5): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ospf6d', '%s/r%s/%s' % (thisDir, i, ospf_config)) - net['r%s' % i].startRouter() - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() -def teardown_module(module): - global net +def test_ospf6_converged(): - print("\n\n** %s: Shutdown Topology" % module.__name__) - print("******************************************\n") + tgen = get_topogen() - # End - Shutdown network - net.stop() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # For debugging, uncomment the next line + #tgen.mininet_cli() -def test_router_running(): - global fatal_error - global net + # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) + logger.info("Waiting for OSPF6 convergence") - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # Set up for regex + pat1 = re.compile('^[0-9]') + pat2 = re.compile('Full') - print("\n\n** Check if FRR/Quagga is running on each Router node") - print("******************************************\n") - sleep(5) + timeout = 60 + while timeout > 0: + logger.info("Timeout in %s: " % timeout), + sys.stdout.flush() - # Make sure that all daemons are running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + # Look for any node not yet converged + for router, rnode in tgen.routers().iteritems(): + resStr = rnode.vtysh_cmd('show ipv6 ospf neigh') - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) + isConverged = False -def test_ospf6_converged(): - global fatal_error - global net + for line in resStr.splitlines(): + res1 = pat1.match(line) + if res1: + isConverged = True + res2 = pat2.search(line) - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + if res2 == None: + isConverged = False + break - # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) - print("\n\n** Verify OSPF6 daemons to converge") - print("******************************************\n") - timeout = 60 - while timeout > 0: - print("Timeout in %s: " % timeout), - sys.stdout.flush() - # Look for any node not yet converged - for i in range(1, 5): - notConverged = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh" 2> /dev/null | grep ^[0-9] | grep -v Full') - if notConverged: - print('Waiting for r%s' %i) + if isConverged == False: + logger.info('Waiting for {}'.format(router)) sys.stdout.flush() break - if notConverged: + + if isConverged: + logger.info('Done') + break + else: sleep(5) timeout -= 5 - else: - print('Done') - print(notConverged) - break - else: + + if timeout == 0: # Bail out with error if a router fails to converge - ospfStatus = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh"') - fatal_error = "OSPFv6 did not converge" - assert False, "OSPFv6 did not converge:\n%s" % ospfStatus + ospfStatus = rnode.vtysh_cmd('show ipv6 ospf neigh') + assert False, "OSPFv6 did not converge:\n{}".format(ospfStatus) - print("OSPFv3 converged.") + logger.info("OSPFv3 converged.") - if timeout < 60: - # Only wait if we actually went through a convergence - print("\nwaiting 15s for routes to populate") - sleep(15) + # For debugging, uncomment the next line + # tgen.mininet_cli() # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + if tgen.routers_have_failure(): + assert tgen.errors == "", tgen.errors -def test_ospfv3_routingTable(): - global fatal_error - global net +def compare_show_ipv6(rname, expected): + """ + Calls 'show ipv6 route' for router `rname` and compare the obtained + result with the expected output. + """ + tgen = get_topogen() - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # Use the vtysh output, with some masking to make comparison easy + current = topotest.ip6_route_zebra(tgen.gears[rname]) - thisDir = os.path.dirname(os.path.realpath(__file__)) + # Use just the 'O'spf lines of the output + linearr = [] + for line in current.splitlines(): + if re.match('^O', line): + linearr.append(line) - # Verify OSPFv3 Routing Table - print("\n\n** Verifying OSPFv3 Routing Table") - print("******************************************\n") - failures = 0 - for i in range(1, 5): - refTableFile = '%s/r%s/show_ipv6_route.ref' % (thisDir, i) - if os.path.isfile(refTableFile): - # Read expected result from file - expected = open(refTableFile).read().rstrip() - # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + current = '\n'.join(linearr) - # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^O"').rstrip() - # Mask out Link-Local mac address portion. They are random... - actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) - # Drop timers on end of line (older Quagga Versions) - actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) - # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + return topotest.difflines(topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output") - # Generate Diff - diff = topotest.get_textdiff(actual, expected, - title1="actual OSPFv3 IPv6 routing table", - title2="expected OSPFv3 IPv6 routing table") +def test_ospfv3_routingTable(): - # Empty string if it matches, otherwise diff contains unified diff - if diff: - sys.stderr.write('r%s failed OSPFv3 (IPv6) Routing Table Check:\n%s\n' % (i, diff)) - failures += 1 - else: - print("r%s ok" % i) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') - assert failures == 0, "OSPFv3 (IPv6) Routing Table verification failed for router r%s:\n%s" % (i, diff) + # For debugging, uncomment the next line + # tgen.mininet_cli() - # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, '{}/show_ipv6_route.ref'.format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial( + compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, '', + count=120, wait=0.5) + assert result, 'OSPFv3 did not converge on {}:\n{}'.format(router, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) def test_linux_ipv6_kernel_routingTable(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() - thisDir = os.path.dirname(os.path.realpath(__file__)) + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') # Verify Linux Kernel Routing Table - print("\n\n** Verifying Linux IPv6 Kernel Routing Table") - print("******************************************\n") + logger.info("Verifying Linux IPv6 Kernel Routing Table") + failures = 0 # Get a list of all current link-local addresses first as they change for # each run and we need to translate them linklocals = [] for i in range(1, 5): - linklocals += net['r%s' % i].get_ipv6_linklocal() + linklocals += tgen.net['r{}'.format(i)].get_ipv6_linklocal() + + # Now compare the routing tables (after substituting link-local addresses) - # Now compare the routing tables (after substituting link-local addresses) for i in range(1, 5): - refTableFile = '%s/r%s/ip_6_address.ref' % (thisDir, i) + refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i)) if os.path.isfile(refTableFile): expected = open(refTableFile).read().rstrip() @@ -331,7 +327,7 @@ def test_linux_ipv6_kernel_routingTable(): expected = ('\n'.join(expected.splitlines())).splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('ip -6 route').rstrip() + actual = tgen.gears['r{}'.format(i)].run('ip -6 route').rstrip() # Mask out Link-Local mac addresses for ll in linklocals: actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) @@ -354,9 +350,9 @@ def test_linux_ipv6_kernel_routingTable(): actual = '\n'.join(filtered_lines).splitlines(1) # Print Actual table - # print("Router r%s table" % i) + # logger.info("Router r%s table" % i) # for line in actual: - # print(line.rstrip()) + # logger.info(line.rstrip()) # Generate Diff diff = topotest.get_textdiff(actual, expected, @@ -368,64 +364,60 @@ def test_linux_ipv6_kernel_routingTable(): sys.stderr.write('r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n' % (i, diff)) failures += 1 else: - print("r%s ok" % i) + logger.info("r%s ok" % i) assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) - def test_shutdown_check_stderr(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") + logger.info("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") pytest.skip('Skipping test for Stderr output') - thisDir = os.path.dirname(os.path.realpath(__file__)) + net = tgen.net - print("\n\n** Verifying unexpected STDERR output from daemons") - print("******************************************\n") + logger.info("\n\n** Verifying unexpected STDERR output from daemons") + logger.info("******************************************") for i in range(1, 5): net['r%s' % i].stopRouter() log = net['r%s' % i].getStdErr('ospf6d') if log: - print("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) log = net['r%s' % i].getStdErr('zebra') if log: - print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) def test_shutdown_check_memleak(): - global fatal_error - global net - - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + "Run the memory leak test and report results." if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") + logger.info("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)") pytest.skip('Skipping test for memory leaks') - - thisDir = os.path.dirname(os.path.realpath(__file__)) + + tgen = get_topogen() + + net = tgen.net for i in range(1, 5): net['r%s' % i].stopRouter() - net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net['r%s' % i].report_memory_leaks( + os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), + os.path.basename(__file__)) if __name__ == '__main__': - setLogLevel('info') - # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # To suppress tracebacks, either use the following pytest call or + # add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) sys.exit(retval) |
