A Set Hook is a function that is invoked within the transaction when an object is modified. When the netconfd-pro server has been configured to provide a candidate configuration, Set Hook code will be invoked when changes are done to the <candidate> configuration. If --target=running then the Set Hook will be invoked at the start of the transaction on the running datastore. This callback will be invoked before EDIT1 or EDIT2 callbacks for the same object.


NOTE: Set Hook callbacks will not be invoke during explicit validate command and in case of the following netconfd-pro parameters are set to TRUE:  --sil-validate-candidate or --sil-skip-load.


Set Hook can be invoked during the Load; However, it is not allowed to add new edits. So, callback are treated as Transaction Hook style callbacks and can perform only validation tasks and cannot edit datastore. Any add_edit() during Load will be ignored, it is not an error just skip the call and go back with NO_ERR status.

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

 

/* Typedef of the agt_hook_cb callback as follows */
typedef status_t 
    (*agt_hook_cb_t) (ses_cb_t *scb,
                      rpc_msg_t *msg,
                      agt_cfg_transaction_t *txcb,
                      op_editop_t editop,
                      val_value_t  *newval,
                      val_value_t  *curval);


Set Hook database edit callback function is hooked into the server with the agt_cb_hook_register function, described below. The agt_cb_hook_register function is used to declare the callback as a specific style callback. The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database and before running configurations are loaded.


extern status_t
    agt_cb_hook_register (const xmlChar *defpath,
                          agt_hook_fmt_t format,
                          agt_hook_type_t type,
                          agt_cb_hook_t cbfn);


defpath
Absolute path expression string indicating which node the callback function is for.
format
different hook formats dictates specific hook functionality
type
different hook types dictates hook invocation point
cbfn
address of callback function to use


The format parameter is important when you want to specify how Set Hook callbacks will be invoked. There are two options available for this parameter:

  • AGT_HOOKFMT_NODE: Set the type of the callback to this value if You want to make sure that the callback will be invoked only when you modify the node that registered the callback but not any of its children.

  • AGT_HOOKFMT_SUBTREE: If the format is set to this value, the callback will be invoked if you edit children as well.

The type parameter is important when you want to set the type of callback. There are two options available for this parameter, either Set Hook or Transaction Hook callback:

  • AGT_HOOK_TYPE_SETHOOK: Set the type of the callback to this value if You want to register Set Hook callback.

  • AGT_HOOK_TYPE_TRANSACTION: Set the type of the callback to this value if You want to register Transaction Hook callback.


The following example code illustrates how hook-based callbacks can be cleaned up.The callbacks cleanup is getting done during module Cleanup Phase.


extern void
    agt_cb_hook_unregister (const xmlChar *defpath);


NOTE: It is possible to register different callbacks for the same object, for instance, there can be Set Hook and Transaction Hook callbacks registered for the same object, as well as there can be EDIT or GET2 callbacks for the same object. However, in order to cleanup callbacks, unregister them, you need to run different cleanup functions. The EDIT and GET2 callbacks have their own unregister functions and hook-based callbacks have their own unregister function. 


EXAMPLES


Let us go through simple examples that will illustrate how to utilize the Set Hook callbacks for the specific purposes. First we need a YANG module. Consider this simplified, but functional, example. 


  container interfaces {
     list interface {
         key "name";

         leaf name {
             type string;
         }
         leaf speed {
             type enumeration {
                 enum 10m;
                 enum 100m;
                 enum auto;
             }
         }
         leaf hook-node {
             type uint32;
         }
         container state {
             leaf admin-state {
                 type boolean;
             }
         }
     }

    leaf status {
      type string;
    }
  }

  ...

  leaf trigger {
    type string;
  }


Add new node


Assume we registered Set Hook callback for the “trigger” leaf element. Thus, whenever the node /if:triggeris edited, the callback function will be called and additional specific data can be updated or populated with desired values.

In this example, we will generate an extra “interface” list entry with key value equal to “vlan1” when a “trigger” node is getting edited with as pecific value equal to “add-edit”. The callback function may look as follows:


