tests: add oper test using existing libyang state tree

Signed-off-by: Christian Hopps <chopps@labn.net>
This commit is contained in:
Christian Hopps 2025-02-17 03:59:38 +00:00
parent 656c4692a0
commit bce25cf43b
9 changed files with 542 additions and 12 deletions

View file

@ -236,13 +236,9 @@ static int frr_test_module_vrfs_vrf_ping(struct nb_cb_rpc_args *args)
return NB_OK; return NB_OK;
} }
/* static struct yang_data *__return_null(struct nb_cb_get_elem_args *args)
* 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)
{ {
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; 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 */ /* clang-format off */
const struct frr_yang_module_info frr_test_module_info = { const struct frr_yang_module_info frr_test_module_info = {
.name = "frr-test-module", .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", .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", .xpath = "/frr-test-module:frr-test-module/c2cont/c2value",
.cbs.get = frr_test_module_c2cont_c2value_get, .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, .xpath = NULL,
}, },

View file

@ -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[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[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/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/ show yang operational-data /frr-test-module:frr-test-module/c2cont/
show yang operational-data /frr-test-module:frr-test-module/c2cont/c2value show yang operational-data /frr-test-module:frr-test-module/c2cont/c2value

View file

@ -125,10 +125,10 @@ test# show yang operational-data /frr-test-module:frr-test-module
} }
] ]
}, },
"c1value": 21,
"c2cont": { "c2cont": {
"c2value": 2868969987 "c2value": 2868969987
} },
"c3value": 21
} }
} }
test# show yang operational-data /frr-test-module:frr-test-module/vrfs/vrf[name='vrf0']/routes/route[2] 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/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": { "frr-test-module:frr-test-module": {
"c1value": 21 "c3value": 21
} }
} }
test# show yang operational-data /frr-test-module:frr-test-module/c2cont test# show yang operational-data /frr-test-module:frr-test-module/c2cont

View file

@ -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 <zebra.h>
#include <sys/stat.h>
#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);
}

View file

@ -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

View file

@ -0,0 +1,5 @@
import frrtest
class TestNbOperData(frrtest.TestRefOut):
program = "./test_oper_exists"

View file

@ -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.

View file

@ -131,6 +131,19 @@ EXTRA_DIST += \
# end # 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 check_PROGRAMS += tests/lib/test_assert
tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS)

View file

@ -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";
}
}
}
}
} }
} }