Using cdb_set_values() to write multiple list entries with a single call to CDB operational datastore

If you implement operational data to be kept in the CDB operational datastore, and you are writing to large lists / tables, your application performance may benefit from using cdb_set_values() instead of cdb_set_elem() or cdb_set_object().

cdb_set_values() allow you to write multiple list / table entries with a single call to CDB operational datastore, instead of using several with cdb_set_object() – one row per call, or cdb_set_elem() – one leaf per call.

Here is a diff of how to replace cdb_set_object() in the $CONFD_DIR/examples.confd/cdb_oper/ifstatus example. Note how cdb_set_values() is called one time only to set multiple list entries, where as before cdb_set_object() was called for every row in the list.

$ diff -u $CONFD_DIR/examples.confd/cdb_oper/ifstatus-orig/ifstatus.c ifstatus.c
--- $CONFD_DIR/examples.confd/cdb_oper/ifstatus-orig/ifstatus.c	2015-07-17 04:56:53.000000000 +0200
+++ ifstatus.c	2015-08-30 16:17:49.983922544 +0200
@@ -28,11 +28,11 @@
 {
     FILE *proc;
     int ret;
-    char buf[BUFSIZ];
-    char *ifname, *p;
+    char statbuf[BUFSIZ];
+    char *ifname, *p, *buf;
     long long counter;
-    confd_value_t val[1 + 4 + 1 + 5];
-    int i;
+    confd_tag_value_t tval[BUFSIZ];
+    int i,len,nlen;
 
     if ((ret = cdb_start_session(sock, CDB_OPERATIONAL)) != CONFD_OK)
         return ret;
@@ -41,46 +41,56 @@
 
     if ((proc = fopen("/proc/net/dev", "r")) == NULL)
         return CONFD_ERR;
-    while (ret == CONFD_OK && fgets(buf, sizeof(buf), proc) != NULL) {
+    i = 0;
+    buf = &statbuf[0];
+    len = sizeof(statbuf);
+    while (fgets(buf, len, proc) != NULL) {
         if ((p = strchr(buf, ':')) == NULL)
             continue;
         *p = ' ';
         if ((ifname = strtok(buf, " \t")) == NULL)
             continue;
-
-        i = 0;
-
-        CONFD_SET_XMLTAG(&val[i], if_receive, if__ns); i++;
+	nlen = strlen(ifname)+3;
+	buf += nlen;
+	len -= nlen;
+        /* <interface> */
+	CONFD_SET_TAG_XMLBEGIN(&tval[i], if_interface, if__ns); i++;
+	/* <name>ifname</name> */
+	CONFD_SET_TAG_STR(&tval[i], if_name, ifname); i++;
+	/* <status> */
+	CONFD_SET_TAG_XMLBEGIN(&tval[i], if_status, if__ns); i++;
+	/* <receive> */
+        CONFD_SET_TAG_XMLBEGIN(&tval[i], if_receive, if__ns); i++;
         GET_COUNTER();          /* rx bytes */
-        CONFD_SET_UINT64(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT64(&tval[i], if_bytes, counter); i++;
         GET_COUNTER();          /* rx packets */
-        CONFD_SET_UINT64(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT64(&tval[i], if_packets, counter); i++;
         GET_COUNTER();          /* rx errs */
-        CONFD_SET_UINT32(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT32(&tval[i], if_errors, counter); i++;
         GET_COUNTER();          /* rx drop  */
-        CONFD_SET_UINT32(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT32(&tval[i], if_dropped, counter); i++;
         /* skip remaining rx counters */
         GET_COUNTER(); GET_COUNTER(); GET_COUNTER(); GET_COUNTER();
-
-        CONFD_SET_XMLTAG(&val[i], if_transmit, if__ns); i++;
+        CONFD_SET_TAG_XMLEND(&tval[i], if_receive, if__ns); i++;
+	
+        CONFD_SET_TAG_XMLBEGIN(&tval[i], if_transmit, if__ns); i++;
         GET_COUNTER();          /* tx bytes */
-        CONFD_SET_UINT64(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT64(&tval[i], if_bytes, counter); i++;
         GET_COUNTER();          /* tx packets */
-        CONFD_SET_UINT64(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT64(&tval[i], if_packets, counter); i++;
         GET_COUNTER();          /* tx errs */
-        CONFD_SET_UINT32(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT32(&tval[i], if_errors, counter); i++;
         GET_COUNTER();          /* tx drop  */
-        CONFD_SET_UINT32(&val[i], counter); i++;
+        CONFD_SET_TAG_UINT32(&tval[i], if_dropped, counter); i++;
         GET_COUNTER();          /* skip */
         GET_COUNTER();          /* tx colls */
-        CONFD_SET_UINT32(&val[i], counter); i++;
-
-        ret = cdb_set_object(sock, val, i,
-                             "/interfaces/interface{%s}/status", ifname);
-        if (ret == CONFD_ERR && confd_errno == CONFD_ERR_BADPATH)
-            /* assume interface doesn't exist in config */
-            ret = CONFD_OK;
+        CONFD_SET_TAG_UINT32(&tval[i], if_collisions, counter); i++;
+	CONFD_SET_TAG_XMLEND(&tval[i], if_transmit, if__ns); i++;
+	CONFD_SET_TAG_XMLEND(&tval[i], if_status, if__ns); i++;
+	CONFD_SET_TAG_XMLEND(&tval[i], if_interface, if__ns); i++;
     }
+    ret = cdb_set_values(sock, tval, i, "/interfaces");
+
     fclose(proc);

We modify the YANG model slightly so that we can with cdb_set_values() always display all interfaces available with stats.

$ diff -u $CONFD_DIR/examples.confd/cdb_oper/ifstatus-orig/if.yang if.yang
--- $CONFD_DIR/examples.confd/cdb_oper/ifstatus-orig/if.yang	2015-07-17 04:56:53.000000000 +0200
+++ if.yang	2015-08-30 12:05:40.729898471 +0200
@@ -14,24 +14,13 @@
     list interface {
       key name;
       max-elements 1024;
+      config false;
+      tailf:cdb-oper;
+	      
       leaf name {
         type string;
       }
-      list address {
-        key name;
-        max-elements 64;
-        leaf name {
-          type inet:ipv4-address;
-        }
-        leaf prefix-length {
-          type int32;
-          mandatory true;
-        }
-      }
       container status {
-        config false;
-        tailf:cdb-oper;
-
         container receive {
           leaf bytes {
             type uint64;

Quick demo:

Terminal window 1

$pwd
$CONFD_DIR/examples.confd/cdb_oper/ifstatus
$ make clean all start
...
### In another terminal window, start the CLI (make cli)
### Starting the ifstatus agent
./ifstatus

Terminal window 2:

$ make cli
...
admin connected from 127.0.0.1 using console on tailf
# show interfaces 
NAME  BYTES      PACKETS  ERRORS  DROPPED  BYTES     PACKETS  ERRORS  DROPPED  COLLISIONS  
-------------------------------------------------------------------------------------------
eth0  272695599  268326   0       0        15561251  125588   0       0        0           
lo    92784568   157955   0       0        92784568  157955   0       0        0

Hi,

I have tried similar sample using the ietf-interfaces.yang. But, could not get more than one entry created using cdb_set_values. Only the last entry (MaxEntriesOper) is getting created.

for (i = 0, idx = 1; idx <= MaxEntriesOper; idx++)
{
CONFD_SET_TAG_XMLBEGIN(&tval[i], if_interface, if__ns); i++;
snprintf(buf, BUFSIZ, “%s%d_%d”, “ifname_”, AppId, idx);
/* if_name /
CONFD_SET_TAG_STR(&tval[i], if_name, buf); i++;
/
*/
CONFD_SET_TAG_XMLBEGIN(&tval[i], if_statistics, if__ns); i++;

            CONFD_SET_TAG_UINT64(&tval[i], if_in_octets, 10); i++;
            CONFD_SET_TAG_UINT64(&tval[i], if_in_unicast_pkts, 20); i++;
            CONFD_SET_TAG_UINT64(&tval[i], if_in_broadcast_pkts, 30); i++;
            CONFD_SET_TAG_UINT64(&tval[i], if_in_multicast_pkts, 40); i++;
            CONFD_SET_TAG_UINT32(&tval[i], if_in_discards, 50); i++;
            CONFD_SET_TAG_UINT32(&tval[i], if_in_errors,60); i++;
            CONFD_SET_TAG_UINT32(&tval[i], if_in_unknown_protos, 70); i++;

            CONFD_SET_TAG_UINT64(&tval[i], if_out_octets, 110); i++;
            CONFD_SET_TAG_UINT64(&tval[i], if_out_unicast_pkts, 120); i++;
            CONFD_SET_TAG_UINT64(&tval[i], if_out_broadcast_pkts, 130); i++;
            CONFD_SET_TAG_UINT64(&tval[i], if_out_multicast_pkts, 140); i++;
            CONFD_SET_TAG_UINT32(&tval[i], if_out_discards, 150); i++;
            CONFD_SET_TAG_UINT32(&tval[i], if_out_errors, 160); i++;

            CONFD_SET_TAG_XMLEND(&tval[i], if_statistics, if__ns); i++;
            CONFD_SET_TAG_XMLEND(&tval[i], if_interface, if__ns); i++;

     }

    ret = cdb_set_values(dataSock, tval, i, "/interfaces-state");

Can you please correct me if am wrong?

Thanks,
Babu

Hi Babu,

The problem lies with your use (or reuse) of the buf character array. You are using the same buf array pointer for all of your CONFD_SET_TAG_STR( ) calls which points to the last if_name that you have set it to at the end of your for loop. You are essentially setting one instance of your interface list MaxEntriesOper number of times with the same values in your cdb_set_values( ) call which resulted into only a single row in your interface list.

You should rewrite your code as follows:

char buf[MaxEntriesOper][BUFSIZ];

for (idx=0; idx < MaxEntriesOper; idx++) {
    snprintf(buf[idx], BUFSIZ, "%s%d_%d", "ifname_", AppId, idx);
    /* list interface */
    CONFD_SET_TAG_XMLBEGIN(&tval[i], if_interface, if__ns); i++;

    /* if_name key */
    CONFD_SET_TAG_STR(&tval[i], if_name, buf[idx]); i++;
...

Regards,

Wai

Hi Wai,

Assumed that the macro CONFD_SET_TAG_STR copies the buffer.
Thanks for pointing out.

Best Regards,
Babu

what are the values for if_interface, if_name; what should we initialize them with?

The YANG “list interface”, here referred to as If_interface taken from the header file generated by the ConfD compiler from the ietf-interface.yang model is the list name, so it has no value.
The YANG “leaf name” of type string, here if_name, is the list key and typically is the name of an interface in your system.

See the ConfD example set examples.confd/linuxcfg example that implements the ietf-interface.yang model and more for a typical Linux system.´
A demo of the linuxcfg example using a Docker container:

How do we set values if the leaf elements are union and other complex structuers

Reading the confd_types(3) man page seems like a good place to start regarding “union and other complex structures”. The man pages are appendixes to the ConfD User Guide too.