/******************************************************************** 
* FUNCTION  sethook_callback_edit
* 
* Callback function for server object handler 
* Used to provide a callback for a specific named object
*
* Set Hook: 
*   trigger: edit /if:trigger
*   add_edit: 
*       add nodes: populate 1 list entry with name=vlan1
*
*           path: /if:interfaces/if:interface[if:name=vlan1]
*
*********************************************************************/ 
static status_t 
    sethook_callback_edit (ses_cb_t *scb, 
                          rpc_msg_t *msg, 
                          agt_cfg_transaction_t *txcb, 
                          op_editop_t editop, 
                          val_value_t  *newval, 
                          val_value_t  *curval) 
{ 
    log_debug("\nEnter SET Hook callback"); 

    status_t res = NO_ERR; 
    val_value_t *errorval = (curval) ? curval : newval; 

    const xmlChar *defpath = 
        (const xmlChar *)"/if:interfaces"; 

     switch (editop) { 
    case OP_EDITOP_LOAD: 
        break; 
    case OP_EDITOP_MERGE: 
    case OP_EDITOP_REPLACE: 
    case OP_EDITOP_CREATE: 
        /* add a new edit if the "/if:trigger" value is "add-edit" */ 
        if (newval && 
            !xml_strcmp(VAL_STR(newval), (const xmlChar *)"add-edit")) { 

            /* find object template of the desired node */ 
            obj_template_t *targobj = 
                      ncx_match_any_object_ex(EXAMPLE_MODNAME, 
                                              (const xmlChar *)"interfaces", 
                                              FALSE, 
                                              NCX_MATCH_FIRST, 
                                              FALSE, 
                                              &res); 
            if (!targobj) { 
                return ERR_NCX_INVALID_VALUE; 
            }

           /* create edit_value container value */ 
            val_value_t *editval = val_new_value(); 
            if (editval == NULL) { 
                return ERR_INTERNAL_MEM; 
            } 
            val_init_from_template(editval, targobj); 

            /* malloce and construct list value
    */ 
            uint32 key = 11; 
            val_value_t *list_value = create_list_entry(VAL_OBJ(editval), 
                                                        key, 
                                                        &res); 
            if (!list_value) { 
                val_free_value(editval); 
                return res; 
            } 

            /* add a new list entry */ 
            res = val_child_add(list_value, editval); 
            if (res != NO_ERR) {
                val_free_value(list_value);
            } else {
                /* add a new edit, MERGE on defpath with 1 new list entry */ 
                res = agt_val_add_edit(scb, 
                                       msg, 
                                       txcb, 
                                       defpath, 
                                       editval, 
                                       OP_EDITOP_MERGE); 
            } 

            /* clean up the editval */ 
            val_free_value(editval);
        } 
        break; 
    case OP_EDITOP_DELETE: 
        break; 
    default: 
        res = SET_ERROR(ERR_INTERNAL_VAL); 
    } 

    if (res != NO_ERR) { 
        agt_record_error(scb, 
                         &msg->mhdr, 
                         NCX_LAYER_CONTENT, 
                         res, 
                         NULL, 
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE, 
                         errorval, 
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE, 
                         errorval); 
    }

    return res; 

}  /* sethook_callback_edit */ 


As a result, whenever some north bound agent edit the /if:trigger with a specific value, the callback is invoked and additionally creates a new interface /if:interfaces/if:interface[if:name=value]. The function that is responsible for the list entry creation may look as follows:


