ConfD User Community

No change when using tailf:display-hint

Using Confd 7.2.1. I want to display binary value as hex instead of base64, tried adding tailf:display-hint to model:

    leaf value {
      type binary;
      description
        "TLV value.";
      tailf:display-hint '1x:';
   }

Using CONFD_SET_TAG_BINARY() to set the value, but in CLI it still displays as base64.

Thanks,
Barry

Does something like this work?

  leaf my-binary {
    type binary;
    tailf:display-hint '1x:';
  }

$ confd_cmd -c "mset /my-binary $(echo 'hello' | base64)"
$ confd_cli -u admin -C
# show running-config my-binary
my-binary 68:65:6C:6C:6F:A

Best regards

Thanks yes this works (config variable stored in cdb). I’d like to use display-hint for an operational (ro) value returned from a client process. I use confd_data_reply_next_object_tag_value_arrays() with the binary leaf value set using CONFD_SET_TAG_BINARY().
Thanks,
Barry

I can’t recreate your issue as you have described it so far. I added a leaf of type binary to the examples.confd/intro/5-c_stats/arpe.yang YANG model

$ diff -u arpe.yang.orig arpe.yang
--- arpe.yang.orig	2020-04-02 11:46:05.000000000 +0200
+++ arpe.yang	2020-04-02 11:21:58.000000000 +0200
@@ -54,6 +54,12 @@
         type boolean;
         mandatory true;
       }
+      leaf value {
+        type binary;
+        description
+          "TLV value.";
+        tailf:display-hint '1x:';
+      }
     }
   }
 }

Then added a get_next_value() callback to examples.confd/intro/5-c_stats/arpstat.c

$ diff -u arpstat.c.orig arpstat.c
--- arpstat.c.orig	2020-04-02 11:41:51.000000000 +0200
+++ arpstat.c	2020-04-02 11:39:22.000000000 +0200
@@ -307,7 +307,62 @@
     confd_data_reply_value(tctx, &v);
     return CONFD_OK;
 }
+static int navals;
+static int max_nobjs = 100; // Chunk size hardcoded here to simplify
 
+static int get_next_object_tag(struct confd_trans_ctx *tctx,
+			     confd_hkeypath_t *keypath, long next)
+{
+  int i;
+  confd_tag_value_t *tv;
+  struct confd_tag_next_object *tobj;
+  struct arpdata *dp = tctx->t_opaque;
+  struct aentry *curr;
+
+  if (next == -1) {  /* first call */
+    if (need_arp(dp)) {
+      if (run_arp(dp) == CONFD_ERR)
+	return CONFD_ERR;
+    }
+    curr = dp->arp_entries;
+  } else {
+    curr = (struct aentry *)next;
+  }
+  if (curr == NULL) {
+    confd_data_reply_next_key(tctx, NULL, -1, -1);
+    return CONFD_OK;
+  }
+
+  tobj = malloc(sizeof(struct confd_tag_next_object) * (max_nobjs + 1));
+  tv = (confd_tag_value_t *) malloc(sizeof(confd_tag_value_t) * max_nobjs * navals);
+  
+  for (i = 0; curr != NULL && i < max_nobjs; curr = curr->next, i++) { /* Collect max_nobjs or as many as there is rows in the table / list entries */
+    tobj[i].tv = &tv[i * navals];
+    
+    CONFD_SET_TAG_IPV4(&(tobj[i].tv[0]), arpe_ip, curr->ip4);
+    CONFD_SET_TAG_STR(&(tobj[i].tv[1]), arpe_ifname, curr->iface);
+    if (curr->hwaddr == NULL) {
+      CONFD_SET_TAG_NOEXISTS(&(tobj[i].tv[2]), arpe_hwaddr);
+    } else {
+      CONFD_SET_TAG_STR(&(tobj[i].tv[2]), arpe_hwaddr, curr->hwaddr);
+    }
+    CONFD_SET_TAG_BOOL(&(tobj[i].tv[3]), arpe_permanent, curr->perm);
+    CONFD_SET_TAG_BOOL(&(tobj[i].tv[4]), arpe_published, curr->pub);
+    unsigned const char *ucs = (unsigned const char *)"hello world";
+    CONFD_SET_TAG_BINARY(&(tobj[i].tv[5]), arpe_value, ucs, strlen((char *)ucs));
+    tobj[i].n = navals;
+    tobj[i].next = (long)curr->next;
+  }
+  
+  if (curr == NULL) {
+    tobj[i++].tv = NULL;
+  }
+  
+  confd_data_reply_next_object_tag_value_arrays(tctx, tobj, i, 0); /* Reply with an array of object arrays. I.e. the whole table / list */
+  free(tv);
+  free(tobj);
+  return CONFD_OK;
+}
 int main(int argc, char *argv[])
@@ -316,6 +371,7 @@
     int debuglevel = CONFD_TRACE;
     struct confd_trans_cbs trans;
     struct confd_data_cbs data;
+    struct confd_cs_node *object;
 
     memset(&trans, 0, sizeof (struct confd_trans_cbs));
     trans.init = s_init;
@@ -324,6 +380,7 @@
     memset(&data, 0, sizeof (struct confd_data_cbs));
     data.get_elem = get_elem;
     data.get_next = get_next;
+    data.get_next_object = get_next_object_tag;
     strcpy(data.callpoint, arpe__callpointid_arpe);
 
     /* initialize confd library */
@@ -336,7 +393,8 @@
     if (confd_load_schemas((struct sockaddr*)&addr,
                            sizeof (struct sockaddr_in)) != CONFD_OK)
         confd_fatal("Failed to load schemas from confd\n");
