The confd_cmd utility has an option “dump_schema” that utilizes the ConfD confd_get_nslist()/confd_val2str() APIs to print out the schema. I noticed that our YANG model’s leaf “range” is not reported by this utility. Looking into the confd_cs_node_info structure, I can see the default value (defval) is provided, but it does not appear to provide the model’s range. Is it possible to obtain the schema’s range via any of the ConfD APIs?
Hi Peter!
The range statement is not included in the schema information. If you want that information in the schema information you can for example use the tailf:meta-data
extension to provide key+value information for the node in the schema.
I put together a small tailf:meta-data + tailf:meta-value + cdb_get_modifications() example that add an annotated yang model with the tailf:meta-data key+value statements, see my-yang-ann.yang, and a subscriber, see sub.c. When a subscription event occur, the subscribers just print the meta-data key and value + changes retrieved via cdb_get_modifications()
Quick demo:
Terminal window 1:
$ cat my-yang.yang
module my-yang {
namespace "http://tail-f.com/ns/example/my-yang";
prefix "my";
container my-container {
leaf my-number {
type int32 {
range "min..0|24..3967|4048..max";
}
}
}
}
$ cat my-yang-ann.yang
module my-yang-ann {
namespace "urn:dummy";
prefix "dummy";
import tailf-common {
prefix "tailf";
}
import my-yang {
prefix "x";
}
tailf:annotate /x:my-container/x:my-number {
tailf:meta-data "range" {
tailf:meta-value "min..0|24..3967|4048..max";
}
}
}
$ make all start
...
../confd-6.3.1/bin/confd -c ./confd.conf —addloadpath ../confd-6.3.1/etc/confd
./sub
TRACE Connected (maapi) to ConfD
TRACE MAAPI_LOAD_ALL_NS
TRACE MAAPI_LOAD_HASH_DB
TRACE Connected (cdb) to ConfD
TRACE Connected (cdb) to ConfD
TRACE CDB_SUBSCRIBE /my-container/my-number --> CONFD_OK
TRACE CDB_SUBSCRIBE_DONE --> CONFD_OK
Terminal window 2:
$ confd_cmd -d -d -c 'mset /my-container/my-number 42'
mset "/my-container/my-number" "42"
CMD_MAAPI is true [mtid = 0]
TRACE Connected (maapi) to ConfD
TRACE MAAPI_START_USER_SESSION --> CONFD_OK
TRACE MAAPI_START_TRANS --> CONFD_OK
TRACE MAAPI_SET_ELEM2 /my-container/my-number --> CONFD_OK
TRACE MAAPI_APPLY_TRANS --> CONFD_OK
TRACE MAAPI_STOP_TRANS --> CONFD_OK
TRACE MAAPI_END_USER_SESSION --> CONFD_OK
Terminal window 1:
Trace CDB_SUBSCRIPTION_EVENT --> 6
*** Config updated
TRACE CDB_GET_MODIFICATIONS --> CONFD_OK
my-container begin
my-number tailf:meta-data range
my-number tailf:meta-value min..0|24..3967|4048..max
my-number 42
my-container end
TRACE CDB_SYNC_SUB CDB_DONE_PRIORITY --> CONFD_OK
Terminal window 2:
$ netconf-console-tcp --get-schema my-yang
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">
<data xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"><![CDATA[module my-yang {
namespace "http://tail-f.com/ns/example/my-yang";
prefix "my";
import tailf-common {
prefix "tailf";
}
container my-container {
leaf my-number {
type int32 {
range "min..0|24..3967|4048..max";
}
default 42;
}
}
}
]]></data>
</rpc-reply>
$ cat sub.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/poll.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <confd.h>
#include <confd_lib.h>
#include <confd_cdb.h>
#include "my-yang.h"
/* copied from confd_cmd.c */
static void print_modifications(confd_tag_value_t *val, int nvals,
struct confd_cs_node *start_node,
int start_indent)
{
int i, j, indent = start_indent;
struct confd_cs_node root, *pnode = start_node, *node;
char tmpbuf[BUFSIZ];
char *tmp;
for (i=0; i<nvals; i++) {
if (indent == start_indent && start_node == NULL) {
node = confd_find_cs_root(CONFD_GET_TAG_NS(&val[i]));
root.children = node;
pnode = &root;
}
switch (CONFD_GET_TAG_VALUE(&val[i])->type) {
case C_XMLBEGIN:
tmp = "begin";
if (pnode != NULL)
pnode = confd_find_cs_node_child(pnode, val[i].tag);
break;
case C_XMLBEGINDEL:
tmp = "begin-deleted";
if (pnode != NULL)
pnode = confd_find_cs_node_child(pnode, val[i].tag);
break;
case C_XMLEND:
tmp = "end";
if (pnode != NULL)
pnode = pnode->parent;
indent -= 2;
break;
case C_XMLTAG:
tmp = "created";
break;
case C_NOEXISTS:
tmp = "deleted";
break;
default:
if (pnode == NULL ||
(node = confd_find_cs_node_child(pnode, val[i].tag)) == NULL ||
confd_val2str(node->info.type, CONFD_GET_TAG_VALUE(&val[i]),
tmpbuf, sizeof(tmpbuf)) == CONFD_ERR) {
confd_pp_value(tmpbuf, sizeof(tmpbuf), CONFD_GET_TAG_VALUE(&val[i]));
}
tmp = tmpbuf;
if(node != NULL) {
if(node->info.meta_data != NULL) {
for(j=0; node->info.meta_data[j].key != NULL; j++) {
fprintf(stderr, "%*s%s %s %s\n", indent, "",
confd_hash2str(CONFD_GET_TAG_TAG(&val[i])), "tailf:meta-data", node->info.meta_data[j].key);
if(node->info.meta_data[j].value != NULL) {
fprintf(stderr, "%*s%s %s %s\n", indent, "",
confd_hash2str(CONFD_GET_TAG_TAG(&val[i])), "tailf:meta-value", node->info.meta_data[j].value);
}
}
}
}
}
fprintf(stderr, "%*s%s %s\n", indent, "",
confd_hash2str(CONFD_GET_TAG_TAG(&val[i])), tmp);
switch (CONFD_GET_TAG_VALUE(&val[i])->type) {
case C_XMLBEGIN:
case C_XMLBEGINDEL:
indent += 2;
break;
default:
break;
}
}
}
static void free_tag_values(confd_tag_value_t *tv, int n)
{
int i;
for (i = 0; i < n; i++) {
confd_free_value(CONFD_GET_TAG_VALUE(&tv[i]));
}
}
int main(int argc, char **argv)
{
struct sockaddr_in addr;
int c, status, subsock, sock;
int headpoint;
enum confd_debug_level dbgl = CONFD_TRACE;
char *confd_addr = "127.0.0.1";
int confd_port = CONFD_PORT;
char *path = "/my-container/my-number";
while ((c = getopt(argc, argv, "dsa:p:")) != EOF) {
switch (c) {
case 'd': dbgl = CONFD_DEBUG; break;
case 's': dbgl = CONFD_SILENT; break;
case 'a': confd_addr = optarg; break;
case 'p': confd_port = atoi(optarg); break;
}
}
addr.sin_addr.s_addr = inet_addr(confd_addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(confd_port);
confd_init(argv[0], stderr, dbgl);
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
confd_fatal("%s: Failed to create socket", argv[0]);
if (confd_load_schemas((struct sockaddr*)&addr,
sizeof (struct sockaddr_in)) != CONFD_OK)
confd_fatal("%s: Failed to load schemas from confd\n", argv[0]);
if (cdb_connect(sock, CDB_DATA_SOCKET, (struct sockaddr *)&addr,
sizeof(struct sockaddr_in)) != CONFD_OK)
confd_fatal("%s: Failed to connect to ConfD", argv[0]);
if ((subsock = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
confd_fatal("Failed to open socket\n");
if (cdb_connect(subsock, CDB_SUBSCRIPTION_SOCKET, (struct sockaddr*)&addr,
sizeof (struct sockaddr_in)) < 0)
confd_fatal("Failed to cdb_connect() to confd \n");
/* setup subscription point */
if ((status = cdb_subscribe(subsock, 3, my__ns, &headpoint,
path))
!= CONFD_OK) {
confd_fatal("Terminate: subscribe %d\n", status);
}
if (cdb_subscribe_done(subsock) != CONFD_OK)
confd_fatal("cdb_subscribe_done() failed");
while (1) {
int status;
struct pollfd set[1];
set[0].fd = subsock;
set[0].events = POLLIN;
set[0].revents = 0;
if (poll(&set[0], sizeof(set)/sizeof(*set), -1) < 0) {
if (errno != EINTR) {
perror("Poll failed:");
continue;
}
}
if (set[0].revents & POLLIN) {
int sub_points[1];
int reslen;
if ((status = cdb_read_subscription_socket(subsock,
&sub_points[0],
&reslen)) != CONFD_OK) {
confd_fatal("terminate sub_read: %d\n", status);
}
if (reslen > 0) {
int nvals;
confd_tag_value_t *val;
fprintf(stderr, "*** Config updated \n");
if ((status = cdb_get_modifications(subsock,
sub_points[0],
CDB_GET_MODS_INCLUDE_LISTS,
&val,
&nvals,
NULL)) == CONFD_OK) {
print_modifications(val, nvals, NULL, 0);
free_tag_values(val, nvals);
free(val);
} else {
fprintf(stderr, "failed to get modifications: %s\n", confd_strerror(confd_errno));
}
}
if ((status = cdb_sync_subscription_socket(subsock,
CDB_DONE_PRIORITY))
!= CONFD_OK) {
confd_fatal("failed to sync subscription: %d\n", status);
}
}
}
}
The above example can extend the confd_cmd “dump_schema” command like this:
$ diff -u ../../../../confd-6.3.1/src/confd/tools/confd_cmd.c ./confd_cmd.c
--- ../../../../confd-6.3.1/src/confd/tools/confd_cmd.c 2016-11-23 11:13:07.000000000 +0100
+++ ./confd_cmd.c 2017-02-08 09:41:41.000000000 +0100
@@ -1817,6 +1817,17 @@
#undef COMMA
printf(" flags=%s", tmpbuf);
}
+ if(node->info.meta_data != NULL) {
+ int j;
+ printf(" meta_data");
+ for(j=0; node->info.meta_data[j].key != NULL; j++) {
+ printf(" key=%s", node->info.meta_data[j].key);
+ if(node->info.meta_data[j].value != NULL) {
+ printf(" value=%s", node->info.meta_data[j].value);
+ }
+ }
+ }
+
// if (node->parent) printf(" parent=%s", confd_hash2str(node->parent->tag));
// if (node->next) printf(" next=%s", confd_hash2str(node->next->tag));
printf("\n");
Also, while the range statement is indeed not included in the schema as such, the restriction is present, and will be applied if confd_str2val() is used to convert a string to an integer (i.e. the function will fail if the string represents a value that is outside the allowed range(s)). Is there some specific use case for having access to the actual statement?