/********************************************************************
* FUNCTION create_list_entry
* 
* Make a list entry based on the key
*
* INPUTS:
*   parentobj == parent object template to use
*   keyname == value of the key
*   res = return status
*
* RETURNS:
*    val_value_t of listval entry if no error
*    else NULL
*
*********************************************************************/
static val_value_t *
    create_list_entry(obj_template_t *parentobj,
                      const xmlChar *keyname,
                      status_t *res)
{
    /* get the obj template for the list obj */
    obj_template_t *list_obj =
        obj_find_child(parentobj,
                       EXAMPLE_MODNAME,
                       (const xmlChar *)"interface");
    if (!list_obj) {
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }

    /* add all the /container/list nodes */
    val_value_t *list_value = val_new_value();
    if (!list_value) {
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }
    val_init_from_template(list_value, list_obj);

    /* make key leaf entry */
    val_value_t *child =
        agt_make_leaf(list_obj,
                      (const xmlChar *)"name",
                       keyname,
                       res);
    if (!child) {
        val_free_value(list_value);
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }
    *res = val_child_add(child, list_value);
    if (*res != NO_ERR) {
        val_free_value(list_value);
        return NULL;
    }

    /* make some extra leaf entries */
    child =
        agt_make_uint_leaf(list_obj,
                           (const xmlChar *)"hook-node",
                           (uint32)1000,
                           res);
    if (!child) {
        val_free_value(list_value);
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }
    *res = val_child_add(child, list_value);
    if (*res != NO_ERR) {
        val_free_value(child);
        val_free_value(list_value);
        return NULL;
    }

    /* generate the internal index Q chain */
    *res = val_gen_index_chain(list_obj, list_value);
    if (*res != NO_ERR) {
        log_error("\nError: could not generate index chain (%s)",
            get_error_string(*res));
        val_free_value(list_value);
        return NULL;
    }

    return list_value;

} /* create_list_entry */


NOTE: When you are constructing a list node make sure that a key node is getting created first, and then all other children are getting added. Also, make sure that you use val_gen_index_chain API function after you construct the list, so it will be normalized and all required fields will be set accordingly.


To ensure that the data added by add_edit was successfully generated an application can retrieve running configuration and device state information. The server should reply with:


     <rpc-reply message-id="101"
                xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
        <interfaces xmlns="http://yumaworks.com/ns/interfaces"> 
          </interface> 
             <name>vlan1</name> 
    <observed-speed>1000</observed-speed> 

          </interface>
        </interfaces> 

    <trigger>add-edit</trigger> 
     </rpc-reply>


Delete node


As specified earlier, Set Hook was registered for the “trigger” leaf element. Thus, whenever the node /if:trigger is edited, the callback function will be called and additional specific data can be updated or populated with desired values. In this example, we will delete just added /if:interface container with all its children when a “trigger” node is getting deleted. The callback function may look as follows:


/******************************************************************** 
* FUNCTION  sethook_callback_edit
* 
* Callback function for server object handler 
* Used to provide a callback for a specific named object
*
* Set Hook: 
*   trigger: delete /trigger
*   add_edit: 
*       delete nodes: delete the whole container
*
*           path: /if:interfaces
*
*********************************************************************/ 
static status_t 
    sethook_callback_edit (ses_cb_t *scb, 
                          rpc_msg_t *msg, 
                          agt_cfg_transaction_t *txcb, 
                          op_editop_t editop, 
                          val_value_t  *newval, 
                          val_value_t  *curval) 
{ 
    log_debug("\nEnter SET Hook callback"); 

    status_t res = NO_ERR; 
    val_value_t *errorval = (curval) ? curval : newval; 

    const xmlChar *defpath = 
        (const xmlChar *)"/if:interfaces"; 

     switch (editop) { 
    case OP_EDITOP_LOAD: 
        break; 
    case OP_EDITOP_MERGE: 
    case OP_EDITOP_REPLACE: 
    case OP_EDITOP_CREATE: 
        break; 
    case OP_EDITOP_DELETE: 
        /* delete the interfaces container if the curval of 'trigger' node is
         * "delete-all"
         */ 
   if (curval && 
            !xml_strcmp(VAL_STR(curval), (const xmlChar *)"delete-all")) { 
 
            res = agt_val_add_edit(scb, 
                                   msg, 
                                   txcb, 
                                   defpath, 
                                   NULL, // editval 
                                   editop); 
        } 
        break; 
    default: 
        res = SET_ERROR(ERR_INTERNAL_VAL); 
    } 

    if (res != NO_ERR) { 
        agt_record_error(scb, 
                         &msg->mhdr, 
                         NCX_LAYER_CONTENT, 
                         res, 
                         NULL, 
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE, 
                         errorval, 
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE, 
                         errorval); 
    }

    return res; 

}  /* sethook_callback_edit */ 


