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: Add a New 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 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;
  }
}


Assume we registered Set Hook callback for the “trigger” leaf node. Thus, whenever the node /trigger is edited, the Set Hook 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 specific value equal to “add-edit”. 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: edit /trigger
*   add_edit:
*       add nodes: populate 1 list entry with name=vlan1
*
*           path: /interfaces/interface[name=vlan1]
*
*********************************************************************/
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:
        /* add a new edit if the "/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(y_sethook_example_M_sethook_example,
                                              (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);

            /* malloc and construct list value */
            val_value_t *list_value =
                  create_list_entry(VAL_OBJ(editval),
                                    (const xmlChar *)"vlan1",
                                    &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 */



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.



As a result, whenever some north bound agent edit the /trigger with a specific value, the callback is invoked and additionally creates a new interface /interfaces/interface[name=value]. 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="create"
        xmlns="http://netconfcentral.org/ns/sethook-example">add-edit</trigger>
    </config>
  </edit-config>


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


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

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 create edit on sethook-example:trigger
Found validate user callback for create:sethook-example:trigger
agt_cfg: use undo Q for 'trigger'
agt_cfg: add undo nested_silcall for 'sethook-example:trigger'
undo for create op on sethook-example:trigger
silcall for sethook-example:trigger
Finished setup of transaction callbacks


Start invoking Set Hook callback for create on sethook-example:trigger
Enter SET Hook callback
Start adding Set Hook edit for merge 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

add_default: checking 'sethook-example:interfaces'
add_node_default: checking 'sethook-example:interface'
add_default: checking 'sethook-example:interface'
add_node_default: checking 'sethook-example:state'
add empty NP container 'sethook-example:state'
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
Start child-undo for 'sethook-example:state'
Finish invoking Set Hook callback on sethook-example:trigger
Start invoking validate SIL callback for create on sethook-example:trigger
 (trigger) Enter EDIT-2 callback
 ------PHASE:validate;EDITOP:create

Finished invoking user callback on sethook-example:trigger
Starting setup of Added Edits callbacks
Checking for validate user callback for create edit on sethook-example:interfaces
Found validate user callback for create:sethook-example:interfaces
agt_cfg: use undo Q for 'interfaces'
agt_cfg: add undo nested_silcall for 'sethook-example:interfaces'
Checking for validate user callback for create edit on sethook-example:interface
Found validate user callback for create:sethook-example:interface
agt_cfg: use cur_silcall Q for 'interface'
agt_cfg: add undo nested_silcall for 'sethook-example:interface'
Skipping callback on edit2 terminal sethook-example:name
Skipping callback on edit2 terminal sethook-example:hook-node
Checking for validate user callback for create edit on sethook-example:state
Found validate user callback for create:sethook-example:state
agt_cfg: use cur_silcall Q for 'state'
agt_cfg: add undo nested_silcall for 'sethook-example:state'
undo for create op on sethook-example:interfaces
silcall for sethook-example:interfaces
 silcall for sethook-example:interface
  silcall for sethook-example:state
Finished setup of Added Edits callbacks


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

Finished invoking user callback on sethook-example:interfaces
Start invoking validate SIL callback for create on sethook-example:interface
 (interface) Enter EDIT-2 callback
 ------PHASE:validate;EDITOP:create

Finished invoking user callback on sethook-example:interface
Start invoking validate SIL callback for create on sethook-example:state
 (state) Enter EDIT-2 callback
 ------PHASE:validate;EDITOP:create

Finished invoking user callback on sethook-example:state

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

apply_write_val: trigger start
apply_write_val[3]: apply create on /sethook-ex:trigger
Add child 'trigger' to parent 'config'
apply_write_val: interfaces start
add_default: checking 'sethook-example:interfaces'
add_node_default: checking 'sethook-example:interface'
add_default: checking 'sethook-example:interface'
add_node_default: checking 'sethook-example:state'
add_default: checking 'sethook-example:state'
apply_write_val[3]: apply create on /sethook-ex:interfaces
start delete_dead_nodes2 for transaction
delete_dead_nodes2: undo for sethook-example:trigger
start delete_dead_nodes for node '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 delete_dead_nodes for node '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 2175 *****

Start full commit of transaction 2175: 2 edits on candidate config
edit-transaction 2175: on session 3 by admin@127.0.0.1
  time: 2020-04-12T21:24:31Z
  message-id: 2
  trace-id: --
  datastore: candidate
  operation: create
  target: /sethook-ex:trigger
  comment: none

edit-transaction 2175: on session 3 by admin@127.0.0.1
  time: 2020-04-12T21:24:31Z
  message-id: 2
  trace-id: --
  datastore: candidate
  operation: create
  target: /sethook-ex:interfaces
  comment: none

Complete commit OK of transaction 2175 on candidate database


To ensure that the data added by add_edit was successfully generated an application can retrieve running configurations. The server should reply with:


 <data>
  <interfaces xmlns="http://netconfcentral.org/ns/sethook-example">
   <interface>
    <name>vlan1</name>
    <hook-node>1000</hook-node>
   </interface>
  </interfaces>
  <trigger xmlns="http://netconfcentral.org/ns/sethook-example">add-edit</trigger>
 </data>