summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/developer/cli.rst5
-rw-r--r--doc/developer/topotests.rst42
-rw-r--r--isisd/isis_snmp.c21
-rw-r--r--lib/command.c1
-rw-r--r--lib/command_graph.c4
-rw-r--r--lib/command_graph.h1
-rw-r--r--lib/command_lex.l1
-rw-r--r--lib/command_match.c25
-rw-r--r--lib/command_parse.y16
-rw-r--r--lib/command_py.c39
-rw-r--r--pathd/path_cli.c9
-rw-r--r--tests/lib/cli/test_cli.c4
-rw-r--r--tests/lib/cli/test_cli.in17
-rw-r--r--tests/lib/cli/test_cli.refout.in96
-rw-r--r--tests/topotests/example_test/r1/zebra.conf8
-rw-r--r--tests/topotests/example_test/r2/zebra.conf4
-rw-r--r--tests/topotests/example_test/test_template.py140
-rw-r--r--tests/zebra/test_lm_plugin.c6
-rw-r--r--tools/permutations.c16
19 files changed, 347 insertions, 108 deletions
diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst
index edabe61d92..a24a4ecb7e 100644
--- a/doc/developer/cli.rst
+++ b/doc/developer/cli.rst
@@ -139,6 +139,7 @@ by the parser.
selector: "<" `selector_seq_seq` ">" `varname_token`
: "{" `selector_seq_seq` "}" `varname_token`
: "[" `selector_seq_seq` "]" `varname_token`
+ : "![" `selector_seq_seq` "]" `varname_token`
selector_seq_seq: `selector_seq_seq` "|" `selector_token_seq`
: `selector_token_seq`
selector_token_seq: `selector_token_seq` `selector_token`
@@ -218,6 +219,10 @@ one-or-more selection and repetition.
provide mutual exclusion. User input matches at most one option.
- ``[square brackets]`` -- Contains sequences of tokens that can be omitted.
``[<a|b>]`` can be shortened to ``[a|b]``.
+- ``![exclamation square brackets]`` -- same as ``[square brackets]``, but
+ only allow skipping the contents if the command input starts with ``no``.
+ (For cases where the positive command needs a parameter, but the parameter
+ is optional for the negative case.)
- ``{curly|braces}`` -- similar to angle brackets, but instead of mutual
exclusion, curly braces indicate that one or more of the pipe-separated
sequences may be provided in any order.
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index fa6a1ba660..c52d210ee5 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -983,22 +983,20 @@ Writing Tests
"""""""""""""
Test topologies should always be bootstrapped from
-:file:`tests/topotests/example-test/test_template.py` because it contains
+:file:`tests/topotests/example_test/test_template.py` because it contains
important boilerplate code that can't be avoided, like:
Example:
.. code:: py
- # For all registered routers, load the zebra configuration file
- CWD = os.path.dirname(os.path.realpath(__file__))
- for rname, router in router_list.items():
- router.load_config(
- TopoRouter.RD_ZEBRA,
- os.path.join(CWD, '{}/zebra.conf'.format(rname))
- )
- # os.path.join() joins the CWD string with arguments adding the necessary
- # slashes ('/'). Arguments must not begin with '/'.
+ # For all routers arrange for:
+ # - starting zebra using config file from <rtrname>/zebra.conf
+ # - starting ospfd using an empty config file.
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+
- The topology definition or build function
@@ -1013,27 +1011,31 @@ Example:
# topology build code
...
-- pytest ``setup_module()`` and ``teardown_module()`` to start the topology
+- pytest setup/teardown fixture to start the topology and supply `tgen` argument
+ to tests.
.. code:: py
- def setup_module(module):
+
+ @pytest.fixture(scope="module")
+ def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
tgen = Topogen(topodef, module.__name__)
# or
tgen = Topogen(build_topo, module.__name__)
- tgen.start_topology('debug')
+ ...
- def teardown_module(_m):
- tgen = get_topogen()
- tgen.stop_topology()
+ # Start and configure the router daemons
+ tgen.start_router()
-- ``__main__`` initialization code (to support running the script directly)
+ # Provide tgen as argument to each test function
+ yield tgen
-.. code:: py
+ # Teardown after last test runs
+ tgen.stop_topology()
- if __name__ == '__main__':
- sys.exit(pytest.main(["-s"]))
Requirements:
diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c
index d530faa151..c530eb9169 100644
--- a/isisd/isis_snmp.c
+++ b/isisd/isis_snmp.c
@@ -283,13 +283,6 @@ SNMP_LOCAL_VARIABLES
*
* 2. I could be replaced in unit test environment
*/
-#ifndef ISIS_SNMP_HAVE_TIME_FUNC
-static uint32_t isis_snmp_time(void)
-{
- return (uint32_t)time(NULL);
-}
-
-#endif
/* ISIS-MIB instances. */
static oid isis_oid[] = {ISIS_MIB};
@@ -2083,7 +2076,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name,
struct isis_circuit *circuit;
uint32_t up_ticks;
uint32_t delta_ticks;
- uint32_t now_time;
+ time_t now_time;
int res;
*write_method = NULL;
@@ -2191,7 +2184,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name,
return SNMP_INTEGER(0);
up_ticks = (uint32_t)netsnmp_get_agent_uptime();
- now_time = isis_snmp_time();
+ now_time = time(NULL);
if (circuit->last_uptime >= now_time)
return SNMP_INTEGER(up_ticks);
@@ -2501,11 +2494,11 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name,
oid *oid_idx;
size_t oid_idx_len;
int res;
- uint32_t val;
+ time_t val;
struct isis_adjacency *adj;
uint32_t up_ticks;
uint32_t delta_ticks;
- uint32_t now_time;
+ time_t now_time;
*write_method = NULL;
@@ -2577,7 +2570,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name,
* It seems that we want remaining timer
*/
if (adj->last_upd != 0) {
- val = isis_snmp_time();
+ val = time(NULL);
if (val < (adj->last_upd + adj->hold_time))
return SNMP_INTEGER(adj->last_upd
+ adj->hold_time - val);
@@ -2594,7 +2587,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name,
up_ticks = (uint32_t)netsnmp_get_agent_uptime();
- now_time = isis_snmp_time();
+ now_time = time(NULL);
if (adj->last_flap >= now_time)
return SNMP_INTEGER(up_ticks);
@@ -2853,7 +2846,7 @@ static int isis_snmp_trap_throttle(oid trap_id)
if (isis == NULL || !isis->snmp_notifications || !smux_enabled())
return 0;
- time_now = isis_snmp_time();
+ time_now = time(NULL);
if ((isis_snmp_trap_timestamp[trap_id] + 5) > time_now)
/* Throttle trap rate at 1 in 5 secs */
diff --git a/lib/command.c b/lib/command.c
index fcaf466c65..53aa064705 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -74,6 +74,7 @@ const struct message tokennames[] = {
item(JOIN_TKN),
item(START_TKN),
item(END_TKN),
+ item(NEG_ONLY_TKN),
{0},
};
/* clang-format on */
diff --git a/lib/command_graph.c b/lib/command_graph.c
index c6c3840455..15c8302e63 100644
--- a/lib/command_graph.c
+++ b/lib/command_graph.c
@@ -388,6 +388,7 @@ static void cmd_node_names(struct graph_node *gn, struct graph_node *join,
case START_TKN:
case JOIN_TKN:
+ case NEG_ONLY_TKN:
/* "<foo|bar> WORD" -> word is not "bar" or "foo" */
prevname = NULL;
break;
@@ -511,6 +512,9 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
case JOIN_TKN:
color = "#ddaaff";
break;
+ case NEG_ONLY_TKN:
+ color = "#ffddaa";
+ break;
case WORD_TKN:
color = "#ffffff";
break;
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 2754dca67d..c20c9874c2 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -64,6 +64,7 @@ enum cmd_token_type {
JOIN_TKN, // marks subgraph end
START_TKN, // first token in line
END_TKN, // last token in line
+ NEG_ONLY_TKN, // filter token, match if "no ..." command
SPECIAL_TKN = FORK_TKN,
};
diff --git a/lib/command_lex.l b/lib/command_lex.l
index 9c096995f5..ec366ce7e1 100644
--- a/lib/command_lex.l
+++ b/lib/command_lex.l
@@ -82,6 +82,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
{VARIABLE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return VARIABLE;}
{WORD} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return WORD;}
{RANGE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return RANGE;}
+!\[ {yylval->string = NULL; return EXCL_BRACKET;}
. {return yytext[0];}
%%
diff --git a/lib/command_match.c b/lib/command_match.c
index 5703510148..56a7ae422b 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -42,7 +42,7 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack");
/* matcher helper prototypes */
static int add_nexthops(struct list *, struct graph_node *,
- struct graph_node **, size_t);
+ struct graph_node **, size_t, bool);
static enum matcher_rv command_match_r(struct graph_node *, vector,
unsigned int, struct graph_node **,
@@ -79,6 +79,13 @@ static enum match_type match_variable(struct cmd_token *, const char *);
static enum match_type match_mac(const char *, bool);
+static bool is_neg(vector vline, size_t idx)
+{
+ if (idx >= vector_active(vline))
+ return false;
+ return !strcmp(vector_slot(vline, idx), "no");
+}
+
enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
struct list **argv, const struct cmd_element **el)
{
@@ -248,7 +255,7 @@ static enum matcher_rv command_match_r(struct graph_node *start, vector vline,
// get all possible nexthops
struct list *next = list_new();
- add_nexthops(next, start, NULL, 0);
+ add_nexthops(next, start, NULL, 0, is_neg(vline, 1));
// determine the best match
for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) {
@@ -349,6 +356,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
{
// pointer to next input token to match
char *input_token;
+ bool neg = is_neg(vline, 0);
struct list *
current =
@@ -363,7 +371,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
// add all children of start node to list
struct graph_node *start = vector_slot(graph->nodes, 0);
- add_nexthops(next, start, &start, 0);
+ add_nexthops(next, start, &start, 0, neg);
unsigned int idx;
for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++) {
@@ -428,7 +436,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
listnode_add(next, newstack);
} else if (matchtype >= minmatch)
add_nexthops(next, gstack[0], gstack,
- idx + 1);
+ idx + 1, neg);
break;
default:
trace_matcher("no_match\n");
@@ -478,7 +486,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
* output, instead of direct node pointers!
*/
static int add_nexthops(struct list *list, struct graph_node *node,
- struct graph_node **stack, size_t stackpos)
+ struct graph_node **stack, size_t stackpos, bool neg)
{
int added = 0;
struct graph_node *child;
@@ -494,8 +502,13 @@ static int add_nexthops(struct list *list, struct graph_node *node,
if (j != stackpos)
continue;
}
+
+ if (token->type == NEG_ONLY_TKN && !neg)
+ continue;
+
if (token->type >= SPECIAL_TKN && token->type != END_TKN) {
- added += add_nexthops(list, child, stack, stackpos);
+ added +=
+ add_nexthops(list, child, stack, stackpos, neg);
} else {
if (stack) {
nextstack = XMALLOC(
diff --git a/lib/command_parse.y b/lib/command_parse.y
index f5e42cc304..3e2cdc79af 100644
--- a/lib/command_parse.y
+++ b/lib/command_parse.y
@@ -105,6 +105,9 @@
%token <string> MAC
%token <string> MAC_PREFIX
+/* special syntax, value is irrelevant */
+%token <string> EXCL_BRACKET
+
/* union types for parsed rules */
%type <node> start
%type <node> literal_token
@@ -372,6 +375,19 @@ selector: '[' selector_seq_seq ']' varname_token
}
;
+/* ![option] productions */
+selector: EXCL_BRACKET selector_seq_seq ']' varname_token
+{
+ struct graph_node *neg_only = new_token_node (ctx, NEG_ONLY_TKN, NULL, NULL);
+
+ $$ = $2;
+ graph_add_edge ($$.start, neg_only);
+ graph_add_edge (neg_only, $$.end);
+ cmd_token_varname_set ($2.end->data, $4);
+ XFREE (MTYPE_LEX, $4);
+}
+;
+
%%
#undef scanner
diff --git a/lib/command_py.c b/lib/command_py.c
index 7f19008fbf..90344ae1e5 100644
--- a/lib/command_py.c
+++ b/lib/command_py.c
@@ -197,21 +197,30 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
if (gn->data) {
struct cmd_token *tok = gn->data;
switch (tok->type) {
-#define item(x) case x: wrap->type = #x; break;
- item(WORD_TKN) // words
- item(VARIABLE_TKN) // almost anything
- item(RANGE_TKN) // integer range
- item(IPV4_TKN) // IPV4 addresses
- item(IPV4_PREFIX_TKN) // IPV4 network prefixes
- item(IPV6_TKN) // IPV6 prefixes
- item(IPV6_PREFIX_TKN) // IPV6 network prefixes
- item(MAC_TKN) // MAC address
- item(MAC_PREFIX_TKN) // MAC address with mask
-
- /* plumbing types */
- item(FORK_TKN) item(JOIN_TKN) item(START_TKN)
- item(END_TKN) default
- : wrap->type = "???";
+#define item(x) \
+ case x: \
+ wrap->type = #x; \
+ break /* no semicolon */
+
+ item(WORD_TKN); // words
+ item(VARIABLE_TKN); // almost anything
+ item(RANGE_TKN); // integer range
+ item(IPV4_TKN); // IPV4 addresses
+ item(IPV4_PREFIX_TKN); // IPV4 network prefixes
+ item(IPV6_TKN); // IPV6 prefixes
+ item(IPV6_PREFIX_TKN); // IPV6 network prefixes
+ item(MAC_TKN); // MAC address
+ item(MAC_PREFIX_TKN); // MAC address with mask
+
+ /* plumbing types */
+ item(FORK_TKN);
+ item(JOIN_TKN);
+ item(START_TKN);
+ item(END_TKN);
+ item(NEG_ONLY_TKN);
+#undef item
+ default:
+ wrap->type = "???";
}
wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED);
diff --git a/pathd/path_cli.c b/pathd/path_cli.c
index bd629a2b70..46242fd05a 100644
--- a/pathd/path_cli.c
+++ b/pathd/path_cli.c
@@ -352,7 +352,16 @@ static int segment_list_has_src_dst(
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv6_adjacency");
node_src_id = adj_src_ipv6_str;
+ } else {
+ /*
+ * This is just to make the compiler happy about
+ * node_src_id not being initialized. This
+ * should never happen unless we change the cli
+ * function.
+ */
+ assert(!"We must have a adj_src_ipv4_str or a adj_src_ipv6_str");
}
+
/* addresses */
snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address",
index_str);
diff --git a/tests/lib/cli/test_cli.c b/tests/lib/cli/test_cli.c
index 8dba1e29f0..f8d74018dd 100644
--- a/tests/lib/cli/test_cli.c
+++ b/tests/lib/cli/test_cli.c
@@ -40,6 +40,8 @@ DUMMY_DEFUN(cmd12, "alt a A.B.C.D");
DUMMY_DEFUN(cmd13, "alt a X:X::X:X");
DUMMY_DEFUN(cmd14,
"pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]");
+DUMMY_DEFUN(cmd15, "no pat g ![ WORD ]");
+DUMMY_DEFUN(cmd16, "[no] pat h {foo ![A.B.C.D$foo]|bar X:X::X:X$bar} final");
#include "tests/lib/cli/test_cli_clippy.c"
@@ -81,5 +83,7 @@ void test_init(int argc, char **argv)
install_element(ENABLE_NODE, &cmd13_cmd);
}
install_element(ENABLE_NODE, &cmd14_cmd);
+ install_element(ENABLE_NODE, &cmd15_cmd);
+ install_element(ENABLE_NODE, &cmd16_cmd);
install_element(ENABLE_NODE, &magic_test_cmd);
}
diff --git a/tests/lib/cli/test_cli.in b/tests/lib/cli/test_cli.in
index 5c146ef984..bd685a6231 100644
--- a/tests/lib/cli/test_cli.in
+++ b/tests/lib/cli/test_cli.in
@@ -74,6 +74,23 @@ pat f
pat f foo
pat f key
+no pat g
+no pat g test
+no pat g test more
+
+pat h foo ?1.2.3.4 final
+no pat h foo ?1.2.3.4 final
+pat h foo final
+no pat h foo final
+pat h bar final
+no pat h bar final
+pat h bar 1::2 final
+no pat h bar 1::2 final
+pat h bar 1::2 foo final
+no pat h bar 1::2 foo final
+pat h bar 1::2 foo 1.2.3.4 final
+no pat h bar 1::2 foo 1.2.3.4 final
+
alt a a?b
alt a 1 .2?.3.4
alt a 1 :2? ::?3
diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in
index 1f38e08b20..84365810d5 100644
--- a/tests/lib/cli/test_cli.refout.in
+++ b/tests/lib/cli/test_cli.refout.in
@@ -147,7 +147,7 @@ test# papat
% Command incomplete.
test# pat
a b c d e f
-g
+g h
test# pat
% Command incomplete.
test#
@@ -263,6 +263,100 @@ cmd10 with 3 args.
[01] f@(null): f
[02] key@(null): key
test#
+test# no pat g
+cmd15 with 3 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+test# no pat g test
+cmd15 with 4 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+[03] WORD@g: test
+test# no pat g test more
+% [NONE] Unknown command: no pat g test more
+test#
+test# pat h foo
+ A.B.C.D 04
+test# pat h foo 1.2.3.4 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+[04] final@(null): final
+test# no pat h foo
+ A.B.C.D 04
+ bar 05
+ final 07
+test# no pat h foo 1.2.3.4 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] A.B.C.D@foo: 1.2.3.4
+[05] final@(null): final
+test# pat h foo final
+% [NONE] Unknown command: pat h foo final
+test# no pat h foo final
+cmd16 with 5 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] final@(null): final
+test# pat h bar final
+% [NONE] Unknown command: pat h bar final
+test# no pat h bar final
+% [NONE] Unknown command: no pat h bar final
+test# pat h bar 1::2 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] final@(null): final
+test# no pat h bar 1::2 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] final@(null): final
+test# pat h bar 1::2 foo final
+% [NONE] Unknown command: pat h bar 1::2 foo final
+test# no pat h bar 1::2 foo final
+cmd16 with 7 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] final@(null): final
+test# pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 7 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 1.2.3.4
+[06] final@(null): final
+test# no pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 8 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] A.B.C.D@foo: 1.2.3.4
+[07] final@(null): final
+test#
test# alt a
test# alt a a
WORD 02
diff --git a/tests/topotests/example_test/r1/zebra.conf b/tests/topotests/example_test/r1/zebra.conf
new file mode 100644
index 0000000000..b733b7b03c
--- /dev/null
+++ b/tests/topotests/example_test/r1/zebra.conf
@@ -0,0 +1,8 @@
+interface r1-eth0
+ ip address 192.168.1.1/24
+
+interface r1-eth1
+ ip address 192.168.2.1/24
+
+interface r1-eth2
+ ip address 192.168.3.1/24 \ No newline at end of file
diff --git a/tests/topotests/example_test/r2/zebra.conf b/tests/topotests/example_test/r2/zebra.conf
new file mode 100644
index 0000000000..c0921f54c9
--- /dev/null
+++ b/tests/topotests/example_test/r2/zebra.conf
@@ -0,0 +1,4 @@
+interface r2-eth0
+ ip address 192.168.1.2/24
+interface r2-eth1
+ ip address 192.168.3.2/24
diff --git a/tests/topotests/example_test/test_template.py b/tests/topotests/example_test/test_template.py
index e94bb905a5..4c073f259c 100644
--- a/tests/topotests/example_test/test_template.py
+++ b/tests/topotests/example_test/test_template.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
#
# <template>.py
# Part of NetDEF Topology Tests
@@ -29,54 +29,69 @@
import sys
import pytest
-# Import topogen and topotest helpers
-from lib.topogen import Topogen, TopoRouter, get_topogen
-
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
# TODO: select markers based on daemons used during test
# pytest module level markers
-"""
-pytestmark = pytest.mark.bfdd # single marker
pytestmark = [
- pytest.mark.bgpd,
- pytest.mark.ospfd,
- pytest.mark.ospf6d
-] # multiple markers
-"""
-
-
+ # pytest.mark.babeld,
+ # pytest.mark.bfdd,
+ # pytest.mark.bgpd,
+ # pytest.mark.eigrpd,
+ # pytest.mark.isisd,
+ # pytest.mark.ldpd,
+ # pytest.mark.nhrpd,
+ # pytest.mark.ospf6d,
+ pytest.mark.ospfd,
+ # pytest.mark.pathd,
+ # pytest.mark.pbrd,
+ # pytest.mark.pimd,
+ # pytest.mark.ripd,
+ # pytest.mark.ripngd,
+ # pytest.mark.sharpd,
+ # pytest.mark.staticd,
+ # pytest.mark.vrrpd,
+]
+
+# Function we pass to Topogen to create the topology
def build_topo(tgen):
"Build function"
# Create 2 routers
- for routern in range(1, 3):
- tgen.add_router("r{}".format(routern))
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
- # Create a switch with just one router connected to it to simulate a
- # empty network.
+ # Create a p2p connection between r1 and r2
+ tgen.add_link(r1, r2)
+
+ # Create a switch with one router connected to it to simulate a empty network.
switch = tgen.add_switch("s1")
- switch.add_link(tgen.gears["r1"])
+ switch.add_link(r1)
- # Create a connection between r1 and r2
+ # Create a p2p connection between r1 and r2
switch = tgen.add_switch("s2")
- switch.add_link(tgen.gears["r1"])
- switch.add_link(tgen.gears["r2"])
+ switch.add_link(r1)
+ switch.add_link(r2)
-def setup_module(mod):
- "Sets up the pytest environment"
+# New form of setup/teardown using pytest fixture
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
# This function initiates the topology build with Topogen...
- tgen = Topogen(build_topo, mod.__name__)
+ tgen = Topogen(build_topo, request.module.__name__)
- # The basic topology above could also have be more easily specified using a
- # dictionary, remove the build_topo function and use the following instead:
+ # A basic topology similar to the above could also have be more easily specified
+ # using a # dictionary, remove the build_topo function and use the following
+ # instead:
#
# topodef = {
# "s1": "r1"
# "s2": ("r1", "r2")
# }
- # tgen = Topogen(topodef, mod.__name__)
+ # tgen = Topogen(topodef, request.module.__name__)
# ... and here it calls initialization functions.
tgen.start_topology()
@@ -84,42 +99,69 @@ def setup_module(mod):
# This is a sample of configuration loading.
router_list = tgen.routers()
- # For all registred routers, load the zebra configuration file
- # CWD = os.path.dirname(os.path.realpath(__file__))
+ # For all routers arrange for:
+ # - starting zebra using config file from <rtrname>/zebra.conf
+ # - starting ospfd using an empty config file.
for rname, router in router_list.items():
- router.load_config(
- TopoRouter.RD_ZEBRA,
- # Uncomment next line to load configuration from ./router/zebra.conf
- # os.path.join(CWD, '{}/zebra.conf'.format(rname))
- )
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
- # After loading the configurations, this function loads configured daemons.
+ # Start and configure the router daemons
tgen.start_router()
+ # Provide tgen as argument to each test function
+ yield tgen
-def teardown_module(mod):
- "Teardown the pytest environment"
- tgen = get_topogen()
-
- # This function tears down the whole topology.
+ # Teardown after last test runs
tgen.stop_topology()
-def test_call_cli():
- "Dummy test that just calls tgen.cli() so we can interact with the build."
- tgen = get_topogen()
- # Don't run this test if we have any failure.
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
+ pytest.skip("skipped because of previous test failure")
+
+
+# ===================
+# The tests functions
+# ===================
+
- # logger.info("calling CLI")
- # tgen.cli()
+def test_get_version(tgen):
+ "Test the logs the FRR version"
+
+ r1 = tgen.gears["r1"]
+ version = r1.vtysh_cmd("show version")
+ logger.info("FRR version is: " + version)
+
+
+def test_connectivity(tgen):
+ "Test the logs the FRR version"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ output = r1.cmd_raises("ping -c1 192.168.1.2")
+ output = r2.cmd_raises("ping -c1 192.168.3.1")
+
+
+@pytest.mark.xfail
+def test_expect_failure(tgen):
+ "A test that is current expected to fail but should be fixed"
+
+ assert False, "Example of temporary expected failure that will eventually be fixed"
+
+
+@pytest.mark.skip
+def test_will_be_skipped(tgen):
+ "A test that will be skipped"
+ assert False
# Memory leak test template
-def test_memory_leak():
+def test_memory_leak(tgen):
"Run the memory leak test and report results."
- tgen = get_topogen()
+
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c
index 4a9344fee4..ecfb085793 100644
--- a/tests/zebra/test_lm_plugin.c
+++ b/tests/zebra/test_lm_plugin.c
@@ -77,7 +77,7 @@ static int lm_release_chunk_pi(struct zserv *client, uint32_t start,
/* use external allocations */
-static void lp_plugin_init()
+static void lp_plugin_init(void)
{
/* register our own hooks */
hook_register(lm_client_connect, test_client_connect);
@@ -86,7 +86,7 @@ static void lp_plugin_init()
hook_register(lm_release_chunk, lm_release_chunk_pi);
}
-static void lp_plugin_cleanup()
+static void lp_plugin_cleanup(void)
{
/* register our own hooks */
hook_unregister(lm_client_connect, test_client_connect);
@@ -98,7 +98,7 @@ static void lp_plugin_cleanup()
/* tests */
-static void test_lp_plugin()
+static void test_lp_plugin(void)
{
struct label_manager_chunk *lmc;
diff --git a/tools/permutations.c b/tools/permutations.c
index f51d4a4ec9..b280cc15b1 100644
--- a/tools/permutations.c
+++ b/tools/permutations.c
@@ -61,9 +61,22 @@ void permute(struct graph_node *start)
struct cmd_token *stok = start->data;
struct graph_node *gnn;
struct listnode *ln;
+ bool is_neg = false;
// recursive dfs
listnode_add(position, start);
+
+ for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
+ struct cmd_token *tok = gnn->data;
+
+ if (tok->type == WORD_TKN && !strcmp(tok->text, "no")) {
+ is_neg = true;
+ break;
+ }
+ if (tok->type < SPECIAL_TKN)
+ break;
+ }
+
for (unsigned int i = 0; i < vector_active(start->to); i++) {
struct graph_node *gn = vector_slot(start->to, i);
struct cmd_token *tok = gn->data;
@@ -82,6 +95,9 @@ void permute(struct graph_node *start)
fprintf(stdout, "\n");
} else {
bool skip = false;
+
+ if (tok->type == NEG_ONLY_TKN && !is_neg)
+ continue;
if (stok->type == FORK_TKN && tok->type != FORK_TKN)
for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
if (gnn == gn) {