A Transaction Hook is a callback function that is invoked within the transaction when an object is modified to provide one more level of validation or an extra action. The Transaction Hook is similar to the Set Hook except this callback is invoked just before the data is finally committed to the running datastore. This callback will be invoked after EDIT1or EDIT2 callbacks are all done. Note that the Transaction Hook is not allowed to change anything, It is not allowed to call agt_val_add_edit, or to alter the edits or the datastore in any way. Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Hook callbacks call agt_val_add_edit, the operation will be ignored. Do not return error just ignore add_edit calls.
The following function template definition is used for the Transaction Hook callback functions:
/* Typedef of the agt_hook_cb callback */ typedef status_t (*agt_cb_hook_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);
The Transaction Hook 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 Transaction 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. In case you want to register Transaction Hook callback select corresponding type of the 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. Where defpath is an absolute path expression string indicating which node the callback function is for.
extern void agt_cb_hook_unregister (const xmlChar *defpath);
EXAMPLES
The Transaction Hook callback can be set to specific object at run-time with a callback as follows. Assume we have a leaf node "example" in the YANG module. Register the Transaction Hook to the /example node of the example YANG module.
/******************************************************************** * FUNCTION interfaces_init * * initialize the server instrumentation library. * Initialization Phase 1 * *********************************************************************/ static status_t interfaces_init (void) { ... /* Register an object specific Transaction Hook callback */ res = agt_cb_hook_register((const xmlChar *)”/example”, AGT_HOOKFMT_NODE, AGT_HOOK_TYPE_TRANSACTION, hooks_hook_edit); ... }
Now, whenever the /example node is edited, the Transaction Hook callback will be invoked and it will perform specific validation actions. The callback function may look as follows:
/******************************************************************** * FUNCTION hooks_hook_edit * * Callback function for server object handler * Used to provide a callback for a specific named object * * Transaction-Hook: * trigger: edit /example * effect: * - if testval node exist the DELETE operation will be denied * - if testval is false, the operation will be denied * * 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 hooks_hook_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 Transaction-Hook hooks_hook_edit callback"); status_t res = NO_ERR; status_t res2 = NO_ERR; val_value_t *errorval = (curval) ? curval : newval; val_value_t *testval = agt_val_get_data(txcb->cfg_id, (const xmlChar *)"/if:interfaces/if:interface[if:name]/if:enabled", &res2); switch (editop) { case OP_EDITOP_LOAD: break; case OP_EDITOP_MERGE: case OP_EDITOP_REPLACE: case OP_EDITOP_CREATE: if (testval) { res = ERR_NCX_ACCESS_DENIED; } break; case OP_EDITOP_DELETE: if (testval && VAL_BOOL(testval)) { res = ERR_NCX_ACCESS_DENIED; } else { res2 = NO_ERR; } 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; } /* hooks_hook_edit */
So whenever some north bound agent edit an /example node the callback kicks in and additionally validates the /interfaces/interface[name]/enabled node.
Subtree format Transaction Hook callbacks
In the case when your register the callback with AGT_HOOKFMT_SUBTREE format the server will invoke the callback any time you edit the children of the node that holds the callback.
The invocation will be done after all the children of the callback holder node are processed and all other callbacks are invoked for children.
Consider ietf-interfaces YANG data model and assume we register the Transaction Hook callback for the "if:interfaces" top level container. So whenever some north bound agent edits an /if:interfaces node or any of its children the callback is invoked and additionally runs some validations or does some extra desired actions. Based on this validation, the operation can be denied and the server will proceed to the Rollback Phase.
When you edit multiple children of the parent callback the server will invoke only one Transaction Hook callback for all of the edits and after all the children are processed.
Edit nested tree. The following invocation order will be set for the Transaction Hook in case of Subtree Format and in case the edit is nested complex edit:
- EDIT callback for top level node if any (E.g. EDIT2 on /if:interfaces)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for deeper node if any (E.g. EDIT2 on /if:interfaces/if:interface/.....)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- Transaction Hook callback for (/if:interfaces)
Update/Modify only children of the callback holder parent node. The following invocation order will be set for the Transaction Hook in case of Subtree Format and in case the edit is only on the children node:
- EDIT callback for children node if any (E.g. EDIT2 on /if:interfaces/....)
- Transaction Hook callback for callback holder node (/if:interfaces)
Edit multiple siblings of the callback holder. The following invocation order will be set for the Transaction Hook in case of Subtree or Node Format and in case there are multiple edits on the same node that holds the Transaction Hook callback. For example, assume we register Transaction Hook callback on "if:interfaces/if:interface" list and the agent modifies multiple interfaces within the same edit.
- EDIT callback for top level node if any (E.g. EDIT2 on /if:interfaces)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for deeper node if any (E.g. EDIT2 on /if:interfaces/if:interface/.....)
- Multiple Transaction Hook callbacks for each modified (/if:interfaces/if:interface)
Edit multiple children siblings of the callback holder. The following invocation order will be set for the Transaction Hook in case of Subtree Format and in case there are multiple edits on the children nodes of the node that holds the Transaction Hook callback. For example, assume we register Transaction Hook callback on "if:interfaces" container and the agent modifies multiple children interfaces within the same edit.
- EDIT callback for top level node if any (E.g. EDIT2 on /if:interfaces)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for nested node if any (E.g. EDIT2 on /if:interfaces/if:interface)
- EDIT callback for deeper node if any (E.g. EDIT2 on /if:interfaces/if:interface/.....)
- A single Transaction Hook callback for (/if:interfaces)