-
+    object = confd_cs_node_cd(NULL, "/arpe:arpentries/arpe");
+    navals = confd_max_object_size(object);
     if ((dctx = confd_init_daemon("arpe_daemon")) == NULL)
         confd_fatal("Failed to initialize confdlib\n");

Quick demo:

$ pwd
/Users/tailf/confd-7.3.1/examples.confd/intro/5-c_stats
$ make clean all start
$ confd_cli -u admin -C
# show arpentries
IP               IFNAME  HWADDR             PERMANENT  PUBLISHED  VALUE                             
----------------------------------------------------------------------------------------------------
172.20.10.1      en0     d2:c5:f3:60:cf:64  false      false      68:65:6C:6C:6F:20:77:6F:72:6C:64  
224.0.0.251      en0     1:0:5e:0:0:fb      true       false      68:65:6C:6C:6F:20:77:6F:72:6C:64  
239.255.255.250  en0     1:0:5e:7f:ff:fa    true       false      68:65:6C:6C:6F:20:77:6F:72:6C:64  
# exit
$ confd_load -o -Fp -p /arpentries
<config xmlns="http://tail-f.com/ns/config/1.0">
  <arpentries xmlns="http://tail-f.com/ns/example/arpe">
    <arpe>
      <ip>172.20.10.1</ip>
      <ifname>en0</ifname>
      <hwaddr>d2:c5:f3:60:cf:64</hwaddr>
      <permanent>false</permanent>
      <published>false</published>
      <value>aGVsbG8gd29ybGQ=</value>
    </arpe>
    <arpe>
      <ip>224.0.0.251</ip>
      <ifname>en0</ifname>
      <hwaddr>1:0:5e:0:0:fb</hwaddr>
      <permanent>true</permanent>
      <published>false</published>
      <value>aGVsbG8gd29ybGQ=</value>
    </arpe>
    <arpe>
      <ip>239.255.255.250</ip>
      <ifname>en0</ifname>
      <hwaddr>1:0:5e:7f:ff:fa</hwaddr>
      <permanent>true</permanent>
      <published>false</published>
      <value>aGVsbG8gd29ybGQ=</value>
    </arpe>
  </arpentries>
</config>
$ confd_load -o -Fc -p /arpentries
arpentries arpe 172.20.10.1 en0
 hwaddr    d2:c5:f3:60:cf:64
 permanent false
 published false
 value     68:65:6C:6C:6F:20:77:6F:72:6C:64
!
arpentries arpe 224.0.0.251 en0
 hwaddr    1:0:5e:0:0:fb
 permanent true
 published false
 value     68:65:6C:6C:6F:20:77:6F:72:6C:64
!
arpentries arpe 239.255.255.250 en0
 hwaddr    1:0:5e:7f:ff:fa
 permanent true
 published false
 value     68:65:6C:6C:6F:20:77:6F:72:6C:64
!

Hi, it is working for me now. The leaf where I added display-hint was in a submodule where I had also added:

  import tailf-common {
    prefix tailf;
 }

But it did not work until I also added the above import to the containing module.

Thanks for the help!
Barry

Sorry, one more question, should it be possible to use an annotation too add display-hint? I tried adding via tailf:annotate to the leaf (which is part of OpenConfig model) but it didn’t seem to have any effect.
Thanks,
Barry

If you are annotating something in a grouping, instead of using the schema-based tailf:annotate you need to use the YANG model-based tailf:annotate-module/statement.

  tailf:annotate-module my-yang-module-name {
    tailf:annotate-statement grouping[name='my-grouping-name'] {
      tailf:annotate-statement leaf[name='value'] {
        tailf:display-hint '1x:'
      }
    }
  }

Thanks, tailf:annotate-module works! However I’m annotating an openconfig submodule (grouping isis-lsdb-generic-tlv in openconfig-isis-lsp) and it appears I have to import tailf-common in the containing openconfig-network-instance module in order for it to work.
Thanks,
Barry

Just

Just import the tailf-common YANG module from the annotation module.

module my-yang-module-ann {
  namespace "urn:dummy";
  prefix dummy;

  import tailf-common {
    prefix tailf;
  }

  tailf:annotate-module my-yang-module-name {
    tailf:annotate-statement grouping[name='my-grouping-name'] {
      tailf:annotate-statement leaf[name='value'] {
        tailf:display-hint '1x:'
      }
    }
  }
}

Yes I had the import in the annotation module (which is annotating the grouping in a submodule), but it doesn’t work. I have to also import in the parent module. I’ll see if I can come up with a simplified example, but my code looks like this:

module openconfig-isis-annotations {
  yang-version 1.1;
  namespace "urn:dummy";
  prefix dummy;

  import openconfig-isis {
    prefix oc-isis;
  }

  import tailf-common {
     prefix tailf;
  }

  tailf:annotate-module "openconfig-isis-lsp" {
     tailf:annotate-statement "grouping[name='isis-lsdb-generic-tlv']" {
       tailf:annotate-statement "leaf[name='value']" {
         tailf:display-hint '1x:';
       }
     }
  }
}

I have to also add the import tailf-common to the containing module or it does not work:

module openconfig-network-instance {
  yang-version "1";
  namespace "http://openconfig.net/yang/network-instance";
  prefix "oc-netinst";
  ....
  import openconfig-isis { prefix "oc-isis"; }

  import tailf-common { prefix tailf; }
  ....
}

Thanks,
Barry

You get a namespace issue with the annotation when you use an imported annotated grouping from another namespace.
For the imported annotated grouping, you get to annotate it in all places where you use it. In your example:

tailf:annotate "/oc-netinst:node-that-use-the-oc-isis-grouping/oc-netinst:value" {
    tailf:display-hint '1x:';
}