As a result, the server will delete the whole /if:interfaces container if “trigger” value is set to 'delete-all' and some north bound agent attempts to DELETE /trigger node. To ensure that the data deleted by add_edit was successfully deleted an application can retrieve running configuration and device state information. The server should reply with:


     <rpc-reply message-id="101"
                xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <data></data>
     </rpc-reply>


Populate new node with extra nodes


Consider the same data model as for the previous examples; however, now we will register callback for the top level /if:interfaces container element. In this example, we will populate an extra leafs when the container node is created. Register a Set Hook to the /if:interfaces container.


    /* Register an object specific Set Hook callback */ 
    res = agt_cb_hook_register((const xmlChar*)"/if:interface", 
                               AGT_HOOKFMT_NODE, 
                               AGT_HOOK_TYPE_SETHOOK, 
                               sethook_callback_edit);


Now, when a user wants to create the container, the callback function will be called and container can be updated with desired additional values.

NOTE: Callback format should be AGT_HOOKFMT_NODE in order to invoke this callback only for a new container edit. If the format is AGT_HOOKFMT_SUBTREE, the callback will be invoked if you edit children as well. In this case newval will not represent container value and cannot not be used to construct a new updated edit value.


The callback function may look as follows:


/******************************************************************** 
* FUNCTION sethook_callback_edit
* 
* Callback function for server object handler 
* Used to provide a callback for a specific named object 
* 
* Set Hook: 
*   trigger: create /interfaces
*       create container and populate extra nodes 
* 
*   add_edit effect: populate extra nodes within the container 
*        when /interfaces is created 
* 
*********************************************************************/ 
static status_t 
    sethook_callback_edit (ses_cb_t *scb, 
                          rpc_msg_t *msg, 
                          agt_cfg_transaction_t *txcb, 
                          op_editop_t editop, 
                          val_value_t  *newval, 
                          val_value_t  *curval) 
{ 
    log_debug("\nEnter SET Hook callback"); 

    status_t res = NO_ERR; 
    val_value_t *errorval = (curval) ? curval : newval; 

    /* defpath specified target root */ 
    const xmlChar *defpath = 
        (const xmlChar *)"/if:interfaces"; 
 
    switch (editop) { 
    case OP_EDITOP_LOAD: 
        break; 
    case OP_EDITOP_MERGE: 
    case OP_EDITOP_REPLACE: 
   break;
    case OP_EDITOP_CREATE: 
        /* populate a new container with several leafs */ 
        if (newval) { 

            /* use newval value to write a new edits */ 
            val_value_t *editval = 
                val_clone_config_data(newval, &res); 
            if (editval == NULL) { 
                return ERR_INTERNAL_MEM; 
            } 

            /* malloced and construct some leaf values */ 
            val_value_t *child = 
                agt_make_leaf(VAL_OBJ(editval), 
                              (const xmlChar *)"status", 
                              (const xmlChar *)"extra-leaf", 
                               &res); 
            if (!child) { 
                val_free_value(editval); 
                return ERR_NCX_INVALID_VALUE; 
            } 
            res = val_child_add(child, editval); 
            if (res != NO_ERR) {
                val_free_value(child);
            } else {
                /* add a new edit on defpath and populate new entries */ 
                res = agt_val_add_edit(scb, 
                                       msg, 
                                       txcb, 
                                       defpath, 
                                       editval, 
                                       OP_EDITOP_MERGE); 
            } 

            /* clean up the editval */ 
            val_free_value(editval); 
        } 
        break; 
    case OP_EDITOP_DELETE: 

        break; 
    default: 
        res = SET_ERROR(ERR_INTERNAL_VAL); 
    } 

    if (res != NO_ERR) { 
        agt_record_error(scb, 
                         &msg->mhdr, 
                         NCX_LAYER_CONTENT, 
                         res, 
                         NULL, 
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE, 
                         errorval, 
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE, 
                         errorval); 
    }

    return res; 

}  /* sethook_callback_edit */ 


