Is the schema range available via confd_cs_node_info?

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?

1 Like

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

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

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?

1 Like