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.

For more derails, refer to the following article:

How do I use a Set Hook callback?



Example 1: Delete a Node 


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. 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 Set Hook callback is not part of the auto-generated code and you will need to modify this stub SIL code and add registration for your Set Hook callback and the callback functions.


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


> make_sil_dir_pro sethook-example --sil-get2 --sil-edit2


module sethook-example {
  namespace "http://netconfcentral.org/ns/sethook-example";
  prefix "sethook-ex";

  revision 2020-03-20 {
    description "Initial revision.";
  }

  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;
  }
}


As specified in the attached SIL code, the Set Hook is registered for the “trigger” leaf node. Thus, whenever the node /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 existing /interface container with all its children when a “trigger” node is getting deleted. The callback function may look as follows:


/********************************************************************
* FUNCTION  sethook_callback
*
* 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: /interfaces
*
*********************************************************************/
static status_t
    sethook_callback (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 *)"/sethook-ex: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 */



As a result, whenever some north bound agent delete the /trigger with a specific value, the callback is invoked and additionally deletes interfaces container  /interfaces. For more details on how this callback is registered and cleaned up refer to the attached SIL code.


Edit-config:


  <edit-config>
    <target>
      <candidate/>
    </target>
    <default-operation>merge</default-operation>
    <test-option>set</test-option>
    <config>
      <trigger
        xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
        nc:operation="delete"
        xmlns="http://netconfcentral.org/ns/sethook-example"/>
    </config>
  </edit-config>


The following output may be seen during the operation described above. The three phase edit-config processing may look as follows:


***** start validate phase on candidate for session 3, transaction 2179 *****

Start container in validate commit for yuma-netconf:config
Start leaf in validate commit for sethook-example:trigger
agt_acm: check write <trigger> allowed for user 'admin'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: sethook-example:trigger start
Start undo for 'sethook-example:trigger'
Starting setup of transaction callbacks
Checking for validate user callback for delete edit on sethook-example:trigger
Found validate user callback for delete:sethook-example:trigger
agt_cfg: use undo Q for 'trigger'
agt_cfg: add undo nested_silcall for 'sethook-example:trigger'
undo for delete op on sethook-example:trigger
silcall for sethook-example:trigger
Finished setup of transaction callbacks

Start invoking Set Hook callback for delete on sethook-example:trigger
Enter SET Hook callback
Start adding Set Hook edit for delete on /sethook-ex:interfaces
agt_val: retrieving '/sethook-ex:interfaces' from candidate datastore
eval_expr
xpath value result for '/sethook-ex:interfaces'
  typ: nodeset = 
   node HDR sethook-ex:interfaces

Start container in validate commit for sethook-example:interfaces
agt_acm: check write <interfaces> allowed for user 'admin'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: sethook-example:interfaces start
Start undo for 'sethook-example:interfaces'
Start list in validate commit for sethook-example:interface
agt_acm: check write <interface> allowed for user 'admin'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: sethook-example:interface start
Start child-undo for 'sethook-example:interface'
Start leaf in validate commit for sethook-example:name
agt_acm: check write <name> allowed for user 'admin'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: sethook-example:name start
Extending edit2 parent SIL callback for child 'name'
Start child-undo for 'sethook-example:name'
Start leaf in validate commit for sethook-example:hook-node
agt_acm: check write <hook-node> allowed for user 'admin'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: sethook-example:hook-node start
Extending edit2 parent SIL callback for child 'hook-node'
Start child-undo for 'sethook-example:hook-node'
Start container in validate commit for sethook-example:state
agt_acm: check write <state> allowed for user 'admin'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: sethook-example:state start
agt_val: Skipping default subtree 'state'
Finish invoking Set Hook callback on sethook-example:trigger


Start invoking validate SIL callback for delete on sethook-example:trigger
 (trigger) Enter EDIT-2 callback
 ------PHASE:validate;EDITOP:delete

Finished invoking user callback on sethook-example:trigger

***** start apply phase on candidate for session 3, transaction 2179 *****

apply_write_val: trigger start
apply_write_val[3]: apply delete on /sethook-ex:trigger
apply_write_val: interfaces start
apply_write_val[3]: apply delete on /sethook-ex:interfaces
agt_val: Marking 'interfaces' node as uncommitted delete
add_default: checking 'sethook-example:interfaces'
add_node_default: checking 'sethook-example:interface'
start delete_dead_nodes2 for transaction
delete_dead_nodes2: undo for sethook-example:trigger
start run_external_xpath_tests for 'sethook-example:trigger'
start delete_extern_nodes for 'trigger'
delete_dead_nodes2: undo for sethook-example:interfaces
start run_external_xpath_tests for 'sethook-example:interfaces'
start delete_extern_nodes for 'interfaces'
start run_external_xpath_tests for 'sethook-example:interface'
start delete_extern_nodes for 'interface'
start run_external_xpath_tests for 'sethook-example:name'
start run_external_xpath_tests for 'sethook-example:speed'
start run_external_xpath_tests for 'sethook-example:hook-node'
start run_external_xpath_tests for 'sethook-example:state'
start delete_extern_nodes for 'state'
start run_external_xpath_tests for 'sethook-example:admin-state'
start run_external_xpath_tests for 'sethook-example:status'

