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>