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.

../_images/silsa_hooks.png

To manipulate the datastore contents in the same transaction:

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.

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

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>):

  1. Transaction Start

  2. Set Order Hook

  3. Set Hook and then Set Order Hook for the added edit if any

  4. SIL Callbacks (only during Validation Phase)

  5. Post Set Hook and then Set Order Hook for the added edit if any

  6. SIL Callbacks for added edits (only during Validation Phase)

  7. Transaction Complete

The possible invocation order during the <commit> operation may look as follows, processing on the <running> datastore, after this operation.

  1. Transaction Start

  2. Set Order Hook

  3. SIL Callbacks (Validate Phase)

  4. Validate Complete Edit Phase Complete

  5. SIL Callbacks (Apply Phase)

  6. Apply Complete Edit Phase Complete

  7. SIL Callbacks (Commit Phase)

  8. Transaction Hook

  9. Commit/Rollback Complete Edit Phase Complete

  10. 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):

  1. Transaction Start

  2. Set Order Hook

  3. Set Hook and then Set Order Hook for the added edit if any

  4. SIL Callbacks (Validate Phase)

  5. Post Set Hook and then Set Order Hook for the added edit if any

  6. SIL Callbacks for added edits (Validate Phase) Edit Phase Complete

  7. SIL Callbacks (Apply Phase) Edit Phase Complete

  8. SIL Callbacks (Commit Phase) Edit Phase Complete

  9. Transaction Hook

  10. Transaction Complete

The possible invocation order during the 'validate' operation may look as follows, processing on the <running> datastore only:

  1. Transaction Start

  2. Set Order Hook

  3. SIL Callbacks (Validate Phase) Edit Phase Complete

  4. Transaction Complete

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

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 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");

     // ...
 }

Set Hook Callback and Add Edit API Function Examples

The following YANG module will be used in this example:

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:

/********************************************************************
* 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 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;

    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);

            /* malloce and construct list value, for more
             * examples refer to libhooks-test/src/hooks-test.c library
		*/
            uint32 key = 11;
            val_value_t *list_value = create_list_entry(VAL_OBJ(editval),
                                                        key,
                                                        &res);
            if (!list_value) {
                val_free_value(editval);
                return res;
            }

            /* add a new list entry */
            res = val_child_add(list_value, editval);
            if (res != NO_ERR) {
                val_free_value(list_value);
            } else {
                /* add a new edit, MERGE on defpath with 1 new list entry */
                res = agt_val_add_edit(scb,
                                       msg,
                                       txcb,
                                       defpath,
                                       editval,
                                       OP_EDITOP_MERGE);
            }

            /* clean up the editval */
            val_free_value(editval);
        }
        break;
    case OP_EDITOP_DELETE:
        break;
    default:
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }

    if (res != NO_ERR) {
        agt_record_error(scb,
                         &msg->mhdr,
                         NCX_LAYER_CONTENT,
                         res,
                         NULL,
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
                         errorval,
                         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
                         errorval);
    }

    return res;

}  /* sethook_callback_edit */

Refer to create_list_entry for an example of the list entry creation.

Example Response Message:

<rpc-reply message-id="101"
  xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="http://yumaworks.com/ns/interfaces">
    <interface>
      <name>vlan1</name>
      <observed-speed>1000</observed-speed>
    </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:
        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_edit */

Set Hook Callback and 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.

Refer to the Add Edit Extended API section for details on the "agt_val_add_edit_ex" API function.

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"
#define HOOKS_TEST_REV (const xmlChar *)"2019-01-01"

