From bce25cf43bca7b5bddd7126ab16e9018d8c9dbfa Mon Sep 17 00:00:00 2001 From: Christian Hopps Date: Mon, 17 Feb 2025 03:59:38 +0000 Subject: [PATCH] tests: add oper test using existing libyang state tree Signed-off-by: Christian Hopps --- tests/lib/northbound/test_oper_data.c | 26 +- tests/lib/northbound/test_oper_data.in | 2 +- tests/lib/northbound/test_oper_data.refout | 8 +- tests/lib/northbound/test_oper_exists.c | 266 +++++++++++++++++++ tests/lib/northbound/test_oper_exists.in | 8 + tests/lib/northbound/test_oper_exists.py | 5 + tests/lib/northbound/test_oper_exists.refout | 208 +++++++++++++++ tests/lib/subdir.am | 13 + yang/frr-test-module.yang | 18 ++ 9 files changed, 542 insertions(+), 12 deletions(-) create mode 100644 tests/lib/northbound/test_oper_exists.c create mode 100644 tests/lib/northbound/test_oper_exists.in create mode 100644 tests/lib/northbound/test_oper_exists.py create mode 100644 tests/lib/northbound/test_oper_exists.refout diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 0b334c6522..a38325173a 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -236,13 +236,9 @@ static int frr_test_module_vrfs_vrf_ping(struct nb_cb_rpc_args *args) return NB_OK; } -/* - * XPath: /frr-test-module:frr-test-module/c1value - */ -static struct yang_data * -frr_test_module_c1value_get_elem(struct nb_cb_get_elem_args *args) +static struct yang_data *__return_null(struct nb_cb_get_elem_args *args) { - return yang_data_new_uint8(args->xpath, 21); + return NULL; } /* @@ -263,6 +259,14 @@ static enum nb_error frr_test_module_c2cont_c2value_get(const struct nb_node *nb return NB_OK; } +/* + * XPath: /frr-test-module:frr-test-module/c3value + */ +static struct yang_data *frr_test_module_c3value_get_elem(struct nb_cb_get_elem_args *args) +{ + return yang_data_new_uint8(args->xpath, 21); +} + /* clang-format off */ const struct frr_yang_module_info frr_test_module_info = { .name = "frr-test-module", @@ -316,12 +320,20 @@ const struct frr_yang_module_info frr_test_module_info = { }, { .xpath = "/frr-test-module:frr-test-module/c1value", - .cbs.get_elem = frr_test_module_c1value_get_elem, + .cbs.get_elem = __return_null, }, { .xpath = "/frr-test-module:frr-test-module/c2cont/c2value", .cbs.get = frr_test_module_c2cont_c2value_get, }, + { + .xpath = "/frr-test-module:frr-test-module/c3value", + .cbs.get_elem = frr_test_module_c3value_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/c4cont/c4value", + .cbs.get_elem = __return_null, + }, { .xpath = NULL, }, diff --git a/tests/lib/northbound/test_oper_data.in b/tests/lib/northbound/test_oper_data.in index 94fcdc1e1c..bed83b8d74 100644 --- a/tests/lib/northbound/test_oper_data.in +++ b/tests/lib/northbound/test_oper_data.in @@ -2,7 +2,7 @@ show yang operational-data /frr-test-module:frr-test-module show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[2] show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[3]/interface show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[10] -show yang operational-data /frr-test-module:frr-test-module/c1value +show yang operational-data /frr-test-module:frr-test-module/c3value show yang operational-data /frr-test-module:frr-test-module/c2cont show yang operational-data /frr-test-module:frr-test-module/c2cont/ show yang operational-data /frr-test-module:frr-test-module/c2cont/c2value diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout index 57061d0371..2feadf4b77 100644 --- a/tests/lib/northbound/test_oper_data.refout +++ b/tests/lib/northbound/test_oper_data.refout @@ -125,10 +125,10 @@ test# show yang operational-data /frr-test-module:frr-test-module } ] }, - "c1value": 21, "c2cont": { "c2value": 2868969987 - } + }, + "c3value": 21 } } test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[2] @@ -174,10 +174,10 @@ test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name= } test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[10] {} -test# show yang operational-data /frr-test-module:frr-test-module/c1value +test# show yang operational-data /frr-test-module:frr-test-module/c3value { "frr-test-module:frr-test-module": { - "c1value": 21 + "c3value": 21 } } test# show yang operational-data /frr-test-module:frr-test-module/c2cont diff --git a/tests/lib/northbound/test_oper_exists.c b/tests/lib/northbound/test_oper_exists.c new file mode 100644 index 0000000000..17afcc7fd4 --- /dev/null +++ b/tests/lib/northbound/test_oper_exists.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * Copyright (C) 2025 LabN Consulting, L.L.C. + */ + +#include +#include + +#include "debug.h" +#include "frrevent.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "lib_vty.h" +#include "log.h" +#include "northbound.h" +#include "northbound_cli.h" + +static struct event_loop *master; +static struct lyd_node *data_tree; +static uint data_tree_lock; + +const char *data_json = "\n" + "{\n" + " \"frr-test-module:frr-test-module\": {\n" + " \"vrfs\": {\n" + " \"vrf\": [\n" + " {\n" + " \"name\": \"vrf0\",\n" + " \"interfaces\": {\n" + " \"interface\": [\n" + " \"eth0\",\n" + " \"eth1\",\n" + " \"eth2\",\n" + " \"eth3\"\n" + " ],\n" + " \"interface-new\": [\n" + " \"eth0\",\n" + " \"eth1\",\n" + " \"eth2\",\n" + " \"eth3\"\n" + " ]\n" + " },\n" + " \"routes\": {\n" + " \"route\": [\n" + " {\n" + " \"prefix\": \"10.0.0.0/32\",\n" + " \"next-hop\": \"172.16.0.0\",\n" + " \"interface\": \"eth0\",\n" + " \"metric\": 0,\n" + " \"active\": [null]\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.1/32\",\n" + " \"next-hop\": \"172.16.0.1\",\n" + " \"interface\": \"eth1\",\n" + " \"metric\": 1\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.2/32\",\n" + " \"next-hop\": \"172.16.0.2\",\n" + " \"interface\": \"eth2\",\n" + " \"metric\": 2,\n" + " \"active\": [null]\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.3/32\",\n" + " \"next-hop\": \"172.16.0.3\",\n" + " \"interface\": \"eth3\",\n" + " \"metric\": 3\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.4/32\",\n" + " \"next-hop\": \"172.16.0.4\",\n" + " \"interface\": \"eth4\",\n" + " \"metric\": 4,\n" + " \"active\": [null]\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.5/32\",\n" + " \"next-hop\": \"172.16.0.5\",\n" + " \"interface\": \"eth5\",\n" + " \"metric\": 5\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " {\n" + " \"name\": \"vrf1\",\n" + " \"interfaces\": {\n" + " \"interface\": [\n" + " \"eth0\",\n" + " \"eth1\",\n" + " \"eth2\",\n" + " \"eth3\"\n" + " ],\n" + " \"interface-new\": [\n" + " \"eth0\",\n" + " \"eth1\",\n" + " \"eth2\",\n" + " \"eth3\"\n" + " ]\n" + " },\n" + " \"routes\": {\n" + " \"route\": [\n" + " {\n" + " \"prefix\": \"10.0.0.0/32\",\n" + " \"next-hop\": \"172.16.0.0\",\n" + " \"interface\": \"eth0\",\n" + " \"metric\": 0,\n" + " \"active\": [null]\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.1/32\",\n" + " \"next-hop\": \"172.16.0.1\",\n" + " \"interface\": \"eth1\",\n" + " \"metric\": 1\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.2/32\",\n" + " \"next-hop\": \"172.16.0.2\",\n" + " \"interface\": \"eth2\",\n" + " \"metric\": 2,\n" + " \"active\": [null]\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.3/32\",\n" + " \"next-hop\": \"172.16.0.3\",\n" + " \"interface\": \"eth3\",\n" + " \"metric\": 3\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.4/32\",\n" + " \"next-hop\": \"172.16.0.4\",\n" + " \"interface\": \"eth4\",\n" + " \"metric\": 4,\n" + " \"active\": [null]\n" + " },\n" + " {\n" + " \"prefix\": \"10.0.0.5/32\",\n" + " \"next-hop\": \"172.16.0.5\",\n" + " \"interface\": \"eth5\",\n" + " \"metric\": 5\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"c2cont\": {\n" + " \"c2value\": 2868969987\n" + " },\n" + " \"c3value\": 21\n" + " }\n" + "}\n"; + + +static const struct lyd_node *test_oper_get_tree_locked(const char *xpath) +{ + ++data_tree_lock; + return data_tree; +} + +static void test_oper_unlock_tree(const struct lyd_node *tree __attribute__((unused))) +{ + data_tree_lock--; +} + +static int __rpc_return_ok(struct nb_cb_rpc_args *args) +{ + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_test_module_info = { + .name = "frr-test-module", + .get_tree_locked = test_oper_get_tree_locked, + .unlock_tree = test_oper_unlock_tree, + .nodes = { + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/ping", + .cbs.rpc = __rpc_return_ok, + }, + { + .xpath = NULL, + }, + } +}; +/* clang-format on */ + +static const struct frr_yang_module_info *const modules[] = { + &frr_test_module_info, +}; + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + + lyd_free_all(data_tree); + + cmd_terminate(); + vty_terminate(); + nb_terminate(); + yang_terminate(); + event_master_free(master); + + log_memstats(NULL, true); + if (!isexit) + exit(0); +} + + +static struct lyd_node *load_data(void) +{ + struct ly_in *in = NULL; + struct lyd_node *tree = NULL; + LY_ERR err; + + err = ly_in_new_memory(data_json, &in); + if (!err) + err = lyd_parse_data(ly_native_ctx, NULL, in, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_OPERATIONAL, &tree); + ly_in_free(in, 0); + if (err) { + fprintf(stderr, "LYERR: %s\n", getcwd(NULL, 0)); + fprintf(stderr, "LYERR: %s\n", ly_last_errmsg()); + exit(1); + } + return tree; +} + +/* main routine. */ +int main(int argc, char **argv) +{ + struct event thread; + + /* Set umask before anything for security */ + umask(0027); + + /* master init. */ + master = event_master_create(NULL); + + // zlog_aux_init("NONE: ", ZLOG_DISABLED); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + vty_init(master, false); + lib_cmd_init(); + debug_init(); + nb_init(master, modules, array_size(modules), false, false); + + /* Create artificial data. */ + data_tree = load_data(); + + /* Read input from .in file. */ + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (event_fetch(master, &thread)) + event_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/lib/northbound/test_oper_exists.in b/tests/lib/northbound/test_oper_exists.in new file mode 100644 index 0000000000..7b83c27a0b --- /dev/null +++ b/tests/lib/northbound/test_oper_exists.in @@ -0,0 +1,8 @@ +show yang operational-data /frr-test-module:frr-test-module +show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[2] +show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[3]/interface +show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[10] +show yang operational-data /frr-test-module:frr-test-module/c3value +show yang operational-data /frr-test-module:frr-test-module/c2cont +show yang operational-data /frr-test-module:frr-test-module/c2cont/ +show yang operational-data /frr-test-module:frr-test-module/c2cont/c2value diff --git a/tests/lib/northbound/test_oper_exists.py b/tests/lib/northbound/test_oper_exists.py new file mode 100644 index 0000000000..423414eb85 --- /dev/null +++ b/tests/lib/northbound/test_oper_exists.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestNbOperData(frrtest.TestRefOut): + program = "./test_oper_exists" diff --git a/tests/lib/northbound/test_oper_exists.refout b/tests/lib/northbound/test_oper_exists.refout new file mode 100644 index 0000000000..4060a096fd --- /dev/null +++ b/tests/lib/northbound/test_oper_exists.refout @@ -0,0 +1,208 @@ +test# show yang operational-data /frr-test-module:frr-test-module +{ + "frr-test-module:frr-test-module": { + "vrfs": { + "vrf": [ + { + "name": "vrf0", + "interfaces": { + "interface": [ + "eth0", + "eth1", + "eth2", + "eth3" + ], + "interface-new": [ + "eth0", + "eth1", + "eth2", + "eth3" + ] + }, + "routes": { + "route": [ + { + "prefix": "10.0.0.0/32", + "next-hop": "172.16.0.0", + "interface": "eth0", + "metric": 0, + "active": [null] + }, + { + "prefix": "10.0.0.1/32", + "next-hop": "172.16.0.1", + "interface": "eth1", + "metric": 1 + }, + { + "prefix": "10.0.0.2/32", + "next-hop": "172.16.0.2", + "interface": "eth2", + "metric": 2, + "active": [null] + }, + { + "prefix": "10.0.0.3/32", + "next-hop": "172.16.0.3", + "interface": "eth3", + "metric": 3 + }, + { + "prefix": "10.0.0.4/32", + "next-hop": "172.16.0.4", + "interface": "eth4", + "metric": 4, + "active": [null] + }, + { + "prefix": "10.0.0.5/32", + "next-hop": "172.16.0.5", + "interface": "eth5", + "metric": 5 + } + ] + } + }, + { + "name": "vrf1", + "interfaces": { + "interface": [ + "eth0", + "eth1", + "eth2", + "eth3" + ], + "interface-new": [ + "eth0", + "eth1", + "eth2", + "eth3" + ] + }, + "routes": { + "route": [ + { + "prefix": "10.0.0.0/32", + "next-hop": "172.16.0.0", + "interface": "eth0", + "metric": 0, + "active": [null] + }, + { + "prefix": "10.0.0.1/32", + "next-hop": "172.16.0.1", + "interface": "eth1", + "metric": 1 + }, + { + "prefix": "10.0.0.2/32", + "next-hop": "172.16.0.2", + "interface": "eth2", + "metric": 2, + "active": [null] + }, + { + "prefix": "10.0.0.3/32", + "next-hop": "172.16.0.3", + "interface": "eth3", + "metric": 3 + }, + { + "prefix": "10.0.0.4/32", + "next-hop": "172.16.0.4", + "interface": "eth4", + "metric": 4, + "active": [null] + }, + { + "prefix": "10.0.0.5/32", + "next-hop": "172.16.0.5", + "interface": "eth5", + "metric": 5 + } + ] + } + } + ] + }, + "c2cont": { + "c2value": 2868969987 + }, + "c3value": 21 + } +} +test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[2] +{ + "frr-test-module:frr-test-module": { + "vrfs": { + "vrf": [ + { + "name": "vrf0", + "routes": { + "route": [ + { + "prefix": "10.0.0.1/32", + "next-hop": "172.16.0.1", + "interface": "eth1", + "metric": 1 + } + ] + } + } + ] + } + } +} +test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[3]/interface +{ + "frr-test-module:frr-test-module": { + "vrfs": { + "vrf": [ + { + "name": "vrf0", + "routes": { + "route": [ + { + "interface": "eth2" + } + ] + } + } + ] + } + } +} +test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[10] +{} +test# show yang operational-data /frr-test-module:frr-test-module/c3value +{ + "frr-test-module:frr-test-module": { + "c3value": 21 + } +} +test# show yang operational-data /frr-test-module:frr-test-module/c2cont +{ + "frr-test-module:frr-test-module": { + "c2cont": { + "c2value": 2868969987 + } + } +} +test# show yang operational-data /frr-test-module:frr-test-module/c2cont/ +{ + "frr-test-module:frr-test-module": { + "c2cont": { + "c2value": 2868969987 + } + } +} +test# show yang operational-data /frr-test-module:frr-test-module/c2cont/c2value +{ + "frr-test-module:frr-test-module": { + "c2cont": { + "c2value": 2868969987 + } + } +} +test# +end. diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am index 1a21684f16..ca74306543 100644 --- a/tests/lib/subdir.am +++ b/tests/lib/subdir.am @@ -131,6 +131,19 @@ EXTRA_DIST += \ # end +check_PROGRAMS += tests/lib/northbound/test_oper_exists +tests_lib_northbound_test_oper_exists_CFLAGS = $(TESTS_CFLAGS) +tests_lib_northbound_test_oper_exists_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_northbound_test_oper_exists_LDADD = $(ALL_TESTS_LDADD) +tests_lib_northbound_test_oper_exists_SOURCES = tests/lib/northbound/test_oper_exists.c +nodist_tests_lib_northbound_test_oper_exists_SOURCES = yang/frr-test-module.yang.c +EXTRA_DIST += \ + tests/lib/northbound/test_oper_exists.in \ + tests/lib/northbound/test_oper_exists.py \ + tests/lib/northbound/test_oper_exists.refout \ + # end + + check_PROGRAMS += tests/lib/test_assert tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS) diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang index 773a959553..909c199b2f 100644 --- a/yang/frr-test-module.yang +++ b/yang/frr-test-module.yang @@ -139,5 +139,23 @@ module frr-test-module { } } } + choice bchoice { + description "a choice statement"; + case case3 { + leaf c3value { + type uint8; + description "A uint8 value for case 3"; + } + } + case case4 { + container c4cont { + description "case 2 container"; + leaf c4value { + type uint32; + description "A uint32 value for case 4"; + } + } + } + } } }