AVAILABLE SINCE 19.10-9 RELEASE


This section describes All In One (AIO) GET2 mechanism that allows to call a single GET2 callback for the whole Subtree. This article focuses on how to use AIO callback with JSON and XML buffers as a return value to the server.


AIO GET2 callbacks follow exactly the same template as regular GET2 callbacks, they use the same registration API and are getting invoked the same way for SIL and SIL-SA as regular GET2 callbacks. The main difference is that there will not be any callback invocations for descendant children and there will not be any callbacks auto generated at all for any of AIO node children.


The yangdump auto-generation code does not auto generate callbacks for children of the top All In One parent. The callbacks will be generated only for the top nodes that have the extension specified. The extension definition looks as follows:


  extension sil-aio-get2 {
    description
      "Used within a data definition statement to define
       the GET2 retrieval mechanism.
       This extension affects the descendant data nodes.

       This extension can be used in a container or list
       to force the server to treat that data subtree as
       a terminal node for GET2.

       The entire subtree would be expected in one retrieval
       in one callback invocation.

       The entire subtree can be specified in the JSON
       or XML buffer that will be used for return values.
       The server will parse and handle the buffer and process
       retrieval based on the provide JSON or XML encoded buffer.

       The 'parmstr' argument can specify the encoding that will
       be used in the callback. Available options are:
         - xml
         - json

       If not specified default val value retrieval mechanism
       will be assumed.
      ";
    argument parmstr;
  }


For more information refer to the following article: 

How do I use All In One (AIO) GET2 callback?


For more example on how to use AIO callbacks with different extension parameters refer to the following articles:

How do I use All In One (AIO) GET2 callback with val value?



Let us go through simple examples that will illustrate how to utilize the AIO callbacks for the specific purposes. First we need a YANG module. Consider this simplified, but functional, example. You can download this YANG module from attachments and run make_sil_dir_pro to auto-generate stub SIL code for this module.

Note, the AIO callback is not part of the auto-generated code by default and you will have to add an extension to the desired node that you want to become an AIO callback node. Otherwise, the auto generated code will generate regular GET2 callback for that node(s).


In this example we are going to use "sil-aio-get2" extension with 'xml' and "json" parameters, meaning we are going to use XML and JSON buffers as a return values in the AIO callback.


SIL code Generation command that was used in this example. Please refer to the attached SIL code.


> make_sil_dir_pro aio-test --sil-get2 sil-edit2


As an example, to illustrate how the "sil-aio-get2" extension can be used in a YANG module refer to the following YANG module. It can be found in the attachments in this article. Note, that you have to import "yumaworks-extensions" YANG module:


module aio-test {
  namespace "http://yumaworks.com/ns/aio-test";
  prefix "aiotest";

  import yumaworks-extensions { prefix ywx; }

  revision 2020-02-20 {
    description "Initial Version";
  }


  /* All in One callback within config true and GET2 reg callbacks */
  container get3-config-cont {
    presence "Presence container";

    list config-list {
      key name;
      leaf name { type string; }

      list nonconfig-list {               // Regular GET2 CB
        config false;
        key name;
        leaf name { type string; }

        list nonconfig-list2 {            // All in One XML GET2 CB
          ywx:sil-aio-get2 "xml";

          key name;
          leaf name { type string; }

          list nonconfig-list3 {          // No callback
            key name;
            leaf name { type string; }
            leaf not-keyname { type string; }

            container int-con {           // No callback
              leaf con-leaf { type int32; }
            }
          }
        }
      }
    }
  }

  /* All in One callback within config true NP-container */
  container get3-state-cont {          // All in One get2 CB JSON
    ywx:sil-aio-get2 "json";
    config false;

    leaf D { type int8; }

    choice thing-choice {               // No callback
      case A {
        leaf A1 { type string; }
        leaf A2 { type string; }
      }
      case B {
        leaf B3 { type string; }
        leaf B2 { type string; }
      }
      case C {                          // active case
        leaf C1 {type string; }
        leaf C2 {type string; }
      }
    }
  }
}



AIO XML Callback Examples



In the YANG module example above we define top level configuration parent "get3-config-cont/config-list" and next regular GET2 child "nonconfig-list" and then the AIO GET2 list child "nonconfig-list2" with XML parameter in the extension.


In this case the server will invoke GET2 callbacks only for "nonconfig-list" and then for AIO "nonconfig-list2" list and will not invoke any descendant level callbacks. The server will expect that the AIO "nonconfig-list2" callback will supply well formed XML buffer to the GET2 control block.


NOTE: if the callback target is a list, the XML buffer must provide parent node in the buffer. If the list is a top level node use the following wrapper:


 <config xmlns=\"http://netconfcentral.org/ns/yuma-ncx\"></config>


There cannot be multiple siblings in XML, it will make XML malformed and the server will not be able to parse this buffer and will return an error. As a result the server will skip over this callback nodes in the output and will not generate any output for them.


The AIO GET2 callback may look as follow for the "/get3-config-cont/config-list/nonconfig-list/nonconfig-list2" list.

Refer to the attachment SIL code for complete code and more details:


