diff options
| -rw-r--r-- | bgpd/bgp_evpn.c | 50 | ||||
| -rw-r--r-- | doc/developer/topotests.rst | 82 | ||||
| -rw-r--r-- | ldpd/ldpd.c | 3 | ||||
| -rw-r--r-- | lib/libfrr.c | 14 | ||||
| -rw-r--r-- | lib/module.c | 92 | ||||
| -rw-r--r-- | lib/module.h | 4 | ||||
| -rw-r--r-- | pimd/pim_cmd.c | 8 | ||||
| -rw-r--r-- | pimd/pim_nb_config.c | 7 | ||||
| -rw-r--r-- | tests/lib/test_grpc.cpp | 18 | ||||
| -rw-r--r-- | tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py | 7 | ||||
| -rw-r--r-- | tests/topotests/lib/ltemplate.py | 6 | ||||
| -rw-r--r-- | tools/coccinelle/zlog_no_newline.cocci | 20 |
12 files changed, 247 insertions, 64 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 6248ad927b..3219ae13b5 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -4335,6 +4335,54 @@ static void update_autort_vni(struct hash_bucket *bucket, struct bgp *bgp) } /* + * Handle autort change for L3VNI. + */ +static void update_autort_l3vni(struct bgp *bgp) +{ + if ((CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) + && (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))) + return; + + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { + if (is_l3vni_live(bgp)) + uninstall_routes_for_vrf(bgp); + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp); + + /* Remove auto generated RT */ + evpn_auto_rt_import_delete_for_vrf(bgp); + + list_delete_all_node(bgp->vrf_import_rtl); + + /* Map auto derive or configured RTs */ + evpn_auto_rt_import_add_for_vrf(bgp); + } + + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + list_delete_all_node(bgp->vrf_export_rtl); + + evpn_auto_rt_export_delete_for_vrf(bgp); + + evpn_auto_rt_export_add_for_vrf(bgp); + + if (is_l3vni_live(bgp)) + bgp_evpn_map_vrf_to_its_rts(bgp); + } + + if (!is_l3vni_live(bgp)) + return; + + /* advertise type-5 routes if needed */ + update_advertise_vrf_routes(bgp); + + /* install all remote routes belonging to this l3vni + * into corresponding vrf + */ + install_routes_for_vrf(bgp); +} + +/* * Public functions. */ @@ -4706,6 +4754,8 @@ void bgp_evpn_handle_autort_change(struct bgp *bgp) (void (*)(struct hash_bucket *, void*))update_autort_vni, bgp); + if (bgp->l3vni) + update_autort_l3vni(bgp); } /* diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index c52d210ee5..b4f6ec521c 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -130,12 +130,42 @@ And create ``frr`` user and ``frrvty`` group as follows: Executing Tests --------------- +Configure your sudo environment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Topotests must be run as root. Normally this will be accomplished through the +use of the ``sudo`` command. In order for topotests to be able to open new +windows (either XTerm or byobu/screen/tmux windows) certain environment +variables must be passed through the sudo command. One way to do this is to +specify the :option:`-E` flag to ``sudo``. This will carry over most if not all +your environment variables include ``PATH``. For example: + +.. code:: shell + + sudo -E python3 -m pytest -s -v + +If you do not wish to use :option:`-E` (e.g., to avoid ``sudo`` inheriting +``PATH``) you can modify your `/etc/sudoers` config file to specifically pass +the environment variables required by topotests. Add the following commands to +your ``/etc/sudoers`` config file. + +.. code:: shell + + Defaults env_keep="TMUX" + Defaults env_keep+="TMUX_PANE" + Defaults env_keep+="STY" + Defaults env_keep+="DISPLAY" + +If there was already an ``env_keep`` configuration there be sure to use the +``+=`` rather than ``=`` on the first line above as well. + + Execute all tests in distributed test mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: shell - py.test -s -v -nauto --dist=loadfile + sudo -E pytest -s -v -nauto --dist=loadfile The above command must be executed from inside the topotests directory. @@ -167,7 +197,7 @@ the run. Here we see that 4 tests have failed. We an dig deeper by displaying the captured logs and errors. First let's redisplay the results enumerated by adding -the ``-E`` flag +the :option:`-E` flag .. code:: shell @@ -353,6 +383,12 @@ be run within ``tmux`` (or ``screen``)_, as ``gdb``, the shell or ``vtysh`` will be launched using that windowing program, otherwise ``xterm`` will be attempted to launch the given programs. +NOTE: you must run the topotest (pytest) such that your DISPLAY, STY or TMUX +environment variables are carried over. You can do this by passing the +:option:`-E` flag to ``sudo`` or you can modify your ``/etc/sudoers`` config to +automatically pass that environment variable through to the ``sudo`` +environment. + .. _screen: https://www.gnu.org/software/screen/ .. _tmux: https://github.com/tmux/tmux/wiki @@ -364,7 +400,7 @@ One can have a debugging CLI invoked on test failures by specifying the .. code:: shell - pytest --cli-on-error all-protocol-startup + sudo -E pytest --cli-on-error all-protocol-startup The debugging CLI can run shell or vtysh commands on any combination of routers It can also open shells or vtysh in their own windows for any combination of @@ -415,7 +451,7 @@ Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``. .. code:: shell - pytest --vtysh=rt1,rt2 all-protocol-startup + sudo -E pytest --vtysh=rt1,rt2 all-protocol-startup Debugging with GDB """""""""""""""""" @@ -436,7 +472,7 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router .. code:: shell - pytest --gdb-routers=r1 \ + sudo -E pytest --gdb-routers=r1 \ --gdb-daemons=bgpd,zebra \ --gdb-breakpoints=nb_config_diff \ all-protocol-startup @@ -453,7 +489,7 @@ memleak detection is enabled. .. code:: shell - pytest --valgrind-memleaks all-protocol-startup + sudo -E pytest --valgrind-memleaks all-protocol-startup .. _topotests_docker: @@ -555,21 +591,21 @@ top level directory of topotest: $ # Change to the top level directory of topotests. $ cd path/to/topotests $ # Tests must be run as root, since micronet requires it. - $ sudo pytest + $ sudo -E pytest In order to run a specific test, you can use the following command: .. code:: shell $ # running a specific topology - $ sudo pytest ospf-topo1/ + $ sudo -E pytest ospf-topo1/ $ # or inside the test folder $ cd ospf-topo1 - $ sudo pytest # to run all tests inside the directory - $ sudo pytest test_ospf_topo1.py # to run a specific test + $ sudo -E pytest # to run all tests inside the directory + $ sudo -E pytest test_ospf_topo1.py # to run a specific test $ # or outside the test folder $ cd .. - $ sudo pytest ospf-topo1/test_ospf_topo1.py # to run a specific one + $ sudo -E pytest ospf-topo1/test_ospf_topo1.py # to run a specific one The output of the tested daemons will be available at the temporary folder of your machine: @@ -588,7 +624,7 @@ You can also run memory leak tests to get reports: .. code:: shell $ # Set the environment variable to apply to a specific test... - $ sudo env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py + $ sudo -E env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py $ # ...or apply to all tests adding this line to the configuration file $ echo 'memleak_path = /tmp/memleak_report_' >> pytest.ini $ # You can also use your editor @@ -626,12 +662,12 @@ Some things to keep in mind: - Using sleep is almost never appropriate. As an example: if the test resets the peers in BGP, the test should look for the peers re-converging instead of just sleeping an arbitrary amount of time and continuing on. See - `verify_bgp_convergence` as a good example of this. In particular look at it's - use of the `@retry` decorator. If you are having troubles figuring out what to - look for, please do not be afraid to ask. + ``verify_bgp_convergence`` as a good example of this. In particular look at + it's use of the ``@retry`` decorator. If you are having troubles figuring out + what to look for, please do not be afraid to ask. - Don't duplicate effort. There exists many protocol utility functions that can - be found in their eponymous module under `tests/topotests/lib/` (e.g., - `ospf.py`) + be found in their eponymous module under ``tests/topotests/lib/`` (e.g., + ``ospf.py``) @@ -827,11 +863,11 @@ that using the following example commands: .. code:: shell $ # Running your bootstraped topology - $ sudo pytest -s --topology-only new-topo/test_new_topo.py + $ sudo -E pytest -s --topology-only new-topo/test_new_topo.py $ # Running the test_template.py topology - $ sudo pytest -s --topology-only example-test/test_template.py + $ sudo -E pytest -s --topology-only example-test/test_template.py $ # Running the ospf_topo1.py topology - $ sudo pytest -s --topology-only ospf-topo1/test_ospf_topo1.py + $ sudo -E pytest -s --topology-only ospf-topo1/test_ospf_topo1.py Parameters explanation: @@ -851,7 +887,7 @@ output: .. code:: shell - frr/tests/topotests# sudo pytest -s --topology-only ospf_topo1/test_ospf_topo1.py + frr/tests/topotests# sudo -E pytest -s --topology-only ospf_topo1/test_ospf_topo1.py ============================= test session starts ============================== platform linux -- Python 3.9.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 rootdir: /home/chopps/w/frr/tests/topotests, configfile: pytest.ini @@ -1011,8 +1047,8 @@ Example: # topology build code ... -- pytest setup/teardown fixture to start the topology and supply `tgen` argument - to tests. +- pytest setup/teardown fixture to start the topology and supply ``tgen`` + argument to tests. .. code:: py diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 000d1a3301..e24eba7cd4 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -94,10 +94,9 @@ static void ldp_load_module(const char *name) { const char *dir; dir = ldpd_di.module_path ? ldpd_di.module_path : frr_moduledir; - char moderr[256]; struct frrmod_runtime *module; - module = frrmod_load(name, dir, moderr, sizeof(moderr)); + module = frrmod_load(name, dir, NULL,NULL); if (!module) { fprintf(stderr, "%s: failed to load %s", __func__, name); log_warnx("%s: failed to load %s", __func__, name); diff --git a/lib/libfrr.c b/lib/libfrr.c index d03437328b..9b05bb4fbf 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -674,13 +674,19 @@ static void frr_mkdir(const char *path, bool strip) strerror(errno)); } +static void _err_print(const void *cookie, const char *errstr) +{ + const char *prefix = (const char *)cookie; + + fprintf(stderr, "%s: %s\n", prefix, errstr); +} + static struct thread_master *master; struct thread_master *frr_init(void) { struct option_chain *oc; struct frrmod_runtime *module; struct zprivs_ids_t ids; - char moderr[256]; char p_instance[16] = "", p_pathspace[256] = ""; const char *dir; dir = di->module_path ? di->module_path : frr_moduledir; @@ -734,11 +740,9 @@ struct thread_master *frr_init(void) frrmod_init(di->module); while (modules) { modules = (oc = modules)->next; - module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr)); - if (!module) { - fprintf(stderr, "%s\n", moderr); + module = frrmod_load(oc->arg, dir, _err_print, __func__); + if (!module) exit(1); - } XFREE(MTYPE_TMP, oc); } diff --git a/lib/module.c b/lib/module.c index 1d51a6396d..4037bfbeb0 100644 --- a/lib/module.c +++ b/lib/module.c @@ -26,9 +26,11 @@ #include "module.h" #include "memory.h" #include "lib/version.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name"); DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments"); +DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error"); static struct frrmod_info frrmod_default_info = { .name = "libfrr", @@ -67,14 +69,64 @@ void frrmod_init(struct frrmod_runtime *modinfo) execname = modinfo->info->name; } -struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err, - size_t err_len) +/* + * If caller wants error strings, it should define non-NULL pFerrlog + * which will be called with 0-terminated error messages. These + * messages will NOT contain newlines, and the (*pFerrlog)() function + * could be called multiple times for a single call to frrmod_load(). + * + * The (*pFerrlog)() function may copy these strings if needed, but + * should expect them to be freed by frrmod_load() before frrmod_load() + * returns. + * + * frrmod_load() is coded such that (*pFerrlog)() will be called only + * in the case where frrmod_load() returns an error. + */ +struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, + void (*pFerrlog)(const void *, const char *), + const void *pErrlogCookie) { void *handle = NULL; char name[PATH_MAX], fullpath[PATH_MAX * 2], *args; struct frrmod_runtime *rtinfo, **rtinfop; const struct frrmod_info *info; +#define FRRMOD_LOAD_N_ERRSTR 10 + char *aErr[FRRMOD_LOAD_N_ERRSTR]; + unsigned int iErr = 0; + + memset(aErr, 0, sizeof(aErr)); + +#define ERR_RECORD(...) \ + do { \ + if (pFerrlog && (iErr < FRRMOD_LOAD_N_ERRSTR)) { \ + aErr[iErr++] = asprintfrr(MTYPE_MODULE_LOAD_ERR, \ + __VA_ARGS__); \ + } \ + } while (0) + +#define ERR_REPORT \ + do { \ + if (pFerrlog) { \ + unsigned int i; \ + \ + for (i = 0; i < iErr; ++i) { \ + (*pFerrlog)(pErrlogCookie, aErr[i]); \ + } \ + } \ + } while (0) + +#define ERR_FREE \ + do { \ + unsigned int i; \ + \ + for (i = 0; i < iErr; ++i) { \ + XFREE(MTYPE_MODULE_LOAD_ERR, aErr[i]); \ + aErr[i] = 0; \ + } \ + iErr = 0; \ + } while (0) + snprintf(name, sizeof(name), "%s", spec); args = strchr(name, ':'); if (args) @@ -85,32 +137,41 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err, snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so", dir, execname, name); handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); + if (!handle) + ERR_RECORD("loader error: dlopen(%s): %s", + fullpath, dlerror()); } if (!handle) { snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir, name); handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); + if (!handle) + ERR_RECORD("loader error: dlopen(%s): %s", + fullpath, dlerror()); } } if (!handle) { snprintf(fullpath, sizeof(fullpath), "%s", name); handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); + if (!handle) + ERR_RECORD("loader error: dlopen(%s): %s", fullpath, + dlerror()); } if (!handle) { - if (err) - snprintf(err, err_len, - "loading module \"%s\" failed: %s", name, - dlerror()); + ERR_REPORT; + ERR_FREE; return NULL; } + /* previous dlopen() errors are no longer relevant */ + ERR_FREE; + rtinfop = dlsym(handle, "frr_module"); if (!rtinfop) { dlclose(handle); - if (err) - snprintf(err, err_len, - "\"%s\" is not an FRR module: %s", name, - dlerror()); + ERR_RECORD("\"%s\" is not an FRR module: %s", name, dlerror()); + ERR_REPORT; + ERR_FREE; return NULL; } rtinfo = *rtinfop; @@ -122,17 +183,13 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err, if (rtinfo->finished_loading) { dlclose(handle); - if (err) - snprintf(err, err_len, "module \"%s\" already loaded", - name); + ERR_RECORD("module \"%s\" already loaded", name); goto out_fail; } if (info->init && info->init()) { dlclose(handle); - if (err) - snprintf(err, err_len, - "module \"%s\" initialisation failed", name); + ERR_RECORD("module \"%s\" initialisation failed", name); goto out_fail; } @@ -140,11 +197,14 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err, *frrmod_last = rtinfo; frrmod_last = &rtinfo->next; + ERR_FREE; return rtinfo; out_fail: XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args); XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name); + ERR_REPORT; + ERR_FREE; return NULL; } diff --git a/lib/module.h b/lib/module.h index 6275877cb3..ae1ca2f757 100644 --- a/lib/module.h +++ b/lib/module.h @@ -91,7 +91,9 @@ extern struct frrmod_runtime *frrmod_list; extern void frrmod_init(struct frrmod_runtime *modinfo); extern struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, - char *err, size_t err_len); + void (*pFerrlog)(const void *, + const char *), + const void *pErrlogCookie); #if 0 /* not implemented yet */ extern void frrmod_unload(struct frrmod_runtime *module); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 5fb2ddf732..4cd94e0df9 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3842,7 +3842,7 @@ static void pim_cli_legacy_mesh_group_behavior(struct vty *vty, xpath_member_value)) { member_dnode = yang_dnode_get(vty->candidate_config->dnode, xpath_member_value); - if (!yang_is_last_list_dnode(member_dnode)) + if (!member_dnode || !yang_is_last_list_dnode(member_dnode)) return; } @@ -9761,7 +9761,7 @@ DEFPY(no_ip_msdp_mesh_group_member, return CMD_WARNING_CONFIG_FAILED; } - nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, xpath_member_value, NB_OP_DESTROY, NULL); /* * If this is the last member, then we must remove the group altogether @@ -9795,7 +9795,7 @@ DEFPY(ip_msdp_mesh_group_source, "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - /* Create mesh group member. */ + /* Create mesh group source. */ strlcat(xpath_value, "/source", sizeof(xpath_value)); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str); @@ -9826,7 +9826,7 @@ DEFPY(no_ip_msdp_mesh_group_source, "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - /* Create mesh group member. */ + /* Create mesh group source. */ strlcat(xpath_value, "/source", sizeof(xpath_value)); nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index f4627cbcc2..b9da8ec068 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1168,6 +1168,7 @@ int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args) { struct pim_msdp_mg_mbr *mbr; struct pim_msdp_mg *mg; + const struct lyd_node *mg_dnode; switch (args->event) { case NB_EV_VALIDATE: @@ -1176,9 +1177,11 @@ int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: mbr = nb_running_get_entry(args->dnode, NULL, true); - mg = nb_running_get_entry(args->dnode, "../", true); - + mg_dnode = + yang_dnode_get_parent(args->dnode, "msdp-mesh-groups"); + mg = nb_running_get_entry(mg_dnode, NULL, true); pim_msdp_mg_mbr_del(mg, mbr); + nb_running_unset_entry(args->dnode); break; } diff --git a/tests/lib/test_grpc.cpp b/tests/lib/test_grpc.cpp index 491796802a..0aa1bbb7e1 100644 --- a/tests/lib/test_grpc.cpp +++ b/tests/lib/test_grpc.cpp @@ -81,11 +81,16 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = { static int grpc_thread_stop(struct thread *thread); +static void _err_print(const void *cookie, const char *errstr) +{ + std::cout << "Failed to load grpc module:" << errstr << std::endl; +} + static void static_startup(void) { // struct frrmod_runtime module; // static struct option_chain *oc; - char moderr[256] = {}; + cmd_init(1); zlog_aux_init("NONE: ", LOG_DEBUG); @@ -94,17 +99,14 @@ static void static_startup(void) /* Load the server side module -- check libtool path first */ std::string modpath = std::string(binpath) + std::string("../../../lib/.libs"); - grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr, sizeof(moderr)); + grpc_module = frrmod_load("grpc:50051", modpath.c_str(), 0, 0); if (!grpc_module) { modpath = std::string(binpath) + std::string("../../lib"); - grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr, - sizeof(moderr)); + grpc_module = frrmod_load("grpc:50051", modpath.c_str(), + _err_print, 0); } - if (!grpc_module) { - std::cout << "Failed to load grpc module:" << moderr - << std::endl; + if (!grpc_module) exit(1); - } static_debug_init(); diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 8fd344696e..fce8e708f2 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -206,7 +206,7 @@ def ltemplatePreRouterStartHook(): for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr)) - if cc.getOutput() != 4: + if cc.getOutput() != 0: InitSuccess = False logger.info( "Unexpected output seen ({} times, tests will be skipped".format( @@ -214,6 +214,11 @@ def ltemplatePreRouterStartHook(): ) ) else: + rtrs = ["r1", "r3", "r4", "ce4"] + for rtr in rtrs: + logger.info("{} configured".format(rtr)) + cc.doCmd(tgen, rtr, "ip -d link show type vrf") + cc.doCmd(tgen, rtr, "ip link show") InitSuccess = True logger.info("VRF config successful!") return InitSuccess diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py index 910573c14c..c98bfac9ee 100644 --- a/tests/topotests/lib/ltemplate.py +++ b/tests/topotests/lib/ltemplate.py @@ -217,6 +217,7 @@ class ltemplateRtrCmd: self.resetCounts() def doCmd(self, tgen, rtr, cmd, checkstr=None): + logger.info("doCmd: {} {}".format(rtr, cmd)) output = tgen.net[rtr].cmd(cmd).strip() if len(output): self.output += 1 @@ -227,9 +228,10 @@ class ltemplateRtrCmd: else: self.match += 1 return ret - logger.info("command: {} {}".format(rtr, cmd)) logger.info("output: " + output) - self.none += 1 + else: + logger.info("No output") + self.none += 1 return None def resetCounts(self): diff --git a/tools/coccinelle/zlog_no_newline.cocci b/tools/coccinelle/zlog_no_newline.cocci new file mode 100644 index 0000000000..20cf9d2c78 --- /dev/null +++ b/tools/coccinelle/zlog_no_newline.cocci @@ -0,0 +1,20 @@ +// zlog_* should not have \n or \r at the end usually. +// spatch --sp-file tools/coccinelle/zlog_no_newline.cocci --macro-file tools/cocci.h ./ 2>/dev/null + +@r@ +expression fmt; +identifier func =~ "zlog_"; +position p; +@@ +( + func(fmt)@p +| + func(fmt, ...)@p +) + +@script:python@ +fmt << r.fmt; +p << r.p; +@@ +if "\\n" in str(fmt) or "\\r" in str(fmt): + print("Newline in logging function detected %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)) |
