The Dynamic Default Hook function is the user/system callback that can be used to set up dynamic default value to the non-default leafy nodes during load or edit-config operations. It is invoked when the server checks if the node has any defaults and if there is no any YANG defined defaults the server can update the node with custom “system” default value.


Note: The Dynamic Default Hook callback is NOT to overwrite a YANG default value, it is only for the nodes that do not have a default statement defined. The YANG default values can only be changed statically with help of deviations.

The Dynamic Default Hook callback should be used to dynamically change a value of the node.

This callback will NOT make the value of the leaf or leaf-list to be default value as if YANG default statement sets it.


If the callback fails and return an error the transaction will be terminated and the server will generate an error. However, the ERR_NCX_SKIPPED or ERR_NCX_NO_INSTANCE status will cause the serve to skip over the callback node and do not terminate the transaction.


The Dynamic Default Hook callbacks can be invoked during the following operations:

  • Load startup configuration: the server will try to add defaults for all the nodes in the startup configuration and in addition will try to invoke Dynamic Default Hook callbacks to set up custom dynamic default nodes if any;
  • EDIT configurations: the server will try to add defaults for all the nodes in the <edit-config> and in addition will try to invoke Dynamic Default Hook callbacks to set up custom dynamic default nodes if any.


The Dynamic Default Hook callbacks specifications and limitations:

  • Allowed only for leaf or leaf-list nodes;
  • Not allowed for keys nodes;
  • Not allowed for RPC/ACTION input nodes;
  • Not invoked on deletion of any nodes;
  • No support for yangdump-pro auto-generated code;
  • No support for SIL-SA;
  • Only one dynamic default value is allowed for leaf-list nodes.


The following function template definition is used for Dynamic Default Hook callback functions:


/********************************************************************
* Typedef of the ncx_def_hook_cbfn_t callback
*
* The Dynamic Default Hook function is the user/system callback
* that is invoked before the server checks if the node has
* any defaults and if there is not any YANG defined defaults
* the server can update the node with custom "system" default
* value.
*
* Run an instrumentation-defined function
* for val set default event
*
* INPUTS:
*   parentval == parent val_value node to use
*   obj == object template to use
*
* OUTPUTS
*   *buff == malloced buffer that represents a new default value
*
* RETURNS:
*   status: ERR_NCX_SKIPPED or ERR_NCX_NO_INSTANCE will cause the server
*           to skip over this node. Otherwise the server will report an
*           error and ternimate the transaction
*
*********************************************************************/
typedef status_t
    (*ncx_def_hook_cbfn_t) (struct val_value_t_ *parentval,
                            struct obj_template_t_ *obj,
                            xmlChar **buff);



The Dynamic Default Hook callback function is hooked into the server with the agt_cb_def_hook_register function, described below:


/********************************************************************
* FUNCTION agt_cb_def_hook_register
*
* Register an object specific Dynamic Default callback function
* to enable custom default value setup for a specified node
*
* INPUTS:
*   defpath == Xpath with default (or no) prefixes
*              defining the object that will get the callbacks
*   cbfn    == address of callback function to use for
*              Dynamic Default callbacks
*
* RETURNS:
*   status
*********************************************************************/
extern status_t
    agt_cb_def_hook_register (const xmlChar *defpath,
                              ncx_def_hook_cbfn_t cbfn);


The following example code illustrates how the callbacks can be cleaned up. The callbacks clean up is getting done during module Cleanup Phase.


/********************************************************************
* FUNCTION agt_cb_def_hook_unregister
*
* Unregister Dynamic Default callback functions for a specific object
*
* INPUTS:
*   defpath == definition XPath location
*
* RETURNS:
*   none
*********************************************************************/
extern void
    agt_cb_def_hook_unregister (const xmlChar *defpath);




Dynamic Default Hook Callback Example


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


NoteThe Dynamic Default Hook callback is not part of the auto-generated code and you will have to add a registration, clean up and callback function to a desired node that you want to become a Dynamic node.


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


> make_sil_dir_pro dyn-def-hook --sil-get2 sil-edit2


Refer to the following YANG module. It can be found in the attachments in this article:


module dyn-def-hook {
  namespace "http://netconfcentral.org/ns/dyn-def-hook";
  prefix "hkdef";

  revision "2020-05-29" {
    description "Initial revision.";
  }

  typedef state {
    type enumeration {
      enum disabled;
      enum enabled;
      enum non-def-enum;
    }
  }

  typedef admin-state {
    type state;
  }

  typedef type-enum {
    type enumeration {
      enum vlan;
      enum ethernet;
      enum lag;
      enum channel;
    }
  }

  container interface-cont {

    leaf admin-status-dyn {               // Dynamic Default Hook
      type admin-state;
    }

    leaf admin-status-def {
      type admin-state;
      default enabled;
    }

    list interface {
      key name;

      leaf name {
        type string;
      }

      leaf type {                         // Dynamic Default Hook
        mandatory true;
        type type-enum;
      }

      leaf admin-status-def {
        type admin-state;
        default enabled;
      }

      leaf admin-status-dyn {             // Dynamic Default Hook
        type admin-state;
      }

      leaf-list ll-leaf {                 // Dynamic Default Hook
        type string;
      }
    }
  }
}


