Is the schema range available via confd_cs_node_info?

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