In-Transaction Callbacks
In-transaction APIs are used in netconfd-pro to allow an application to update the configuration data and perform additional actions while the transaction is in progress.
The following diagram illustrates what callbacks are invoked and when to expect them to be invoked during the transaction.
To manipulate the datastore contents in the same transaction:
Add Edit and Get Data APIs
The following API functions can be called from the Set Hook or Post Set Hook callbacks:
Add Edit: Add an edit operation to the current transaction
SIL: agt_val_add_edit
SIL-SA: sil_sa_add_edit
Get Data: Retrieve an instance of a data node from the server.
SIL: agt_val_get_data
SIL-SA: sil_sa_get_data
To perform additional validations, security checks or any other actions with the target datastore in the same transaction:
To perform additional validations, security checks or any other actions with the special <startup> datastore before it is changed:
To perform additional actions and manipulations with the target datastore in the same transaction during edit operations such as the <commit> operation, there are Commit Completeness callbacks:
The Commit Completeness callbacks should no longer be used. A single callback can now be used instead, starting in the 22.10T-10 release:
Note
In-Transaction callbacks are not automatically generated by yangdump-pro code generation. They must be added manually.
The following SIL callback APIs are supported:
Set Hook
Similar to the EDIT1, EDIT2, or EDIT3 callbacks, except this callback is only invoked once at the start of the edit for a particular data node.
This callback will be invoked during the Validate Phase.
This callback can alter the data in the current edit and also add new edits to the current transaction.
This callback will be invoked before EDIT1, EDIT2, or EDIT3 callbacks for the same object.
Post Set Hook
Similar to the Set Hook callback except that it is invoked when an object is modified but AFTER EDIT callback is done for the same object.
This callback will be invoked during the Validate Phase.
If --target=candidate is used, this callback will be invoked when changes are done to the <candidate> datastore
If --target=running is used, this callback will be invoked when changes are done to the <running> datastore, invoked at the start of the transaction on the <running> datastore.
This callback will be invoked AFTER a EDIT1, EDIT2, or EDIT3 callback for the same object.
Transaction Hook
Similar to the Set Hook callback except this callback is invoked after the data is committed to the running datastore.
This callback will be invoked during the Commit Phase.
This callback will be invoked after EDIT1, EDIT2, or EDIT3 callbacks for the same object.
Transaction Start
This callback that is intended to provide access to the transaction control block and its parameters right before the transaction is started.
Transaction Complete
This callback is intended to provide access to the transaction control block and its parameters right after the transaction is completed.
Called for every transaction completion, not just transactions that complete without errors.
Validate Complete
This callback is intended to allow access to the <running> and <candidate> datastores (equivalent to completion of validation phase during <commit>).
Apply Complete
This callback is intended to allow access to the <running> and <candidate> datastores (equivalent to completion of apply phase during <commit>).
Commit Complete
This callback is a user/system callback that is invoked when the <commit> operation completes without errors or the internal <replay-config> operation completes without errors.
Rollback Complete
This callback is a user/system callback that is invoked after and if the Rollback Phase has been processed during the <commit> operation.
Edit Phase Complete
This callback is a user/system callback that is invoked after each edit phase.
It is intended to replace the separate phase complete callbacks and provide a consistent API for final processing and approval after each edit phase.
Invoked for all edit operations, and not affected by the --sil-cc-callback-all parameter.
Set Order Hook
This callback allows the SIL Priority to be set for data nodes in the EDIT transaction.
This callback will be invoked during the Validate Phase.
Set the secondary SIL priority for instances of the same list object that is being set in the same edit.
This allows the instance SIL callback order to be set in addition to the object priority.
Startup Hook
This callback function is invoked before any changes are done to the <startup> datastore.
It is invoked only if the <startup> capability is enabled.
Dynamic Default Hook
This is a user/system callback that can be used to set up dynamic default value to the non-default leafy nodes during edit operations.
This callback will be invoked during the Validate Phase.
It is invoked when the server checks if the node has any defaults for leafy nodes without static YANG "default" statements
The server supports SIL-SA callbacks for the following In-Transaction APIs:
SA Transaction Start
This callback is intended to provide access to the edit transaction right before the transaction is started.
The server does not provide the transaction control block to SIL-SA callbacks
Instead it passes the transaction ID for reference and some information about the transaction, such as datastore target and the callback phase.
SA Transaction Complete
This callback is intended to provide access to the edit transaction right after the transaction is completed.
Called for every transaction completion, not just transactions that complete without errors.
The server does not provide transaction control block
Instead it passes only the transaction ID for reference.
SA Set Hook
This callback is intended to alter the data in the current edit and or add new edits to the current transaction.
The transaction control block is not available.
Instead the server provides all necessary information as a set of parameters.
SA Post Set Hook
This callback is intended to alter the data in the current edit and or add new edits to the current transaction after the EDIT callbacks for the same node.
The transaction control block is not available.
Instead the server provides all necessary information as a set of parameters.
SA Transaction Hook
This callback is intended to provide a validation point for specific node(s).
The server does not provide the transaction control block.
Instead it passes the transaction ID for reference and some information about the transaction.
SA Validate Complete
This callback is intended to provide a validation point after the Validate Phase is completed.
The server does not provide running and candidate values.
Instead it passes the transaction ID for reference and Get Data API can be used to run additional validation if required.
SA Apply Complete
This callback is intended to provide a validation point after the Apply Phase is completed.
The server does not provide running and candidate values.
Instead it passes the transaction ID for reference and Get Data API can be used to run additional validation if required.
SA Commit Complete
This callback is intended to provide a validation point after the Commit Phase is completed.
The server does not provide running and candidate values.
Instead it passes the transaction ID for reference and Get Data API can be used to run additional validation if required.
Note: any return value from the SIL-SA Commit Complete callback is ignored by the server. A rollback is not triggered if this callback returns an error status.
SA Rollback Complete
This callback is intended to provide a validation point after the Rollback Phase is completed.
The server does not provide running and candidate values.
Instead it passes the transaction ID for reference and Get Data API can be used to run additional validation if required.
Callback Invocation Order
Assume all the possible callbacks are registered and the edit is complex as possible. The possible invocation order may look as follows, processing on the <candidate> datastore (before the <commit>):
Transaction Start
Set Order Hook
Set Hook and then Set Order Hook for the added edit if any
SIL Callbacks (only during Validation Phase)
Post Set Hook and then Set Order Hook for the added edit if any
SIL Callbacks for added edits (only during Validation Phase)
Transaction Complete
The possible invocation order during the <commit> operation may look as follows, processing on the <running> datastore, after this operation.
Transaction Start
Set Order Hook
SIL Callbacks (Validate Phase)
Validate Complete Edit Phase Complete
SIL Callbacks (Apply Phase)
Apply Complete Edit Phase Complete
SIL Callbacks (Commit Phase)
Transaction Hook
Commit/Rollback Complete Edit Phase Complete
Transaction Complete
The <commit> operation is not available if the default target is set to <running>. Thus, the possible invocation order during the edit operation may look as follows, processing on the running datastore only (the same invocation order will be during Load, Reload and Restart operations):
Transaction Start
Set Order Hook
Set Hook and then Set Order Hook for the added edit if any
SIL Callbacks (Validate Phase)
Post Set Hook and then Set Order Hook for the added edit if any
SIL Callbacks for added edits (Validate Phase) Edit Phase Complete
SIL Callbacks (Apply Phase) Edit Phase Complete
SIL Callbacks (Commit Phase) Edit Phase Complete
Transaction Hook
Transaction Complete
The possible invocation order during the 'validate' operation may look as follows, processing on the <running> datastore only:
Transaction Start
Set Order Hook
SIL Callbacks (Validate Phase) Edit Phase Complete
Transaction Complete
Set Hook
A Set Hook is a function that is invoked within the transaction when an object is modified. It is invoked when changes are done to the target configuration datastore, depending on the --target parameter.
This callback will be invoked before a EDIT1, EDIT2, or EDIT3 callback for the same object.
Set Hook can be invoked during the <load-config> operation, however, it is not allowed to add new edits. Any Add Edit API calls during this operation will be ignored.
Note
Manipulation of datastores nodes is only allowed for Set Hook and Post Set Hook callbacks. If any other In-Transaction callbacks invoke the Add Edit API the operation will be ignored.
Set Hook, Post Set Hook and Transaction Hook callbacks will not be invoked during an explicit <validate> operation and (for Set-Hook) in case the --sil-validate-candidate parameter is set to "false".
Set Hook Callback
The following function template definition is used for Set Hook callback functions:
-
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)
Typedef of the agt_cb_hook_t callback.
Callback function for server object handler Used to provide a callback for a specific named object
Set Hook: Similar to the EDIT1 or EDIT2 callbacks, except this callback is only invoked once at the start of the edit for a particular data node. This callback can alter the data in the current edit and also add new edits to the current transaction. This callback will be invoked before a EDIT1 or EDIT2 callback for the same object.
Post Set Hook: Is a postponed Set Hook callback analogous function that is invoked within the transaction when an object is modified but AFTER EDIT callback is done for the same object. This callback will be invoked AFTER a EDIT1 or EDIT2 callback for the same object.
Transaction Hook: Similar to a Set Hook except this callback is invoked just before the data is committed to the running datastore. This callback will be invoked after a EDIT1 or EDIT2 callbacks for the same object.
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param txcb:
transaction control block in progress
- Param editop:
edit operation enumeration for the node being edited
- Param newval:
container object holding the proposed changes to apply to the current config, depending on the editop value. Will not be NULL.
- Param curval:
current container values from the <running> or <candidate> configuration, if any. Could be NULL for create and other operations.
- Return:
status
Set Hook Callback Initialization and Cleanup
A Set Hook database edit callback function is registered with the 'agt_cb_hook_register' function, described below.
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_hook_register(const xmlChar *defpath, agt_hook_fmt_t format, agt_hook_type_t type, agt_cb_hook_t cbfn)
Register an object specific Hook callback function.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
format -- different hook formats dictates specific hook functionality
type -- different hook types dictates hook invocation point
cbfn -- address of callback function to use for all callback phases
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
/* Register an object specific Set Hook callback */
status_t res =
agt_cb_hook_register((const xmlChar *)"/if:trigger",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback_edit);
return res;
}
The 'format' parameter is used to specify how Set 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 used to set the type of callback. There are two options available for this parameter, either Set Hook or Transaction Hook 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.
-
void agt_cb_hook_unregister(const xmlChar *defpath)
Unregister a Hook callback.
This function unregisters a Hook callback.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
Example Usage:
void interfaces_cleanup (void)
{
// ...
agt_cb_hook_unregister((const xmlChar *)"/if:trigger");
// ...
}
Add Edit
The 'Add Edit' API is used to add an edit to the transaction in progress within Set Hook callback.
The Add Edit API limitations:
The Set Hook and Add Edit API are not part of the auto-generated code. The stub SIL or SIL-SA code must be updated manually.
Manipulation of datastore nodes is only allowed for Set Hook and Post Set Hook callbacks. If any other In-Transaction callbacks invoke the Add Edit API, the operation will be ignored.
The server disregards Add Edit requests in these situations: - During the load event. - For all callbacks except Set and Post Set Hook callbacks.
If the Add Edit is changing the same subtree as the current <edit-config> in progress, the server does not support the following: - The 'insert' and 'move' operations on the nodes being modified. - The skip_cb parameter for the added callbacks. - Updates to values are supported only when creating new values. Specifically:
A new value can be added or modified only during its creation.
Updating a new value is not permitted if a value already exists for the same node.
The MERGE operation on complex nodes is not supported, and the server does not support combining "add_edit" modifications, <edit-config> requested values, and existing values in the datastore.
The "add_edit" operation cannot be applied to the target node if any of its child nodes are already being modified by an <edit-config> request.
The skip_cb parameter can be used only when the default target is <running>. The server will always invoke callbacks for added edits if the target is <candidate>.
The Add Edit API allowed use cases:
Only allowed to be invoked from Set or Post Set Hook callbacks
Add extra node(s) that are not part of the current <edit-config>
Delete node(s) that are not part of the current <edit-config>
Update node(s) that are part of the current <edit-config> with above restrictions.
Add Edit API
The following function template definition is used for Add Edit API callback functions:
-
status_t agt_val_add_edit(ses_cb_t *scb, rpc_msg_t *msg, agt_cfg_transaction_t *txcb, const xmlChar *defpath, val_value_t *edit_value, op_editop_t editop)
Create new edit based on edit_value.
if its NULL or invalid the error will be generated.
Only allowed for Set Hooks or Post Set Hook, the rest are ignored. Add_edit is not allowed for default nodes or default NP-contaners
- Parameters:
scb -- session invoking the edit
msg -- incoming commit rpc_msg_t in progress
txcb -- transaction in progress
defpath -- XPath path of object instance
edit_value -- val_value_t representing newnode in transaction only needed for create, merge, replace, not delete
editop -- edit operation to use
- Returns:
status
Add Edit Extended API
The Add Edit Extended API can be used to insert or move new list or leaf-list entries to the transaction in progress. In addition, all other operation can be used. The insert parameters insert_where and insert_point must be set to NULL if they are not used.
The following function template definition is used for Add Edit Extended API callback functions:
-
status_t agt_val_add_edit_ex(ses_cb_t *scb, rpc_msg_t *msg, agt_cfg_transaction_t *txcb, const xmlChar *defpath, val_value_t *edit_value, const xmlChar *edit_operation, const xmlChar *insert_where, const xmlChar *insert_point)
Create a new edit based on edit_value.
if its NULL or invalid the error will be generated. (extended)
Move or insertion OP available.
Only allowed for Set Hooks or Post Set Hook, the rest are ignored. Add_edit is not allowed for default nodes or default NP-contaners
- Parameters:
scb -- session invoking the edit
msg -- incoming commit rpc_msg_t in progress
txcb -- transaction in progress
defpath -- XPath path of object instance
edit_value -- val_value_t representing newnode in transaction only needed for create, merge, replace, not delete
edit_operation -- <operation string>.
"create"
"delete"
"insert"
"merge"
"move"
"replace"
"remove"
insert_where -- <insert enum string>.
"before"
"after"
"first"
"last"
Will be used only if the operations are "move" or "insert". Ignored otherwise.
insert_point --
is a XPath encoded string like the defpath. Only for 'before' or 'after' insert_where paramter. The insert_where must be set to 'before' or 'after' if insert_point specified. Will be used only if the operations are "move" or "insert".
Ignored otherwise.
E.g: "/test3[string.1='entry2'][uint32.1='2']"
- Returns:
status
Add Edit Maximum API
The Add Edit maximum API can be used to insert or move new list or leaf-list entries to the transaction in progress and also controls whether the server should invoke callbacks for added edits or not. In addition, all other operation can be used. The insert parameters insert_where and insert_point must be set to NULL if they are not used.
The following function template definition is used for Add Edit Maximum API callback functions:
-
status_t agt_val_add_edit_max(ses_cb_t *scb, rpc_msg_t *msg, agt_cfg_transaction_t *txcb, const xmlChar *defpath, val_value_t *edit_value, const xmlChar *edit_operation, const xmlChar *insert_where, const xmlChar *insert_point, boolean skip_cb)
Create a new edit based on edit_value.
if its NULL or invalid the error will be generated.
Move or insertion OP available. Skip callbacks for added edits option is available.
Only allowed for Set Hooks or Post Set Hook, the rest are ignored.
- Parameters:
scb -- session invoking the edit
msg -- incoming commit rpc_msg_t in progress
txcb -- transaction in progress
defpath -- XPath path of object instance
edit_value -- val_value_t
representing newnode in transaction
only needed for create, merge, replace, not delete
edit_operation -- <operation string>.
"create"
"delete"
"insert"
"merge"
"move"
"replace"
"remove"
insert_where -- <insert enum string>.
"before"
"after"
"first"
"last"
Will be used only if the operations are "move" or "insert". Ignored otherwise.
insert_point --
is a XPath encoded string like the defpath. Only for 'before' or 'after' insert_where paramter. The insert_where must be set to 'before' or 'after' if insert_point specified. Will be used only if the operations are "move" or "insert".
Ignored otherwise.
E.g: "/test3[string.1='entry2'][uint32.1='2']"skip_cb -- TRUE if DO NOT invoke callbacks for an edded edit if any. FALSE if SKIP any callback for added edit including Transaction, EDIT1, EDIT2 callbacks Only when target=running
- Returns:
status
The following code snippet from agt/agt_val_silcall.c
shows the usage of this function:
agt_sil_added_edit_t *edit =
(agt_sil_added_edit_t *)dlq_firstEntry(edded_editQ);
for (; edit && (res == NO_ERR);
edit = (agt_sil_added_edit_t *)dlq_nextEntry(edit)) {
res =
agt_val_add_edit_max(scb,
msg,
txcb,
edit->path,
edit->editval,
edit->editop,
edit->where,
edit->point,
edit->skipcb);
if (res != NO_ERR) {
log_error("\nError: failed to add edit from subsystem '%s' "
"defpath '%s' (%s)\n",
subsys_id,
edit->path,
get_error_string(res));
}
}
SIL-SA Add Edit API
The SIL-SA version of the Add Edit API has a different function template since the transaction control block is not available in the SIL-SA code. The complete functionality of the Add Edit API is still available and all the edits will be added the same way as in SIL code.
The following function template definition is used for SIL-SA Add Edit API callback functions:
-
status_t sil_sa_add_edit(const xmlChar *defpath, val_value_t *edit_value, const xmlChar *edit_operation, const xmlChar *insert_where, const xmlChar *insert_point, boolean skip_cb)
Add an edit to the current transaction from SIL-SA.
Save an added edit in the SIL-SA Control block for further hook-response processing
Move or insertion OP available. Skip callbacks for added edits option is available.
Only allowed for Set Hooks or Post Set Hook, the rest are ignored.
See also
db_api_edit_full2
- Parameters:
defpath -- XPath path of object instance
edit_value -- val_value_t
representing newnode in transaction
only needed for create, merge, replace, not delete
edit_operation -- <operation string>
"create"
"delete"
"insert"
"merge"
"move"
"replace"
"remove"
insert_where -- <insert enum string> (NULL if not used)
"before"
"after"
"first"
"last"
Will be used only if the operations are "move" or "insert".
Ignored otherwise.
insert_point --
is a XPath encoded string like the defpath. Only for 'before' or 'after' insert_where paramter. The insert_where must be set to 'before' or 'after' if insert_point specified. Will be used only if the operations are "move" or "insert".
Ignored otherwise.
E.g: "/test3[string.1='entry2'][uint32.1='2']"skip_cb -- TRUE if DO NOT invoke callbacks for an edded edit if any. FALSE if SKIP any callback for added edit including Transaction, EDIT1, EDIT2 callbacks Only when target=running
- Returns:
status
The following code snippet shows how this API may be used,
if (newval &&
!xml_strcmp(VAL_NAME(newval), (const xmlChar *)"ports")) {
const xmlChar *edit_operation = (const xmlChar *)"delete";
const xmlChar *insert_point = NULL;
const xmlChar *insert_where = NULL;
boolean skip_cb = FALSE;
val_value_t *testval = sil_sa_get_data(NCX_CFGID_RUNNING,
defpath,
&res2);
if (testval) {
log_info("\n+++ Got testval %s (%s)",
VAL_NAME(testval),
get_error_string(res));
}
val_value_t *testval2 = sil_sa_get_data(NCX_CFGID_RUNNING,
defpath2,
&res2);
if (testval2) {
log_info("\n+++ Got testval2 %s (%s)",
VAL_NAME(testval2),
get_error_string(res));
}
if (testval) {
res =
sil_sa_add_edit(defpath,
NULL, // edit_value
edit_operation,
insert_where,
insert_point,
skip_cb);
}
}
Add Edit API Function Examples
The following YANG module will be used in these examples:
module sethook-example {
namespace "http://netconfcentral.org/ns/sethook-example";
prefix "sethook-ex";
revision 2024-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;
}
}
Add a New Node
The Set Hook callback was registered for the /trigger
leaf element.
Whenever the node is edited, the callback function will be invoked and
additional data can be added to the edit transaction.
In this example, an extra "interface" list entry will be generated, with key value equal to "vlan1" when a "trigger" node is getting edited with a specific value equal to "add-edit".
The callback function may look as follows:
#define EXAMPLE_MODNAME (const xmlChar *)"sethook-example"
/********************************************************************
* FUNCTION create_list_entry
*
* Make a list entry
*
* INPUTS:
* parentobj == parent object template to use
* keyname == value of the key
* res = return status
*
* RETURNS:
* val_value_t of listval entry if no error
* else NULL
*
*********************************************************************/
static val_value_t *
create_list_entry (obj_template_t *parentobj,
const xmlChar *keyname,
status_t *res)
{
/* get the obj template for the list obj */
obj_template_t *list_obj =
obj_find_child(parentobj,
EXAMPLE_MODNAME,
(const xmlChar *)"interface");
if (!list_obj) {
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
/* add all the /container/list nodes */
val_value_t *list_value = val_new_value();
if (!list_value) {
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
val_init_from_template(list_value, list_obj);
/* make key leaf entry */
val_value_t *child =
agt_make_leaf(list_obj,
(const xmlChar *)"name",
keyname,
res);
if (!child) {
val_free_value(list_value);
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
*res = val_child_add(child, list_value);
if (*res != NO_ERR) {
val_free_value(list_value);
return NULL;
}
/* make some extra leaf entries */
child =
agt_make_uint_leaf(list_obj,
(const xmlChar *)"hook-node",
(uint32)1000,
res);
if (!child) {
val_free_value(list_value);
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
*res = val_child_add(child, list_value);
if (*res != NO_ERR) {
val_free_value(child);
val_free_value(list_value);
return NULL;
}
/* generate the internal index Q chain */
*res = val_gen_index_chain(list_obj, list_value);
if (*res != NO_ERR) {
log_error("\nError: could not generate index chain (%s)",
get_error_string(*res));
val_free_value(list_value);
return NULL;
}
return list_value;
} /* create_list_entry */
/********************************************************************
* FUNCTION sethook_callback_edit
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set Hook:
* trigger: edit /if:trigger
* add_edit:
* add nodes: populate a container with 1 list entry with name=vlan1
*
* path: /if:interfaces/if:interface[if:name=vlan1]
*
*********************************************************************/
static status_t
sethook_callback_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 SET Hook callback");
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
/* Edit target. Note that this will create a new container with
* a list entry.
*/
const xmlChar *defpath =
(const xmlChar *)"/if: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 "/if: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(EXAMPLE_MODNAME,
(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, for more
* examples refer to libhooks-test/src/hooks-test.c library
*/
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:
FLAG_INT_ERROR;
return 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_edit */
/********************************************************************
* FUNCTION init_example_test
*
* Initialize the example module callbacks
*
*********************************************************************/
static status_t
init_example_test (void)
{
status_t res = NO_ERR;
/* Set Hook callback registration */
res = agt_cb_hook_register((const xmlChar *)"/sethook-ex:trigger",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback_edit);
if (res != NO_ERR) {
return res;
}
return NO_ERR;
} /* init_example_test */
/********************************************************************
* FUNCTION cleanup_example_test
*
* Cleanup the example module callbacks
*
*********************************************************************/
static void
cleanup_example_test (void)
{
/* Set Hook callback clean up */
agt_cb_unregister_callbacks(y_sethook_example_M_sethook_example,
(const xmlChar *)"/sethook-ex:trigger");
} /* cleanup_example_test */
Example <get> Response Message after the transaction is done:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces xmlns="http://netconfcentral.org/ns/sethook-example">
<interface>
<name>vlan1</name>
<hook-node>1000</hook-node>
</interface>
</interfaces>
<trigger>add-edit</trigger>
</rpc-reply>
Delete a Node
In this example, the /if:interfaces
container and
all its children will be deleted when the "trigger" leaf
node is getting deleted.
As a result, the server will delete the whole /if:interfaces
container if 'trigger' value is set to 'delete-all' and a client
attempts to delete the /trigger
node.
/********************************************************************
* FUNCTION sethook_callback_edit
*
* 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: /if:interfaces
*
*********************************************************************/
static status_t
sethook_callback_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 SET Hook callback");
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
const xmlChar *defpath =
(const xmlChar *)"/if: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:
FLAG_INT_ERROR;
return 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_edit */
Update a New Node
Refer to the Add Edit API limitations for updating the newval in the same transaction.
The Set Hook callback is very powerful tool and the callback function can be used to manipulate with new values in the <edit-config>. However, it is only supported to update values in the edit configuration when the CREATE operation is used to create new values. It is NOT supported to update values for any other operation.
The following YANG module will be used in these examples:
module sethook-example_update {
namespace "http://netconfcentral.org/ns/sethook-example";
prefix "sethook-ex";
revision 2024-03-20 {
description "Initial revision.";
}
container interfaces { // edit2 cb + Set Hook (Node)
leaf status {
type string;
}
list interface { // edit2 cb
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 trigger { // edit2 cb
type string;
}
container subsystems { // edit2 cb
presence true;
list subsystem { // edit2 cb + Set Hook (Node)
key "name";
leaf name {
type string;
}
container cpu-resource { // edit2 cb
list socket { // edit2 cb
key "socket-id";
leaf socket-id {
type uint32;
}
container cpu { // edit2 cb
leaf num-of-cores {
type uint32;
}
}
}
}
container worker-threads { // edit2 cb
list worker-thread { // edit2 cb
key "thread-name";
leaf thread-name {
type string {
length "1..128";
}
}
}
}
}
}
}
Update Example 1
Assume we registered Set Hook callback for the "interfaces" container node. Thus, whenever the node /interfaces node is edited, the Set Hook callback function will be called and additional specific data can be populated with desired values.
In this example, we will generate an extra "status" leaf when we create a new "interfaces" container. 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: create /interfaces
* create container and populate extra nodes
*
* add_edit effect: populate extra nodes within the container
* when /interfaces is created
*
*********************************************************************/
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;
/* defpath specified target root */
const xmlChar *defpath =
(const xmlChar *)"/sethook-ex:interfaces";
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
break;
case OP_EDITOP_CREATE:
/* populate a new container with several leafs */
if (newval) {
/* use newval value to write a new edits */
val_value_t *editval =
val_clone_config_data(newval, &res);
if (editval == NULL) {
return ERR_INTERNAL_MEM;
}
/* add any other children to target container, can add any amount
* of children here.
*/
obj_template_t *chobj =
obj_find_child(VAL_OBJ(editval),
y_sethook_ex_M_sethook_ex,
(const xmlChar *)"status");
if (chobj) {
val_value_t *child =
val_make_simval_obj(chobj,
(const xmlChar *)"extra-leaf",
&res);
if (child) {
res = val_child_add(child, editval);
}
} else {
res = ERR_NCX_NOT_FOUND;
}
if (res == NO_ERR) {
/* add a new edit on defpath and populate new entries */
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 */
/********************************************************************
* FUNCTION y_sethook_example_update_init
*
* initialize the sethook-example server instrumentation library
*
* INPUTS:
* modname == requested module name
* revision == requested version (NULL for any)
*
* RETURNS:
* error status
********************************************************************/
status_t
y_sethook_example_update_init (const xmlChar *modname,
const xmlChar *revision)
{
status_t res = NO_ERR;
...
/* Set Hook to update edits in same transaction */
res = agt_cb_hook_register((const xmlChar *)"/sethook-ex:interfaces",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback);
if (res != NO_ERR) {
return res;
}
return res;
} /* y_sethook_example_update_init */
/********************************************************************
* FUNCTION y_sethook_example_update_cleanup
* cleanup the server instrumentation library
*
********************************************************************/
void y_sethook_example_update_cleanup (void)
{
...
/* Set Hook callback clean up */
agt_cb_unregister_callbacks(y_sethook_ex_M_sethook_ex,
(const xmlChar *)"/sethook-ex:interfaces");
} /* y_sethook_example_update_cleanup */
Note the callback format should be AGT_HOOKFMT_NODE in order to invoke this callback only for a new node edit. If the format is AGT_HOOKFMT_SUBTREE, the callback will be invoked if you edit children as well. In this case newval will not represent container value and cannot not be used to construct a new updated edit value.
As a result, whenever some north bound agent edit the /interfaces container with a specific value, the callback is invoked and additionally adds an extra leaf within the same container.
Edit-config in this scenario may look as follow:
<edit-config>
<target>
<candidate/>
</target>
<default-operation>merge</default-operation>
<test-option>set</test-option>
<config>
<interfaces xmlns="http://netconfcentral.org/ns/sethook-example">
<interface>
<name xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
nc:operation="create">vlan2</name>
</interface>
</interfaces>
</config>
</edit-config>
The following output may be seen during the operation described above.
Start invoking Set Hook callback for create on sethook-example_update:interfaces
Enter SET Hook callback
Start adding Set Hook edit for merge on /sethook-ex:interfaces
add_edit: expand container undo for 'sethook-example_update:interfaces'
Finish invoking Set Hook callback on sethook-example_update:interfaces
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">
<status>extra-leaf</status>
<interface>
<name>vlan2</name>
</interface>
</interfaces>
</data>
Update Example 2
Assume we registered Set Hook callback for the "subsystem" list node. Thus, whenever the node "/subsystems/subsystem" node is edited, the Set Hook callback function will be called and additional specific data can be populated with desired values.
In this example, we will generate an extra "/subsystems/subsystem/worker-threads" container when we create a new "/subsystems/subsystem" node.
The callback function may look as follows:
/********************************************************************
* FUNCTION create_list_entry
*
* Make a list entry based on the key
*
* INPUTS:
* res = return status
*
* RETURNS:
* val_value_t of listval entry if no error
* else NULL
*
*********************************************************************/
static val_value_t *
create_list_entry (obj_template_t *list_obj,
const xmlChar *keyname,
const xmlChar *keystr,
status_t *res)
{
/* add all the /container/list nodes */
val_value_t *list_value = val_new_value();
if (!list_value) {
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
val_init_from_template(list_value, list_obj);
/* make key leaf entry */
val_value_t *child =
agt_make_leaf(list_obj,
keyname,
keystr,
res);
if (!child) {
val_free_value(list_value);
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
val_add_child(child, list_value);
/* generate the internal index Q chain */
*res = val_gen_index_chain(list_obj, list_value);
if (*res != NO_ERR) {
log_error("\nError: could not generate index chain (%s)",
get_error_string(*res));
val_free_value(list_value);
return NULL;
}
return list_value;
} /* create_list_entry */
/********************************************************************
* FUNCTION sethook_callback2
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set-Hook: (
* format: NODE; SUBTREE is NOT supported
* trigger: CREATE
* path: /subsystems/subsystem[name='virtual']
*
* add_edit effect: populate extra container
* when /subsystems/subsystem is created
*
* 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
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)
{
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
handle_other(newval, curval);
val_value_t *editval = NULL;
obj_template_t *cont_obj = NULL;
obj_template_t *list_obj = NULL;
if (newval) {
cont_obj =
obj_find_child(VAL_OBJ(newval),
y_sethook_ex_M_sethook_ex,
(const xmlChar *)"worker-threads");
if (cont_obj) {
list_obj =
obj_find_child(cont_obj,
y_sethook_ex_M_sethook_ex,
(const xmlChar *)"worker-thread");
}
}
/* defpath specified target root */
const xmlChar *defpath =
(const xmlChar *)"/subsystems/subsystem[name='virtual']";
boolean add_edit = FALSE;
if (newval && cont_obj && list_obj) {
val_value_t *key_val =
val_find_child(newval,
y_sethook_ex_M_sethook_ex,
(const xmlChar *)"name");
if (key_val &&
!xml_strcmp(VAL_STR(key_val), (const xmlChar *)"virtual")) {
add_edit = TRUE;
}
}
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
break;
case OP_EDITOP_CREATE:
if (add_edit) {
log_info("\n\n Start adding Edit value for worker-thread");
/* clone value in order to write a new edits */
editval = val_clone_config_data(newval, &res);
if (!editval) {
return ERR_NCX_INVALID_VALUE;
}
/* now can add more children to the newval */
val_value_t *cont_val = val_new_value();
if (!cont_val) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(cont_val, cont_obj);
/* Create list entries */
val_value_t *list_value =
create_list_entry(list_obj,
(const xmlChar *)"thread-name",
(const xmlChar *)"worker-0-1",
&res);
if (!list_value) {
log_error("\ncreate_list_entry failed, res %d", res);
val_free_value(cont_val);
val_free_value(editval);
return res;
}
res = val_child_add(list_value, cont_val);
if (res == NO_ERR) {
res = val_child_add(cont_val, editval);
if (res == ERR_NCX_DUP_ENTRY) {
/* In case NP container already exist (was created by the server
* as a default NP-container)
* clean up the value before adding a new one
*/
val_value_t *chval =
val_find_child_fast(editval,
obj_get_nsid(VAL_OBJ(cont_val)),
obj_get_name(VAL_OBJ(cont_val)));
if (chval && val_is_default(chval)) {
val_remove_child(chval);
val_free_value(chval);
chval = NULL;
res = val_child_add(cont_val, editval);
}
}
}
log_info("\n\n Edit value - added container to value");
val_dump_value(editval, 3, DBG4);
/* add a new edit on defpath and populate new entry */
if (res == NO_ERR) {
res = agt_val_add_edit(scb,
msg,
txcb,
defpath,
editval,
OP_EDITOP_CREATE);
}
/* 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_callback2 */
/********************************************************************
* FUNCTION y_sethook_example_update_init
*
* initialize the sethook-example server instrumentation library
*
* INPUTS:
* modname == requested module name
* revision == requested version (NULL for any)
*
* RETURNS:
* error status
********************************************************************/
status_t
y_sethook_example_update_init (const xmlChar *modname,
const xmlChar *revision)
{
status_t res = NO_ERR;
...
/* Set Hook to update edits in same transaction */
res = agt_cb_hook_register((const xmlChar *)"/sethook-ex:subsystems/sethook-ex:subsystem",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback2);
if (res != NO_ERR) {
return res;
}
return res;
} /* y_sethook_example_update_init */
/********************************************************************
* FUNCTION y_sethook_example_update_cleanup
* cleanup the server instrumentation library
*
********************************************************************/
void y_sethook_example_update_cleanup (void)
{
...
/* Set Hook callback clean up */
agt_cb_unregister_callbacks(y_sethook_example_M_sethook_example,
(const xmlChar *)"/sethook-ex:subsystems/sethook-ex:subsystem");
} /* y_sethook_example_update_cleanup */
As a result, whenever some north bound agent creates the "/subsystems/subsystem", the callback is invoked and additionally creates a new "/subsystems/subsystem/worker-threads" container.
Edit-config in this scenario may look as follow:
<edit-config>
<target>
<candidate/>
</target>
<config>
<subsystems xmlns="http://netconfcentral.org/ns/sethook-example">
<subsystem>
<name>virtual</name>
<cpu-resource>
<socket>
<socket-id>0</socket-id>
<cpu>
<num-of-cores>1</num-of-cores>
</cpu>
</socket>
</cpu-resource>
</subsystem>
<subsystem>
<name>virtual2</name>
<cpu-resource>
<socket>
<socket-id>0</socket-id>
<cpu>
<num-of-cores>1</num-of-cores>
</cpu>
</socket>
</cpu-resource>
</subsystem>
</subsystems>
</config>
</edit-config>
To ensure that the data added by "add_edit" was successfully generated an application can retrieve running configurations.
The server should reply with:
<data>
<subsystems xmlns="http://netconfcentral.org/ns/sethook-example">
<subsystem>
<name>virtual</name>
<cpu-resource>
<socket>
<socket-id>0</socket-id>
<cpu>
<num-of-cores>1</num-of-cores>
</cpu>
</socket>
</cpu-resource>
<worker-threads>
<worker-thread>
<thread-name>worker-0-1</thread-name>
</worker-thread>
</worker-threads>
</subsystem>
<subsystem>
<name>virtual2</name>
<cpu-resource>
<socket>
<socket-id>0</socket-id>
<cpu>
<num-of-cores>1</num-of-cores>
</cpu>
</socket>
</cpu-resource>
<worker-threads>
<worker-thread>
<thread-name>worker-0-1</thread-name>
</worker-thread>
</worker-threads>
</subsystem>
</subsystems>
</data>
Add Edit Extended API Function Examples
This section will demonstrate how to insert or move a specific list or leaf-list entry using the 'add_edit_ex' API function. This API is only allowed to be used within the Set Hook callbacks as described in the previous sections.
Note
The server does not support the insert and move operation on the nodes that are being modified in the same transaction at the same time.
The following YANG module will be used for this example:
module example {
namespace "http://netconfcentral.org/ns/example";
prefix "ex";
revision 2019-01-01 {
description "Initial revision.";
}
leaf insert-list-check {
type string;
}
leaf insert-leaf-list-check {
type string;
}
leaf move-list-check {
type string;
}
leaf move-leaf-list-check {
type string;
}
list hook-list-test {
key name;
ordered-by user;
leaf name {
type string;
}
leaf a2 {
type string;
}
leaf b2 {
type uint32;
default 5;
}
}
leaf-list hook-leaf-list-test {
ordered-by user;
type string;
}
}
Insert New List Nodes
Assume the Set Hook callback for the "insert-list-check"
leaf element was registered. Whenever the node
/ex:insert-list-check
is edited,
the callback function will be called and additional specific entry can
be inserted or moved within the datastore.
In this example, new "hook-list-test" list entries will be inserted when a "insert-list-check" node is modified.
#define HOOKS_TEST_MOD (const xmlChar *)"example"
/********************************************************************
* FUNCTION create_list_entry
*
* Make a list entry based on the key
*
* INPUTS:
* res = return status
*
* RETURNS:
* val_value_t of listval entry if no error
* else NULL
*
*********************************************************************/
static val_value_t *
create_list_entry (obj_template_t *list_obj,
const xmlChar *keyname,
const xmlChar *keystr,
status_t *res)
{
/* add all the /container/list nodes */
val_value_t *list_value = val_new_value();
if (!list_value) {
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
val_init_from_template(list_value, list_obj);
/* make key leaf entry */
val_value_t *child =
agt_make_leaf(list_obj,
keyname,
keystr,
res);
if (!child) {
val_free_value(list_value);
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
val_add_child(child, list_value);
/* generate the internal index Q chain */
*res = val_gen_index_chain(list_obj, list_value);
if (*res != NO_ERR) {
log_error("\nError: could not generate index chain (%s)",
get_error_string(*res));
val_free_value(list_value);
return NULL;
}
return list_value;
} /* create_list_entry */
/********************************************************************
* FUNCTION hooks_sethook_edit_insert
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* 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_sethook_edit_insert (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)
{
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
/* look for a top-node data object */
obj_template_t *targobj =
ncx_match_any_object_ex(HOOKS_TEST_MOD,
(const xmlChar *)"hook-list-test",
FALSE,
NCX_MATCH_FIRST,
FALSE,
&res);
if (!targobj) {
return ERR_NCX_NOT_FOUND;
}
val_value_t *editval = NULL;
const xmlChar *defpath = NULL;
const xmlChar *edit_operation = NULL;
const xmlChar *insert_point = NULL;
const xmlChar *insert_where = NULL;
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
case OP_EDITOP_CREATE:
/* INSERT KEY2 AFTER KEY1 */
if (newval && !xml_strcmp(VAL_STR(newval), (const xmlChar *)"trigger")) {
editval =
create_list_entry(targobj,
(const xmlChar *)"name",
(const xmlChar *)"key2",
&res);
if (!editval) {
return res;
}
/* defpath specified target */
defpath = (const xmlChar *)"/ex:hook-list-test[name='key2']";
edit_operation = (const xmlChar *)"insert";
insert_where = (const xmlChar *)"after";
/* assume we already have key1 in the datastore */
insert_point = (const xmlChar *)"/ex:hook-list-test[name='key1']";
/* INSERT KEY3 BEFORE KEY1 */
} else if (newval &&
!xml_strcmp(VAL_STR(newval), (const xmlChar *)"trigger2")) {
editval =
create_list_entry(targobj,
(const xmlChar *)"name",
(const xmlChar *)"key3",
&res);
if (!editval) {
return res;
}
/* defpath specified target */
defpath = (const xmlChar *)"/ex:hook-list-test[name='key3']";
edit_operation = (const xmlChar *)"insert";
insert_where = (const xmlChar *)"before";
insert_point = (const xmlChar *)"/ex:hook-list-test[name='key1']";
/* INSERT KEY4 LAST */
} else if (newval &&
!xml_strcmp(VAL_STR(newval), (const xmlChar *)"trigger3")) {
editval =
create_list_entry(targobj,
(const xmlChar *)"name",
(const xmlChar *)"key4",
&res);
if (!editval) {
return res;
}
/* defpath specified target */
defpath = (const xmlChar *)"/ex:hook-list-test[name='key4']";
edit_operation = (const xmlChar *)"insert";
insert_where = (const xmlChar *)"last";
insert_point = NULL;
/* INSERT KEY5 FIRST */
} else if (newval &&
!xml_strcmp(VAL_STR(newval), (const xmlChar *)"trigger4")) {
editval =
create_list_entry(targobj,
(const xmlChar *)"name",
(const xmlChar *)"key5",
&res);
if (!editval) {
return res;
}
/* defpath specified target */
defpath = (const xmlChar *)"/ex:hook-list-test[name='key5']";
edit_operation = (const xmlChar *)"insert";
insert_where = (const xmlChar *)"first";
insert_point = NULL;
/* Negative test, missing insert point */
} else if (newval &&
!xml_strcmp(VAL_STR(newval), (const xmlChar *)"trigger-fail")) {
editval =
create_list_entry(targobj,
(const xmlChar *)"name",
(const xmlChar *)"NEW",
&res);
if (!editval) {
return res;
}
/* defpath specified target */
defpath = (const xmlChar *)"/ex:hook-list-test[name='NEW']";
edit_operation = (const xmlChar *)"insert";
insert_where = (const xmlChar *)"before";
insert_point = (const xmlChar *)"/ex:hook-list-test[name='MISSING']";
/* Negative test, where is missing */
} else if (newval &&
!xml_strcmp(VAL_STR(newval), (const xmlChar *)"trigger-fail2")) {
editval =
create_list_entry(targobj,
(const xmlChar *)"name",
(const xmlChar *)"NEW",
&res);
if (!editval) {
return res;
}
/* defpath specified target */
defpath = (const xmlChar *)"/ex:hook-list-test[name='NEW']";
edit_operation = (const xmlChar *)"insert";
insert_where = NULL;
insert_point = (const xmlChar *)"/ex:hook-list-test[name='key2']";
} else {
/* nothing to add or modify. Specific criteria not met */
break;
}
/* add a new edit on defpath and populate new entry */
if (res == NO_ERR) {
res = agt_val_add_edit_ex(scb,
msg,
txcb,
defpath,
editval,
edit_operation,
insert_where,
insert_point);
}
if (editval) {
/* clean up the editval */
val_free_value(editval);
}
/* Final Result should look as follows:
*
* example:hook-list-test key5 { inserted 'first' by Set-Hook
* name key5
* }
* example:hook-list-test key3 { inserted 'before' by Set-Hook
* name key3
* }
* example:hook-list-test key1 { created by edit-config
* name key1
* }
* example:hook-list-test key2 { inserted 'after' by Set-Hook
* name key2
* }
* example:hook-list-test key4 { inserted 'last' by Set-Hook
* name key4
* }
*
*/
break;
case OP_EDITOP_DELETE:
break;
default:
FLAG_INT_ERROR;
return 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_sethook_edit_insert */
/********************************************************************
* FUNCTION init_example_test
*
* Initialize the example module callbacks
*
*********************************************************************/
static status_t
init_example_test (void)
{
status_t res = NO_ERR;
res = agt_cb_hook_register((const xmlChar *)"/ex:insert-list-check",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
hooks_sethook_edit_insert);
if (res != NO_ERR) {
return res;
}
return NO_ERR;
} /* init_example_test */
/********************************************************************
* FUNCTION cleanup_example_test
*
* Cleanup the example module callbacks
*
*********************************************************************/
static void
cleanup_example_test (void)
{
agt_cb_hook_unregister((const xmlChar *)"/ex:insert-list-check");
} /* cleanup_example_test */
SIL-SA Set Hook Callback
The Set Hook API is available for SIL-SA functions.
The following function template definition is used for Set Hook callback for SIL-SA functions:
-
typedef status_t (*agt_cb_sa_hook_t)(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)
Typedef of the SIL-SA version of agt_cb_hook_t callback.
Callback function for server object handler Used to provide a callback for a specific named object
Set Hook: Similar to the EDIT1 or EDIT2 callbacks, except this callback is only invoked once at the start of the edit for a particular data node. This callback can alter the data in the current edit and also add new edits to the current transaction. This callback will be invoked before a EDIT1 or EDIT2 callback for the same object.
Post Set Hook: Is a postponed Set Hook callback analogous function that is invoked within the transaction when an object is modified but AFTER EDIT callback is done for the same object. This callback will be invoked AFTER a EDIT1 or EDIT2 callback for the same object.
Transaction Hook: Similar to a Set Hook except this callback is invoked just before the data is committed to the running datastore. This callback will be invoked after a EDIT1 or EDIT2 callbacks for the same object.
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param editop:
edit operation enumeration for the node being edited
- Param newval:
container object holding the proposed changes to apply to the current config, depending on the editop value. Will not be NULL.
- Param curval:
current container values from the <running> or <candidate> configuration, if any. Could be NULL for create and other operations.
- Param transaction_id:
transaction ID of the transaction control block in progress
- Param isvalidate:
TRUE if this Transaction is for <validate> operation
- Param isload:
TRUE if this Transaction is for a Load operation
- Param isrunning:
TRUE if this Transaction is for the the running datastore
- Return:
status
SIL-SA Set Hook Callback Initialization and Cleanup
The 'Set Hook SIL-SA' callback function is registered with the 'agt_cb_sa_hook_register' function.
The 'format' parameter is used to specify how Set Hook callbacks will be invoked.
There are two options available:
AGT_HOOKFMT_NODE
: Invoked only when the node that registered the callback is modified.AGT_HOOKFMT_SUBTREE
: Invoked if any node in the subtree is modified.
The 'type' parameter is used to set the type of callback.
There are two options available:
AGT_HOOK_TYPE_SETHOOK
: Register a Set Hook callback.AGT_HOOK_TYPE_TRANSACTION
: Register Transaction Hook callback.
-
status_t agt_cb_sa_hook_register(const xmlChar *defpath, agt_hook_fmt_t format, agt_hook_type_t type, agt_cb_sa_hook_t cbfn)
Register an object specific Hook callback function.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
format -- different hook formats dictates specific hook functionality
type -- different hook types dictates hook invocation point
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Note
Set Hook callbacks will not be invoked if any of the following conditions are true:
<validate> operation is being processed
--sil-validate-candidate=false and target is <candidate>
--sil-skip-load=true and operation is <load-config>
Example initialization function:
static status_t interfaces_init (void)
{
/* Register an object specific Set Hook callback */
status_t res =
agt_cb_sa_hook_register((const xmlChar *)"/if:trigger",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback_edit);
// ...
}
-
void agt_cb_sa_hook_unregister(const xmlChar *defpath)
Unregister a SIL-SA Hook SIL-SA callback.
This function unregisters a Hook callback.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
Example cleanup function:
void interfaces_cleanup (void)
{
// ...
agt_cb_sa_hook_unregister((const xmlChar *)"/if:trigger");
// ...
}
Important Differences Between SIL and SIL-SA
The following table illustrates most important difference between SIL and SIL-SA version of the Set Hook callback:
Difference |
SIL |
SIL-SA |
Type Definition |
agt_cb_hook_t |
agt_cb_sa_hook_t |
Registration Function |
ag t_cb_hook_register() |
agt_c b_sa_hook_register() |
Clean Up Function |
agt_ cb_hook_unregister() |
agt_cb_ sa_hook_unregister() |
Add Edit API |
agt_val_add_edit() |
sil_sa_add_edit() |
Get Data API |
agt_val_get_data() |
sil_sa_get_data() |
Transaction Control Block |
Available |
NOT available |
SIL-SA Add Edit API Function Examples
The following YANG module is used for this example:
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 a Set Hook callback is registered for the "trigger" leaf node. In this example, an extra "interface" list entry with key value equal to "vlan1" will be generated, when a "trigger" node is getting edited with as specific value equal to "add-edit":
/********************************************************************
* 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 */
An <edit-config> request that will trigger desired Set Hook actions may look as follows:
<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>
As a result, the callback is invoked and additionally creates a new
interface /interfaces/interface[name=value]
.
Example Response with the created data:
<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>
Refer to Add Edit for more examples.
Post Set Hook
A Post Set Hook callback is a postponed Set Hook callback that is invoked within the transaction when an object is modified but AFTER the EDIT callback is done for the same object.
Note
This callback will not be invoked in case the --sil-root-check-first parameter is set to TRUE when the server is run with --target=running .
The same callback template is used for Set Hook and Post Set Hook. Refer to the Set Hook Callback section for details.
Post Set Hook Callback Initialization and Cleanup
A Post Set Hook callback function is registered with the 'agt_cb_post_sethook_register' function, described below. The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_post_sethook_register(const xmlChar *defpath, agt_cb_hook_t cbfn)
Register an object specific Post Set Hook callback function.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
cbfn -- address of callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* Register an object specific Post Set Hook callback */
res =
agt_cb_post_sethook_register((const xmlChar *)"/if:trigger",
sethook_callback_edit);
// ...
}
The same "unregister" function is used as a Set Hook. Refer to the Set Hook Callback Initialization and Cleanup section for details.
Example Usage:
void interfaces_cleanup (void)
{
// ...
agt_cb_hook_unregister((const xmlChar *)"/if:trigger");
// ...
}
SIL-SA Post Set Hook Callback
A SIL-SA version of the Post Set Hook callback is supported. This callback serves the same purpose as the Post Set Hook callback.
SIL-SA Post Set Hook Callback Initialization and Cleanup
A Post Set Hook callback function is hooked into the server with the 'agt_cb_sa_post_sethook_register' function, described below. The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_sa_post_sethook_register(const xmlChar *defpath, agt_cb_sa_hook_t cbfn)
Register an object specific SIL-SA Post Set Hook callback function.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* Register an object specific Post Set Hook callback */
res =
agt_cb_sa_post_sethook_register((const xmlChar *)"/if:trigger",
post_sethook_callback_edit);
// ...
}
-
void agt_cb_sa_post_sethook_unregister(const xmlChar *defpath)
Unregister a SIL-SA Post Set Hook callback.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback unregister
Example Usage:
void interfaces_cleanup (void)
{
// ...
agt_cb_sa_hook_unregister((const xmlChar *)"/if:trigger");
// ...
}
Hooks Callback Interaction with EDIT2 or EDIT3 Callbacks
In case there are multiple Set-Hook and Post Set Hook callbacks registered and the callbacks add several extra edits to the transaction with help of Add Edit API the server will have the following interaction with added edits and normal edits that are coming from PDU.
Assume a Set-Hook, Post Set Hook, Set-Order-Hook, Transaction-Hook and EDIT2 or EDIT3 callback are all registered for the same object.
Any time the 'interface' list is edited the server invokes the Set-Hook and Post Set Hook callback, which adds an extra 'interface' entry, in addition to the regular edit.
The following callback invocation order is expected in this case:
Hook Callback for <candidate> Datastore
Edit on candidate datastore:
Transaction Start
Set Order Hook for
/if:interface[name=vlan1]
Set Hook for
/if:interface[name=vlan1]
to add/if:interface[name=vlan2]
Set Order Hook for
/if:interface[name=vlan2]
SIL Callback (Validate Phase) for
/if:interface[name=vlan1]
Post Set Hook for
/if:interface[name=vlan1]
to add/if:interface[name=vlan3]
SIL Callback (Validate Phase) for
/if:interface[name=vlan2]
SIL Callback (Validate Phase) for
/if:interface[name=vlan3]
Transaction Complete
Edit on running datastore (after <commit>):
Transaction Start
SIL Callback (Validate Phase) for
/if:interface[name=vlan1]
SIL Callback (Validate Phase) for
/if:interface[name=vlan2]
SIL Callback (Validate Phase) for
/if:interface[name=vlan3]
Validate Complete
SIL Callback (Apply Phase) for
/if:interface[name=vlan1]
SIL Callback (Apply Phase) for
/if:interface[name=vlan2]
SIL Callback (Apply Phase) for
/if:interface[name=vlan3]
Apply Complete
SIL Callback (Commit Phase) for
/if:interface[name=vlan1]
SIL Callback (Commit Phase) for
/if:interface[name=vlan2]
SIL Callback (Commit Phase) for
/if:interface[name=vlan3]
Transaction Hook for
/if:interface[name=vlan1]
Transaction Hook for
/if:interface[name=vlan2]
Transaction Hook for
/if:interface[name=vlan3]
Commit/Rollback Complete
Transaction Complete
Hook Callback for <running> Datastore
Assume the same scenario but the default target in this case is set to <running>. The callbacks invocation order is expected to be:
Transaction Start
Set Order Hook for
/if:interface[name=vlan1]
Set Hook for
/if:interface[name=vlan1]
to add/if:interface[name=vlan2]
Set Order Hook for
/if:interface[name=vlan2]
SIL Callback (Validate Phase) for
/if:interface[name=vlan1]
Post Set Hook for
/if:interface[name=vlan1]
to add/if:interface[name=vlan3]
SIL Callback (Validate Phase) for
/if:interface[name=vlan2]
SIL Callback (Validate Phase) for
/if:interface[name=vlan3]
SIL Callback (Apply Phase) for
/if:interface[name=vlan1]
SIL Callback (Apply Phase) for
/if:interface[name=vlan2]
SIL Callback (Apply Phase) for
/if:interface[name=vlan3]
SIL Callback (Commit Phase) for
/if:interface[name=vlan1]
SIL Callback (Commit Phase) for
/if:interface[name=vlan2]
SIL Callback (Commit Phase) for
/if:interface[name=vlan3]
Transaction Hook for
/if:interface[name=vlan1]
Transaction Hook for
/if:interface[name=vlan2]
Transaction Hook for
/if:interface[name=vlan3]
Transaction Complete
In case default target is set to <running> there is an option to control callback invocation for added edits with help of a new "agt_val_add_edit_max" API. Refer to the Add Edit Maximum API section for details.
If the 'skip_c'* parameter is set to TRUE then the server will not invoke any callback for added edits. In this case the callback order will look as follows. Assume the same scenario but the default target in this case is set to <running>.
Transaction Start
Set Order Hook for
/if:interface[name=vlan1]
Set Hook for
/if:interface[name=vlan1]
to add/if:interface[name=vlan2]
Set Order Hook for
/if:interface[name=vlan2]
SIL Callback (Validate Phase) for
/if:interface[name=vlan1]
Post Set Hook for
/if:interface[name=vlan1]
to add/if:interface[name=vlan3]
SIL Callback (Apply Phase) for
/if:interface[name=vlan1]
SIL Callback (Commit Phase) for
/if:interface[name=vlan1]
Transaction Hook for
/if:interface[name=vlan1]
Transaction Complete
Transaction Hook
A Transaction Hook is a function that is invoked within the transaction when an object is modified.
The Transaction Hook is similar to the Set Hook except this callback is invoked after the data is committed to the running datastore during Commit Phase.
This callback will be invoked after all EDIT callbacks for the same object.
Transaction Hook Callback
The same callback template is used for Set Hook and Transaction Hook. Refer to the Set Hook Callback section for details.
Transaction Hook Callback Initialization and Cleanup
A Transaction Hook callback function is registered and unregistered with the 'agt_cb_hook_register' and 'agt_cb_hook_unregister; functions.
Refer to the Set Hook Callback Initialization and Cleanup section for details.
Note
- If the callback is registred to a list node:
The server will invoke the Transaction callback for each modified list entry.
If the format is set to
AGT_HOOKFMT_SUBTREE
, the server will only invoke the callback for list entries where the list's child nodes have been modified.
The following example shows how a Transaction Hook may be registered:
static status_t interfaces_init (void)
{
// ...
/* Register an object specific Transaction Hook callback */
res =
agt_cb_hook_register((const xmlChar *)"/if:trigger",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_TRANSACTION,
trans_callback_edit);
// ...
}
Transaction Hook Callback Function Example
Whenever a /if:interfaces
node is edited,
the callback is invoked and additionally validates the
/if:interfaces/if:status
node. Based on this validation, the
operation can be denied.
/********************************************************************
* FUNCTION transhook_callback
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Transaction-Hook:
* trigger: DELETE /interface
* effect:
* - if testval node exist the CREATE operation will be denied
* - if testval is ‘deny-delete’, the operation will be denied
*
********************************************************************/
static status_t
transhook_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 Transaction-Hook callback");
status_t res = NO_ERR;
status_t res2 = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
const xmlChar *defpath =
(const xmlChar *)"/if:interfaces/if:status";
/* find a test node and validate its value */
val_value_t *testval =
agt_val_get_data(txcb->cfg_id,
defpath,
&res2);
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
case OP_EDITOP_CREATE:
/* deny an edit, if the test exist */
if (testval) {
res = ERR_NCX_ACCESS_DENIED;
}
break;
case OP_EDITOP_DELETE:
/* deny an edit, if the test value set to “deny-delete” */
if (testval &&
!xml_strcmp(VAL_STR(testval), (const xmlChar *)"deny-delete") {
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_transhook_edit */
SIL-SA Transaction Hook Callback
Refer to the Transaction Hook section for more details.
The Transaction Hook uses the same function template as the Set Hook. Refer to the SIL-SA Set Hook Callback section for details.
SIL-SA Transaction Hook Callback Initialization and Cleanup
A SIL-SA Transaction Hook callback function is registered with the 'agt_cb_sa_hook_register' function, described below.
The SIL-SA Transaction Hook uses the same registration function as the SIL-SA Set Hook. Refer to the SIL-SA Set Hook Callback Initialization and Cleanup section for details.
The Transaction Hook callbacks will not be invoked during explicit
<validate> operation and the --sil-skip-load CLI parameter is
set to TRUE
.
Example Initialization function for a Transaction Hook callback:
static status_t interfaces_init (void)
{
// ...
/* Register an object specific Transaction Hook callback */
res =
agt_cb_sa_hook_register((const xmlChar *)"/if:trigger",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_TRANSACTION,
trans_sa_callback_edit);
// ...
}
SIL-SA Transaction Hook Callback Function Example
The Transaction Hook can be set to specific object at run-time with
a callback as follows.
Register the Transaction Hook for the /example
node of the
example YANG module.
Whenever the /example
node is edited, the Transaction Hook
callback will be invoked and it will perform specific validation
actions.
Example callback function:
/********************************************************************
* FUNCTION hook_cb
*
* 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
* 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.
* transaction_id == transaction ID of the transaction control block in progress
* isvalidate == TRUE if this Transaction is for <validate> operation
* isload == TRUE if this Transaction is for a Load operation
* isrunning == TRUE if this Transaction is for the the running datastore
*
* RETURNS:
* status
********************************************************************/
static status_t
hook_cb (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;
status_t res2 = 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********************************************");
log_debug2("\nEnter hooks_sethook_edit callback for silsa-test "
"---- 1");
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");
}
/* get the node that want to validate */
val_value_t *testval =
sil_sa_get_data(NCX_CFGID_RUNNING,
(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;
} /* hook_cb */
Transaction Start
The Transaction Start function is the user/system callback that is invoked before any changes to the candidate database will be committed.
This is a good place to initialize specific pointers that will be cleaned after the transaction complete during the Transaction Complete callback invocation.
Transaction Start Callback will be invoked for the following operations:
edit-config
restore
commit
validate
Transaction Start Callback
The following function template definition is used for Transaction Start callback functions:
-
typedef status_t (*agt_cb_trans_start_t)(agt_cfg_transaction_t *txcb)
Typedef of the trans_start callback.
The Start Transaction function is the user/system callback that is invoked before any changes to the candidate database will be committed.
Max Callbacks: No limit (except available heap memory)
- Param txcb:
transaction control block in progress
- Return:
status
Transaction Start Callback Initialization and Cleanup
The Transaction Start callback function is registered with the 'agt_cb_trans_start_register' function.
-
status_t agt_cb_trans_start_register(agt_cb_trans_start_t cbfn)
Register a Transaction Start callback.
This function registers a Transaction Start callback that will be called before any changes to the candidate database will be committed when the transaction is getting initialized.
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
Example Usage:
static status_t
interfaces_init (void)
{
// ...
/* Register a Transaction Start callback */
res = agt_cb_trans_start_register(transaction_start);
if (res != NO_ERR) {
return res;
}
// ...
}
The Transaction Start callback function is cleaned up with the 'agt_cb_trans_start_unregister' function.
-
void agt_cb_trans_start_unregister(agt_cb_trans_start_t cbfn)
Unregister a Transaction Start callback.
This function unregisters a Transaction Start callback.
- Parameters:
cbfn -- address of callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister a Transaction Start callback */
agt_cb_trans_start_unregister(transaction_start);
// ...
}
Transaction Start Callback Function Examples
The Transaction Start callback function can apply multiple actions before the transaction is actually started. This callback function may deny the start of the transaction if some specific check is unmet.
In this example, if the node "trigger" is present in the datastore then the server will deny the transaction.
/********************************************************************
* FUNCTION transaction_start
*
* Start Transaction callback
* The Start Transaction function is the user/system
* callback that is invoked before any changes to the
* candidate database will be committed.
*
* INPUTS:
* txcb == transaction control block in progress
*
* RETURNS:
* status
********************************************************************/
static status_t
transaction_start (agt_cfg_transaction_t *txcb)
{
status_t res = NO_ERR;
/* Skip callback for Validate operation if needed */
if (txcb->is_validate) {
return res;
}
log_debug("\nEnter transaction_start callback");
/* deny the start of transaction if some specific check is unmet. E.g:
* if there is a specific node present in the datastore
*/
val_value_t *val =
agt_val_get_data(txcb->cfg_id,
(const xmlChar *)"/if:trigger",
&res);
if (val) {
res = ERR_NCX_ACCESS_DENIED;
}
/* update a comment string of this transaction */
txcb->comment = (const xmlChar *)"special transaction";
/* callback could send custom notifications */
/* callback could write a sys, audit, vendor specific,
* etc log entries
*/
return res;
} /* transaction_start */
SIL-SA Transaction Start Callback
The SIL-SA Transaction Start function has the same purpose as the SIL Transaction Start function. The following function template definition is used for SIL-SA Transaction Start callback functions:
-
typedef status_t (*agt_cb_sa_trans_start_t)(const xmlChar *transaction_id, boolean isvalidate, boolean isrollback, boolean isrunning)
Typedef of the sa_trans_start callback.
The Start Transaction function is the user/system callback that is invoked before any changes to the candidate database will be committed. This is for SIL-SA subsystem only
Max Callbacks: No limit (except available heap memory)
- Param transaction_id:
transaction ID in progress
- Param isvalidate:
TRUE if this is Transaction is for Validate
- Param isrollback:
TRUE if this is Transaction for Rollback or Load
- Param isrunning:
TRUE if running datastore is being modified
- Return:
status
SIL-SA Transaction Start Callback Initialization and Cleanup
The SIL-SA Transaction Start callback function is registered with the 'agt_cb_sa_trans_start_register' function.
-
status_t agt_cb_sa_trans_start_register(agt_cb_sa_trans_start_t cbfn)
Register a Transaction Start callback.
This function registers a Transaction Start callback that will be called before any changes to the candidate database will be committed when the transaction is getting initialized. This API is available only for SIL-SA Subsystem
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
For SIL-SA the registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* Register a Transaction Start callback. */
res = agt_cb_sa_trans_start_register(transaction_start);
if (res != NO_ERR) {
return res;
}
// ...
}
The callbacks can be cleaned up automatically whether when the subsystem goes away or when the server shuts down, but there will not be any cleanup during <unload> operation. As a result, the SIL-SA code must provide an unregister function to cleanup the callback during <unload> operation.
The Transaction Start callback function is cleaned up with the 'agt_cb_trans_start_unregister' function.
-
void agt_cb_sa_trans_start_unregister(agt_cb_sa_trans_start_t cbfn)
Uregister a Transaction Start callback.
This function unregisters a Transaction Start callback. This API is available only for SIL-SA Subsystem
- Parameters:
cbfn -- address of callback function to unregister
SIL-SA Transaction Start Callback Function Examples
This example callback simply logs all the available parameters and denies the transaction if it is for the <validate> operation on the running datastore, and return "Invalid Number" error status.
/********************************************************************
* FUNCTION silsa_transaction_start
*
* Start Transaction SA callback
* The Start Transaction function is the user/system
* callback that is invoked before any changes to the
* database will be committed.
*
* RETURNS:
* status
********************************************************************/
static status_t
silsa_transaction_start (const xmlChar *transaction_id_val,
boolean isvalidate_val,
boolean isrollback_val,
boolean isrunning_val)
{
if (!transaction_id_val) {
log_error("\ntransaction_id value not set");
return ERR_INTERNAL_VAL;
}
const xmlChar *user = sil_sa_get_username();
const xmlChar *client_addr = sil_sa_get_client_addr();
if (LOGDEBUG2) {
log_debug2("\n\n********************************************"
"\nEnter silsa_transaction_start callback for silsa-test "
"---- 2"
"\ntransaction_id -- %s"
"\nuser_id -- %s"
"\nclient_addr -- %s"
"\nisvalidate -- %s"
"\nisrollback -- %s"
"\nisrunning -- %s"
"\n********************************************\n\n",
transaction_id_val,
user,
client_addr,
isvalidate_val ? NCX_EL_TRUE : NCX_EL_FALSE,
isrollback_val ? NCX_EL_TRUE : NCX_EL_FALSE,
isrunning_val ? NCX_EL_TRUE : NCX_EL_FALSE);
}
/* return an error when "validate" operation is in progress */
if (!isrollback_val && isvalidate_val && isrunning_val) {
return ERR_NCX_INVALID_NUM;
} else {
return NO_ERR;
}
} /* silsa_transaction_start */
In the example, two additional SIL-SA APIs are used to retrieve current transaction "user name" and "address":
/* Get the user_id value from the message header */
const xmlChar *user = sil_sa_get_username();
/* Get the client address (client_addr value from the message header) */
const xmlChar *client_addr = sil_sa_get_client_addr();
Transaction Complete
The Transaction Complete function is the user/system callback that is invoked after the transactions has been processed.
Transaction Complete Callback will be invoked for the following operations:
edit-config
restore
commit
validate
Transaction Complete Callback
The following function template definition is used for Transaction Complete callback functions:
-
typedef void (*agt_cb_trans_complete_t)(agt_cfg_transaction_t *txcb)
Typedef of the trans_complete callback.
The Transaction Complete function is the user/system callback that is invoked after the transactions has been processed.
Max Callbacks: No limit (except available heap memory) 1 Per SIL
- Param txcb:
transaction control block in progress
Transaction Complete Callback Initialization and Cleanup
The Transaction Complete callback function is registered with the 'agt_cb_trans_complete_register' function.
-
status_t agt_cb_trans_complete_register(agt_cb_trans_complete_t cbfn)
Register a Transaction Complete callback.
This function registers a Transaction Complete callback that will be called after the transactions has been processed.
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration.
Example Usage:
static status_t
interfaces_init (void)
{
// ...
/* Register a Transaction Complete callback. */
res = agt_cb_trans_complete_register(transaction_complete);
if (res != NO_ERR) {
return res;
}
// ...
}
The Transaction Complete callback function clean up is done with the 'agt_cb_trans_complete_unregister' function.
-
void agt_cb_trans_complete_unregister(agt_cb_trans_complete_t cbfn)
Unregister a Transaction Complete callback.
This function unregisters a Transaction Complete callback.
- Parameters:
cbfn -- address of callback function to unregister
Transaction Complete Callback Function Example
In this example, the Transaction Complete callback function simply logs a message. It is usually used to cleanup any system resources that were allocated during the transaction.
/********************************************************************
* FUNCTION transaction_complete
*
* Complete Transaction callback
* The Transaction Complete function is the
* user/system callback that is invoked after
* the transactions has been processed.
*
* INPUTS:
* txcb == transaction control block in progress
********************************************************************/
static void
transaction_complete (agt_cfg_transaction_t *txcb)
{
log_debug("\nEnter transaction_complete callback");
/* send custom notifications */
/* cleanup any system resources for this transaction */
} /* transaction_complete */
SIL-SA Transaction Complete Callback
The SIL-SA Transaction Complete function is the similar to the SIL Transaction Complete function.
The following function template definition is used for SIL-SA Transaction Complete callback functions:
-
typedef void (*agt_cb_sa_trans_complete_t)(const xmlChar *transaction_id_val)
Typedef of the sa_trans_complete callback.
The Complete Transaction function is the user/system callback that is invoked at the end of the transaction This is for SIL-SA subsystem only
Max Callbacks: No limit (except available heap memory)
- Param transaction_id_val:
transaction ID in progress
SIL-SA Transaction Complete Callback Initialization and Cleanup
The SIL-SA Transaction Complete callback function is registered with the 'agt_cb_sa_trans_complete_register' function.
-
status_t agt_cb_sa_trans_complete_register(agt_cb_sa_trans_complete_t cbfn)
Register a Transaction Complete callback.
This function registers a Transaction Complete callback that will be called after the transactions has been processed. This API is available only for SIL-SA Subsystem
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* Register a Transaction Complete callback. */
res = agt_cb_sa_trans_complete_register(silsa_transaction_complete);
if (res != NO_ERR) {
return res;
}
// ...
}
The SIL-SA code must provide an unregister function to cleanup the callback during <unload> operation.
-
void agt_cb_sa_trans_complete_unregister(agt_cb_sa_trans_complete_t cbfn)
Uregister a Transaction Complete callback.
Uregister a Transaction Complete callback. This API is available only for SIL-SA Subsystem
- Parameters:
cbfn -- address of callback function to unregister
SIL-SA Transaction Complete Callback Function Example
In this example, the Transaction Complete callback function simply logs an error message.
/********************************************************************
* FUNCTION silsa_transaction_complete
*
* Complete Transaction callback
* The Complete Transaction function is the user/system
* callback that is invoked at the end of the transaction
*
*
* Max Callbacks: Unlimited
*
* INPUTS:
* transaction_id_val == transaction id
*
* RETURNS:
* none
********************************************************************/
static void
silsa_transaction_complete (const xmlChar *transaction_id_val)
{
if (!transaction_id_val) {
log_error("\ntransaction_id value not set");
return;
}
/* This might run some cleanup of values that were set
* during the Transaction Start callback
*/
} /* silsa_transaction_complete */
Set Order Hook
The Set Order Hook callback function is the user/system callback that is used to provide an access for a specific list instance object and modify its secondary SIL priority.
It is invoked in document order for each edited instance of the specified object.
The callback returns the desired secondary SIL priority for the specific list instance.
This callback is invoked once per edited instance and after any Set Hook is called for the object and instance.
The Set Order Hook callback function can be registered only for YANG "list" objects.
The Set Order Hook callback can be used with the Set Hook or Post Set Hook callback or by itself.
The Set Order Hook is invoked before SIL or any other callbacks for the same object.
Set Order Hook Callback
The following function template definition is used for Set Order Hook callback functions:
-
typedef uint8 (*agt_cb_order_hook_t)(agt_cfg_transaction_t *txcb, op_editop_t editop, val_value_t *newval, val_value_t *curval, status_t *res)
Typedef of the agt_order_hook_cb callback.
Callback function for server object handler Used to provide a callback for a specific named object
Set-Order-Hook: Invoked in document order for each edited instance of the specified object. The callback needs to return the desired secondary SIL priority for the specific list instance
This callback is invoked once per edited instance and after any Set Hook is called for the object and instance.
- Param txcb:
transaction control block in progress
- Param editop:
edit operation enumeration for the node being edited
- Param newval:
container object holding the proposed changes to apply to the current config, depending on the editop value. Will not be NULL.
- Param curval:
current container values from the <running> or <candidate> configuration, if any. Could be NULL for create and other operations.
- Param res:
[out]
address of return status
*res status of callback; status error will cause the transaction to be terminated and rollback started
- Return:
the secondary SIL priority to assign to the object instance
Set Order Hook Callback Initialization and Cleanup
The Set Order Hook callback function is registered with the 'agt_cb_order_hook_register' function.
-
status_t agt_cb_order_hook_register(const xmlChar *defpath, agt_cb_order_hook_t cbfn)
Register an object specific Set-Order-Hook callback function.
Needs to be registered for the list object that will be 2nd-ordered Will not be called for subtree list nodes
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
cbfn -- address of callback function to use for all callback phases
- Returns:
the status of the operation
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* Register an object specific Set-Order-Hook callback function */
res = agt_cb_order_hook_register((const xmlChar*)"/if:interface/if:interface",
set_order_hook);
if (res != NO_ERR) {
return res;
}
// ...
}
Whenever the /if:interface/if:interface
list is getting
modified, the callback function will be invoked and desired secondary
SIL priority for the list instance can be specified. As a result, the
server will apply list of edits based on this priority.
The callbacks cleanup is done during the module Cleanup Phase.
-
void agt_cb_order_hook_unregister(const xmlChar *defpath)
Unregister a Set-Order-Hook callback.
This function unregisters a Set-Order-Hook callback.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callback
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister an object specific Set-Order-Hook callback function */
agt_cb_order_hook_unregister((const xmlChar*)"/if:interface/if:interface");
// ...
}
Set Order Hook Callback Function Example
In this example, the Set Order Hook callback function applies SIL priority to a list edit based on a key value.
Whenever a
/if:interfaces/if:interface
list entry is edited, the callback is invoked and additionally assigns desired priority to this edit.The priority will be assigned only if there is a new value.
It will be assigned only if the edit operation is not "delete" or "remove" because during the edits for those operations the server does not allocate a new value.
/********************************************************************
* FUNCTION set_order_hook
*
* Callback function is
* used to provide a callback for a specific named object
*
* This callback is invoked once per instance before any
* Set Hook or Post Set Hook is called for the object and instance.
*
* INPUTS:
* 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.
* res == address of return status
* OUTPUTS:
* *res == status of callback; status == error will cause the
* transaction to be terminated and rollback started
* RETURNS:
* the secondary SIL priority to assign to the object instance
********************************************************************/
static uint8
set_order_hook (agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval,
status_t *res)
{
(void)txcb;
(void)editop;
(void)curval;
uint8 retprior = 0;
/* Set the priority only if the operation is not delete and if
* a new value has keys, that will be compared later
*/
if (newval && val_has_index(newval)) {
/* Get the first index entry, if any for this value node */
const val_index_t *c1 = val_get_first_index(newval);
if (!c1) {
return ERR_INTERNAL_VAL;
}
if (!xml_strcmp(VAL_STR(c1->val), (const xmlChar *)"vlan1")) {
log_debug("\n Setting Priority to 140 for index:%s \n",
VAL_STR(c1->val));
retprior = 100;
} else if (!xml_strcmp(VAL_STR(c1->val), (const xmlChar *)"ethernet1/1/10")) {
log_debug("\n Setting Priority to 160 for index:%s \n",
VAL_STR(c1->val));
retprior = 150;
} else {
log_debug("\n Setting Priority to 130 for index:%s \n",
VAL_STR(c1->val));
retprior = 200;
}
}
return retprior;
} /* set_order_hook */
For example, if the north bound agent is creating 3 interfaces with different key values at the same time using the following <edit-config> RPC:
<edit-config>
<target>
<candidate/>
</target>
<default-operation>merge</default-operation>
<test-option>set</test-option>
<config>
<interfaces>
<interface>
<name>ethernet1/1/1</name>
</interface>
<interface>
<name>vlan1</name>
</interface>
<interface>
<name>ethernet1/1/10</name>
</interface>
</interfaces>
</config>
</edit-config>
By the regular server logic, the server would validate/apply/commit these interfaces in order they are specified in the edit. However, using the Set Order Hook the server now will apply these "interfaces" based on their priority assigned in the callback function. The first list instance that will be processed by the server will be the "interface" with the key value set to "vlan1" since it has the highest priority - "100".
So, the edits order would look as follows, from the server's logging:
***** start commit phase on candidate for session 5, transaction 1234358 *****
Start full commit of transaction 1234358: 3 edits on candidate config
edit-transaction 1234358: on session 5 by --@::1
message-id: --
trace-id: --
datastore: candidate
operation: create
target: /if:interfaces/if:interface[if:name="vlan1"]
comment: none
edit-transaction 1234358: on session 5 by --@::1
message-id: --
trace-id: --
datastore: candidate
operation: create
target: /if:interfaces/if:interface[if:name="ethernet1/1/10"]
comment: none
edit-transaction 1234358: on session 5 by --@::1
message-id: --
trace-id: --
datastore: candidate
operation: create
target: /if:interfaces/if:interface[if:name="ethernet1/1/1"]
comment: none
Get Data
The 'Get Data' API is used to retrieve data nodes from the server.
If the XPath expression matches multiple nodes, the error ERR_TOO_MANY_ENTRIES will be returned and no return value will be set. In this case, to get the matching node the XPath have to specify the parent node and then traverse the tree to match the criteria.
Note
The Get Data API is not supported during the load event, as the datastore is not yet ready to process this API call. If used in this scenario, the server will return an error.
Get Data API
The following function template definition is used for Get Data API callback functions:
-
val_value_t *agt_val_get_data(ncx_cfg_t cfg_id, const xmlChar *defpath, status_t *retres)
Invoke get data callback.
- Parameters:
cfg_id -- configuration datastore ID to use
defpath -- XPath path of object instance
retres -- [out] address of return status; *retres return status
- Returns:
pointer to data node if found
This should be treated as a const pointer
Returned pointer is the real data from the target datastore
The following code snippet shows an example of this API:
/* defpath specified target */
const xmlChar *defpath =
(const xmlChar *)"/hk:interfaces/hk:interface[name='ypw1027-vlan1']/hk:invocation-check";
status_t res = NO_ERR;
val_value_t *useval =
agt_val_get_data(NCX_CFGID_CANDIDATE, defpath, &res);
if (useval) {
// use the value from server here
}
// DO NOT FREE useval! Treat as const pointer instead!
SIL-SA Get Data API
The SIL-SA version of the Get Data API has a different template definition than the SIL version. The following function template definition is used for SIL-SA Get Data API callback functions:
-
val_value_t *sil_sa_get_data(ncx_cfg_t cfg_id, const xmlChar *defpath, status_t *retres)
Get some YANG data from the server.
Get the val_value based on Xpath of object instance This function will return value only if there is existing node in the datastore or there is defaults for the node.
- Parameters:
cfg_id -- configuration datastore ID to use
defpath -- XPath path of object instance
retres -- [out] address of return status; *retres return status
- Returns:
pointer to requested data if found.
Must NOT free this value!
value stored as sil_sa_cb.hook_get_value
Only 1 GET from SIL-SA at a time can be done
The following code snippet shows an example of this API:
status_t res = NO_ERR;
const xmlChar *defpath =
(const xmlChar *)"/testsystem";
val_value_t *testval =
sil_sa_get_data(NCX_CFGID_CANDIDATE, defpath, &res);
if (testval) {
log_debug("\nGot testval %s (%s)",
VAL_NAME(testval),
get_error_string(res));
// use the testval
}
// DO NOT FREE testval! Treat as const pointer instead!
Get Data API Expected Values
The following examples use this small YANG module to show values for the 'test3' list node:
module testgetdata {
yang-version 1.1;
namespace "urn:yumaworks:params:xml:ns:yang:testgetdata";
prefix "testgetdata";
revision 2023-04-07;
container test1 {
list test2 {
key "a b";
leaf a { type int32; }
leaf b { type string; }
list test3 {
key "c d";
leaf c { type string; }
leaf d { type string; }
}
}
}
}
The code snippet demonstrates how to obtain expected values within EDIT callbacks.
Note
When called from the Set Hook, which is triggered before the incoming edit is applied to the datastore, the expected values may not yet be present in the datastore and therefore cannot be retrieved.
status_t res2 = NO_ERR;
status_t res3 = NO_ERR;
const xmlChar *defpath =
(const xmlChar *)"/test1/test2[a='1'][b='b1']/test3[c='c1'][d='d1']";
val_value_t *candval =
agt_val_get_data(NCX_CFGID_CANDIDATE, defpath, &res2);
val_value_t *runval =
agt_val_get_data(NCX_CFGID_RUNNING, defpath, &res3);
switch (cbtyp) {
case AGT_CB_VALIDATE:
case AGT_CB_APPLY:
case AGT_CB_COMMIT:
case AGT_CB_ROLLBACK:
/* Check the value in <cadidate> ds using Get Data API */
if (candval) {
log_debug("\nGot candval in %s phase:",
agt_cbtype_name(cbtyp));
val_dump_value(candval, 5, DBG);
} else {
log_debug("\ncandval in %s phase not present (%s)",
agt_cbtype_name(cbtyp),
get_error_string(res2));
}
/* Check the value in <running> ds using Get Data API */
if (runval) {
log_debug("\nGot runval in %s phase:",
agt_cbtype_name(cbtyp));
val_dump_value(runval, 5, DBG);
} else {
log_debug("\nrunval in %s phase not present (%s)",
agt_cbtype_name(cbtyp),
get_error_string(res2));
}
break;
default:
FLAG_INT_ERROR;
res = ERR_INTERNAL_VAL;
}
Here's a step-by-step explanation:
1. Define a Path to Retrieve Data:
defpath is a variable holding a path as an XML character string.
This path "/test1/test2[a='1'][b='b1']/test3[c='c1'][d='d1']" is used to specify the location within a <candidate> and <running> database structure from which data should be retrieved.
The path includes keys [a='1'][b='b1'] and [c='c1'][d='d1'] to precisely target the required data.
2. Retrieve Data:
candval and runval are assigned the result of Get Data API, which attempts to fetch data from a <candidate> and <running> configuration datastores respectively using the specified defpath. &res2 and &res3 are pointers to store the result statuss of these retrieval operation.
3. Switch Statement Based on Callback Type (cbtyp):
The code uses a switch statement to perform different operations based on the value of cbtyp, which represents the type of Phase being processed.
Each case logs the phase it is in (Validate, Apply, Commit, Rollback) along with the results of the Get Data retrieval operation.
This logging is used to illustrate the flow of operations and the outcomes of each phase data retrieval.
Fetch newly created data (on <candidate>)
The following example illustrates the Get Data values during creation of a new value on <candidate> datastore:
create /test1/test2[a='1'][b='b1']/test3[c='c1'][d='d1']
Validate Phase
Got candval in validate phase: test3 c1 d1 { c c1 d d1 } runval in validate phase not present (ok)
Apply Phase Commit Phase Rollback Phase
No callbacks are invoked for Apply, Commit and Rollback Phases on <candidate>.
Fetch newly created data (on <running>)
The following example illustrates the Get Data values during creation of a new value on <running> datastore:
commit
Validate Phase
candval in apply phase not present (require-instance test failed) Got runval in apply phase: test3 c1 d1 { c c1 d d1 }
Note
The <candidate> value is not longer exist in the <candidate> datastore. It is in <running> now. The Get Data API returns an error that the data is missing when the Get Data call is done on <candidate>.
Apply Phase
candval in apply phase not present (require-instance test failed) Got runval in apply phase: test3 c1 d1 { c c1 d d1 }
Commit Phase
candval in apply phase not present (require-instance test failed) Got runval in apply phase: test3 c1 d1 { c c1 d d1 }
Rollback Phase
The <commit> operation failed during Apply Phase which triggers Rollback:
candval in apply phase not present (require-instance test failed) Got runval in apply phase: test3 c1 d1 { c c1 d d1 }
Fetch newly created data (target=running)
The following example illustrates the Get Data values during creation of a new value when the --target=running:
create /test1/test2[a='1'][b='b1']/test3[c='c1'][d='d1']
Validate Phase
candval in validate phase not present (invalid value) Got runval in validate phase: test3 c1 d1 { c c1 d d1 }
Note
The 'invalid value' error when the retrieval is done on <candidate> datastore is expected since this datastore does not exist when the --target=running.
Apply Phase
candval in validate phase not present (invalid value) Got runval in validate phase: test3 c1 d1 { c c1 d d1 }
Commit Phase
candval in validate phase not present (invalid value) Got runval in validate phase: test3 c1 d1 { c c1 d d1 }
Rollback Phase
The <commit> operation failed during Apply Phase which triggers Rollback:
candval in apply phase not present (invalid value) Got runval in apply phase: test3 c1 d1 { c c1 d d1 }
Fetch data outside current edit (on <candidate>)
The following example illustrates how to fetch a data outside the current edit on <candidate> datastore:
Assume the list entry test3[c='c1'][d='d1'] is already created on <candidate>. But it is not committed to the <running> datastore yet.
Now the following operations is performed.
create /test1/test2[a='2'][b='b2']/test3[c='c2'][d='d2']
The Get Data API outcome values would be as below:
Validate Phase
Got candval in validate phase: test3 c1 d1 { c c1 d d1 } runval in validate phase not present (ok)
Apply Phase Commit Phase Rollback Phase
No callbacks are invoked for Apply, Commit and Rollback Phases on <candidate>.
Fetch committed data outside current edit (on <candidate>)
Assume the list entry test3[c='c1'][d='d1'] is already committed to <running>.
Now the following operations is performed.
create /test1/test2[a='2'][b='b2']/test3[c='c2'][d='d2']
The Get Data API outcome values would be as below:
Validate Phase
Got candval in validate phase: test3 c1 d1 { c c1 d d1 } Got runval in validate phase: test3 c1 d1 { c c1 d d1 }
Note
The value is present in both datastores since it was already committed before the retrieval in previous transaction.
Apply Phase Commit Phase Rollback Phase
No callbacks are invoked for Apply, Commit and Rollback Phases on <candidate>.
Fetch data outside current edit (target=running)
The following example illustrates how to fetch a data outside the current edit when the --target=running.
Assume the list entry test3[c='c1'][d='d1'] exist in <running> datastore.
Now the following operations is performed.
create /test1/test2[a='2'][b='b2']/test3[c='c2'][d='d2']
The Get Data API outcome values will be the same as in Fetch newly created data (target=running)
Fetch data during <validate> operation
The following example illustrates how to fetch a data during <validate> operation. Assume the list entry test3[c='c1'][d='d1'] is not yet committed to <running> but it is in the <candidate> datastore.
validate source=candidate
Got candval in validate phase: test3 c1 d1 { c c1 d d1 } runval in validate phase not present (ok)
commit
validate source=candidate or running
Got candval in validate phase: test3 c1 d1 { c c1 d d1 } Got runval in validate phase: test3 c1 d1 { c c1 d d1 }
If --target=running the Get Data outcomes will be as follows:
validate source=candidate
candval in validate phase not present (invalid value) Got runval in validate phase: test3 c1 d1 { c c1 d d1 }
Startup Hook
The Startup Hook function is a user/system callback that is invoked before any changes are done to the <startup> datastore.
It is invoked only if the :startup capability is enabled.
The Startup Hook callback is invoked just before the <startup> datastore is modified.
Any number of callbacks can be registered.
This callback can reject the operation by returning an error.
If the callback fails then no further actions will be executed and the server would reply with the error.
The Startup Hook callback is invoked before any modifications to the <startup> datastore during any edit operation, such as <edit-config>, <copy-config> and <delete-config> operations.
If an application needs to perform additional validations, check or any other actions before the <startup> datastore is modified or accessed, the Startup Hook can be used.
The callback will be called for the following operations:
Any <edit-config> if the --target=running and the :startup capability is enabled
After the <commit> operation if --target=candidate and and the :startup capability is enabled
After <copy-config> if the target of the copying is the <startup> datastore
<delete-config> if the target of deletion is the <startup> datastore
Startup Hook Callback
The following function template definition is used for Startup Hook callback functions:
-
typedef status_t (*agt_cb_startup_hook_t)(ses_cb_t *scb, rpc_msg_t *msg, cfg_template_t *source_config, cfg_template_t *target_config)
Typedef of the agt_cb_startup_hook_t callback.
The Startup Hook callback is the user/system callback that is invoked right before the <startup> is getting modified.
The Startup Hook is object independent and module independent which means you don't have to link them to the specific object as it's done for EDIT or GET callbacks, and you don't have to link them to any specific module
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param source_config:
datastore which is being copied
- Param target_config:
datastore that is being edited
- Return:
status
Startup Hook Callback Initialization and Cleanup
The Startup Hook callback function is registered with the 'agt_cb_startup_hook_register' function. The registration is done during the Initialization Phase 1.
-
status_t agt_cb_startup_hook_register(agt_cb_startup_hook_t cbfn)
Register a Startup Hook callback.
This function registers a Startup Hook callback that will be called before any changes to the startup file/database.
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register startup hook callback */
res = agt_cb_startup_hook_register(startup_hook_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
Now whenever the <startup> datastore is being modified the 'startup_hook_callback' function is called.
-
void agt_cb_startup_hook_unregister(agt_cb_startup_hook_t cbfn)
Unregister a Startup Hook callback.
This function unregisters a Startup Hook callback.
- Parameters:
cbfn -- address of callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister startup hook callback */
agt_cb_startup_hook_unregister(startup_hook_callback);
// ...
}
Startup Hook Callback Example
This example callback function simply logs a message. It could reject the operation after checking the parameters.
/********************************************************************
* FUNCTION startup_hook_callback
*
* Startup Hook callback
* The Startup Hook Complete function is the
* user/system callback that is invoked before
* any changes to the <startup> during edit-config, copy-config and
* delete-config operation.
*
* Max Callbacks: No limit (except available heap memory)
*
* INPUT:
* scb == session control block making the request
* msg == incoming rpc_msg_t in progress
* source_config == datastore which is being copied
* target_config == datastore that is being edited
*
* RETURNS:
* status
********************************************************************/
static status_t startup_hook_callback (ses_cb_t *scb,
rpc_msg_t *msg,
cfg_template_t *source_config,
cfg_template_t *target_config)
{
(void)scb;
(void)msg;
(void)source_config;
(void)target_config;
log_debug("\n\nEnter startup_hook_callback callback");
/* notify application that the <startup> is being modified */
log_debug("\n <startup> is being modified");
return NO_ERR;
} /* startup_hook_callback */
Using Commit Completeness Callbacks
There are 4 Commit Completeness callbacks:
Note
The Edit Phase Complete callback should be used instead of these callbacks (starting in 22.10T-10)
The Commit Completeness Callbacks are not regular SIL callbacks. Unlike an EDIT1, EDIT2, or EDIT3 callback, these callbacks are not specific to any particular YANG data node.
All edit validation for the phase has been completed already when a Commit Completeness Callback is invoked. These callbacks are invoked after the entire transaction phase is done, so edits can be checked without worrying about the order individual edits are applied.
None of the Commit Completeness callbacks are allowed to change the edit transaction in progress
The callbacks are expected to inspect and validate the edit transaction, and then approve or reject the transaction.
Normally, these callbacks are only invoked for the <commit> operation.
If the --sil-cc-callback-all parameter is set to 'true' then these callbacks will be invoked for all edit operations.
The parameters passed to each callback are different, depending on the edit operation.
This callback is skipped if any SIL or SIL-SA callbacks return an error.
All registered completeness callbacks are invoked in the order they are registered.
Any error returned by a callback will cancel any remaining registered callbacks for the edit transaction.
Validate Complete
The Validate Complete function is the user/system callback that is invoked after the Validate Phase has been processed during an edit transaction such as the <commit> operation.
If the callback fails the status of the failing callback is returned immediately and no further callbacks are made. As a result, the server will abort the commit.
If there are multiple callbacks registered, all the registered callbacks will be called one after another right after the Validate Phase during the commit operation is done.
Validate Complete Callback
The following function template definition is used for Validate Complete callback functions:
-
typedef status_t (*agt_cb_validate_complete_t)(ses_cb_t *scb, rpc_msg_t *msg, val_value_t *candidate, val_value_t *running)
Typedef of the agt_cb_validate_complete_t callback.
The Validate Complete callback is the user/system callback that is invoked after the Validate Phase has been processed during the <commit> operation.
The Validate Complete is object independent and module independent which means you don't have to link them to the specific object as it's done for EDIT or GET callbacks, and you don't have to link them to any specific module
Max Callbacks: No limit (except available heap memory)
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param candidate:
candidate val_value_t for the config database to use
- Param running:
running val_value_t for the config database to use
- Return:
status
Validate Complete Parameters
If the '--sil-cc-callback-all parameter is 'false` then only the 'Commit' section below applies to this callback:
Load-Config (Init Phase)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: NULL (no target datastore exists yet)
Edit-Config or Copy-Config
scb: client session invoking the <edit-config> or <copy-config> operation
msg: real request message for this operation
candidate: source root of the <config> element being loaded
running: target datastore (could be 'candidate' or 'running')
Commit
scb: client session invoking the <commit> operation
msg: real request message for this operation
candidate: source <candidate> datastore <config> element
running: target <running> datastore <config> element
Load Config (Restore)
scb: dummy session invoking the <load-config> operation. Started by the <restore> operation.
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target root of the <running> datastore <config> element
Load-Config (Confirmed Commit Timeout)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target <running> datastore <config> element
Validate
scb: client session invoking the <validate> operation
msg: real request message for this operation
candidate: source datastore <config> element being validated. This is usually the root for the <candidate> or <running> datastore.
running: NULL
Validate Complete Callback Initialization and Cleanup
The Validate Complete callback function is registered with the 'agt_cb_validate_complete_register' function, The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_validate_complete_register(agt_cb_validate_complete_t cbfn)
Register a Validate Complete callback.
This function registers a Validate Complete callback that will be called after the Validation phase has been processed during the <commit>.
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register validate complete callback */
res =
agt_cb_validate_complete_register(validate_compl_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
Now, whenever the Validate Phase is done for the <commit> or other operation, the callback function will be called and provide the access to the candidate and running datastores.
-
void agt_cb_validate_complete_unregister(agt_cb_validate_complete_t cbfn)
Unregister a Validate Complete callback.
This function unregisters a Validate Complete callback.
- Parameters:
cbfn -- address of callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister Validate Complete callback */
agt_cb_validate_complete_unregister(validate_compl_callback);
// ...
}
Validate Complete Callback Example
The following example code shows a Validate Complete callback.
It does not do anything except return NO_ERR
.
/********************************************************************
* FUNCTION validate_compl_callback
*
* Validate Complete callback
*
* Max Callbacks: No limit (except available heap memory)
*
* INPUTS:
* scb == session control block making the request
* msg == incoming rpc_msg_t in progress
* candidate == candidate val_value_t for the config database to use
* running == running val_value_t for the config database to use
*
* RETURNS:
* status
********************************************************************/
static status_t
validate_compl_callback (ses_cb_t *scb,
rpc_msg_t *msg,
val_value_t *candidate,
val_value_t *running)
{
(void)scb;
(void)msg;
(void)candidate;
(void)running;
/* notify application that the Validate phase is done */
/* validate candidate */
/* validate running */
return NO_ERR;
} /* validate_compl_callback */
SIL-SA Validate Complete
The SIL-SA version of the Validate Complete callback function is similar to the SIL Validate Complete function.
For SIL-SA usage the server does not provide running and candidate values. Instead it passes the transaction ID for reference and Get Data API can be used to run additional validation if required.
SIL-SA Validate Complete Callback
The following function template definition is used for SIL-SA Validate Complete callback functions:
-
typedef status_t (*agt_cb_sa_validate_complete_t)(const xmlChar *transaction_id)
Typedef of the agt_cb_sa_validate_complete_t callback.
The SIL-SA Validate Complete callback is the user/system callback that is invoked after the Validate Phase has been processed during the <commit> operation.
SIL-SA Version
Max Callbacks: No limit (except available heap memory)
- Param transaction_id:
transaction ID in progress
- Return:
status
SIL-SA Validate Complete Callback Initialization and Cleanup
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.
-
status_t agt_cb_sa_validate_complete_register(agt_cb_sa_validate_complete_t cbfn)
Register a SIL-SA Validate Complete callback.
This function registers a SIL-SA Validate Complete callback that will be called after the Validation phase has been processed during the <commit>.
- Parameters:
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register SIL-SA validate complete callback */
res = agt_cb_sa_validate_complete_register(hooks_validate_complete_cb);
if (res != NO_ERR) {
return res;
}
// ...
}
Now, whenever the Validate Phase is done for the <commit> or other operation, the callback function will be called and provide the edit transaction ID.
-
void agt_cb_sa_validate_complete_unregister(agt_cb_sa_validate_complete_t cbfn)
Unregister a SIL-SA Validate Complete callback.
This function unregisters a SIL-SA Validate Complete callback.
- Parameters:
cbfn -- address of SIL-SA callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister SIL-SA Validate Complete callback */
agt_cb_sa_validate_complete_unregister(hooks_validate_complete_cb);
// ...
}
SIL-SA Validate Complete Callback Example
The following example code shows a SIL-SA Validate Complete callback.
/********************************************************************
* FUNCTION hooks_validate_complete_cb
*
* SIL-SA Validate Complete callback.
*
*
* RETURNS:
* status
********************************************************************/
static status_t
hooks_validate_complete_cb (const xmlChar *transaction_id_val)
{
if (!transaction_id_val) {
log_error("\ntransaction_id value not set");
return ERR_INTERNAL_VAL;
}
const xmlChar *user = sil_sa_get_username();
const xmlChar *client_addr = sil_sa_get_client_addr();
status_t res = NO_ERR;
if (LOGDEBUG2) {
log_debug2("\n\n********************************************");
log_debug2("\nEnter hooks_validate_complete_cb callback");
log_debug2("\ntransaction_id -- %s", transaction_id_val);
log_debug2("\nuser_id -- %s", user);
log_debug2("\nclient_addr -- %s", client_addr);
log_debug2("\n********************************************\n\n");
}
return res;
} /* hooks_validate_complete_cb */
Apply Complete
The Apply Complete function is the user/system callback that is invoked after the Apply Phase has been processed during the <commit> operation.
If a callback fails the status of the failing callback is returned immediately and no further callbacks are made. As a result, the server will abort the commit.
Apply Complete Callback
The following function template definition is used for Apply Complete callback functions:
-
typedef status_t (*agt_cb_apply_complete_t)(ses_cb_t *scb, rpc_msg_t *msg, val_value_t *candidate, val_value_t *running)
Typedef of the agt_cb_apply_complete_t callback.
The Apply Complete callback is the user/system callback that is invoked after the Validate Phase has been processed during the <commit> operation.
The Validate Complete is object independent and module independent which means you don't have to link them to the specific object as it's done for EDIT or GET callbacks, and you don't have to link them to any specific module
Max Callbacks: No limit (except available heap memory)
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param candidate:
candidate val_value_t for the config database to use
- Param running:
running val_value_t for the config database to use
- Return:
status
Apply Complete Parameters
If the '--sil-cc-callback-all parameter is 'false` then only the 'Commit' section below applies to this callback:
Load-Config (Init Phase)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target root of the <running> datastore <config> element
Commit
scb: client session invoking the <commit> operation
msg: real request message for this operation
candidate: source <candidate> datastore <config> element
running: target <running> datastore <config> element
Load Config (Restore)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target root of the <running> datastore <config> element
Load-Config (Confirmed Commit Timeout)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target <running> datastore <config> element
Apply Complete Callback Initialization and Cleanup
The Apply Complete callback function is registered with the 'agt_cb_apply_complete_register' function.
-
status_t agt_cb_apply_complete_register(agt_cb_apply_complete_t cbfn)
Register a Apply Complete callback.
This function registers a Apply Complete callback that will be called right after Apply Phase has been processed during the <commit>
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register apply complete callback */
res = agt_cb_apply_complete_register(apply_compl_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
Whenever the Apply Phase** is done for the commit operation, the callback function will be called and provide the access to the candidate and running datastores.
-
void agt_cb_apply_complete_unregister(agt_cb_apply_complete_t cbfn)
Unregister a Apply Complete callback.
This function unregisters a Apply Complete callback.
- Parameters:
cbfn -- address of callback function to use
Example Usage:
void interfaces_cleanup (void)
{
...
/* Unregister Apply Complete callback */
agt_cb_apply_complete_unregister(apply_compl_callback);
...
}
Apply Complete Callback Example
The following example code shows an Apply Complete callback:
/********************************************************************
* FUNCTION apply_compl_callback
*
* Apply Complete callback
*
* Max Callbacks: No limit (except available heap memory)
*
* INPUTS:
* scb == session control block making the request
* msg == incoming rpc_msg_t in progress
* candidate == candidate val_value_t for the config database to use
* running == running val_value_t for the config database to use
*
* RETURNS:
* status
********************************************************************/
static status_t
apply_compl_callback (ses_cb_t *scb,
rpc_msg_t *msg,
val_value_t *candidate,
val_value_t *running)
{
(void)scb;
(void)msg;
(void)candidate;
(void)running;
/* notify application that the Apply phase is done */
/* process configs here */
return NO_ERR;
} /* apply_compl_callback */
SIL-SA Apply Complete Callback
The SIL-SA version of the Apply Complete callback function is similar to the SIL Apply Complete callback.
The following function template definition is used for SIL-SA Apply Complete callback functions:
-
typedef status_t (*agt_cb_sa_apply_complete_t)(const xmlChar *transaction_id)
Typedef of the agt_cb_sa_apply_complete_t callback.
The SIL-SA Apply Complete callback is the user/system callback that is invoked after the Apply Phase has been processed during the <commit> operation.
SIL-SA Version
Max Callbacks: No limit (except available heap memory)
- Param transaction_id:
transaction ID in progress
- Return:
status
SIL-SA Apply Complete Callback Initialization and Cleanup
The SIL-SA Apply Complete callback function is hooked into the server with the 'agt_cb_sa_apply_complete_register' function. The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_sa_apply_complete_register(agt_cb_sa_apply_complete_t cbfn)
Register a SIL-SA Apply Complete callback.
This function registers a SIL-SA Apply Complete callback that will be called after the Validation phase has been processed during the <commit>.
- Parameters:
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register SIL-SA Apply Complete callback */
res = agt_cb_sa_apply_complete_register(hooks_apply_complete_cb);
if (res != NO_ERR) {
return res;
}
// ...
}
-
void agt_cb_sa_apply_complete_unregister(agt_cb_sa_apply_complete_t cbfn)
Unregister a SIL-SA Apply Complete callback.
This function unregisters a SIL-SA Apply Complete callback.
- Parameters:
cbfn -- address of SIL-SA callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister SIL-SA Apply Complete callback */
agt_cb_sa_apply_complete_unregister(hooks_apply_complete_cb);
// ...
}
SIL-SA Apply Complete Callback Example
The following example code shows a SIL-SA Apply Complete callback function.
/********************************************************************
* FUNCTION hooks_apply_complete_cb
*
* SIL-SA Apply Complete callback.
*
*
* RETURNS:
* status
********************************************************************/
static status_t
hooks_apply_complete_cb (const xmlChar *transaction_id_val)
{
if (!transaction_id_val) {
log_error("\ntransaction_id value not set");
return ERR_INTERNAL_VAL;
}
const xmlChar *user = sil_sa_get_username();
const xmlChar *client_addr = sil_sa_get_client_addr();
status_t res = NO_ERR;
if (LOGDEBUG2) {
log_debug2("\n\n********************************************");
log_debug2("\nEnter hooks_apply_complete_cb callback");
log_debug2("\ntransaction_id -- %s", transaction_id_val);
log_debug2("\nuser_id -- %s", user);
log_debug2("\nclient_addr -- %s", client_addr);
log_debug2("\n********************************************\n\n");
}
return res;
} /* hooks_apply_complete_cb */
Commit Complete
The Commit Complete callback is a user/system callback that is invoked when the Commit Phase of the <commit> operation completes without errors, or the internal <replay-config> operation completes without errors.
This callback uses a module name parameter (for legacy purposes).
One callback per module name can be registered.
The callback functions are not related to the module name in any way!
The callback is invoked at the end of the entire operation, even if the specific module is not part of the edit transaction.
Note
For revisions before 22.10T-10:
Any return value from the SIL Commit Complete callback is ignored by the server.
A rollback is not triggered if this callback returns an error status.
The server will return this error value to the client but the internal commit has already succeeded.
For revisions starting with 22.10T-10:
The return value from the SIL Commit Complete callback is not ignored by the server.
A rollback will be triggered if this callback returns an error status.
The server will return this error value to the client.
Commit Complete Callback
This is a Legacy Callback that does not contain the same parameters as the other Commit Completeness Callbacks.
The callback must use a function like 'cfg_get_root' to obtain the current target datastore.
The source datastore has been commited to the target and may not be available.
The Commit Complete callback return value is ignored by the server.
A rollback is not triggered if this callback returns an error status.
The following function template definition is used for Commit Complete callback functions:
-
typedef status_t (*agt_commit_complete_cb_t)(agt_commit_type_t commit_type)
Typedef of the commit_complete callback.
Specific to the "<commit>" operation
Invoked if the validate, apply, and commit all return NO_ERR
This is a legacy API from yuma
- Param commit_type:
AGT_COMMIT_TYPE_NORMAL is a "<commit>" operation
AGT_COMMIT_TYPE_REPLAY is a replay-commit procedure
- Return:
status
Need to return NO_ERR or else end of transaction can be incompleted
Commit Complete Parameters
If the '--sil-cc-callback-all parameter is 'false` then only the '<commit> operation applies to this callback. If 'true', then this callback is invoked the same for all edit operations.
commit_type:
AGT_COMMIT_TYPE_NORMAL
orAGT_COMMIT_TYPE_REPLAY
Do not return an error during Config Replay operation
Use the Config Replay Callback to process configuration replay events for SIL or SIL-SA synchronization
Commit Complete Callback Initialization and Cleanup
The Commit Complete callback function is registered with the 'agt_cb_commit_complete_register' function.
-
status_t agt_commit_complete_register(const xmlChar *modname, agt_commit_complete_cb_t cb)
Register a Commit Complete callback.
This function registers a Commit Complete callback that will be called right after Commit Phase has been processed during the "<commit>". If a commit complete operation is already registered for the module it will be replaced.
- Parameters:
modname -- name of the module registering the callback
cb -- the commit complete function.
- Returns:
the status of the operation.
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register commit complete callback */
res = agt_commit_complete_register(EXAMPLE_MODNAME,
commit_compl_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
Now, whenever the Commit Phase is done for the commit operation, the callback function will be called.
-
void agt_commit_complete_unregister(const xmlChar *modname)
Unregister a Commit Complete callback.
This function unregisters a Commit Complete callback.
- Parameters:
modname -- name of the module unregistering the callback
The unregister function for the Commit Complete callback is slightly different then for other phases because the module name parameter is used.
void interfaces_cleanup (void)
{
// ...
/* Unregister Commit Complete callback */
agt_commit_complete_unregister(EXAMPLE_MODNAME);
// ...
}
Commit Complete Callback Example
The following example code shows a Commit Complete callback:
/********************************************************************
* FUNCTION commit_compl_callback
*
* Commit Complete callback
*
* INPUTS:
* commit_type == enum identifying commit type (normal or replay)
*
* RETURNS:
* status
********************************************************************/
static status_t
commit_compl_callback (agt_commit_type_t commit_type)
{
(void)commit_type;
/* notify application that the Commit phase is done */
/* this value is ignored by the server, even if an error! */
return NO_ERR;
} /* commit_compl_callback */
SIL-SA Commit Complete Callback
The SIL-SA version of the Commit Complete callback function is similar to the SIL Commit Complete function.
The following function template definition is used for SIL-SA Commit Complete callback functions:
-
typedef status_t (*agt_cb_sa_commit_complete_t)(const xmlChar *transaction_id, agt_commit_type_t commit_type)
Typedef of the agt_cb_sa_commit_complete_t callback.
The SIL-SA Commit Complete callback is the user/system callback that is invoked after the Commit Phase has been processed during the <commit> operation.
SIL-SA Version
Max Callbacks: No limit (except available heap memory)
- Param transaction_id:
transaction ID in progress
- Param commit_type:
commit type; only if phase is Commit
- Return:
status
SIL-SA Commit Complete Callback Initialization and Cleanup
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_sa_commit_complete_register(agt_cb_sa_commit_complete_t cbfn)
Register a SIL-SA Commit Complete callback.
This function registers a SIL-SA Commit Complete callback that will be called after the Validation phase has been processed during the <commit>.
- Parameters:
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register SIL-SA Commit Complete callback */
res = agt_cb_sa_commit_complete_register(hooks_commit_complete_cb);
if (res != NO_ERR) {
return res;
}
// ...
}
Whenever the Commit Phase is done for the <commit> operation, the callback function will be called.
-
void agt_cb_sa_commit_complete_unregister(agt_cb_sa_commit_complete_t cbfn)
Unregister a SIL-SA Commit Complete callback.
This function unregisters a SIL-SA Commit Complete callback.
- Parameters:
cbfn -- address of SIL-SA callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister SIL-SA Commit Complete callback */
agt_cb_sa_commit_complete_unregister(hooks_commit_complete_cb);
// ...
}
SIL-SA Commit Complete Callback Example
The following API function is used in this example:
-
const xmlChar *agt_commit_complete_get_type(agt_commit_type_t commit_type)
Get the keyword for the specified agt_commit_type_t enumeration.
- Parameters:
commit_type -- type to use
- Returns:
string for the hook type, or "none" or "illegal"
The following function shows an example of a SIL-SA Commit Complete callback:
/********************************************************************
* FUNCTION hooks_commit_complete_cb
*
* SIL-SA Commit Complete callback.
*
*
* RETURNS:
* status
********************************************************************/
static status_t
hooks_commit_complete_cb (const xmlChar *transaction_id_val,
agt_commit_type_t commit_type)
{
if (!transaction_id_val) {
log_error("\ntransaction_id value not set");
return ERR_INTERNAL_VAL;
}
const xmlChar *user = sil_sa_get_username();
const xmlChar *client_addr = sil_sa_get_client_addr();
const xmlChar *type = agt_commit_complete_get_type(commit_type);
status_t res = NO_ERR;
status_t res2 = NO_ERR;
if (commit_type == AGT_COMMIT_TYPE_REPLAY) {
return res;
}
if (LOGDEBUG2) {
log_debug2("\n\n********************************************");
log_debug2("\nEnter hooks_commit_complete_cb callback");
log_debug2("\ntransaction_id -- %s", transaction_id_val);
log_debug2("\nuser_id -- %s", user);
log_debug2("\nclient_addr -- %s", client_addr);
log_debug2("\ncommit type -- %s", type);
}
const xmlChar *defpath =
(const xmlChar *)"/testsystem";
val_value_t *testval =
sil_sa_get_data(NCX_CFGID_CANDIDATE,
defpath,
&res2);
if (testval) {
log_info("\n+++ Got testval %s (%s)",
VAL_NAME(testval),
get_error_string(res));
val_value_t *child = val_get_first_child(testval);
if (child) {
if (child->btyp && typ_is_simple(child->btyp)) {
xmlChar *valstr = val_make_sprintf_string(child);
if (valstr == NULL) {
return ERR_INTERNAL_MEM;
}
log_debug("\nchild: %s='%s'",
VAL_NAME(child),
valstr);
/* FORCE ERROR */
if (!xml_strcmp(valstr,
(const xmlChar *)"commit-completeness-test1")) {
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
log_info("\n------ REPORTING AN ERROR (%s)\n",
get_error_string(res));
}
m__free(valstr);
}
}
}
if (LOGDEBUG2) {
log_debug2("\n********************************************\n\n");
}
return res;
} /* hooks_commit_complete_cb */
Rollback Complete
The Rollback Complete function is the user/system callback that is invoked after (and if) the Rollback Phase has been processed during the <commit> operation. The Rollback Complete callback is object independent and module independent.
Note
Even though this callback returns a status, the value
NO_ERR
must be returned for proper operation.
If an error is returned, the transaction may not be
completed properly.
Rollback Complete Callback
The following function template definition is used for Rollback Complete callback functions:
-
typedef status_t (*agt_cb_rollback_complete_t)(ses_cb_t *scb, rpc_msg_t *msg, val_value_t *candidate, val_value_t *running)
Typedef of the agt_cb_rollback_complete_t callback.
The Rollback Complete callback is the user/system callback that is invoked after and if the Rollback Phase has been processed during the <commit> operation.
The Rollback Complete is object independent and module independent which means you don't have to link them to the specific object as it's done for EDIT or GET callbacks, and you don't have to link them to any specific module.
Max Callbacks: No limit (except available heap memory)
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param candidate:
candidate val_value_t for the config database to use
- Param running:
running val_value_t for the config database to use
- Return:
status
Rollback Complete Parameters
If the '--sil-cc-callback-all parameter is 'false` then only the 'Commit' section below applies to this callback:
Load-Config (Init Phase)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target root of the <running> datastore <config> element
Commit
scb: client session invoking the <commit> operation
msg: real request message for this operation
candidate: source <candidate> datastore <config> element
running: target <running> datastore <config> element
Load Config (Restore)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target root of the <running> datastore <config> element
Load-Config (Confirmed Commit Timeout)
scb: dummy session invoking the <load-config> operation
msg: dummy request message for this operation
candidate: source root of the <config> element being loaded
running: target <running> datastore <config> element
Rollback Complete Callback Initialization and Cleanup
The Rollback Complete callback function is registered with the 'agt_cb_rollback_complete_register' function. The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_rollback_complete_register(agt_cb_rollback_complete_t cbfn)
Register a Rollback Complete callback.
This function registers a Rollback Complete callback that will be called right after and if Rollback Phase has been processed during the <commit>
Max Callbacks: No limit (except available heap memory)
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register rollback complete callback */
res = agt_cb_rollback_complete_register(rollback_compl_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
Whenever the Rollback Phase is done for the <commit> operation, the callback function will be invoked.
-
void agt_cb_rollback_complete_unregister(agt_cb_rollback_complete_t cbfn)
Unregister a Rollback Complete callback.
This function unregisters a Rollback Complete callback.
- Parameters:
cbfn -- address of callback function to use
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister Rollback Complete callback */
agt_cb_rollback_complete_unregister(rollback_compl_callback);
// ...
}
Rollback Complete Callback Example
The following callback does not do anything but could invoke system APIs or logging functions to clean up after the rollback was completed.
/********************************************************************
* FUNCTION rollback_compl_callback
*
* Rollback Complete callback
* The Rollback Complete function is the
* user/system callback that is invoked after and if
* Rollback phase has been processed during <commit> op.
*
* Max Callbacks: No limit (except available heap memory)
*
* INPUTS:
* scb == session control block making the request
* msg == incoming rpc_msg_t in progress
* candidate == candidate val_value_t for the config database to use
* running == running val_value_t for the config database to use
*
* RETURNS:
* status
********************************************************************/
static status_t
rollback_compl_callback (ses_cb_t *scb,
rpc_msg_t *msg,
val_value_t *candidate,
val_value_t *running)
{
(void)scb;
(void)msg;
(void)candidate;
(void)running;
log_debug("\n\n @@Enter rollback_compl_callback callback");
return NO_ERR;
} /* rollback_compl_callback */
SIL-SA Rollback Complete Callback
The SIL-SA version of the Rollback Complete callback function is similar to the SIL Rollback Complete function.
The following function template definition is used for SIL-SA Rollback Complete callback functions:
-
typedef status_t (*agt_cb_sa_rollback_complete_t)(const xmlChar *transaction_id)
Typedef of the agt_cb_sa_rollback_complete_t callback.
The SIL-SA Rollback Complete callback is the user/system callback that is invoked after and if the Rollback Phase has been processed during the <commit> operation.
SIL-SA Version
Max Callbacks: No limit (except available heap memory)
- Param transaction_id:
transaction ID in progress
- Return:
status
SIL-SA Rollback Complete Callback Initialization and Cleanup
The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_sa_rollback_complete_register(agt_cb_sa_rollback_complete_t cbfn)
Register a SIL-SA Rollback Complete callback.
This function registers a SIL-SA Rollback Complete callback that will be called after the Validation phase has been processed during the <commit>.
- Parameters:
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* register rollback complete callback */
res = agt_cb_sa_rollback_complete_register(hooks_rollback_complete_cb);
if (res != NO_ERR) {
return res;
}
// ...
}
Whenever the Rollback Phase is done for the <commit> operation, the callback function will be invoked.
-
void agt_cb_sa_rollback_complete_unregister(agt_cb_sa_rollback_complete_t cbfn)
Unregister a SIL-SA Rollback Complete callback.
This function unregisters a SIL-SA Rollback Complete callback.
- Parameters:
cbfn -- address of SIL-SA callback function to use
Example Usage:
/* Unregister SIL-SA Commit Complete callback */
agt_cb_sa_commit_complete_unregister(hooks_commit_complete_cb);
SIL-SA Rollback Complete Callback Example
The following code shows an example SIL-SA Rollback Complete Callback function:
/********************************************************************
* FUNCTION hooks_rollback_complete_cb
*
* SIL-SA Rollback Complete callback.
*
*
* RETURNS:
* status
********************************************************************/
static status_t
hooks_rollback_complete_cb (const xmlChar *transaction_id_val)
{
if (!transaction_id_val) {
log_error("\ntransaction_id value not set");
return ERR_INTERNAL_VAL;
}
const xmlChar *user = sil_sa_get_username();
const xmlChar *client_addr = sil_sa_get_client_addr();
status_t res = NO_ERR;
if (LOGDEBUG2) {
log_debug2("\n\n********************************************");
log_debug2("\nEnter hooks_rollback_complete_cb callback");
log_debug2("\ntransaction_id -- %s", transaction_id_val);
log_debug2("\nuser_id -- %s", user);
log_debug2("\nclient_addr -- %s", client_addr);
log_debug2("\n********************************************\n\n");
}
return res;
} /* hooks_rollback_complete_cb */
Edit Phase Complete
The Edit Phase Complete function is the user/system callback that is invoked at the end of each edit transaction phase.
Note
This callback is intended to replace the following callbacks, starting in release 22.10T-10:
Edit Transaction Termination
If any error is returned during the validate phase then the edit transaction is terminated and no further callbacks will be invoked.
If any error is returned during the apply phase then the edit transaction is terminated and the rollback phase will be started.
If any error is returned during the commit phase then the edit transaction is terminated and the rollback phase will be started.
If any error is returned during the rollback phase then the rollback is logged as a failure.
Edit Phase Complete and Older Completeness Callback Order
In each phase, the older SIL Completeness callbacks will be invoked first.
If successful then all SIL Edit Phase Complete callbacks will be invoked next
If successful then all SIL-SA Completeness and Edit Phase Complete callbacks will be invoked
Edit Phase Complete Callback
The following function template definition is used for Edit Phase Complete callback functions:
-
typedef status_t (*agt_cb_edit_phase_complete_t)(agt_cbtyp_t edit_phase, ses_cb_t *scb, rpc_msg_t *msg, val_value_t *source, val_value_t *target)
Typedef of the agt_cb_edit_phase_complete_t callback.
The Edit Phase Complete callback is the user/system callback that is invoked after each edit transaction phase has been processed during any edit transaction operation.
Edit phases are defined in agt_cbtyp_t enumeration
Callbacks for the AGT_CB_VALIDATE phase may be invoked for the <candidate> or <running> datastore.
Callbacks for the other phases may be invoked for the <running> datastore.
If the return value is not NO_ERR then the edit transaction will be terminated and a rollback will begin.
Max Callbacks: No limit (except available heap memory)
- Param edit_phase:
edit phase enumeration that has just been completed
- Param scb:
session control block making the request
- Param msg:
incoming rpc_msg_t in progress
- Param source:
val_value_t for the source config database to use
- Param target:
val_value_t for the target config database to use
- Return:
status
Edit Phase Complete Parameters
The parameters passed to this function may vary for each edit operation, or each edit phase.
Validate Phase Complete Parameters
The 'edit_phase' parameter is set to
AGT_CB_VALIDATE
.The rest of the parameters are set the same way as the Validate Complete callback funcion, as if the '--sil-cc-callback-all parameter is set to 'true`.
Apply Phase Complete Parameters
The 'edit_phase' parameter is set to
AGT_CB_APPLY
.The rest of the parameters are set the same way as the Apply Complete callback funcion, as if the '--sil-cc-callback-all parameter is set to 'true`.
Commit Phase Complete Parameters
The 'edit_phase' parameter is set to
AGT_CB_COMMIT
.The rest of the parameters are set the same way as the Apply Complete callback funcion, as if the '--sil-cc-callback-all parameter is set to 'true`.
Note that this callback is different than the legacy Commit Complete callback. The parameters are consistent in each phase for this callback.
Rollback Phase Complete Parameters
The 'edit_phase' parameter is set to
AGT_CB_ROLLBACK
.The rest of the parameters are set the same way as the Rollback Complete callback funcion, as if the '--sil-cc-callback-all parameter is set to 'true`.
Edit Phase Complete Callback Initialization and Cleanup
The Edit Phase Complete callback function is registered with the 'agt_cb_edit_phase_complete_register' function, The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_edit_phase_complete_register(agt_cb_edit_phase_complete_t cbfn)
Register an Edit Phase Complete callback.
This function registers an Edit Phase Complete callback that will be called right after each Edit Phase has been processed during an edit transaction.
- Parameters:
cbfn -- address of callback function to use
- Returns:
the status of the operation.
Example Usage:
static status_t example_init (void)
{
// ...
/* register validate complete callback */
res =
agt_cb_edit_phase_complete_register(edit_phase_compl_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
Now, whenever the edit transaction is done, the callback function will be called and provide access to the source and target datastores.
-
void agt_cb_edit_phase_complete_unregister(agt_cb_edit_phase_complete_t cbfn)
Unregister an Edit Phase Complete callback.
This function unregisters an Edit Phase Complete callback.
- Parameters:
cbfn -- address of callback function to use
Example Usage:
void example_cleanup (void)
{
// ...
/* Unregister Edit Phase Complete callback */
agt_cb_edit_phase_complete_unregister(edit_phase_compl_callback);
// ...
}
Edit Phase Complete Callback Example
The following example code shows an Edit Phase Complete callback.
/*
* @brief Example agt_cb_edit_phase_complete_t callback
*
* The Edit Phase Complete callback is the user/system callback
* that is invoked after each edit transaction phase has been processed
* during any edit transaction operation.
*
* Edit phases are defined in agt_cbtyp_t enumeration
*
* Callbacks for the AGT_CB_VALIDATE phase may be invoked
* for the <candidate> or <running> datastore.
*
* Callbacks for the other phases may be invoked
* for the <running> datastore.
*
* If the return value is not NO_ERR then the edit transaction
* will be terminated and a rollback will begin.
*
* Max Callbacks: No limit (except available heap memory)
*
* @param edit_phase edit phase enumeration that has just been completed
* @param scb session control block making the request
* @param msg incoming rpc_msg_t in progress
* @param source val_value_t for the source config database to use
* @param target val_value_t for the target config database to use
* @return status
*/
static status_t
edit_phase_compl_callback (agt_cbtyp_t edit_phase,
ses_cb_t *scb,
rpc_msg_t *msg,
val_value_t *source,
val_value_t *target)
{
if (LOGDEBUG) {
log_debug("\n*** Enter Edit Phase Complete Callback "
"for '%s' (%u)",
agt_cbtype_name(edit_phase),
++cb_trace);
}
status_t res = NO_ERR;
(void)scb;
(void)msg;
(void)source;
(void)target;
/* check the specific phase that has just completed
* and set res to some error if the edit transaction
* should be rejected. (validate, apply, commit)
* For rollback phase this function can only change the
* status of the rollback printed in log messages
*/
switch (edit_phase) {
case AGT_CB_VALIDATE:
case AGT_CB_APPLY:
case AGT_CB_COMMIT:
case AGT_CB_ROLLBACK:
break;
default:
res = ERR_NCX_INVALID_VALUE;
}
return res;
}
SIL-SA Edit Phase Complete
The SIL-SA version of the Edit Phase Complete callback function is similar to the SIL Edit Phase Complete function.
For SIL-SA usage the server does not provide running and candidate values. Instead it passes the edit phase enumeration and the transaction ID for reference.
SIL-SA Edit Phase Complete Callback
The following function template definition is used for SIL-SA Edit Phase Complete callback functions:
-
typedef status_t (*agt_cb_sa_edit_phase_complete_t)(agt_cbtyp_t edit_phase, const xmlChar *transaction_id)
Typedef of the agt_cb_sa_edit_phase_complete_t callback.
The SIL-SA Edit Phase Complete callback is the user/system callback that is invoked after each Edit Phase has been processed during any edit transaction.
SIL-SA Version
Max Callbacks: No limit (except available heap memory)
- Param edit_phase:
edit-phase in progress
- Param transaction_id:
transaction ID in progress
- Return:
status
SIL-SA Edit Phase Complete Callback Initialization and Cleanup
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.
-
status_t agt_cb_sa_edit_phase_complete_register(agt_cb_sa_edit_phase_complete_t cbfn)
Register a SIL-SA Edit Phase Complete callback.
This function registers a SIL-SA Edit Phase Complete callback that will be called after each edit transaction phase has been processed during an edit transaction.
- Parameters:
cbfn -- address of SIL-SA callback function to use
- Returns:
the status of the operation
Example Usage:
static status_t example_init (void)
{
// ...
/* register SIL-SA edit phase complete callback */
res = agt_cb_sa_edit_phase_complete_register(sa_edit_phase_complete);
if (res != NO_ERR) {
return res;
}
// ...
}
Now, whenever the Phase Complete callback is done for any edit transaction, the callback function will be called and provide the access to the candidate and running datastores.
-
void agt_cb_sa_edit_phase_complete_unregister(agt_cb_sa_edit_phase_complete_t cbfn)
Unregister a SIL-SA Edit Phase Complete callback.
This function unregisters a SIL-SA Edit Phase Complete callback.
- Parameters:
cbfn -- address of SIL-SA callback function to use
Example Usage:
void example_cleanup (void)
{
// ...
/* Unregister SIL-SA Edit Phase Complete callback */
agt_cb_sa_edit_phase_complete_unregister(sa_edit_phase_complete);
// ...
}
SIL-SA Edit Phase Complete Callback Example
The following example code shows a SIL-SA Edit Phase Complete callback.
/********************************************************************
* FUNCTION sa_edit_phase_complete
*
* SIL-SA Edit Phase Complete callback.
*
* The SIL-SA Edit Phase Complete callback is the user/system callback
* that is invoked after each Edit Phase has been processed
* during any edit transaction.
*
* SIL-SA Version
*
* Max Callbacks: No limit (except available heap memory)
*
* @param edit_phase edit-phase in progress
* @param transaction_id transaction ID in progress
*
* @return status
*/
static status_t
sa_edit_phase_complete (agt_cbtyp_t edit_phase,
const xmlChar *transaction_id)
{
log_debug("\nEnter sa_edit_phase_complete for '%s' (%s)",
transaction_id,
agt_cbtype_name(edit_phase));
status_t res = NO_ERR;
/* check the transaction and return an error if needed */
return res;
}
Dynamic Default Hook
The Dynamic Default Hook function is the user/system callback that can be used to set up a default value for data nodes during edit operations.
It is invoked when the server checks if an edit needs to have any defaults added. If there is no YANG defined default, the server will check if a custom "system" default value needs to be added using this callback.
The Dynamic Default Hook callback is not part of the auto-generated code and the registration, clean up and callback function must be added manually.
This callback cannot overwrite a YANG default value.
It is only for the nodes that do not have a default statement defined.
The YANG default values can only be changed statically with help of deviations.
If the callback fails and return an error the transaction will be terminated and the server will generate an error. However, the
ERR_NCX_SKIPPED
orERR_NCX_NO_INSTANCE
status will cause the server to skip over the callback node and not terminate the transaction.
The Dynamic Default Hook callbacks specifications and limitations:
Allowed only for leaf or leaf-list nodes
Not allowed for keys nodes
Not allowed for RPC/ACTION input nodes
Not invoked on deletion of any nodes
No support for yangdump-pro auto-generated code
No support for SIL-SA
Only one dynamic default value is allowed for leaf-list nodes.
Dynamic Default Hook Callback
The following function template definition is used for Dynamic Default Hook callback functions:
-
typedef status_t (*ncx_def_hook_cbfn_t)(struct val_value_t_ *parentval, struct obj_template_t_ *obj, xmlChar **buff)
Typedef of the ncx_def_hook_cbfn_t callback.
The Dynamic Default Hook function is the user/system callback that is invoked before the server checks if the node has any defaults and if there is not any YANG defined defaults the server can update the node with custom "system" default value.
Run an instrumentation-defined function for val set default event
- Param parentval:
parent val_value node to use
- Param obj:
object template to use
- Param *buff:
[out] malloced buffer that represents a new default value
- Return:
status: ERR_NCX_SKIPPED or ERR_NCX_NO_INSTANCE will cause the server to skip over this node. Otherwise the server will report an error and ternimate the transaction
Dynamic Default Hook Callback Initialization and Cleanup
The Dynamic Default Hook callback function is hooked into the server with the 'agt_cb_def_hook_register' function. The registration is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database.
-
status_t agt_cb_def_hook_register(const xmlChar *defpath, ncx_def_hook_cbfn_t cbfn)
Register an object specific Dynamic Default callback function to enable custom default value setup for a specified node.
- Parameters:
defpath -- Xpath with default (or no) prefixes defining the object that will get the callbacks
cbfn -- address of callback function to use for Dynamic Default callbacks
- Returns:
status
Example Usage:
static status_t interfaces_init (void)
{
// ...
/* Register a Dynamic Default Hook callback. */
res =
agt_cb_def_hook_register((const xmlChar *)"/interfaces/interface/type",
default_hook_callback);
if (res != NO_ERR) {
return res;
}
// ...
}
-
void agt_cb_def_hook_unregister(const xmlChar *defpath)
Unregister Dynamic Default callback functions for a specific object.
- Parameters:
defpath -- definition XPath location
Example Usage:
void interfaces_cleanup (void)
{
// ...
/* Unregister a Dynamic Default Hook callback */
agt_cb_def_hook_unregister((const xmlChar *)"/interfaces/interface/type");
// ...
}
Dynamic Default Hook Callback Function Example
In this example the SIL code is going to register callbacks for multiple leafy nodes in order to make them dynamic nodes. A callback for "type" leaf will be added, so any time a new "interface" is created the server will be able to dynamically update the "type" leaf in that interface based on the "name" value.
/********************************************************************
* FUNCTION default_hook_callback
*
* The Dynamic Default Hook function is the user/system callback
* that is invoked before the server checks if the node has
* any defaults and if there is not any YANG defined defaults
* the server can update the node with custom "system" default
* value.
*
* Run an instrumentation-defined function
* for val set default event
*
* INPUTS:
* parentval == parent val_value node to use
* obj == object template to use
*
* OUTPUTS
* *buff == malloced buffer that represents a new default value
*
* RETURNS:
* status: ERR_NCX_SKIPPED or ERR_NCX_NO_INSTANCE will cause the server
* to skip over this node. Otherwise the server will report an
* error and ternimate the transaction
*
*********************************************************************/
static status_t
default_hook_callback (val_value_t *parentval,
obj_template_t *obj,
xmlChar **buff)
{
status_t res = NO_ERR;
if (LOGDEBUG) {
log_debug("\n\nEnter Dynamic Default Hook callback for '%s'",
obj_get_name(obj));
}
/* Check the key value of the parent list to setup the
* Dynamic leaf accordingly
*/
val_value_t *child_val = NULL;
if (parentval) {
child_val =
val_find_child(parentval,
DEF_HOOK_TEST_MOD,
(const xmlChar *)"name");
}
const xmlChar *defval = NULL;
if (child_val && !xml_strcmp(obj_get_name(obj), (const xmlChar *)"type")) {
if (LOGDEBUG) {
log_debug("\nCurrent list key value:'%s'",
VAL_STR(child_val));
}
if (!xml_strncmp(VAL_STR(child_val), (const xmlChar *)"vlan", 4)) {
/* check if the interface key value is starting with 'vlan' */
defval = (const xmlChar *)"vlan";
} else if (!xml_strncmp(VAL_STR(child_val), (const xmlChar *)"lag", 3)) {
/* check if the interface key value is starting with 'lag' */
defval = (const xmlChar *)"lag";
} else {
/* otherwise just skip over this node */
return ERR_NCX_NO_INSTANCE;
}
} else {
if (typ_is_string(obj_get_basetype(obj))) {
defval = (const xmlChar *)"NON-DEF-STR";
} else {
defval = (const xmlChar *)"non-def-enum";
}
}
if (LOGDEBUG) {
log_debug("\nDynamic Default value set to:'%s'",
defval);
}
/* Malloc a buffer that will be used to set the default value */
*buff = xml_strdup(defval);
if (*buff == NULL) {
return ERR_NCX_SKIPPED;
}
return res;
} /* default_hook_callback */