***** start commit phase on candidate for session 3, transaction 2179 *****

Start full commit of transaction 2179: 2 edits on candidate config
edit-transaction 2179: on session 3 by admin@127.0.0.1
  time: 2020-04-12T22:32:39Z
  message-id: 5
  trace-id: --
  datastore: candidate
  operation: delete
  target: /sethook-ex:trigger
  comment: none

edit-transaction 2179: on session 3 by admin@127.0.0.1
  time: 2020-04-12T22:32:39Z
  message-id: 5
  trace-id: --
  datastore: candidate
  operation: delete
  target: /sethook-ex:interfaces
  comment: none

Complete commit OK of transaction 2179 on candidate database


To ensure that the data deleted by add_edit was successfully deleted an application can retrieve running configurations. When the server receives get-config request, the server should reply with:


 <data></data>




Example 2: Delete a List Node


Let us go through another example where the Set Hook invocation will trigger deletion of a list node. 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 Set Hook callback is not part of the auto-generated code and you will need to modify this stub SIL code and add registration for your Set Hook callback and the callback functions.


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


> make_sil_dir_pro sethook-example --sil-get2 --sil-edit2


module sethook-example {
  namespace "http://netconfcentral.org/ns/sethook-example";
  prefix "sethook-ex";

  revision 2020-12-14 {
    description "Add more examples for Set Hook List Deletion.";
  }

  list list-a {                             // edit2 cb
    key "name";

    leaf name {
      type string;
    }

    list list-b {                           // edit2 cb
      key "name";

      leaf name {
        type string;
      }
    }

    list list-c {                           // edit2 cb + Set Hook (Node)
      key "name";

      leaf name {
        type string;
      }
    }
  }
}


As specified in the attached SIL code, the Set Hook for this example is registered for the “/list-a/list-c” list node. Thus, whenever the list "/list-a/list-c" 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 existing “/list-a/list-b” list with all its children when a "/list-a/list-c" list is getting deleted. The callback function may look as follows:


