AVAILABLE SINCE 20.10-0 RELEASE 


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 EDIT callbacks for the same object.


For more derails regarding Set Hook callbacks, refer to the following article:

How do I use SIL-SA version of the 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_sa_dir to auto-generate stub SIL-SA 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-SA code and add registration for your Set Hook callback functions.


SIL-SA code generation command that was used in this example. Please refer to the attached SIL-SA code.


> make_sil_sa_dir  silsa-sethook-example --sil-get2

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

  revision 2020-08-18 {
    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,
                      op_editop_t editop,
                      val_value_t *newval,
                      val_value_t *curval,
                      const xmlChar *transaction_id,
                      boolean isvalidate,
                      boolean isload,
                      boolean isrunning)
{
    status_t res = NO_ERR;
    val_value_t *errorval = (curval) ? curval : newval;

    const xmlChar *user = sil_sa_get_username();
    const xmlChar *client_addr = sil_sa_get_client_addr();

    if (LOGDEBUG2) {
        log_debug2("\n\n********************************************");

        print_callback_info(errorval,
                            AGT_CB_VALIDATE,
                            editop,
                            (const xmlChar *)"SETHOOK");

        log_debug2("\ntransaction_id -- %s", transaction_id);
        log_debug2("\nuser_id -- %s", user);
        log_debug2("\nclient_addr -- %s", client_addr);
        log_debug2("\nisvalidate -- %s",
            isvalidate ? NCX_EL_TRUE : NCX_EL_FALSE);
        log_debug2("\nisload -- %s",
            isload ? NCX_EL_TRUE : NCX_EL_FALSE);
        log_debug2("\nisrunning -- %s",
            isrunning ? NCX_EL_TRUE : NCX_EL_FALSE);
        log_debug2("\n********************************************\n\n");
    }

    const xmlChar *defpath =
        (const xmlChar *)"/sa-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_find_object(silsa_sethook_example_mod,
                                (const xmlChar *)"interfaces");
            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 */
                const xmlChar *edit_operation = (const xmlChar *)"merge";
                const xmlChar *insert_point = NULL;
                const xmlChar *insert_where = NULL;
                boolean skip_cb = FALSE;

                res =
                    sil_sa_add_edit(defpath,
                                    editval,
                                    edit_operation,
                                    insert_where,
                                    insert_point,
                                    skip_cb);
            }

            /* 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. Refer to the attached SIL-SA code for more information for this callback function.


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/silsa-sethook-example">add-edit</trigger>
    </config>
  </edit-config>


The following server output may be seen during the operation described above. The three phase edit-config processing may look as follows. Note that the server sends <server-request> where it specifies the type of the edit as "set hook".


SIL-SA Transaction Start callbacks OK

***** start validate phase on candidate for session 5, transaction 112033 *****

Start container in validate commit for yuma-netconf:config
Start leaf in validate commit for silsa-sethook-example:trigger
agt_acm: check write <trigger> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: silsa-sethook-example:trigger start
Start undo for 'silsa-sethook-example:trigger'
Starting setup of transaction callbacks
Checking for validate user callback for create edit on silsa-sethook-example:trigger
Found validate user callback for create:silsa-sethook-example:trigger
agt_cfg: add set-hook silcall for silsa-sethook-example:trigger
agt_cfg: add silcall for silsa-sethook-example:trigger
undo for create op on silsa-sethook-example:trigger
Finished setup of transaction callbacks
agt_val: Starting remote SIL validate phase
ycontrol_msg: sending server-request # 7 for sil-sa
ses_msg: send 1.1 buff:988 for s:4

trace_buff:

#982
<?xml version="1.0" encoding="UTF-8"?>
<ycontrol xmlns:ya="http://yumaworks.com/ns/yumaworks-attrs"
 xmlns="http://yumaworks.com/ns/yumaworks-ycontrol">
 <message-id>7</message-id>
 <message-type>server-request</message-type>
 <server-id>server1</server-id>
 <subsys-id>subsys1</subsys-id>
 <service-id>sil-sa</service-id>
 <payload>
  <sil-sa xmlns="http://yumaworks.com/ns/yumaworks-sil-sa">
   <start-transaction>
    <transaction-id>112033</transaction-id>
    <user-id>tony</user-id>
    <client-addr>127.0.0.1</client-addr>
    <target>candidate</target>
    <validate>true</validate>
    <reverse-edit>false</reverse-edit>
    <load-config>false</load-config>
    <is-hook-load>false</is-hook-load>
    <is-hook-validate>false</is-hook-validate>
    <edit>
     <id>1</id>
     <operation>create</operation>
     <path>/sa-sethook-ex:trigger</path>
     <hook-type>set-hook</hook-type>
     <newval>
      <trigger ya:datapath="/sa-sethook-ex:trigger"
       xmlns="http:
ses_msg: send 1.1 buff:472 for s:4

trace_buff:

#462
tconfcentral.org/ns/silsa-sethook-example">add-edit</trigger>
     </newval>
    </edit>
    <edit>
     <id>2</id>
     <operation>create</operation>
     <path>/sa-sethook-ex:trigger</path>
     <hook-type>none</hook-type>
     <newval>
      <trigger ya:datapath="/sa-sethook-ex:trigger"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">add-edit</trigger>
     </newval>
    </edit>
   </start-transaction>
  </sil-sa>
 </payload>
</ycontrol>
agt_sil: Creating SIL-SA transaction timer callback with interval '30' seconds
agt_val: enter ncxserver YControl mode


As a result the subsystem will process this request and send an appropriate reply to the server. The subsystem log output may look as follows:


ycontrol_io: read for control session start
ses read OK (1000) on session 1
ses_msg: allocate msg 0x55e69705da30
ses_msg: reused in buff 0x55e696f9b0c0 for s 1
ycontrol_io: read for control session start
ses read OK (460) on session 1
ses_msg: reused in buff 0x55e696f95c70 for s 1
ycontrol_ses: msg ready for control session
yc_parse: Overriding datapath obj 'any' with '/sa-sethook-ex:trigger'
yc_parse: Overriding datapath obj 'any' with '/sa-sethook-ex:trigger'
ycontrol: dispatch message to service 'sil-sa'
sil_sa: set-hook callback on edit 1: 'trigger' for '/sa-sethook-ex:trigger'


********************************************
 (trigger) Enter SETHOOK callback
 ------PHASE:validate;EDITOP:create

transaction_id -- 112033
user_id -- user
client_addr -- 127.0.0.1
isvalidate -- false
isload -- false
isrunning -- false
********************************************


sil_sa_hook: Start adding Set Hook edit for merge on /sa-sethook-ex:interfaces
sil_sa: done add edit for defpath '/sa-sethook-ex:interfaces'
sil_sa: validate callback on edit 2: 'trigger' for '/sa-sethook-ex:trigger'

 (trigger) Enter EDIT callback
 ------PHASE:validate;EDITOP:create

sil_sa: sending <transaction-response> with added edits
ycontrol_msg: sending subsys-response # 4 for sil-sa
ses_msg: reused out buff 0x55e696faecd0 for s 1
ses_msg: free msg 0x55e69705da30 for session 1
ses got send request on session 1
ses_msg: send 1.1 buff:967 for s:1

trace_buff:
<?xml version="1.0" encoding="UTF-8"?>
<ycontrol xmlns="http://yumaworks.com/ns/yumaworks-ycontrol">
 <message-id>4</message-id>
 <message-type>subsys-response</message-type>
 <server-id>server1</server-id>
 <subsys-id>subsys1</subsys-id>
 <service-id>sil-sa</service-id>
 <payload>
  <sil-sa xmlns="http://yumaworks.com/ns/yumaworks-sil-sa">
   <transaction-response>
    <transaction-id>112033</transaction-id>
    <added-edit>
     <path>/sa-sethook-ex:interfaces</path>
     <operation>merge</operation>
     <skip-callback>false</skip-callback>
     <edit>
      <interfaces
       xmlns:ya="http://yumaworks.com/ns/yumaworks-attrs"
       ya:datapath="/sa-sethook-ex:interfaces"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">
       <interface>
        <name>vlan1</name>
        <hook-node>1000</hook-node>
       </interface>
      </interfaces>
     </edit>
    </added-edit>
   </transaction-response>
  </sil-sa>
 </payload>
</ycontrol>


As a result of Set Hook invocation the subsystem will add an extra edit and will response to the server to let it to add the edit to the transaction. The server output may look as follows:


agt_ycontrol: Got <ycontrol> message:
yctl:ycontrol {
  message-id 4
  message-type subsys-response
  server-id server1
  subsys-id subsys1
  service-id sil-sa
  payload {
    ysil:sil-sa {
      transaction-response {
        transaction-id 112033
        added-edit {
          path /sa-sethook-ex:interfaces
          operation merge
          skip-callback false
          edit {
            sa-sethook-ex:interfaces {
              interface  vlan1 {
                name vlan1
                hook-node 1000
              }
            }
          }
        }
      }
    }
  }
}

agt_sil: adding transaction-response 4 for subsys subsys1
ses_msg: free msg 0x55ad28134850 for session 4
ycontrol_mode_done for TXID '112033'
agt_val: exit ncxserver YControl mode
agt_val: start processing added edits for 'subsys1' (ok)

Start adding Set Hook edit for merge on /sa-sethook-ex:interfaces
agt_val: retrieving '/sa-sethook-ex:interfaces' from candidate datastore
eval_expr
xpath value result for '/sa-sethook-ex:interfaces'
  typ: nodeset = 
   node VALHDR sa-sethook-ex:interfaces (1)


add_default: checking 'silsa-sethook-example:interfaces'
add_node_default: checking 'silsa-sethook-example:interface'
add_default: checking 'silsa-sethook-example:interface'
add_node_default: checking 'silsa-sethook-example:state'
add empty NP container 'silsa-sethook-example:state'
add_default: checking 'silsa-sethook-example:state'
Start container in validate commit for silsa-sethook-example:interfaces
agt_acm: check write <interfaces> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: silsa-sethook-example:interfaces start
Start undo for 'silsa-sethook-example:interfaces'
Start list in validate commit for silsa-sethook-example:interface
agt_acm: check write <interface> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: silsa-sethook-example:interface start
Start child-undo for 'silsa-sethook-example:interface'
Start leaf in validate commit for silsa-sethook-example:name
agt_acm: check write <name> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: silsa-sethook-example:name start
Start leaf in validate commit for silsa-sethook-example:hook-node
agt_acm: check write <hook-node> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_simval:validate: silsa-sethook-example:hook-node start
Start container in validate commit for silsa-sethook-example:state
agt_acm: check write <state> allowed for user 'tony'
agt_acm: PERMIT (access-control off)
invoke_cpxval:validate: silsa-sethook-example:state start
Start child-undo for 'silsa-sethook-example:state'
agt_val: stop processing added edits for 'subsys1' (ok)

Starting setup of Added Edits callbacks
Checking for validate user callback for create edit on silsa-sethook-example:interfaces
Found validate user callback for create:silsa-sethook-example:interfaces
agt_cfg: add silcall for silsa-sethook-example:interfaces
Checking for validate user callback for create edit on silsa-sethook-example:interface
Found validate user callback for create:silsa-sethook-example:interface
agt_cfg: add silcall for silsa-sethook-example:interface
Checking for validate user callback for create edit on silsa-sethook-example:name
Checking for validate user callback for create edit on silsa-sethook-example:hook-node
Found validate user callback for create:silsa-sethook-example:hook-node
agt_cfg: add silcall for silsa-sethook-example:hook-node
Checking for validate user callback for create edit on silsa-sethook-example:state
Found validate user callback for create:silsa-sethook-example:state
agt_cfg: add silcall for silsa-sethook-example:state
undo for create op on silsa-sethook-example:interfaces
Finished setup of Added Edits callbacks
ycontrol_msg: sending server-request # 8 for sil-sa
ses_msg: send 1.1 buff:988 for s:4

trace_buff:

#982
<?xml version="1.0" encoding="UTF-8"?>
<ycontrol xmlns:ya="http://yumaworks.com/ns/yumaworks-attrs"
 xmlns="http://yumaworks.com/ns/yumaworks-ycontrol">
 <message-id>8</message-id>
 <message-type>server-request</message-type>
 <server-id>server1</server-id>
 <subsys-id>subsys1</subsys-id>
 <service-id>sil-sa</service-id>
 <payload>
  <sil-sa xmlns="http://yumaworks.com/ns/yumaworks-sil-sa">
   <start-transaction>
    <transaction-id>112033</transaction-id>
    <user-id>tony</user-id>
    <client-addr>127.0.0.1</client-addr>
    <target>candidate</target>
    <validate>true</validate>
    <reverse-edit>false</reverse-edit>
    <load-config>false</load-config>
    <is-hook-load>false</is-hook-load>
    <is-hook-validate>false</is-hook-validate>
    <edit>
     <id>2</id>
     <operation>create</operation>
     <path>/sa-sethook-ex:interfaces</path>
     <hook-type>none</hook-type>
     <newval>
      <interfaces ya:datapath="/sa-sethook-ex:interfaces"
       xmlns="
ses_msg: send 1.1 buff:988 for s:4

trace_buff:

#982
://netconfcentral.org/ns/silsa-sethook-example">
       <interface>
        <name>vlan1</name>
        <hook-node>1000</hook-node>
        <state/>
       </interface>
      </interfaces>
     </newval>
    </edit>
    <edit>
     <id>3</id>
     <operation>create</operation>
     <path>/sa-sethook-ex:interfaces/sa-sethook-ex:interface</path>
     <hook-type>none</hook-type>
     <newval>
      <interface
       ya:datapath="/sa-sethook-ex:interfaces/sa-sethook-ex:interface"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">
       <name>vlan1</name>
       <hook-node>1000</hook-node>
       <state/>
      </interface>
     </newval>
     <keys>
      <name
       ya:datapath="/sa-sethook-ex:interfaces/sa-sethook-ex:interface/sa-sethook-ex:name"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">vlan1</name>
     </keys>
    </edit>
    <edit>
     <id>4</id>
     <operation>create</operation>
     <path>/sa-sethook-ex:interfaces/sa-
ses_msg: send 1.1 buff:988 for s:4

trace_buff:

#982
ook-ex:interface/sa-sethook-ex:hook-node</path>
     <hook-type>none</hook-type>
     <newval>
      <hook-node
       ya:datapath="/sa-sethook-ex:interfaces/sa-sethook-ex:interface/sa-sethook-ex:hook-node"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">1000</hook-node>
     </newval>
     <keys>
      <name
       ya:datapath="/sa-sethook-ex:interfaces/sa-sethook-ex:interface/sa-sethook-ex:name"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">vlan1</name>
     </keys>
    </edit>
    <edit>
     <id>5</id>
     <operation>create</operation>
     <path>/sa-sethook-ex:interfaces/sa-sethook-ex:interface/sa-sethook-ex:state</path>
     <hook-type>none</hook-type>
     <newval>
      <state
       ya:datapath="/sa-sethook-ex:interfaces/sa-sethook-ex:interface/sa-sethook-ex:state"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example"/>
     </newval>
     <keys>
      <name
       ya:datapath="/sa-sethook-ex:interfaces/s
ses_msg: send 1.1 buff:212 for s:4

trace_buff:

#202
thook-ex:interface/sa-sethook-ex:name"
       xmlns="http://netconfcentral.org/ns/silsa-sethook-example">vlan1</name>
     </keys>
    </edit>
   </start-transaction>
  </sil-sa>
 </payload>
</ycontrol>
agt_sil: Creating SIL-SA transaction timer callback with interval '30' seconds
agt_val: enter ncxserver YControl mode


The server will add an extra edit generated by the subsystem and will call the subsystem again in order to invoke the callbacks for newly added nodes. 


NOTE: this invocation may be skipped by the sil_sa_add_edit() API parameter skip_cb. If it is set to TRUE then the server will not try to invoke any callbacks for newly added node(s).


After the regular edit configuration procedure for this newly added nodes the subsystem should reply with OK message or an ERROR. If the result of this subsystem invocation is successful the server will continue the translation and the output of the server log may look as follows:


agt_ycontrol: Got <ycontrol> message:
yctl:ycontrol {
  message-id 8
  message-type subsys-response
  server-id server1
  subsys-id subsys1
  service-id sil-sa
  ok 
}

ses_msg: free msg 0x55ad28134850 for session 4
ycontrol_mode_done for TXID '112033'
agt_val: exit ncxserver YControl mode
agt_val: Finished remote SIL validate phase

***** start apply phase on candidate for session 5, transaction 112033 *****

apply_write_val: trigger start
apply_write_val[5]: apply create on /sa-sethook-ex:trigger
Add child 'trigger' to parent 'config'
apply_write_val: interfaces start
add_default: checking 'silsa-sethook-example:interfaces'
add_node_default: checking 'silsa-sethook-example:interface'
add_default: checking 'silsa-sethook-example:interface'
add_node_default: checking 'silsa-sethook-example:state'
add_default: checking 'silsa-sethook-example:state'
apply_write_val[5]: apply create on /sa-sethook-ex:interfaces
start delete_dead_nodes2 for transaction
delete_dead_nodes2: undo for silsa-sethook-example:trigger
start delete_dead_nodes for node 'silsa-sethook-example:trigger'
start run_external_xpath_tests for 'silsa-sethook-example:trigger'
start delete_extern_nodes for 'trigger'
delete_dead_nodes2: undo for silsa-sethook-example:interfaces
start delete_dead_nodes for node 'silsa-sethook-example:interfaces'
start run_external_xpath_tests for 'silsa-sethook-example:interfaces'
start delete_extern_nodes for 'interfaces'
start run_external_xpath_tests for 'silsa-sethook-example:interface'
start delete_extern_nodes for 'interface'
start run_external_xpath_tests for 'silsa-sethook-example:name'
start run_external_xpath_tests for 'silsa-sethook-example:speed'
start run_external_xpath_tests for 'silsa-sethook-example:hook-node'
start run_external_xpath_tests for 'silsa-sethook-example:state'
start delete_extern_nodes for 'state'
start run_external_xpath_tests for 'silsa-sethook-example:admin-state'
start run_external_xpath_tests for 'silsa-sethook-example:status'

***** start commit phase on candidate for session 5, transaction 112033 *****

Start full commit of transaction 112033: 2 edits on candidate config
edit-transaction 112033: on session 5 by tony@127.0.0.1
  time: 2020-08-18T23:54:41Z
  message-id: 2
  trace-id: --
  datastore: candidate
  operation: create
  target: /sa-sethook-ex:trigger
  comment: none

edit-transaction 112033: on session 5 by tony@127.0.0.1
  time: 2020-08-18T23:54:41Z
  message-id: 2
  trace-id: --
  datastore: candidate
  operation: create
  target: /sa-sethook-ex:interfaces
  comment: none

Complete commit OK of transaction 112033 on candidate database

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


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