/********************************************************************
* 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:
        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_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:

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 Set Hook Callback and 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;
  }
}

Note

The Set Hook callback is not part of the auto-generated code the stub SIL-SA code must be created and modified manually.

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>

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:

  1. Transaction Start

  2. Set Order Hook for /if:interface[name=vlan1]

  3. Set Hook for /if:interface[name=vlan1] to add /if:interface[name=vlan2]

  4. Set Order Hook for /if:interface[name=vlan2]

  5. SIL Callback (Validate Phase) for /if:interface[name=vlan1]

  6. Post Set Hook for /if:interface[name=vlan1] to add /if:interface[name=vlan3]

  7. SIL Callback (Validate Phase) for /if:interface[name=vlan2]

  8. SIL Callback (Validate Phase) for /if:interface[name=vlan3]

  9. Transaction Complete

Edit on running datastore (after <commit>):

  1. Transaction Start

  2. SIL Callback (Validate Phase) for /if:interface[name=vlan1]

  3. SIL Callback (Validate Phase) for /if:interface[name=vlan2]

  4. SIL Callback (Validate Phase) for /if:interface[name=vlan3]

  5. Validate Complete

  6. SIL Callback (Apply Phase) for /if:interface[name=vlan1]

  7. SIL Callback (Apply Phase) for /if:interface[name=vlan2]

  8. SIL Callback (Apply Phase) for /if:interface[name=vlan3]

  9. Apply Complete

  10. SIL Callback (Commit Phase) for /if:interface[name=vlan1]

  11. SIL Callback (Commit Phase) for /if:interface[name=vlan2]

  12. SIL Callback (Commit Phase) for /if:interface[name=vlan3]

  13. Transaction Hook for /if:interface[name=vlan1]

  14. Transaction Hook for /if:interface[name=vlan2]

  15. Transaction Hook for /if:interface[name=vlan3]

  16. Commit/Rollback Complete

  17. 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:

  1. Transaction Start

  2. Set Order Hook for /if:interface[name=vlan1]

  3. Set Hook for /if:interface[name=vlan1] to add /if:interface[name=vlan2]

  4. Set Order Hook for /if:interface[name=vlan2]

  5. SIL Callback (Validate Phase) for /if:interface[name=vlan1]

  6. Post Set Hook for /if:interface[name=vlan1] to add /if:interface[name=vlan3]

  7. SIL Callback (Validate Phase) for /if:interface[name=vlan2]

  8. SIL Callback (Validate Phase) for /if:interface[name=vlan3]

  9. SIL Callback (Apply Phase) for /if:interface[name=vlan1]

  10. SIL Callback (Apply Phase) for /if:interface[name=vlan2]

  11. SIL Callback (Apply Phase) for /if:interface[name=vlan3]

  12. SIL Callback (Commit Phase) for /if:interface[name=vlan1]

  13. SIL Callback (Commit Phase) for /if:interface[name=vlan2]

  14. SIL Callback (Commit Phase) for /if:interface[name=vlan3]

  15. Transaction Hook for /if:interface[name=vlan1]

  16. Transaction Hook for /if:interface[name=vlan2]

  17. Transaction Hook for /if:interface[name=vlan3]

  18. 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>.

  1. Transaction Start

  2. Set Order Hook for /if:interface[name=vlan1]

  3. Set Hook for /if:interface[name=vlan1] to add /if:interface[name=vlan2]

  4. Set Order Hook for /if:interface[name=vlan2]

  5. SIL Callback (Validate Phase) for /if:interface[name=vlan1]

  6. Post Set Hook for /if:interface[name=vlan1] to add /if:interface[name=vlan3]

  7. SIL Callback (Apply Phase) for /if:interface[name=vlan1]

  8. SIL Callback (Commit Phase) for /if:interface[name=vlan1]

  9. Transaction Hook for /if:interface[name=vlan1]

  10. Transaction Complete

Note

The skip_cb parameter can be used only when the default target is <running>. The server will always invoke callbacks for added edits in case the target is <candidate>.

Note

The skip_cb parameter cannot be used when Add Edit API is used to update the value in the same subtree in the same transaction.

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.

This callback will be invoked after EDIT1, EDIT2, or EDIT3 callbacks for the same object during the Commit Phase.

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.

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

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

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

Add Edit

The 'Add Edit' API is used to add an edit to the transaction in progress.

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.

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

The following example from the hooks_sethook_edit function shows how to create and add an edit.

 /* find object template of the desired node */
 obj_template_t *targobj =
           ncx_match_any_object_ex((const xmlChar *)"ietf-interfaces",
                                   (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);

 /* malloced and construct list value, for more
  * examples refer to libhooks-test/src/hooks-test.c library
  */
 uint32 key = 11;
 val_value_t *list_value = create_list_entry(VAL_OBJ(editval),
                                             key,
                                             &res);
 if (!list_value) {
     val_free_value(editval);
     return res;
 }

 /* add a new list entry */
 val_add_child_sorted(list_value, editval);

 /* add a new edit, MERGE on defpath with 1 new list entry */
 if (res == NO_ERR) {
     res = agt_val_add_edit(scb,
                            msg,
                            txcb,
                            defpath,
                            editval,
                            OP_EDITOP_MERGE);
 }

 /* clean up the editval */
 val_free_value(editval);

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.

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.

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

Note

The server does not support the 'insert' and 'move' operation on the nodes that are being modified at the same transaction at the same time.

Usage Example:

Refer to the Set Hook example containing 'hooks_sethook_edit_insert' in the Set Hook Callback and Add Edit Extended API Function Examples section.

The code snippet below from that function adds the edit using this API:

/* 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);
}

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.

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.

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

Note

The skip_cb parameter can be used only when the default target is <running>. The server will always invoke callbacks for added edits in case the target is <candidate>.

Note

The skip_cb parameter cannot be used when Add Edit API is used to update the value in the same subtree in the same transaction.

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);
    }
}

Get Data

The 'Get Data' API is used to retrieve data nodes from the server.

If the XPath expression matches multiple nodes, then only the first instance is returned. The exact instance is implementation-dependent if the data is “ordered-by system”.

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.

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:

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 res;

}  /* 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 or AGT_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 or ERR_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 */