As a result, whenever some north bound agent creates an /interfaces container the callback is invoked and additionally creates new leafs within this container.


Modify node value 1


Consider the same data model as for the previous examples; however, now we will register callback for the top level /if:trigger leaf. In this example, we will modify its value on the fly when the leaf node is created.


    /* Register an object specific Set Hook callback */ 
    res = agt_cb_hook_register((const xmlChar*)"/if:trigger", 
                               AGT_HOOKFMT_NODE, 
                               AGT_HOOK_TYPE_SETHOOK, 
                               sethook_callback_edit);


The callback function may look as follows:


/********************************************************************
* FUNCTION  sethook_callback_edit
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set Hook:
*   trigger: edit /if:trigger
*     Effect:  change 'trigger' node value to a custom value
*
*
*********************************************************************/
static status_t
    sethook_callback_edit (ses_cb_t *scb,
                          rpc_msg_t *msg,
                          agt_cfg_transaction_t *txcb,
                          op_editop_t editop,
                          val_value_t  *newval,
                          val_value_t  *curval)
{
    log_debug("\nEnter SET Hook callback");

    status_t res = NO_ERR;
    val_value_t *errorval = (curval) ? curval : newval;

    const xmlChar *defpath =
        (const xmlChar *)"/if:trigger";

     switch (editop) {
    case OP_EDITOP_LOAD:
        break;
    case OP_EDITOP_MERGE:
    case OP_EDITOP_REPLACE:
    case OP_EDITOP_CREATE:
        /* modify "/if:trigger" value on the fly */
        if (newval &&
            !xml_strcmp(VAL_NAME(newval), (const xmlChar *)"trigger")) {

            /* This is just an example string value.
             * Generate you own custom value for a new node here.
             */
            const xmlChar *newval = (const xmlChar *)"delete-all";

            log_info("\n changing %s to %s \n",
                VAL_STR(newval), newval);

            /* change the value */
            m__free(VAL_STR(newval));
            VAL_STR(newval) = xml_strdup(newval);

            /* in the case we do NOT have to call add_edit() */
            (void)defpath;
            (void)txcb;
            // res = agt_val_add_edit(scb,
            //                        msg,
            //                        txcb,
            //                        defpath,
            //                        editval,
            //                        OP_EDITOP_MERGE);
        }
        break;
    case OP_EDITOP_DELETE:
        break;
    default:
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }

    if (res != NO_ERR) {
        agt_record_error(scb,
                         &msg->mhdr,
                         NCX_LAYER_CONTENT,
                         res,
                         NULL,
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
                         errorval,
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
                         errorval);
    }

    return res;

}  /* sethook_callback_edit */


As a result, whenever some north bound agent creates the /if:trigger node, the callback will modify it's value to a custom one.

NOTE: If your desired node is a leaf within a container or list you may want to register the Set Hook callback on the container or list and then find your desired value you want to modify or create and then perform desired actions. Otherwise, if you register Set Hook callback on the leaf node within container the server will not invoke Set Hook callbacks when you create the container.

Also, another solution is to make sure that the container or list already pre-exist and only after that you may edit desired leaf node in the container and apply desired actions.



Modify node value 2


Consider the same data model as for the previous examples; however, now  we will register callback for the /if:interfaces/if:interface list node. In this  example, we will modify /if:interfaces/if:interface/if:state container node children values. The trigger for the edit will be creation of a new interface with specific name value.


    /* Register an object specific Set Hook callback */ 
    res = agt_cb_hook_register((const xmlChar*)"/if:interfaces/if:interface", 
                               AGT_HOOKFMT_NODE, 
                               AGT_HOOK_TYPE_SETHOOK, 
                               sethook_callback_edit);


