diff options
Diffstat (limited to 'doc/developer')
| -rw-r--r-- | doc/developer/conf.py | 42 | ||||
| -rw-r--r-- | doc/developer/mgmtd-dev.rst | 53 | ||||
| -rw-r--r-- | doc/developer/ospf-ls-retrans.rst | 69 | ||||
| -rw-r--r-- | doc/developer/ospf.rst | 1 | ||||
| -rw-r--r-- | doc/developer/packaging-debian.rst | 2 | ||||
| -rw-r--r-- | doc/developer/packaging-redhat.rst | 31 | ||||
| -rw-r--r-- | doc/developer/scripting.rst | 5 | ||||
| -rw-r--r-- | doc/developer/topotests.rst | 60 | ||||
| -rw-r--r-- | doc/developer/workflow.rst | 54 |
9 files changed, 279 insertions, 38 deletions
diff --git a/doc/developer/conf.py b/doc/developer/conf.py index 495c604ae0..634f4aa804 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -18,6 +18,7 @@ import re import pygments from sphinx.highlighting import lexers from sphinx.util import logging + logger = logging.getLogger(__name__) # If extensions (or modules to document with autodoc) are in another directory, @@ -53,18 +54,25 @@ source_suffix = ".rst" master_doc = "index" # General information about the project. -project = u"FRR" -copyright = u"2017, FRR" -author = u"FRR authors" +project = "FRR" +copyright = "2017, FRR" +author = "FRR authors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # The short X.Y version. -version = u"?.?" +version = "?.?" # The full version, including alpha/beta/rc tags. -release = u"?.?-?" +release = "?.?-?" + +# Set canonical URL from the Read the Docs Domain +html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") + +# Tell Jinja2 templates the build is running on Read the Docs +if os.environ.get("READTHEDOCS", "") == "True": + html_context["READTHEDOCS"] = True # ----------------------------------------------------------------------------- @@ -95,7 +103,7 @@ replace_vars = { # extract version information, installation location, other stuff we need to # use when building final documents -val = re.compile('^S\["([^"]+)"\]="(.*)"$') +val = re.compile(r'^S\["([^"]+)"\]="(.*)"$') try: with open("../../config.status", "r") as cfgstatus: for ln in cfgstatus.readlines(): @@ -287,7 +295,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, "FRR.tex", u"FRR Developer's Manual", u"FRR", "manual"), + (master_doc, "FRR.tex", "FRR Developer's Manual", "FRR", "manual"), ] # The name of an image file (relative to this directory) to place at the top of @@ -315,7 +323,7 @@ latex_logo = "../figures/frr-logo-medium.png" # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "frr", u"FRR Developer's Manual", [author], 1)] +man_pages = [(master_doc, "frr", "FRR Developer's Manual", [author], 1)] # If true, show URL addresses after external links. # man_show_urls = False @@ -330,7 +338,7 @@ texinfo_documents = [ ( master_doc, "frr", - u"FRR Developer's Manual", + "FRR Developer's Manual", author, "FRR", "One line description of project.", @@ -358,27 +366,29 @@ texinfo_documents = [ with open("../extra/frrlexer.py", "rb") as lex: frrlexerpy = lex.read() -frrfmt_re = re.compile(r'^\s*%(?P<spec>[^\s]+)\s+\((?P<types>.*)\)\s*$') +frrfmt_re = re.compile(r"^\s*%(?P<spec>[^\s]+)\s+\((?P<types>.*)\)\s*$") + def parse_frrfmt(env, text, node): from sphinx import addnodes m = frrfmt_re.match(text) if not m: - logger.warning('could not parse frrfmt:: %r' % (text), location=node) + logger.warning("could not parse frrfmt:: %r" % (text), location=node) node += addnodes.desc_name(text, text) return text - spec, types = m.group('spec'), m.group('types') + spec, types = m.group("spec"), m.group("types") - node += addnodes.desc_sig_operator('%', '%') - node += addnodes.desc_name(spec + ' ', spec + ' ') + node += addnodes.desc_sig_operator("%", "%") + node += addnodes.desc_name(spec + " ", spec + " ") plist = addnodes.desc_parameterlist() - for typ in types.split(','): + for typ in types.split(","): typ = typ.strip() plist += addnodes.desc_parameter(typ, typ) node += plist - return '%' + spec + return "%" + spec + # custom extensions here def setup(app): diff --git a/doc/developer/mgmtd-dev.rst b/doc/developer/mgmtd-dev.rst index 2404ffe2a7..b979af06fa 100644 --- a/doc/developer/mgmtd-dev.rst +++ b/doc/developer/mgmtd-dev.rst @@ -317,12 +317,25 @@ Likewise the client should be cleaned up in the daemon cleanup routine. Back-End XPATH mappings ^^^^^^^^^^^^^^^^^^^^^^^ -In order for ``mgmtd`` to direct configuration to your daemon you need to add +In order for ``mgmtd`` to direct YANG modeled data to your daemon you should add some XPATH mappings to ``mgmtd/mgmt_be_adapter.c``. These XPATHs determine which -configuration changes get sent over the *back-end* interface to your daemon. -There are 2 arrays to update the first for config support and the second for -operational state. - +YANG modeled data (e.g., config changes) get sent over the *back-end* interface +to your daemon. There are 4 arrays to possibly update: configuration, +operational, notification, and RPC. You only need to add entries to the array +that you require mapping for. + +Additionally the back-end client can specify these XPATH mappings when it +first connects to mgmtd using it's initial ``SUBSCRIBE`` message. + +NOTE: the notif array (``be_client_notif_xpaths``), is a slightly different from +the other 3 types (config, oper and rpc) in that it maps xpaths the back-end +client wishes to *receive* notifications for, not the ones it may generate. +Normally a back-end client is generating notifications; however, mgmtd supports +back-end clients also "subscribing" to receive these notifications as well from +other back-end clients through notif_xpath maps. + +Config Map Example +"""""""""""""""""" Below are the strings added for staticd config support: .. code-block:: c @@ -342,6 +355,9 @@ Below are the strings added for staticd config support: #endif }; + +Operational Map Example +""""""""""""""""""""""" Below are the strings added for zebra operational state support (note zebra is not conditionalized b/c it should always be present): @@ -358,6 +374,33 @@ not conditionalized b/c it should always be present): [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths, }; + +RPC Map Example +""""""""""""""" +Below is the string added for ripd RPC support: + +.. code-block:: c + + static const char *const ripd_rpc_xpaths[] = { + "/frr-ripd", + NULL, + }; + + static const char *const *be_client_rpc_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { + #ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = ripd_rpc_xpaths, + #endif + }; + + +Notification Map Example +"""""""""""""""""""""""" +There are no current back-end daemons that wish to receive other back-end +notifications so the array is empty. This may change in the future, and of +course any back-end daemon can utilize the connect (``BeSubscribeReq``) messages +as well. + + MGMTD Internals --------------- diff --git a/doc/developer/ospf-ls-retrans.rst b/doc/developer/ospf-ls-retrans.rst new file mode 100644 index 0000000000..230d7a1c5d --- /dev/null +++ b/doc/developer/ospf-ls-retrans.rst @@ -0,0 +1,69 @@ +OSPF Neighor Retransmission List +================================ + +Overview +-------- + +OSPF neighbor link-state retransmission lists are implemented using +both a sparse Link State Database (LSDB) and a doubly-linked list. +Rather than previous per-neighbor periodic timer, a per-neighbor +timer is set to the expiration time of the next scheduled LSA +retransmission. + +Sparse Link State Database (LSDB) +--------------------------------- + +When an explicit or implied acknowledgment is recieved from a +neighbor in 2-way state or higher, the acknowledge LSA must be +removed from the neighbor's link state retransmission list. In order +to do this efficiently, a sparse LSDB is utilized. LSDB entries also +include a pointer to the corresponding list entry so that it may be +efficiently removed from the doubly-linked list. + +The sparse LSDB is implemented using the OSPF functions is +ospf_lsdb.[c,h]. OSPF LSDBs are implemented as an array of route +tables (lib/table.[c,h]). What is unique of the LS Retransmission +list LSDB is that each entry also has a pointer into the doubly-linked +list to facilitate fast deletions. + +Doubly-Linked List +------------------ + +In addition to the sparse LSDB, LSAs on a neighbor LS retransmission +list are also maintained in a linked-list order chronologically +with the LSA scheduled for the next retransmission at the head of +the list. + +The doubly-link list is implemented using the dlist macros in +lib/typesafe.h. + +LSA LS Retransmission List Addition +------------------------------------ + +When an LSA is added to a neighbor retransmission list, it is +added to both the sparse LSDB and the doubly-linked list with a pointer +in the LSDB route-table node to the list entry. The LSA is added to +the tail of the list with the expiration time set to the current time +with the retransmission interval added. If the neighbor retransmission +timer is not set, it is set to expire at the time of the newly added +LSA. + +LSA LS Retransmission List Deletion +----------------------------------- + +When an LSA is deleted from a neighbor retransmission list, it is +deleted from eboth the sparse LSDB and the doubly-linked list with the +pointer the LSDB route-table node used to efficiently delete the entry +from the list. If the LSA at the head of the list was removed, then +the neighbor retransmission timer is reset to the expiration of the +LSA at the head of the list or canceled if the list is empty. + +Neighbor LS Retransmission List Expiration +------------------------------------------ + +When the neighbor retransmission timer expires, the LSA at the top of +list and any in a configured window (e.g., 50 milliseconds) are +retransmitted. The LSAs that have been retransmitted are removed from +the list and readded to the tail of the list with a new expiration time +which is retransmit-interval seconds in the future. + diff --git a/doc/developer/ospf.rst b/doc/developer/ospf.rst index 837a0bd185..da4802533c 100644 --- a/doc/developer/ospf.rst +++ b/doc/developer/ospf.rst @@ -8,6 +8,7 @@ OSPFD :maxdepth: 2 ospf-api + ospf-ls-retrans ospf-sr cspf diff --git a/doc/developer/packaging-debian.rst b/doc/developer/packaging-debian.rst index c2c3b7e7e1..4109057ee5 100644 --- a/doc/developer/packaging-debian.rst +++ b/doc/developer/packaging-debian.rst @@ -68,6 +68,8 @@ buster.) +----------------+-------------------+-----------------------------------------+ | pkg.frr.pim6d | pkg.frr.nopim6d | builds pim6d (default enabled) | +----------------+-------------------+-----------------------------------------+ + | pkg.frr.grpc | pkg.frr.nogrpc | builds with grpc support (default: no) | + +----------------+-------------------+-----------------------------------------+ * the ``-uc -us`` options to disable signing the packages with your GPG key diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index d88f449926..8037873461 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -67,24 +67,27 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. ############### FRRouting (FRR) configure options ################# # with-feature options - %{!?with_pam: %global with_pam 0 } - %{!?with_ospfclient: %global with_ospfclient 1 } - %{!?with_ospfapi: %global with_ospfapi 1 } - %{!?with_irdp: %global with_irdp 1 } - %{!?with_rtadv: %global with_rtadv 1 } + %{!?with_babeld: %global with_babeld 1 } + %{!?with_bfdd: %global with_bfdd 1 } + %{!?with_bgp_vnc: %global with_bgp_vnc 0 } + %{!?with_cumulus: %global with_cumulus 0 } + %{!?with_eigrpd: %global with_eigrpd 1 } + %{!?with_fpm: %global with_fpm 1 } + %{!?with_mgmtd_test_be_client: %global with_mgmtd_test_be_client 0 } %{!?with_ldpd: %global with_ldpd 1 } - %{!?with_nhrpd: %global with_nhrpd 1 } - %{!?with_eigrp: %global with_eigrpd 1 } - %{!?with_shared: %global with_shared 1 } %{!?with_multipath: %global with_multipath 256 } - %{!?frr_user: %global frr_user frr } - %{!?vty_group: %global vty_group frrvty } - %{!?with_fpm: %global with_fpm 0 } - %{!?with_watchfrr: %global with_watchfrr 1 } - %{!?with_bgp_vnc: %global with_bgp_vnc 0 } + %{!?with_nhrpd: %global with_nhrpd 1 } + %{!?with_ospfapi: %global with_ospfapi 1 } + %{!?with_ospfclient: %global with_ospfclient 1 } + %{!?with_pam: %global with_pam 0 } + %{!?with_pbrd: %global with_pbrd 1 } %{!?with_pimd: %global with_pimd 1 } %{!?with_pim6d: %global with_pim6d 1 } - %{!?with_rpki: %global with_rpki 0 } + %{!?with_vrrpd: %global with_vrrpd 1 } + %{!?with_rtadv: %global with_rtadv 1 } + %{!?with_watchfrr: %global with_watchfrr 1 } + %{!?with_pathd: %global with_pathd 1 } + %{!?with_grpc: %global with_grpc 0 } 8. Build the RPM:: diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst index 7a43314490..f51130b1e3 100644 --- a/doc/developer/scripting.rst +++ b/doc/developer/scripting.rst @@ -523,6 +523,7 @@ object which contains methods corresponding to each of the ``zlog`` levels: log.error("error") log.notice("notice") log.debug("debug") + log.trace("trace") The log messages will show up in the daemon's log output. @@ -579,14 +580,14 @@ accomplished with scripting. 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 - + function on_nomatch (prefix, attributes) log.info("No match") return { diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index e1702c47c7..4f9c94ca03 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -33,10 +33,11 @@ Installing Topotest Requirements net-tools \ python3-pip \ iputils-ping \ + iptables \ tshark \ valgrind python3 -m pip install wheel - python3 -m pip install 'pytest>=6.2.4' 'pytest-xdist>=2.3.0' + python3 -m pip install 'pytest>=8.3.2' 'pytest-asyncio>=0.24.0' 'pytest-xdist>=3.6.1' python3 -m pip install 'scapy>=2.4.5' python3 -m pip install xmltodict python3 -m pip install git+https://github.com/Exa-Networks/exabgp@0659057837cd6c6351579e9f0fa47e9fb7de7311 @@ -411,6 +412,14 @@ for ``master`` branch: and create ``frr`` user and ``frrvty`` group as shown above. +Newer versions of Address Sanitizers require a sysctl to be changed +to allow for the tests to be successfully run. This is also true +for Undefined behavior Sanitizers as well as Memory Sanitizer. + +.. code:: shell + + sysctl vm.mmap_rnd_bits=28 + Debugging Topotest Failures ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -750,6 +759,46 @@ IDE/editor if supported (e.g., the emacs ``cov-mode`` package) NOTE: the *.gcda files in ``/tmp/topotests/gcda`` are cumulative so if you do not remove them they will aggregate data across multiple topotest runs. +How to reproduce failed Tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Generally tests fail but recreating the test failure reliably is not necessarily +easy, or it happens once every 10 runs locally. Here are some generic strategies +that are employed to allow for the test to be reproduced reliably + +.. code:: console + + cd <test directory> + ln -s test_the_test_name.py test_a.py + ln -s test_the_test_name.py test_b.py + +This allows you to run multiple copies of the same test with one full test run. +Additionally if you need to modify the test you don't need to recopy everything +to make it work. By adding multiple copies of the same occassionally failing test +you raise the odds of it failing again. Additionally you have easily accessible +good and bad runs to compare. + +.. code:: console + + sudo -E python3 -m pytest -n <some value> --dist=loadfile + +Choose a n value that is greater than the number of cpu's avalaible on the system. +This changes the timing and may or may not make it more likely that the test fails. +Be aware, though, that this changes memory requirements as well as may make other +tests fail more often as well. You should choose values that do not cause the system +to go into swap usage. + +.. code:: console + + stress -n <number of cpu's to put at 100%> + +By filling up cpu's with programs that do nothing you also change the timing again and +may cause the problem to happen more often. + +There is no magic bullet here. You as a developer might have to experiment with different +values and different combinations of the above to cause the problem to happen more often. +These are just the tools that we know of at this point in time. + .. _topotests_docker: @@ -1292,6 +1341,15 @@ Example: router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") router.load_config(TopoRouter.RD_OSPF) +or using unified config (specifying which daemons to run is optional): + +.. code:: py + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)), [ + TopoRouter.RD_ZEBRA + TopoRouter.RD_MGMTD, + TopoRouter.RD_BGP]) - The topology definition or build function diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index f720f6279e..50bcb2976e 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -531,6 +531,42 @@ After Submitting Your Changes community members. - Your submission is done once it is merged to the master branch. +Reverting the changes +===================== + +When you revert a regular commit in Git, the process is straightforward - it +undoes the changes introduced by that commit. However, reverting a merge commit +is more complex. While it undoes the data changes brought in by the merge, it +does not alter the repository's history or the merge's effect on it. + +Reverting a Merge Commit +------------------------ + +When you revert a merge commit, the following occurs: + +* The changes made by the merge are undone; +* The merge itself remains in the history: it continues to be recognized as the point where two branches were joined; +* Future merges will still treat this as the last shared state, regardless of the revert. + +Thus, a "revert" in Git undoes data changes, but it does not serve as a true "undo" +for the historical effects of a commit. + +Reverting a Merge and Bisectability +----------------------------------- + +Consider the implications of reverting a merge and then reverting that revert. +This scenario complicates the debugging process, especially when using tools like +git bisect. A reverted merge effectively consolidates all changes from the original +merge into a single commit, but in reverse. This creates a challenge for debugging, +as you lose the granularity of individual commits, making it difficult to identify +the specific change causing an issue. + +Considerations +-------------- + +When reverting the changes, e.g. a full Pull Request, we SHOULD revert every commit +individually, and not use git revert on merge commits. + Programming Languages, Tools and Libraries ========================================== @@ -1306,6 +1342,16 @@ MemorySanitizer to ``configure``. +UndefinedSanitizer + Similar to AddressSanitizer, this tool provides runtime instrumentation for + detecting use of undefined behavior in C. Testing your own code with this + tool before submission is encouraged. You can enable it by passing:: + + --enable-undefined-sanitizer + + to ``configure``. If you run FRR with this you will probably also have + to set ``sudo sysctl vm.mmap_rnd_bits=28`` + All of the above tools are available in the Clang/LLVM toolchain since 3.4. AddressSanitizer and ThreadSanitizer are available in recent versions of GCC, but are no longer actively maintained. MemorySanitizer is not available in GCC. @@ -1315,6 +1361,14 @@ but are no longer actively maintained. MemorySanitizer is not available in GCC. The different Sanitizers are mostly incompatible with each other. Please refer to GCC/LLVM documentation for details. +.. note:: + + The different sanitizers also require setting + + sysctl vm.mmap_rnd_bits=28 + + in order to work properly. + frr-format plugin This is a GCC plugin provided with FRR that does extended type checks for ``%pFX``-style printfrr extensions. To use this plugin, |