In this example we are going to register callbacks for multiple leafy nodes in order to make them dynamic nodes. Specifically, we are going to register callback for "type" leaf, so any time a new "interface" is getting loaded or created the server will be able to dynamically update the "type" leaf in that interface based on the "name" value. Refer to the attached YANG module and its SIL code for a complete example.


As an example callback function refer to the following snippet. In this example, if the "name" of the "interface", during the create or load operation, is "vlan" or "lag" then the callback will set the dynamic value to return to the server. The server will use this value to set the "type" value in the same transaction dynamically.


/********************************************************************
* FUNCTION default_hook_callback
*
* The Dynamic Default Hook function is the user/system callback
* that is invoked before the server checks if the node has
* any defaults and if there is not any YANG defined defaults
* the server can update the node with custom "system" default
* value.
*
* Run an instrumentation-defined function
* for val set default event
*
* INPUTS:
*   parentval == parent val_value node to use
*   obj == object template to use
*
* OUTPUTS
*   *buff == malloced buffer that represents a new default value
*
* RETURNS:
*   status: ERR_NCX_SKIPPED or ERR_NCX_NO_INSTANCE will cause the server
*           to skip over this node. Otherwise the server will report an
*           error and ternimate the transaction
*
*********************************************************************/
static status_t
    default_hook_callback (val_value_t *parentval,
                           obj_template_t *obj,
                           xmlChar **buff)
{
    status_t res = NO_ERR;

    if (LOGDEBUG) {
        log_debug("\n\nEnter Dynamic Default Hook callback for '%s'",
                  obj_get_name(obj));
    }

    /* Check the key value of the parent list to setup the
     * Dynamic leaf accordingly
     */
    val_value_t *child_val = NULL;
    if (parentval) {
        child_val =
            val_find_child(parentval,
                           y_dyn_def_hook_M_dyn_def_hook,
                           (const xmlChar *)"name");
    }

    const xmlChar *defval = NULL;

    /* make sure that this is really the right object that you want to
     * modify. Check if the name of the object is correct and that there
     * is related list key node available.
     */
    if (child_val && !xml_strcmp(obj_get_name(obj), (const xmlChar *)"type")) {
        if (LOGDEBUG) {
            log_debug("\nCurrent list key value:'%s'",
                      VAL_STR(child_val));
        }

        /* 'xml_strncmp' is a String compare for xmlChar for at
         * most 'maxlen' xmlChars, where maxlen is max number of
         * xmlChars to compare
         */
        if (!xml_strncmp(VAL_STR(child_val), (const xmlChar *)"vlan", 4)) {

            /* check if the interface key value is starting with 'vlan' */
            defval = (const xmlChar *)"vlan";
        } else if (!xml_strncmp(VAL_STR(child_val), (const xmlChar *)"lag", 3)) {

            /* check if the interface key value is starting with 'lag' */
            defval = (const xmlChar *)"lag";
        } else {

            /* otherwise just skip over this node */
            return ERR_NCX_NO_INSTANCE;
        }
    } else {

        /* Otherwise, apply some other default handing for all other cases */
        if (typ_is_string(obj_get_basetype(obj))) {
            defval = (const xmlChar *)"NON-DEF-STR";
        } else {
            defval = (const xmlChar *)"non-def-enum";
        }
    }

    if (LOGDEBUG) {
        log_debug("\nDynamic Default value set to:'%s'",
                  defval);
    }

    /* Malloc a buffer that will be used to set the default value
     * NO need to free it here, the server will take care of this buff
     */
    *buff = xml_strdup(defval);
    if (*buff == NULL) {
        return ERR_NCX_SKIPPED;
    }

    return res;

}  /* default_hook_callback */


As a result, for example, when we create a new list "interface" entry with "name" that has value "vlan", the server will update "type" node dynamically. The <edit-config> may look as follows:


<edit-config>
  <target>
    <running/>
  </target>
    <config>
    <interface-cont xmlns="http://netconfcentral.org/ns/dyn-def-hook">
      <interface>
        <name>vlan1</name>
      </interface>
    </interface-cont>
  </config>
</edit-config>


In the server you should notice logging information about the callback invocation and setup. The server log may look as follows:


***** start validate phase on running for session 3, transaction 3 *****

....

Enter Dynamic Default Hook callback for 'type'
Current list key value:'vlan1'
Dynamic Default value set to:'vlan'
add dynamic default leaf 'dyn-def-hook:type'

....


To ensure that the dynamic node added by Dynamic Default Hook callback was successfully generated an application can retrieve datastore with help of the following operation as an example.


  <get-config>
    <source>
      <running/>
    </source>
  </get-config>


The server should reply with the following RPC reply:


<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <data>
  <interface-cont xmlns="http://netconfcentral.org/ns/dyn-def-hook">
   <admin-status-dyn>non-def-enum</admin-status-dyn>
   <interface>
    <name>vlan1</name>
    <type>vlan</type>
    <admin-status-dyn>non-def-enum</admin-status-dyn>
    <ll-leaf>NON-DEF-STR</ll-leaf>
   </interface>
  </interface-cont>
 </data>
</rpc-reply>


Note that the "type" node is now set with out any explicit specification of this node in the <edit-config>. Also, in addition to the "type" node in the callback above generated few other values for different nodes as well. The full example can be found in the attachment SIL code with registration, clean up and callback function examples.