Assume we have the running datastore configured as follows:


  data {
    ietf-interfaces:interfaces {
      interface  vlan2 {
        name vlan2
        type ianaift:l2vlan
      }
      interface  vlan1 {
        name vlan1
        type ianaift:l2vlan
        state {
          admin-state false 
        }
      }
    }
  }
}


And now we want to modify "state" container value when a new interface with specific value is created. In our example, a new interface value will be "LAG" that will trigger a "state" container change.

The callback function may look as follows:


/********************************************************************
* FUNCTION sethook_callback_edit
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming rpc_msg_t in progress
*   txcb == transaction control block in progress
*   editop == edit operation enumeration for the node being edited
*   newval == container object holding the proposed changes to
*           apply to the current config, depending on
*           the editop value. Will not be NULL.
*   curval == current container values from the <running>
*           or <candidate> configuration, if any. Could be NULL
*           for create and other operations.
*
* RETURNS:
*    status
*********************************************************************/
static status_t
    sethook_callback_edit (ses_cb_t *scb,
                          rpc_msg_t *msg,
                          agt_cfg_transaction_t *txcb,
                          op_editop_t editop,
                          val_value_t  *newval,
                          val_value_t  *curval)
{
    status_t res = NO_ERR;
    val_value_t *errorval = (curval) ? curval : newval;
    val_value_t *child_val = NULL;

    /* defpath specified target */
    const xmlChar *defpath =
        (const xmlChar *)"/if:interfaces/if:interface[name='vlan1']/if:state";

    if (newval) {
        child_val =
            val_find_child(newval,
                           (const xmlChar *)"ietf-interfaces",
                           (const xmlChar *)"name");
        if (child_val) {
            log_debug("\n>>>>>>>>>> "
                      " callback for %s editop, name=%s",
                       op_editop_name(editop), VAL_STR(child_val));
        }
    }

    switch (editop) {
    case OP_EDITOP_LOAD:
        break;
    case OP_EDITOP_MERGE:
    case OP_EDITOP_REPLACE:
    case OP_EDITOP_CREATE:
        if (newval && !val_is_default(newval) && child_val &&
            !xml_strcmp(VAL_STR(child_val), (const xmlChar *)"LAG"))  {

            val_value_t *testval =
                agt_val_get_data(txcb->cfg_id,
(const xmlChar *)"/if:interfaces/if:interface[name='vlan1']/if:state",
                                 &res);

            val_value_t *editval = NULL;
            if (testval) {
                /* clone just found value in order to write a new edits */
                editval = val_clone_config_data(testval, &res);
                if (!editval) {
                    return ERR_NCX_INVALID_VALUE;
                } else {
                    /* Only when edit target is container or list.
                     * Need to clean any found children in order to
                     * make a new edit and a new value.
                     * Otherwise if the target children leaf exist in this
                     * container the val_add_child() will fail.
                     */
                    val_delete_children(editval);
                }
            } else {
                return ERR_NCX_INVALID_VALUE;
            }

            /* add any other children to target container, can add any amount
             * of children here.
             * In this example we add one leaf "admin-state" with value "true"
             */
            obj_template_t *chobj =
                obj_find_child(testval->obj,
                               (const xmlChar *)"ietf-interfaces",
                               (const xmlChar *)"admin-state");
            if (chobj) {
                val_value_t *child =
                    val_make_simval_obj(chobj, (const xmlChar *)"true", &res);
                if (child) {
                    val_add_child(child, editval);
                } else {
                    val_free_value(editval);
                    return ERR_NCX_INVALID_VALUE;
                }
            } else {
                val_free_value(editval);
                return ERR_NCX_INVALID_VALUE;
            }

            /* add a new edit on defpath and populate new entry */
            if (res == NO_ERR) {

/* defpath specified target */
// const xmlChar *defpath =
//     (const xmlChar *)"/if:interfaces/if:interface[name='vlan1']/if:state";

                res = agt_val_add_edit(scb,
                                       msg,
                                       txcb,
                                       defpath,
                                       editval,
                                       OP_EDITOP_MERGE);
            }
            /* clean up the editval */
            val_free_value(editval);
        }
        break;
    case OP_EDITOP_DELETE:

        break;
    default:
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }

    if (res != NO_ERR) {
        agt_record_error(scb,
                         &msg->mhdr,
                         NCX_LAYER_CONTENT,
                         res,
                         NULL,
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
                         errorval,
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
                         errorval);
    }

    return res;

}  /* sethook_callback_edit */


