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;
}
/*
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",
},
{
.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,
},
--- /dev/null
+// 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);
+}
--- /dev/null
+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.