/********************************************************************
* FUNCTION  sethook_callback2
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set Hook:
*   trigger: delete /list-a/list-c
*   add_edit:
*       delete nodes: delete the list list-b entry
*
*           path: /list-a/list-b
*
*********************************************************************/
static status_t
    sethook_callback2 (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;
    val_value_t *key_val = NULL;

    const xmlChar *defpath =
        (const xmlChar *)"/list-a[name='name1']/list-b[name='name2']";

    /* find a list key value for verification */
    if (curval) {
        key_val =
            val_find_child(curval,
                           y_sethook_example_M_sethook_example,
                           (const xmlChar *)"name");

        if (key_val) {
            log_debug("\n>>>>>>>>>> "
                      " callback for %s editop, name=%s",
                       op_editop_name(editop), VAL_STR(key_val));
        }
    }

    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 list-b list if the curval list key value is
         * "delete-list-b"
         */
        if (key_val &&
            !xml_strcmp(VAL_STR(key_val), (const xmlChar *)"delete-list-b")) {

            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_callback2 */


As a result, whenever some north bound agent delete the "/list-a/list-c" with a specific value, the callback is invoked and additionally deletes a list entry "/list-a/list-b". For more details on how this callback is registered and cleaned up refer to the attached SIL code.


Assume the running datastore contains the following data before the Set Hook invocation to trigger the deletion:


 <data>
  <list-a xmlns="http://netconfcentral.org/ns/sethook-example">
   <name>name1</name>
   <list-b>
    <name>name2</name>
   </list-b>
   <list-c>
    <name>delete-list-b</name>
   </list-c>
  </list-a>
 </data>


Now, in order to trigger the Set Hook callback and add an extra delete edit the following edit-config may be used:


  <edit-config>
    <target>
      <candidate/>
    </target>
    <default-operation>merge</default-operation>
    <test-option>set</test-option>
    <config>
      <list-a xmlns="http://netconfcentral.org/ns/sethook-example">
        <name>name1</name>
        <list-c
          xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
          nc:operation="delete">
          <name>delete-list-b</name>
        </list-c>
      </list-a>
    </config>
  </edit-config>


As a result during the Validate Phase the server will invoke Set Hook callback that will try to add an extra edit to delete "/list-a/list-b" list node. The server log may look as follows:


***** start validate phase on candidate for session 3, transaction 2393 *****

Start container in validate commit for yuma-netconf:config
Start list in validate commit for sethook-example:list-a
agt_acm: check write <list-a> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: sethook-example:list-a start
Start leaf in validate commit for sethook-example:name
agt_acm: check write <name> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: sethook-example:name start
Start list in validate commit for sethook-example:list-c
agt_acm: check write <list-c> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: sethook-example:list-c start
Start undo for 'sethook-example:list-c'
Start leaf in validate commit for sethook-example:name
agt_acm: check write <name> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: sethook-example:name start
Extending edit2 parent SIL callback for child 'name'
Start child-undo for 'sethook-example:name'
Starting setup of transaction callbacks
Checking for validate user callback for delete edit on sethook-example:list-c
Found validate user callback for delete:sethook-example:list-c
agt_cfg: use undo Q for 'list-c'
agt_cfg: add undo nested_silcall for 'sethook-example:list-c'
Skipping callback on edit2 terminal sethook-example:name
undo for delete op on sethook-example:list-c
silcall for sethook-example:list-c
Finished setup of transaction callbacks

Start invoking Set Hook callback for delete on sethook-example:list-c
Enter SET Hook callback
>>>>>>>>>>  callback for delete editop, name=delete-list-b
Start adding Set Hook edit for delete on /list-a[name='name1']/list-b[name='name2']
agt_val: retrieving '/list-a[name='name1']/list-b[name='name2']' from candidate datastore
xpath1: checking list lookup for 'sethook-example:list-a' w/ 1 key
Making list lookup for 'list-a'
Made list lookup OK
Clearing result
Added real_listval 'list-a'
xpath1: checking list lookup for 'sethook-example:list-b' w/ 1 key
Making list lookup for 'list-b'
Made list lookup OK
Clearing result
Added real_listval 'list-b'
eval_expr
xpath value result for '/list-a[name='name1']/list-b[name='name2']'
  typ: nodeset = 
   node VAL sethook-ex:list-b [L:3]


Start list in validate commit for sethook-example:list-b
agt_acm: check write <list-b> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: sethook-example:list-b start
Start undo for 'sethook-example:list-b'
Start leaf in validate commit for sethook-example:name
agt_acm: check write <name> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: sethook-example:name start
Extending edit2 parent SIL callback for child 'name'
Start child-undo for 'sethook-example:name'
Finish invoking Set Hook callback on sethook-example:list-c
Start invoking validate SIL callback for delete on sethook-example:list-c
 (list-c) Enter EDIT-2 callback
 ------PHASE:validate;EDITOP:delete

Finished invoking user callback on sethook-example:list-c
Starting setup of Added Edits callbacks
Checking for validate user callback for delete edit on sethook-example:list-b
Found validate user callback for delete:sethook-example:list-b
agt_cfg: use undo Q for 'list-b'
agt_cfg: add undo nested_silcall for 'sethook-example:list-b'
Skipping callback on edit2 terminal sethook-example:name
undo for delete op on sethook-example:list-b
silcall for sethook-example:list-b
Finished setup of Added Edits callbacks
Start invoking validate SIL callback for delete on sethook-example:list-b
 (list-b) Enter EDIT-2 callback
 ------PHASE:validate;EDITOP:delete

Finished invoking user callback on sethook-example:list-b

***** start apply phase on candidate for session 3, transaction 2393 *****

apply_write_val: list-c start
apply_write_val[3]: apply delete on /sethook-ex:list-a[sethook-ex:name="name1"]/sethook-ex:list-c[sethook-ex:name="delete-list-b"]
agt_val: Marking 'list-a' node as uncommitted delete
apply_write_val: list-b start
apply_write_val[3]: apply delete on /sethook-ex:list-a[sethook-ex:name="name1"]/sethook-ex:list-b[sethook-ex:name="name2"]
agt_val: Marking 'list-a' node as uncommitted delete
start delete_dead_nodes2 for transaction
delete_dead_nodes2: undo for sethook-example:list-c
start run_external_xpath_tests for 'sethook-example:list-c'
start delete_extern_nodes for 'list-c'
start run_external_xpath_tests for 'sethook-example:name'
delete_dead_nodes2: undo for sethook-example:list-b
start run_external_xpath_tests for 'sethook-example:list-b'
start delete_extern_nodes for 'list-b'
start run_external_xpath_tests for 'sethook-example:name'

***** start commit phase on candidate for session 3, transaction 2393 *****

Start full commit of transaction 2393: 2 edits on candidate config
edit-transaction 2393: on session 3 by tony@127.0.0.1
  time: 2020-12-14T20:09:33Z
  message-id: 5
  trace-id: --
  datastore: candidate
  operation: delete
  target: /sethook-ex:list-a[sethook-ex:name="name1"]/sethook-ex:list-c[sethook-ex:name="delete-list-b"]
  comment: none

edit-transaction 2393: on session 3 by tony@127.0.0.1
  time: 2020-12-14T20:09:33Z
  message-id: 5
  trace-id: --
  datastore: candidate
  operation: delete
  target: /sethook-ex:list-a[sethook-ex:name="name1"]/sethook-ex:list-b[sethook-ex:name="name2"]
  comment: none

Complete commit OK of transaction 2393 on candidate database


To ensure that the data deleted by add_edit was successfully deleted an application can retrieve running configurations. When the server receives get-config request, the server should reply with:



 <data>
  <list-a xmlns="http://netconfcentral.org/ns/sethook-example">
   <name>name1</name>
  </list-a>
 </data>