As a result, whenever some north bound agent creates a new interface with name "LAG", the callback will modify the interface with name "vlan1" and update the "admin-state" value. The final running datastore result may look as follows:


  data {
    ietf-interfaces:interfaces {
      interface  vlan2 {
        name vlan2
        type ianaift:l2vlan
      }
      interface  vlan1 {
        name vlan1
        type ianaift:l2vlan
        state {
          admin-state true 
        }
      }
      interface  LAG {
        name LAG
        type ianaift:ieee8023adLag
      }
    }
  }
}




Hooks callback interaction with EDIT2 callbacks (target=candidate)


In case there are multiple Set-Hook  callbacks registered and the callbacks add several extra edits to the  transaction with help of add_edit() API the server will have the  following interaction with added edits and normal edits that are coming  from PDU. Assume we register Set-Hook callback, Set-Order-Hook,  Transaction-Hook and EDIT2 callback for the same list object, for example "ietf-interface" module and "/if:interfaces/if:interface" list object. So any time the "interface" list is getting edited the server invokes Set-Hook callback and adds up an extra "interface" in addition to the regular edit. The following callback invocation order is expected in this case:


Edit on candidate datastore:

  1. Transaction Start
  2. Set Order Hook for "/if:interface[name=vlan1]"
  3. Set-Hook for "/if:interface[name=vlan1]" to add "/if:interface[name=vlan2]"
  4. Set Order Hook for "/if:interface[name=vlan2]"
  5. SIL Callback (Validate Phase) for "/if:interface[name=vlan1]"
  6. SIL Callback (Validate Phase) for "/if:interface[name=vlan2]"
  7. Transaction Complete

Edit on running datastore (after <commit>):

  1. Transaction Start
  2. SIL Callback (Validate Phase) for "/if:interface[name=vlan1]"
  3. SIL Callback (Validate Phase) for "/if:interface[name=vlan2]"
  4. Validate Complete
  5. SIL Callback (Apply Phase) for "/if:interface[name=vlan1]"
  6. SIL Callback (Apply Phase) for "/if:interface[name=vlan2]"
  7. Apply Complete
  8. SIL Callback (Commit Phase) for "/if:interface[name=vlan1]"
  9. SIL Callback (Commit Phase) for "/if:interface[name=vlan2]"
  10. Transaction Hook for "/if:interface[name=vlan1]"
  11. Transaction Hook for "/if:interface[name=vlan2]"
  12. Commit/Rollback Complete
  13. Transaction Complete

Hooks callback interaction with EDIT2 callbacks (target=running)


Assume  the same scenario but the default target in this case is set to  running. The callbacks invocation order is expected to be:

  1. Transaction Start
  2. Set Order Hook for "/if:interface[name=vlan1]"
  3. Set-Hook for "/if:interface[name=vlan1]" to add "/if:interface[name=vlan2]"
  4. Set Order Hook for "/if:interface[name=vlan2]"
  5. SIL Callback (Validate Phase) for "/if:interface[name=vlan1]"
  6. SIL Callback (Validate Phase) for "/if:interface[name=vlan2]"
  7. SIL Callback (Apply Phase) for "/if:interface[name=vlan1]"
  8. SIL Callback (Apply Phase) for "/if:interface[name=vlan2]"
  9. SIL Callback (Commit Phase) for "/if:interface[name=vlan1]"
  10. SIL Callback (Commit Phase) for "/if:interface[name=vlan2]"
  11. Transaction Hook for "/if:interface[name=vlan1]"
  12. Transaction Hook for "/if:interface[name=vlan2]"
  13. Transaction Complete