/********************************************************************
* FUNCTION aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get
*
* Get database object callback for list nonconfig-list2
* Path: list /get3-config-cont/config-list/nonconfig-list/nonconfig-list2
* Fill in 'get2cb' response fields
*
* sil-aio-get2 extension enabled for this object;
* No callbacks for children will be called
*
* XML encoded buffer is expected
*
* INPUTS:
*     see ncx/getcb.h for details (getcb_fn2_t)
*
* RETURNS:
*     error status
********************************************************************/
static status_t
    aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get (
        ses_cb_t *scb,
        xml_msg_hdr_t *msg,
        getcb_get2_t *get2cb)
{
    if (LOGDEBUG) {
        log_debug("\nEnter aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get");
    }

    (void)scb;
    (void)msg;

    status_t res = NO_ERR;

    /* make XML buffer that will be used instead of return val value
     * NOTE if the callback target is a list, the XML buffer must provide
     * parent node in the buffer. There cannot be multiple siblings
     * in XML, it will make XML malformed.
     *
     * If the list is a top level node use the following wrapper:
     * <config xmlns=\"http://netconfcentral.org/ns/yuma-ncx\"></config>
     */
    const xmlChar *xml_buffer = (const xmlChar *)
        "<nonconfig-list xmlns=\"http://yumaworks.com/ns/aio-test\">"
        "<nonconfig-list2>"
        "<name>name1</name><nonconfig-list3>"
        "<name>name1</name></nonconfig-list3><nonconfig-list3>"
        "<name>name2</name></nonconfig-list3></nonconfig-list2>"
        "<nonconfig-list2>"
        "<name>name2</name><nonconfig-list3>"
        "<name>name1</name></nonconfig-list3><nonconfig-list3>"
        "<name>name2</name></nonconfig-list3></nonconfig-list2>"
        "<nonconfig-list2>"
        "<name>name3</name><nonconfig-list3>"
        "<name>name1</name></nonconfig-list3><nonconfig-list3>"
        "<name>name2</name></nonconfig-list3></nonconfig-list2>"
        "</nonconfig-list>";

    /* Add created buffer to return get2cb */
    res = getcb_add_return_aio_buff(get2cb, xml_buffer);

    return res;

} /* aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get */

AIO JSON Callback Examples


A JSON example will be similar to the above XML example, it will use the same API, however instead of using XML buffer the callback will provide JSON buffer to the server.


Assume the same YANG module example above we define top level AIO JSON container "get3-state-cont".


In this case the server will invoke AIO JSON callback for "get3-state-cont" container and will not invoke any descendant level callbacks. The server will expect that the AIO JSON "get3-state-cont" callback will supply the GET2 control block with well formed JSON buffer that will be used as a return value for the callback.


The AIO JSON GET2 callback may look as follow for the "get3-state-cont" container.

Refer to the attachment SIL code for complete code and more details:


/********************************************************************
* FUNCTION aio_test_get3_state_cont_get
*
* Get database object callback for container get3-state-cont
* Path: container /get3-state-cont
* Fill in 'get2cb' response fields
*
* sil-aio-get2 extension enabled for this object;
* No callbacks for children will be called
*
* JSON encoded buffer is expected
*
* INPUTS:
*     see ncx/getcb.h for details (getcb_fn2_t)
*
* RETURNS:
*     error status
********************************************************************/
static status_t
    aio_test_get3_state_cont_get (ses_cb_t *scb,
                                  xml_msg_hdr_t *msg,
                                  getcb_get2_t *get2cb)
{
    if (LOGDEBUG) {
        log_debug("\nEnter aio_test_get3_state_cont_get");
    }

    (void)scb;
    (void)msg;

    status_t res = NO_ERR;

    /* make JSON buffer that will be used instead of return val value */
    const xmlChar *json_buffer = (const xmlChar *)
        "{\"aio-test:get3-state-cont\":{\"D\":42,\"C2\":\"someleaf\"}}";;

    /* Add created buffer to return get2cb */
    res = getcb_add_return_aio_buff(get2cb, json_buffer);

    return res;

} /* aio_test_get3_state_cont_get */


As a result, whenever some north bound agent trying to retrieve the list or container that we have AIO callbacks registered, the callback is invoked and a client gets all the values that AIO callbacks returns.


To ensure that the buffer returned by AIO callback was successfully consumed by the server an application can retrieve operational datastore with help of the following operation as an example. Assume, we already created parent configuration nodes.


  <get>
    <filter type="subtree">
      <get3-config-cont xmlns="http://yumaworks.com/ns/aio-test"/>
    </filter>
  </get>

The server should reply with:


<data>
  <get3-config-cont xmlns="http://yumaworks.com/ns/aio-test">
   <config-list>
    <name>list1</name>
    <nonconfig-list>
     <name>name1</name>
     <nonconfig-list2>
      <name>name1</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
     <nonconfig-list2>
      <name>name2</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
     <nonconfig-list2>
      <name>name3</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
    </nonconfig-list>
    <nonconfig-list>
     <name>name2</name>
     <nonconfig-list2>
      <name>name1</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
     <nonconfig-list2>
      <name>name2</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
     <nonconfig-list2>
      <name>name3</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
    </nonconfig-list>
    <nonconfig-list>
     <name>name3</name>
     <nonconfig-list2>
      <name>name1</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
     <nonconfig-list2>
      <name>name2</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
     <nonconfig-list2>
      <name>name3</name>
      <nonconfig-list3>
       <name>name1</name>
      </nonconfig-list3>
      <nonconfig-list3>
       <name>name2</name>
      </nonconfig-list3>
     </nonconfig-list2>
    </nonconfig-list>
   </config-list>
  </get3-config-cont>
 </data>


Or, for example, if we want to retrieve a node that was generated by AIO JSON callback, a client may send the following <get> request:


  <get>
    <filter type="subtree">
      <get3-state-cont xmlns="http://yumaworks.com/ns/aio-test"/>
    </filter>
  </get>


The server should reply with:


 <data>
  <get3-state-cont xmlns="http://yumaworks.com/ns/aio-test">
   <D>42</D>
   <C2>someleaf</C2>
  </get3-state-cont>
 </data>