diff options
Diffstat (limited to 'doc/developer')
23 files changed, 838 insertions, 304 deletions
diff --git a/doc/developer/building-frr-for-archlinux.rst b/doc/developer/building-frr-for-archlinux.rst index e589a9f724..af1677e89e 100644 --- a/doc/developer/building-frr-for-archlinux.rst +++ b/doc/developer/building-frr-for-archlinux.rst @@ -10,8 +10,8 @@ Installing Dependencies sudo pacman -S \ git autoconf automake libtool make cmake pcre readline texinfo \ pkg-config pam json-c bison flex python-pytest \ - c-ares python systemd python2-ipaddress python-sphinx \ - systemd-libs net-snmp perl libcap libelf + c-ares python python2-ipaddress python-sphinx \ + net-snmp perl libcap libelf .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index 5d3be492de..7a7af42119 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -172,7 +172,6 @@ an example.) --enable-user=frr \ --enable-group=frr \ --enable-vty-group=frrvty \ - --disable-exampledir \ --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index 8d0aea943c..ce11126f70 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -21,7 +21,7 @@ Add packages: sudo yum install git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig \ json-c-devel pam-devel bison flex pytest c-ares-devel \ - python-devel systemd-devel python-sphinx libcap-devel \ + python-devel python-sphinx libcap-devel \ elfutils-libelf-devel .. include:: building-libyang.rst @@ -66,8 +66,6 @@ an example.) --enable-user=frr \ --enable-group=frr \ --enable-vty-group=frrvty \ - --enable-systemd=yes \ - --disable-exampledir \ --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst index 77fe489358..109a7866d9 100644 --- a/doc/developer/building-frr-for-centos8.rst +++ b/doc/developer/building-frr-for-centos8.rst @@ -14,7 +14,7 @@ Add packages: sudo dnf install --enablerepo=PowerTools git autoconf pcre-devel \ automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \ groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \ - c-ares-devel python2-devel systemd-devel libcap-devel \ + c-ares-devel python2-devel libcap-devel \ elfutils-libelf-devel .. include:: building-libyang.rst @@ -59,8 +59,6 @@ an example.) --enable-user=frr \ --enable-group=frr \ --enable-vty-group=frrvty \ - --enable-systemd=yes \ - --disable-exampledir \ --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index 51dd07c42a..5e58854ed7 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -17,7 +17,7 @@ Add packages: sudo apt-get install git autoconf automake libtool make \ libreadline-dev texinfo libjson-c-dev pkg-config bison flex python3-pip \ - libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev \ + libc-ares-dev python3-dev python3-sphinx build-essential \ libsnmp-dev libcap-dev libelf-dev Install newer pytest (>3.0) from pip @@ -57,7 +57,6 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr \ --sysconfdir=/etc/frr \ diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index 919b010314..f8d8025f62 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -11,7 +11,7 @@ Add packages: sudo apt-get install git autoconf automake libtool make \ 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 libcap-dev libelf-dev + libsnmp-dev libcap-dev libelf-dev .. include:: building-libyang.rst @@ -44,7 +44,6 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/opt/frr \ --sbindir=/usr/lib/frr \ --sysconfdir=/etc/frr \ diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst index 5fecd8a826..6ce76ba158 100644 --- a/doc/developer/building-frr-for-fedora.rst +++ b/doc/developer/building-frr-for-fedora.rst @@ -14,7 +14,7 @@ Installing Dependencies sudo dnf install git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig json-c-devel \ pam-devel python3-pytest bison flex c-ares-devel python3-devel \ - python3-sphinx perl-core patch systemd-devel libcap-devel \ + python3-sphinx perl-core patch libcap-devel \ elfutils-libelf-devel .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst index e50d11130a..a78f8b3c2f 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -64,7 +64,6 @@ an example) export CPPFLAGS="-I/usr/pkg/include" ./configure \ --sysconfdir=/usr/pkg/etc/frr \ - --enable-exampledir=/usr/pkg/share/examples/frr \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ --localstatedir=/var/run/frr \ --enable-multipath=64 \ diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index 32d1145edc..a52ece19a1 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -55,7 +55,6 @@ an example) export CPPFLAGS="-I/usr/pkg/include" ./configure \ --sysconfdir=/usr/pkg/etc/frr \ - --enable-exampledir=/usr/pkg/share/examples/frr \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ --localstatedir=/var/run/frr \ --enable-multipath=64 \ diff --git a/doc/developer/building-frr-for-opensuse.rst b/doc/developer/building-frr-for-opensuse.rst index 4e886e9c25..ee6a36a14b 100644 --- a/doc/developer/building-frr-for-opensuse.rst +++ b/doc/developer/building-frr-for-opensuse.rst @@ -13,7 +13,7 @@ Installing Dependencies zypper in git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig libjson-c-devel\ pam-devel python3-pytest bison flex c-ares-devel python3-devel\ - python3-Sphinx perl patch systemd-devel libcap-devel libyang-devel \ + python3-Sphinx perl patch libcap-devel libyang-devel \ libelf-devel Building & Installing FRR diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst index 2cb9536f9b..d79545c859 100644 --- a/doc/developer/building-frr-for-ubuntu1604.rst +++ b/doc/developer/building-frr-for-ubuntu1604.rst @@ -13,8 +13,8 @@ Installing Dependencies apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ 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 libcap-dev \ + libc-ares-dev python3-dev python-ipaddress python3-sphinx \ + install-info build-essential libsnmp-dev perl libcap-dev \ libelf-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst index eb3991c139..39a17fc01c 100644 --- a/doc/developer/building-frr-for-ubuntu1804.rst +++ b/doc/developer/building-frr-for-ubuntu1804.rst @@ -13,8 +13,8 @@ Installing Dependencies sudo apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ 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 libcap-dev \ + libc-ares-dev python3-dev python-ipaddress python3-sphinx \ + install-info build-essential libsnmp-dev perl libcap-dev \ libelf-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst index 58d72e2891..92ddead4a5 100644 --- a/doc/developer/building-frr-for-ubuntu2004.rst +++ b/doc/developer/building-frr-for-ubuntu2004.rst @@ -13,8 +13,8 @@ Installing Dependencies sudo apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ 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 \ + libc-ares-dev python3-dev python-ipaddress python3-sphinx \ + install-info build-essential libsnmp-dev perl \ libcap-dev python2 libelf-dev Note that Ubuntu 20 no longer installs python 2.x, so it must be diff --git a/doc/developer/building-libyang.rst b/doc/developer/building-libyang.rst index a447f58309..c36cd34287 100644 --- a/doc/developer/building-libyang.rst +++ b/doc/developer/building-libyang.rst @@ -10,12 +10,11 @@ The FRR project builds some binary ``libyang`` packages. RPM packages are at our `RPM repository <https://rpm.frrouting.org>`_. DEB packages are available as CI artifacts `here -<https://ci1.netdef.org/browse/LIBYANG-LIBYANG-V2/latestSuccessful/artifact>`_. +<https://ci1.netdef.org/browse/LIBYANG-LIBYANGV2/latestSuccessful/artifact>`_. .. warning:: - ``libyang`` version 2.0.0 or newer is required to build FRR. Currently a tag - (``v2.0.0``) is used from the libyang2 branch. + ``libyang`` version 2.0.0 or newer is required to build FRR. .. note:: @@ -25,20 +24,20 @@ DEB packages are available as CI artifacts `here packages. Depending on your platform, you may also need to install the PCRE - development package. Typically this is ``libpcre-dev`` or ``pcre-devel``. + development package. Typically this is ``libpcre2-dev`` or ``pcre2-devel``. **Option 2: Source Install** .. note:: Ensure that the `libyang build requirements - <https://github.com/CESNET/libyang/tree/libyang2#build-requirements>`_ + <https://github.com/CESNET/libyang/#build-requirements>`_ are met before continuing. Usually this entails installing ``cmake`` and - ``libpcre-dev`` or ``pcre-devel``. + ``libpcre2-dev`` or ``pcre2-devel``. .. code-block:: console - git clone https://github.com/CESNET/libyang.git -b libyang2 + git clone https://github.com/CESNET/libyang.git cd libyang git checkout v2.0.0 mkdir build; cd build diff --git a/doc/developer/conf.py b/doc/developer/conf.py index 20265f4aad..8f282c0790 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -395,8 +395,11 @@ def setup(app): # printfrr extensions app.add_object_type("frrfmt", "frrfmt", parse_node=parse_frrfmt) - # css overrides for HTML theme - app.add_stylesheet("overrides.css") + if "add_css_file" in dir(app): + app.add_css_file("overrides.css") + else: + app.add_stylesheet("overrides.css") + # load Pygments lexer for FRR config syntax # # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst index 5da73f61f6..08e3057ae6 100644 --- a/doc/developer/frr-release-procedure.rst +++ b/doc/developer/frr-release-procedure.rst @@ -49,23 +49,14 @@ FRR Release Procedure 5. Update Changelog for Debian Packages: - Edit :file:`changelog-auto.in`: + Update :file:`debian/changelog`: - - Change last (top of list) entry from ``@VERSION@`` to the **last** - released version number. For example, if ``<version>`` is ``7.3`` and the - last public release was ``7.2``, you would use ``7.2``, changing the file - like so:: + - Run following with **last** release version number and debian revision + (usually -1) as argument to ``dch --newversion VERSION``. For example, if + ``<version>`` is ``7.3`` then you will run ``dch --newversion 7.3-1``. - frr (@VERSION@) RELEASED; urgency=medium - - to:: - - frr (7.2) RELEASED; urgency=medium - - - Add a new entry to the top of the list with a ``@VERSION@`` tag. Make sure - to watch the format. - - - Add the changelog text below this entry. + - The ``dch`` will run an editor, and you should add the changelog text below + this entry, usually that would be: **New upstream version**. - Verify the changelog format using ``dpkg-parsechangelog``. In the repository root: diff --git a/doc/developer/grpc.rst b/doc/developer/grpc.rst index 8029a08b73..cb164bdabf 100644 --- a/doc/developer/grpc.rst +++ b/doc/developer/grpc.rst @@ -4,6 +4,17 @@ Northbound gRPC *************** +To enable gRPC support one needs to add `--enable-grpc` when running +`configure`. Additionally, when launching each daemon one needs to request +the gRPC module be loaded and which port to bind to. This can be done by adding +`-M grpc:<port>` to the daemon's CLI arguments. + +Currently there is no gRPC "routing" so you will need to bind your gRPC +`channel` to the particular daemon's gRPC port to interact with that daemon's +gRPC northbound interface. + +The minimum version of gRPC known to work is 1.16.1. + .. _grpc-languages-bindings: Programming Language Bindings @@ -17,14 +28,277 @@ next step is to generate the FRR northbound bindings. To generate the northbound bindings you'll need the programming language binding generator tools and those are language specific. -Next sections will use Ruby as an example for writing scripts to use +C++ Example +----------- + +The next sections will use C++ as an example for accessing FRR +northbound through gRPC. + +.. _grpc-c++-generate: + +Generating C++ FRR Bindings +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Generating FRR northbound bindings for C++ example: + +:: + # Install gRPC (e.g., on Ubuntu 20.04) + sudo apt-get install libgrpc++-dev libgrpc-dev + + mkdir /tmp/frr-cpp + cd grpc + + protoc --cpp_out=/tmp/frr-cpp \ + --grpc_out=/tmp/frr-cpp \ + -I $(pwd) \ + --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` \ + frr-northbound.proto + + +.. _grpc-c++-if-sample: + +Using C++ To Get Version and Interfaces State +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Below is a sample program to print all interfaces discovered. + +:: + + # test.cpp + #include <string> + #include <sstream> + #include <grpc/grpc.h> + #include <grpcpp/create_channel.h> + #include "frr-northbound.pb.h" + #include "frr-northbound.grpc.pb.h" + + int main() { + frr::GetRequest request; + frr::GetResponse reply; + grpc::ClientContext context; + grpc::Status status; + + auto channel = grpc::CreateChannel("localhost:50051", + grpc::InsecureChannelCredentials()); + auto stub = frr::Northbound::NewStub(channel); + + request.set_type(frr::GetRequest::ALL); + request.set_encoding(frr::JSON); + request.set_with_defaults(true); + request.add_path("/frr-interface:lib"); + auto stream = stub->Get(&context, request); + + std::ostringstream ss; + while (stream->Read(&reply)) + ss << reply.data().data() << std::endl; + + status = stream->Finish(); + assert(status.ok()); + std::cout << "Interface Info:\n" << ss.str() << std::endl; + } + +Below is how to compile and run the program, with the example output: + +:: + + $ g++ -o test test.cpp frr-northbound.grpc.pb.cc frr-northbound.pb.cc -lgrpc++ -lprotobuf + $ ./test + Interface Info: + { + "frr-interface:lib": { + "interface": [ + { + "name": "lo", + "vrf": "default", + "state": { + "if-index": 1, + "mtu": 0, + "mtu6": 65536, + "speed": 0, + "metric": 0, + "phy-address": "00:00:00:00:00:00" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0, + "ptm-status": "disabled" + } + } + }, + { + "name": "r1-eth0", + "vrf": "default", + "state": { + "if-index": 2, + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "02:37:ac:63:59:b9" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0, + "ptm-status": "disabled" + } + } + } + ] + }, + "frr-zebra:zebra": { + "mcast-rpf-lookup": "mrib-then-urib", + "workqueue-hold-timer": 10, + "zapi-packets": 1000, + "import-kernel-table": { + "distance": 15 + }, + "dplane-queue-limit": 200 + } + } + + + +.. _grpc-python-example: + +Python Example +-------------- + +The next sections will use Python as an example for writing scripts to use the northbound. +.. _grpc-python-generate: + +Generating Python FRR Bindings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Generating FRR northbound bindings for Python example: + +:: + + # Install python3 virtual environment capability e.g., + sudo apt-get install python3-venv + + # Create a virtual environment for python grpc and activate + python3 -m venv venv-grpc + source venv-grpc/bin/activate + + # Install grpc requirements + pip install grpcio grpcio-tools + + mkdir /tmp/frr-python + cd grpc + + python3 -m grpc_tools.protoc \ + --python_out=/tmp/frr-python \ + --grpc_python_out=/tmp/frr-python \ + -I $(pwd) \ + frr-northbound.proto + +.. _grpc-python-if-sample: + +Using Python To Get Capabilities and Interfaces State +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Below is a sample script to print capabilities and all interfaces Python +discovered. This demostrates the 2 different RPC results one gets from gRPC, +Unary (`GetCapabilities`) and Streaming (`Get`) for the interface state. + +:: + + import grpc + import frr_northbound_pb2 + import frr_northbound_pb2_grpc + + channel = grpc.insecure_channel('localhost:50051') + stub = frr_northbound_pb2_grpc.NorthboundStub(channel) + + # Print Capabilities + request = frr_northbound_pb2.GetCapabilitiesRequest() + response = stub.GetCapabilities(request) + print(response) + + # Print Interface State and Config + request = frr_northbound_pb2.GetRequest() + request.path.append("/frr-interface:lib") + request.type=frr_northbound_pb2.GetRequest.ALL + request.encoding=frr_northbound_pb2.XML + + for r in stub.Get(request): + print(r.data.data) + +The previous script will output something like: + +:: + + frr_version: "7.7-dev-my-manual-build" + rollback_support: true + supported_modules { + name: "frr-filter" + organization: "FRRouting" + revision: "2019-07-04" + } + supported_modules { + name: "frr-interface" + organization: "FRRouting" + revision: "2020-02-05" + } + [...] + supported_encodings: JSON + supported_encodings: XML + + <lib xmlns="http://frrouting.org/yang/interface"> + <interface> + <name>lo</name> + <vrf>default</vrf> + <state> + <if-index>1</if-index> + <mtu>0</mtu> + <mtu6>65536</mtu6> + <speed>0</speed> + <metric>0</metric> + <phy-address>00:00:00:00:00:00</phy-address> + </state> + <zebra xmlns="http://frrouting.org/yang/zebra"> + <state> + <up-count>0</up-count> + <down-count>0</down-count> + </state> + </zebra> + </interface> + <interface> + <name>r1-eth0</name> + <vrf>default</vrf> + <state> + <if-index>2</if-index> + <mtu>1500</mtu> + <mtu6>1500</mtu6> + <speed>10000</speed> + <metric>0</metric> + <phy-address>f2:62:2e:f3:4c:e4</phy-address> + </state> + <zebra xmlns="http://frrouting.org/yang/zebra"> + <state> + <up-count>0</up-count> + <down-count>0</down-count> + </state> + </zebra> + </interface> + </lib> + +.. _grpc-ruby-example: + +Ruby Example +------------ + +Next sections will use Ruby as an example for writing scripts to use +the northbound. .. _grpc-ruby-generate: Generating Ruby FRR Bindings ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Generating FRR northbound bindings for Ruby example: @@ -52,7 +326,7 @@ Generating FRR northbound bindings for Ruby example: .. _grpc-ruby-if-sample: Using Ruby To Get Interfaces State ----------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here is a sample script to print all interfaces FRR discovered: @@ -141,7 +415,7 @@ The previous script will output something like this: .. _grpc-ruby-bfd-profile-sample: Using Ruby To Create BFD Profiles ---------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In this example you'll learn how to edit configuration using JSON and programmatic (XPath) format. diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst index 0ff0ae3ffe..513cac6179 100644 --- a/doc/developer/include-compile.rst +++ b/doc/developer/include-compile.rst @@ -2,11 +2,6 @@ Clone the FRR git repo and use the included ``configure`` script to configure FRR's build time options to your liking. The full option listing can be obtained by running ``./configure -h``. The options shown below are examples. -.. note:: - - If your platform uses ``systemd``, please make sure to add - ``--enable-systemd=yes`` to your configure options. - .. code-block:: console git clone https://github.com/frrouting/frr.git frr @@ -15,7 +10,6 @@ obtained by running ``./configure -h``. The options shown below are examples. ./configure \ --prefix=/usr \ --includedir=\${prefix}/include \ - --enable-exampledir=\${prefix}/share/doc/frr/examples \ --bindir=\${prefix}/bin \ --sbindir=\${prefix}/lib/frr \ --libdir=\${prefix}/lib/frr \ diff --git a/doc/developer/packaging-debian.rst b/doc/developer/packaging-debian.rst index b57286d5a1..a81e052490 100644 --- a/doc/developer/packaging-debian.rst +++ b/doc/developer/packaging-debian.rst @@ -30,33 +30,30 @@ buster.) .. code-block:: shell - sudo mk-build-deps --install debian/control + sudo mk-build-deps --install --remove debian/control Alternatively, you can manually install build dependencies for your platform as outlined in :ref:`building`. -4. Run ``tools/tarsource.sh -V``: +4. Install `git-buildpackage` package: .. code-block:: shell - ./tools/tarsource.sh -V - - This script sets up the ``debian/changelog-auto`` file with proper version - information. + sudo apt-get install git-buildpackage 5. (optional) Append a distribution identifier if needed (see below under :ref:`multi-dist`.) -6. Build Debian Package: +6. Build Debian Binary and/or Source Packages: .. code-block:: shell - dpkg-buildpackage $options + gbp buildpackage --git-builder=dpkg-buildpackage --git-debian-branch="$(git rev-parse --abbrev-ref HEAD)" $options Where `$options` may contain any or all of the following items: * build profiles specified with ``-P``, e.g. - ``-Ppkg.frr.nortrlib,pkg.frr.nosystemd``. + ``-Ppkg.frr.nortrlib,pkg.frr.rtrlib``. Multiple values are separated by commas and there must not be a space after the ``-P``. @@ -67,20 +64,24 @@ buster.) +================+===================+=========================================+ | pkg.frr.rtrlib | pkg.frr.nortrlib | builds frr-rpki-rtrlib package (or not) | +----------------+-------------------+-----------------------------------------+ - | n/a | pkg.frr.nosystemd | removes libsystemd dependency and | - | | | disables unit file installation | - +----------------+-------------------+-----------------------------------------+ - - .. note:: - - The ``pkg.frr.nosystemd`` option is only intended to support Ubuntu - 14.04 (and should be enabled when building for that.) * the ``-uc -us`` options to disable signing the packages with your GPG key (git builds of the `master` or `stable/X.X` branches won't be signed by default since their target release is set to ``UNRELEASED``.) + * the ``--build=type`` accepts following options (see ``dpkg-buildpackage`` manual page): + + * ``source`` builds the source package + * ``any`` builds the architecture specific binary packages + * ``all`` build the architecture independent binary packages + * ``binary`` build the architecture specific and independent binary packages (alias for ``any,all``) + * ``full`` builds everything (alias for ``source,any,all``) + + Alternatively, you might want to replace ``dpkg-buildpackage`` with + ``debuild`` wrapper that also runs ``lintian`` and ``debsign`` on the final + packages. + 7. Done! If all worked correctly, then you should end up with the Debian packages in @@ -97,12 +98,6 @@ buster.) a manually maintained changelog that contains proper Debian release versioning. - Furthermore, official Debian packages are built in ``3.0 (quilt)`` format - with an "orig" tarball and a "debian" tarball. These tarballs are created - by the ``tarsource.sh`` tool on any branch. The git repository however - contains a ``3.0 (git)`` source format specifier to easily allow direct - git builds. - .. _multi-dist: @@ -111,7 +106,6 @@ Multi-Distribution builds You can optionally append a distribution identifier in case you want to make multiple versions of the package available in the same repository. -Do the following after creating the changelog with `tarsource.sh`: .. code-block:: shell diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index 458cfa0ad4..9e64b912f3 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -18,10 +18,6 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. yum install rpm-build net-snmp-devel pam-devel libcap-devel - If your platform uses systemd:: - - yum install systemd-devel - For CentOS 7 and CentOS 8, the package will be built using python3 and requires additional python3 packages:: diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst index 708f65ff7d..d543ed3560 100644 --- a/doc/developer/scripting.rst +++ b/doc/developer/scripting.rst @@ -14,8 +14,8 @@ is implemented using the standard Lua C bindings. The supported version of Lua is 5.3. C objects may be passed into Lua and Lua objects may be retrieved by C code via -a marshalling system. In this way, arbitrary data from FRR may be passed to -scripts. It is possible to pass C functions as well. +a encoding/decoding system. In this way, arbitrary data from FRR may be passed to +scripts. The Lua environment is isolated from the C environment; user scripts cannot access FRR's address space unless explicitly allowed by FRR. @@ -53,213 +53,297 @@ Reasons against supporting multiple scripting languages: with which a given script can be shared General -^^^^^^^ - -FRR's concept of a script is somewhat abstracted away from the fact that it is -Lua underneath. A script in has two things: - -- name -- state +------- -In code: +FRR's scripting functionality is provided in the form of Lua functions in Lua +scripts (``.lua`` files). One Lua script may contain many Lua functions. These +are respectively encapsulated in the following structures: .. code-block:: c struct frrscript { - /* Script name */ - char *name; + /* Lua file name */ + char *name; - /* Lua state */ - struct lua_State *L; + /* hash of lua_function_states */ + struct hash *lua_function_hash; }; + struct lua_function_state { + /* Lua function name */ + char *name; -``name`` is simply a string. Everything else is in ``state``, which is itself a -Lua library object (``lua_State``). This is an opaque struct that is -manipulated using ``lua_*`` functions. The basic ones are imported from -``lua.h`` and the rest are implemented within FRR to fill our use cases. The -thing to remember is that all operations beyond the initial loading the script -take place on this opaque state object. + lua_State *L; + }; -There are four basic actions that can be done on a script: -- load -- execute -- query state -- unload +`struct frrscript`: Since all Lua functions are contained within scripts, the +following APIs manipulates this structure. ``name`` contains the +Lua script name and a hash of Lua functions to their function names. -They are typically done in this order. +`struct lua_function_state` is an internal structure, but it essentially contains +the name of the Lua function and its state (a stack), which is run using Lua +library functions. +In general, to run a Lua function, these steps must take place: -Loading -^^^^^^^ +- Initialization +- Load +- Call +- Delete -A snippet of Lua code is referred to as a "chunk". These are simply text. FRR -presently assumes chunks are located in individual files specific to one task. -These files are stored in the scripts directory and must end in ``.lua``. +Initialization +^^^^^^^^^^^^^^ -A script object is created by loading a script. This is done with -``frrscript_load()``. This function takes the name of the script and an -optional callback function. The string ".lua" is appended to the script name, -and the resultant filename is looked for in the scripts directory. +The ``frrscript`` object encapsulates the Lua function state(s) from +one Lua script file. To create, use ``frrscript_new()`` which takes the +name of the Lua script. +The string ".lua" is appended to the script name, and the resultant filename +will be used to look for the script when we want to load a Lua function from it. -For example, to load ``/etc/frr/scripts/bingus.lua``: +For example, to create ``frrscript`` for ``/etc/frr/scripts/bingus.lua``: .. code-block:: c - struct frrscript *fs = frrscript_load("bingus", NULL); + struct frrscript *fs = frrscript_new("bingus"); -During loading the script is validated for syntax and its initial environment -is setup. By default this does not include the Lua standard library; there are -security issues to consider, though for practical purposes untrusted users -should not be able to write the scripts directory anyway. If desired the Lua -standard library may be added to the script environment using -``luaL_openlibs(fs->L)`` after loading the script. Further information on -setting up the script environment is in the Lua manual. +The script is *not* read at this stage. +This function cannot be used to test for a script's presence. -Executing -^^^^^^^^^ +Load +^^^^ -After loading, scripts may be executed. A script may take input in the form of -variable bindings set in its environment prior to being run, and may provide -results by setting the value of variables. Arbitrary C values may be -transferred into the script environment, including functions. +The function to be called must first be loaded. Use ``frrscript_load()`` +which takes a ``frrscript`` object, the name of the Lua function +and a callback function. -A typical execution call looks something like this: +For example, to load the Lua function ``on_foo`` +in ``/etc/frr/scripts/bingus.lua``: .. code-block:: c - struct frrscript *fs = frrscript_load(...); + int ret = frrscript_load(fs, "on_foo", NULL); - int status_ok = 0, status_fail = 1; - struct prefix p = ...; - struct frrscript_env env[] = { - {"integer", "STATUS_FAIL", &status_fail}, - {"integer", "STATUS_OK", &status_ok}, - {"prefix", "myprefix", &p}, - {}}; +This function returns 0 if and only if the Lua function was successfully loaded. +A non-zero return could indicate either a missing Lua script, a missing +Lua function, or an error when loading the function. - int result = frrscript_call(fs, env); +During loading the script is validated for syntax and its environment +is set up. By default this does not include the Lua standard library; there are +security issues to consider, though for practical purposes untrusted users +should not be able to write the scripts directory anyway. +Call +^^^^ -To execute a loaded script, we need to define the inputs. These inputs are -passed by binding values to variable names that will be accessible within the -Lua environment. Basically, all communication with the script takes place via -global variables within the script, and to provide inputs we predefine globals -before the script runs. This is done by passing ``frrscript_call()`` an array -of ``struct frrscript_env``. Each struct has three fields. The first identifies -the type of the value being passed; more on this later. The second defines the -name of the global variable within the script environment to bind the third -argument (the value) to. +After loading, Lua functions may be called. -The script is then executed and returns a general status code. In the success -case this will be 0, otherwise it will be nonzero. The script itself does not -determine this code, it is provided by the Lua interpreter. +Input +""""" +Inputs to the Lua script should be given by providing a list of parenthesized +pairs, +where the first and second field identify the name of the variable and the +value it is bound to, respectively. +The types of the values must have registered encoders (more below); the compiler +will warn you otherwise. -Querying State -^^^^^^^^^^^^^^ +These variables are first encoded in-order, then provided as arguments +to the Lua function. In the example, note that ``c`` is passed in as a value +while ``a`` and ``b`` are passed in as pointers. + +.. code-block:: c + + int a = 100, b = 200, c = 300; + frrscript_call(fs, "on_foo", ("a", &a), ("b", &b), ("c", c)); -When a chunk is executed, its state at exit is preserved and can be inspected. -After running a script, results may be retrieved by querying the script's -state. Again this is done by retrieving the values of global variables, which -are known to the script author to be "output" variables. +.. code-block:: lua + + function on_foo(a, b, c) + -- a is 100, b is 200, c is 300 + ... + -A result is retrieved like so: +Output +"""""" .. code-block:: c - struct frrscript_env myresult = {"string", "myresult"}; + int a = 100, b = 200, c = 300; + frrscript_call(fs, "on_foo", ("a", &a), ("b", &b), ("c", c)); + // a is 500, b is 200, c is 300 + + int* d = frrscript_get_result(fs, "on_foo", "d", lua_tointegerp); + // d is 800 + + +.. code-block:: lua + + function on_foo(a, b, c) + b = 600 + return { ["a"] = 500, ["c"] = 700, ["d"] = 800 } + end + + +**Lua functions being called must return a single table of string names to +values.** +(Lua functions should return an empty table if there is no output.) +The keys of the table are mapped back to names of variables in C. Note that +the values in the table can also be tables. Since tables are Lua's primary +data structure, this design lets us return any Lua value. + +After the Lua function returns, the names of variables to ``frrscript_call()`` +are matched against keys of the returned table, and then decoded. The types +being decoded must have registered decoders (more below); the compiler will +warn you otherwise. - char *myresult = frrscript_get_result(fs, &myresult); +In the example, since ``a`` was in the returned table and ``b`` was not, +``a`` was decoded and its value modified, while ``b`` was not decoded. +``c`` was decoded as well, but its decoder is a noop. +What modifications happen given a variable depends whether its name was +in the returned table and the decoder's implementation. - ... do something ... +.. warning:: + Always keep in mind that non const-qualified pointers in + ``frrscript_call()`` may be modified - this may be a source of bugs. + On the other hand, const-qualified pointers and other values cannot + be modified. - XFREE(MTYPE_TMP, myresult); +.. tip:: + You can make a copy of a data structure and pass that in instead, + so that modifications only happen to that copy. -As with arguments, results are retrieved by providing a ``struct -frrscript_env`` specifying a type and a global name. No value is necessary, nor -is it modified by ``frrscript_get_result()``. That function simply extracts the -requested value from the script state and returns it. +``frrscript_call()`` returns 0 if and only if the Lua function was successfully +called. A non-zero return could indicate either a missing Lua script, a missing +Lua function, or an error from the Lua interpreter. -In most cases the returned value will be allocated with ``MTYPE_TMP`` and will -need to be freed after use. +In the above example, ``d`` was not an input to ``frrscript_call()``, so its +value must be explicitly retrieved with ``frrscript_get_result``. +``frrscript_get_result()`` takes a +decoder and string name which is used as a key to search the returned table. +Returns the pointer to the decoded value, or NULL if it was not found. +In the example, ``d`` is a "new" value in C space, +so memory allocation might take place. Hence the caller is +responsible for memory deallocation. -Unloading -^^^^^^^^^ -To destroy a script and its associated state: +Delete +^^^^^^ + +To delete a script and the all Lua states associated with it: + +.. code-block:: c + + frrscript_delete(fs); + + +A complete example +"""""""""""""""""" + +So, a typical execution call, with error checking, looks something like this: .. code-block:: c - frrscript_unload(fs); + struct frrscript *fs = frrscript_new("my_script"); // name *without* .lua + + int ret = frrscript_load(fs, "on_foo", NULL); + if (ret != 0) + goto DONE; // Lua script or function might have not been found + + int a = 100, b = 200, c = 300; + ret = frrscript_call(fs, "on_foo", ("a", &a), ("b", &b), ("c", c)); + if (ret != 0) + goto DONE; // Lua function might have not successfully run -Values returned by ``frrscript_get_result`` are still valid after the script -they were retrieved from is unloaded. + // a and b might be modified + assert(a == 500); + assert(b == 200); -Note that you must unload and then load the script if you want to reset its -state, for example to run it again with different inputs. Otherwise the state -from the previous run carries over into subsequent runs. + // c could not have been modified + assert(c == 300); + // d is new + int* d = frrscript_get_result(fs, "on_foo", "d", lua_tointegerp); -.. _marshalling: + if (!d) + goto DONE; // "d" might not have been in returned table -Marshalling -^^^^^^^^^^^ + assert(*d == 800); + XFREE(MTYPE_SCRIPT_RES, d); // caller responsible for free -Earlier sections glossed over the meaning of the type name field in ``struct -frrscript_env`` and how data is passed between C and Lua. Lua, as a dynamically -typed, garbage collected language, cannot directly use C values without some -kind of marshalling / unmarshalling system to translate types between the two -runtimes. + DONE: + frrscript_delete(fs); + + +.. code-block:: lua + + function on_foo(a, b, c) + b = 600 + return { a = 500, c = 700, d = 800 } + end + + +Note that ``{ a = ...`` is same as ``{ ["a"] = ...``; it is Lua shorthand to +use the variable name as the key in a table. + +Encoding and Decoding +^^^^^^^^^^^^^^^^^^^^^ + +Earlier sections glossed over the types of values that can be passed into +``frrscript_call()`` and how data is passed between C and Lua. Lua, as a +dynamically typed, garbage collected language, cannot directly use C values +without some kind of encoding / decoding system to +translate types between the two runtimes. Lua communicates with C code using a stack. C code wishing to provide data to -Lua scripts must provide a function that marshalls the C data into a Lua +Lua scripts must provide a function that encodes the C data into a Lua representation and pushes it on the stack. C code wishing to retrieve data from -Lua must provide a corresponding unmarshalling function that retrieves a Lua -value from the stack and converts it to the corresponding C type. These two -functions, together with a chosen name of the type they operate on, are -referred to as ``codecs`` in FRR. +Lua must provide a corresponding decoder function that retrieves a Lua +value from the stack and converts it to the corresponding C type. -A codec is defined as: +Encoders and decoders are provided for common data types. +Developers wishing to pass their own data structures between C and Lua need to +create encoders and decoders for that data type. -.. code-block:: c +We try to keep them named consistently. +There are three kinds of encoders and decoders: - typedef void (*encoder_func)(lua_State *, const void *); - typedef void *(*decoder_func)(lua_State *, int); +1. lua_push*: encodes a value onto the Lua stack. + Required for ``frrscript_call``. - struct frrscript_codec { - const char *typename; - encoder_func encoder; - decoder_func decoder; - }; +2. lua_decode*: decodes a value from the Lua stack. + Required for ``frrscript_call``. + Only non const-qualified pointers may be actually decoded (more below). + +3. lua_to*: allocates memory and decodes a value from the Lua stack. + Required for ``frrscript_get_result``. -A typename string and two function pointers. +This design allows us to combine typesafe *modification* of C values as well as +*allocation* of new C values. -``typename`` can be anything you want. For example, for the combined types of -``struct prefix`` and its equivalent in Lua I have chosen the name ``prefix``. -There is no restriction on naming here, it is just a human name used as a key -and specified when passing and retrieving values. +In the following sections, we will use the encoders/decoders for ``struct prefix`` as an example. -``encoder`` is a function that takes a ``lua_State *`` and a C type and pushes -onto the Lua stack a value representing the C type. For C structs, the usual -case, this will typically be a Lua table (tables are the only datastructure Lua -has). For example, here is the encoder function for ``struct prefix``: +Encoding +"""""""" +An encoder function takes a ``lua_State *``, a C type and pushes that value onto +the Lua state (a stack). +For C structs, the usual case, +this will typically be encoded to a Lua table, then pushed onto the Lua stack. + +Here is the encoder function for ``struct prefix``: .. code-block:: c - void lua_pushprefix(lua_State *L, const struct prefix *prefix) + void lua_pushprefix(lua_State *L, struct prefix *prefix) { char buffer[PREFIX_STRLEN]; - zlog_debug("frrlua: pushing prefix table"); - lua_newtable(L); lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN)); lua_setfield(L, -2, "network"); @@ -269,65 +353,151 @@ has). For example, here is the encoder function for ``struct prefix``: lua_setfield(L, -2, "family"); } -This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is: +This function pushes a single value, a table, onto the Lua stack, whose +equivalent in Lua is: .. code-block:: c { ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 } -``decoder`` does the reverse; it takes a ``lua_State *`` and an index into the -stack, and unmarshalls a Lua value there into the corresponding C type. Again -for ``struct prefix``: +Decoding +"""""""" +Decoders are a bit more involved. They do the reverse; a decoder function takes +a ``lua_State *``, pops a value off the Lua stack and converts it back into its +C type. + +There are two: ``lua_decode*`` and ``lua_to*``. The former does no mememory +allocation and is needed for ``frrscript_call``. +The latter performs allocation and is optional. + +A ``lua_decode_*`` function takes a ``lua_State*``, an index, and a pointer +to a C data structure, and directly modifies the structure with values from the +Lua stack. Note that only non const-qualified pointers may be modified; +``lua_decode_*`` for other types will be noops. + +Again, for ``struct prefix *``: .. code-block:: c - void *lua_toprefix(lua_State *L, int idx) + void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix) { - struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix)); + lua_getfield(L, idx, "network"); + (void)str2prefix(lua_tostring(L, -1), prefix); + /* pop the netork string */ + lua_pop(L, 1); + /* pop the prefix table */ + lua_pop(L, 1); + } + + +Note: + - Before ``lua_decode*`` is run, the "prefix" table is already on the top of + the stack. ``frrscript_call`` does this for us. + - However, at the end of ``lua_decode*``, the "prefix" table should be popped. + - The other two fields in the "network" table are disregarded, meaning that any + modification to them is discarded in C space. In this case, this is desired + behavior. + +.. warning:: + + ``lua_decode*`` functions should pop all values that ``lua_to*`` pushed onto + the Lua stack. + For encoders that pushed a table, its decoder should pop the table at the end. + The above is an example. + + + +``int`` is not a non const-qualified pointer, so for ``int``: - lua_getfield(L, idx, "network"); - str2prefix(lua_tostring(L, -1), p); - lua_pop(L, 1); +.. code-block:: c + + void lua_decode_int_noop(lua_State *L, int idx, int i) + { //noop + } + + +A ``lua_to*`` function provides identical functionality except that it first +allocates memory for the new C type before decoding the value from the Lua stack, +then returns a pointer to the newly allocated C type. You only need to implement +this function to use with ``frrscript_get_result`` to retrieve a result of +this type. + +This function can and should be implemented using ``lua_decode_*``: +.. code-block:: c + + void *lua_toprefix(lua_State *L, int idx) + { + struct prefix *p = XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct prefix)); + + lua_decode_prefix(L, idx, p); return p; } -By convention these functions should be called ``lua_to*``, as this is the -naming convention used by the Lua C library for the basic types e.g. -``lua_tointeger`` and ``lua_tostring``. The returned data must always be copied off the stack and the copy must be -allocated with ``MTYPE_TMP``. This way it is possible to unload the script +allocated with ``MTYPE_SCRIPT_RES``. This way it is possible to unload the script (destroy the state) without invalidating any references to values stored in it. +Note that it is the caller's responsibility to free the data. -To register a new type with its corresponding encoding functions: -.. code-block:: c +Registering encoders and decoders for frrscript_call +"""""""""""""""""""""""""""""""""""""""""""""""""""" - struct frrscript_codec frrscript_codecs_lib[] = { - {.typename = "prefix", - .encoder = (encoder_func)lua_pushprefix, - .decoder = lua_toprefix}, - {.typename = "sockunion", - .encoder = (encoder_func)lua_pushsockunion, - .decoder = lua_tosockunion}, - ... - {}}; +To register a new type with its ``lua_push*`` and ``lua_decode*`` functions, +add the mapping in the following macros in ``frrscript.h``: - frrscript_register_type_codecs(frrscript_codecs_lib); +.. code-block:: diff + + #define ENCODE_ARGS_WITH_STATE(L, value) \ + _Generic((value), \ + ... + - struct peer * : lua_pushpeer \ + + struct peer * : lua_pushpeer, \ + + struct prefix * : lua_pushprefix \ + )((L), (value)) + + #define DECODE_ARGS_WITH_STATE(L, value) \ + _Generic((value), \ + ... + - struct peer * : lua_decode_peer \ + + struct peer * : lua_decode_peer, \ + + struct prefix * : lua_decode_prefix \ + )((L), -1, (value)) + + +At compile time, the compiler will search for encoders/decoders for the type of +each value passed in via ``frrscript_call``. If a encoder/decoder cannot be +found, it will appear as a compile warning. Note that the types must +match *exactly*. +In the above example, we defined encoders/decoders for a value of +``struct prefix *``, but not ``struct prefix`` or ``const struct prefix *``. + +``const`` values are a special case. We want to use them in our Lua scripts +but not modify them, so creating a decoder for them would be meaningless. +But we still need a decoder for the type of value so that the compiler will be +satisfied. +For that, use ``lua_decode_noop``: + +.. code-block:: diff + + #define DECODE_ARGS_WITH_STATE(L, value) \ + _Generic((value), \ + ... + + const struct prefix * : lua_decode_noop \ + )(L, -1, value) -From this point on the type names are available to be used when calling any -script and getting its results. .. note:: - Marshalled types are not restricted to simple values like integers, strings - and tables. It is possible to marshall a type such that the resultant object - in Lua is an actual object-oriented object, complete with methods that call - back into defined C functions. See the Lua manual for how to do this; for a - code example, look at how zlog is exported into the script environment. + Encodable/decodable types are not restricted to simple values like integers, + strings and tables. + It is possible to encode a type such that the resultant object in Lua + is an actual object-oriented object, complete with methods that call + back into defined C functions. See the Lua manual for how to do this; + for a code example, look at how zlog is exported into the script environment. Script Environment @@ -356,10 +526,11 @@ Examples For a complete code example involving passing custom types, retrieving results, and doing complex calculations in Lua, look at the implementation of the ``match script SCRIPT`` command for BGP routemaps. This example calls into a -script with a route prefix and attributes received from a peer and expects the -script to return a match / no match / match and update result. +script with a function named ``route_match``, +provides route prefix and attributes received from a peer and expects the +function to return a match / no match / match and update result. -An example script to use with this follows. This script matches, does not match +An example script to use with this follows. This function matches, does not match or updates a route depending on how many BGP UPDATE messages the peer has received when the script is called, simply as a demonstration of what can be accomplished with scripting. @@ -370,64 +541,75 @@ accomplished with scripting. -- Example route map matching -- author: qlyoung -- - -- The following variables are available to us: + -- The following variables are available in the global environment: -- log -- logging library, with the usual functions - -- prefix + -- + -- route_match arguments: + -- table prefix -- the route under consideration - -- attributes + -- table attributes -- the route's attributes - -- peer + -- table peer -- the peer which received this route - -- RM_FAILURE + -- integer RM_FAILURE -- status code in case of failure - -- RM_NOMATCH + -- integer RM_NOMATCH -- status code for no match - -- RM_MATCH + -- integer RM_MATCH -- status code for match - -- RM_MATCH_AND_CHANGE + -- integer RM_MATCH_AND_CHANGE -- status code for match-and-set -- - -- We need to set the following out values: - -- action - -- Set to the appropriate status code to indicate what we did - -- attributes - -- Setting fields on here will propagate them back up to the caller if - -- 'action' is set to RM_MATCH_AND_CHANGE. - - - log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string) - - function on_match (prefix, attrs) - log.info("Match") - action = RM_MATCH - end - - function on_nomatch (prefix, attrs) - log.info("No match") - action = RM_NOMATCH - end - - function on_match_and_change (prefix, attrs) - action = RM_MATCH_AND_CHANGE - log.info("Match and change") - attrs["metric"] = attrs["metric"] + 7 - end - - special_routes = { - ["172.16.10.4/24"] = on_match, - ["172.16.13.1/8"] = on_nomatch, - ["192.168.0.24/8"] = on_match_and_change, - } + -- route_match returns table with following keys: + -- integer action, required + -- resultant status code. Should be one of RM_* + -- table attributes, optional + -- updated route attributes + -- + + function route_match(prefix, attributes, peer, + RM_FAILURE, RM_NOMATCH, RM_MATCH, RM_MATCH_AND_CHANGE) + + log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string) + function on_match (prefix, attributes) + log.info("Match") + return { + attributes = RM_MATCH + } + end - if special_routes[prefix.network] then - special_routes[prefix.network](prefix, attributes) - elseif peer.stats.update_in % 3 == 0 then - on_match(prefix, attributes) - elseif peer.stats.update_in % 2 == 0 then - on_nomatch(prefix, attributes) - else - on_match_and_change(prefix, attributes) - end - + function on_nomatch (prefix, attributes) + log.info("No match") + return { + action = RM_NOMATCH + } + end + + function on_match_and_change (prefix, attributes) + log.info("Match and change") + attributes["metric"] = attributes["metric"] + 7 + return { + action = RM_MATCH_AND_CHANGE, + attributes = attributes + } + end + + special_routes = { + ["172.16.10.4/24"] = on_match, + ["172.16.13.1/8"] = on_nomatch, + ["192.168.0.24/8"] = on_match_and_change, + } + + + if special_routes[prefix.network] then + return special_routes[prefix.network](prefix, attributes) + elseif peer.stats.update_in % 3 == 0 then + return on_match(prefix, attributes) + elseif peer.stats.update_in % 2 == 0 then + return on_nomatch(prefix, attributes) + else + return on_match_and_change(prefix, attributes) + end + end diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index dd797a6949..18317cd33c 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -221,7 +221,6 @@ for ``master`` branch: --prefix=/usr/lib/frr --sysconfdir=/etc/frr \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr --bindir=/usr/lib/frr \ - --enable-exampledir=/usr/lib/frr/examples \ --with-moduledir=/usr/lib/frr/modules \ --enable-multipath=0 --enable-rtadv \ --enable-tcp-zebra --enable-fpm --enable-pimd \ @@ -312,6 +311,20 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router --gdb-breakpoints=nb_config_diff \ all-protocol-startup +Detecting Memleaks with Valgrind +"""""""""""""""""""""""""""""""" + +Topotest can automatically launch all daemons with ``valgrind`` to check for +memleaks. This is enabled by specifying 1 or 2 CLI arguments. +``--valgrind-memleaks`` will enable general memleak detection, and +``--valgrind-extra`` enables extra functionality including generating a +suppression file. The suppression file ``tools/valgrind.supp`` is used when +memleak detection is enabled. + +.. code:: shell + + pytest --valgrind-memleaks all-protocol-startup + .. _topotests_docker: Running Tests with Docker @@ -883,7 +896,7 @@ Example: Requirements: - Directory name for a new topotest must not contain hyphen (``-``) characters. - To separate words, use underscores (``_``). For example, `tests/topotests/bgp_new_example`. + To separate words, use underscores (``_``). For example, ``tests/topotests/bgp_new_example``. - Test code should always be declared inside functions that begin with the ``test_`` prefix. Functions beginning with different prefixes will not be run by pytest. diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index b4ddec10c9..dae175a732 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -880,6 +880,104 @@ doesn't warrant an update to checkpatch, it is documented here. | should be wrapped in parentheses | all usages of the macro, which would be highly disruptive. | +------------------------------------------+---------------------------------------------------------------+ +Types of configurables +---------------------- + +.. note:: + + This entire section essentially just argues to not make configuration + unnecessarily involved for the user. Rather than rules, this is more of + a list of conclusions intended to help make FRR usable for operators. + + +Almost every feature FRR has comes with its own set of switches and options. +There are several stages at which configuration can be applied. In order of +preference, these are: + +- at configuration/runtime, through YANG. + + This is the preferred way for all FRR knobs. Not all daemons and features + are fully YANGified yet, so in some cases new features cannot rely on a + YANG interface. If a daemon already implements a YANG interface (even + partial), new CLI options must be implemented through a YANG model. + + .. warning:: + + Unlike everything else in this section being guidelines with some slack, + implementing and using a YANG interface for new CLI options in (even + partially!) YANGified daemons is a hard requirement. + + +- at configuration/runtime, through the CLI. + + The "good old" way for all regular configuration. More involved for users + to automate *correctly* than YANG. + +- at startup, by loading additional modules. + + If a feature introduces a dependency on additional libraries (e.g. libsnmp, + rtrlib, etc.), this is the best way to encapsulate the dependency. Having + a separate module allows the distribution to create a separate package + with the extra dependency, so FRR can still be installed without pulling + everything in. + + A module may also be appropriate if a feature is large and reasonably well + isolated. Reducing the amount of running the code is a security benefit, + so even if there are no new external dependencies, modules can be useful. + + While modules cannot currently be loaded at runtime, this is a tradeoff + decision that was made to allow modules to change/extend code that is very + hard to (re)adjust at runtime. If there is a case for runtime (un)loading + of modules, this tradeoff can absolutely be reevaluated. + +- at startup, with command line options. + + This interface is only appropriate for options that have an effect very + early in FRR startup, i.e. before configuration is loaded. Anything that + affects configuration load itself should be here, as well as options + changing the environment FRR runs in. + + If a tunable can be changed at runtime, a command line option is only + acceptable if the configured value has an effect before configuration is + loaded (e.g. zebra reads routes from the kernel before loading config, so + the netlink buffer size is an appropriate command line option.) + +- at compile time, with ``./configure`` options. + + This is the absolute last preference for tunables, since the distribution + needs to make the decision for the user and/or the user needs to rebuild + FRR in order to change the option. + + "Good" configure options do one of three things: + + - set distribution-specific parameters, most prominently all the path + options. File system layout is a distribution/packaging choice, so the + user would hopefully never need to adjust these. + + - changing toolchain behavior, e.g. instrumentation, warnings, + optimizations and sanitizers. + + - enabling/disabling parts of the build, especially if they need + additional dependencies. Being able to build only parts of FRR, or + without some library, is useful. **The only effect these options should + have is adding or removing files from the build result.** If a knob + in this category causes the same binary to exist in different variants, + it is likely implemented incorrectly! + + .. note:: + + This last guideline is currently ignored by several configure options. + ``vtysh`` in general depends on the entire list of enabled daemons, + and options like ``--enable-bgp-vnc`` and ``--enable-ospfapi`` change + daemons internally. Consider this more of an "ideal" than a "rule". + + +Whenever adding new knobs, please try reasonably hard to go up as far as +possible on the above list. Especially ``./configure`` flags are often enough +the "easy way out" but should be avoided when at all possible. To a lesser +degree, the same applies to command line options. + + Compile-time conditional code ----------------------------- @@ -1247,7 +1345,6 @@ the command :clicmd:`show pony` would be documented as follows: .. code-block:: rest - .. index:: show pony .. clicmd:: show pony Prints an ASCII pony. Example output::: |
