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);
}
}
}
}