ConfD User Community

How to convert a ConfD tag value array to XML?

It is a small task to convert a tag value array to XML. Below is a quick implementation and demo where we extend the existing ConfD intro/1-2-3-start-query-model example by first getting the subscription configuration updates using a confd_get_modifications() call and then print the tag value array result in in pretty XML format.

Example code:

Let’s first do a diff to see what we added to the 1-2-3 intro example:

$ pwd
/Users/conny/tailf/confd-5.4.2/examples.confd/intro/1-2-3-start-query-model-mod 
$ diff ../1-2-3-start-query-model/dhcpd_conf.c dhcpd_conf.c 

In the 1-2-3 intro example main() function we want to first get the schema information at initialization:

162a230,232
>     if (confd_load_schemas((struct sockaddr*)&addr, 
>                             sizeof (struct sockaddr_in)) != CONFD_OK)
>         confd_fatal("Failed to load schemas from confd\n");

When we receive a subscription modification notification we want to first get the modifications and then we call a helper function to print the tag value array in pretty XML format:

220a291,306
> 	        confd_tag_value_t *values = NULL;
>                 int nvalues, i;
>                 if ((status = cdb_get_modifications(subsock, 
>          					    sub_points[0], 
> 						    CDB_GET_MODS_INCLUDE_LISTS, 
> 					            &values, 
> 						    &nvalues, 
> 						    "/dhcp")) == CONFD_OK) {
> 		    printf("Modifications in pretty printed XML:\n");
> 		    print_modifications_xml_pretty(values, nvalues, NULL, 0);
> 		    for (i = 0; i < nvalues; i++)
> 		        confd_free_value(CONFD_GET_TAG_VALUE(&values[i]));
>                     free(values); 
> 		} else {
> 		    fprintf(stderr, "failed to get modifications %d\n", status);
>                 }

Our helper function that print a tag value array in pretty XML format:

148a149,215
> static void print_modifications_xml_pretty(confd_tag_value_t *val, int nvals,
>                                 struct confd_cs_node *start_node,
>                                 int start_indent)
> {
>     int i, 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;
>             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; //merge
>         }
>         switch (CONFD_GET_TAG_VALUE(&val[i])->type) {
>         case C_XMLBEGIN:
>         case C_XMLBEGINDEL:
> 	    printf("%*s<%s> %s\n", indent, "",
>                confd_hash2str(CONFD_GET_TAG_TAG(&val[i])), tmp);
> 	    indent += 2;
>             break;
> 	case C_XMLEND:
> 	    indent -= 2;
>             printf("%*s</%s> %s\n", indent, "",
>                confd_hash2str(CONFD_GET_TAG_TAG(&val[i])), tmp);
> 	    break;
>         default:
> 	    printf("%*s<%s>%s</%s>\n", indent, "",
> 		   confd_hash2str(CONFD_GET_TAG_TAG(&val[i])), tmp, confd_hash2str(CONFD_GET_TAG_TAG(&val[i])));
>             break;
>         }
>     }
> }

Quick demo:

In terminal window1 we set some new configuration using the netconf-console by issuing a NETCONF <edit-config> request:

$ netconf-console -i

* Enter a NETCONF operation, end with an empty line
 <edit-config>
    <target>
      <running/>
    </target>
    <config>
      <dhcp xmlns="http://tail-f.com/ns/example/dhcpd"
            xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
        <defaultLeaseTime nc:operation="merge">
          PT30M
        </defaultLeaseTime>
        <SubNets>
          <subNet nc:operation="create">
            <net>192.168.128.0</net>
            <mask>255.255.255.0</mask>
            <range>
              <lowAddr>192.168.128.60</lowAddr>
              <hiAddr>192.168.128.89</hiAddr>
            </range>
          </subNet>
        </SubNets>
      </dhcp>
    </config>
  </edit-config>

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="2">
  <ok/>
</rpc-reply>

In terminal window2 the ConfD tag value array result from the cdb_get_modifications() is printed in XML pretty:

...
Modifications in pretty printed XML:
<defaultLeaseTime>PT30M0S.0</defaultLeaseTime>
<SubNets> 
  <subNet> 
    <net>192.168.128.0</net>
    <mask>255.255.255.0</mask>
    <maxLeaseTime>PT7200S.0</maxLeaseTime>
    <range> 
      <dynamicBootP>false</dynamicBootP>
      <lowAddr>192.168.128.60</lowAddr>
      <hiAddr>192.168.128.89</hiAddr>
    </range> 
  </subNet> 
</SubNets> 
…

If you for example want to add NETCONF xml tags like for example nc:operation, xmlns or xmlns:nc , just add them to the print function.

Hello,
I am also looking something very similar. So can you please continue your previous answer and tell us how can we add nc:operation=“merge” vs “create” vs “replace” vs “modify”?
Is there a way to know which operation has happened on element/object using cdb_get_modification.

All I see is xmlbegin/xmlbegindel and end but no modify/merge/replace.
Please clarify what to do if the output we want is similar to the applied input so that we can apply this output to some remote cdb as edit-config.
Thanks,
-gopal

Hi Gopal,

Note that it is the new configuration vs the previous old configuration, i.e. the configuration changes that his presented in the tag value array returned by the cdb_get_modifications() API call. Not the NETCONF operations that was used to set the resulting new configuration.

Hence, in your case when you create a XML representation from the tag value array returned by cdb_get_modifications(), it would be sufficient if you use the “merge” operation for the changed/new configuration indicated by C_XMLBEGIN / C_XMLEND and the “remove” operation (or “delete” operation if you want an error message if you are deleting something that does not exist) on elements as indicated C_NOEXISTS and C_XMLBEGINDEL / C_XMLEND.

Hi Cohult,

I am using cdb_diff_iterate API for getting the difference of old and new data .
How i can convert the same to xml format ? Because for cdb_diff_iterate we will get value in confd_value_t struct not in confd_tag_value_t

How we can modify above print_modifications_xml_pretty to handle confd_value_t struct

Thanks,
Raja

Perhaps something like:

struct confd_cs_node *cs_node = confd_cs_node_cd(NULL, kp);
u_int32_t tag = cs_node->tag 

…and there’s your tag to go with the value.

thanks Cohult, we will try this and let you know update.

-Raja