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:

  • Set Hook

  • Post Set Hook

To perform additional validations, security checks or any other actions with the target datastore in the same transaction:

  • Transaction Hook

  • Start Transaction

  • Complete Transaction

To perform additional actions and manipulations with the target datastore in the same transaction during the <commit> operation, there are Commit Completeness callbacks:

  • Startup Hook

  • Validate Complete

  • Apply Complete

  • Commit Complete

  • Rollback Complete

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 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 EDIT1 or EDIT2 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.

  • 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 or EDIT2 callback for the same object.

Transaction Hook

  • Similar to the Set Hook callback except this callback is invoked just before the data is committed to the running datastore.

  • This callback will be invoked after EDIT1 or EDIT2 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.

Set Order Hook

  • This callback allows the SIL Priority to be set for data nodes in the EDIT transaction.

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

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

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

  5. SIL Callbacks (Apply Phase)

  6. Apply Complete

  7. SIL Callbacks (Commit Phase)

  8. Transaction Hook

  9. Commit/Rollback 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)

  7. SIL Callbacks (Apply Phase)

  8. SIL Callbacks (Commit Phase)

  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)

  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 "true".

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 or EDIT2 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 datastore nodes is only allowed for Set Hook and Post Set Hook callbacks. If any other In-Transaction callbacks invoke the Add Edit API the operation will be ignored.

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

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 just before the data is committed to the running datastore. This callback will be invoked after EDIT1 or EDIT2 callbacks for the same object.

Transaction Hook Callback

The same callback template is used for Set Hook and Transaction Hook. Refer to the Set Hook Callback section for details.

Transaction Hook Callback Initialization and Cleanup

A Transaction Hook callback function is registered and unregistered with the 'agt_cb_hook_register' and 'agt_cb_hook_unregister; functions.

Refer to the Set Hook Callback Initialization and Cleanup section for details.

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 just before the data is committed to the running datastore.

  • 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 txcb

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

address of return status

Retval *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 [email protected]::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 [email protected]::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 [email protected]::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 the datastores is only allowed for Set Hook or Post Set Hook callbacks. If Transaction Hook or Start/Complete Transaction callbacks call 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 is used to insert or move new list or leaf-list entries to the transaction in progress.

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 is 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. Manipulation with datastore are only allowed for Set Hook or Post Set Hook callbacks.

The following function template definition is used for Add Edit Maximum API callback functions:

status_t agt_val_add_edit_max(ses_cb_t *scb, rpc_msg_t *msg, agt_cfg_transaction_t *txcb, const xmlChar *defpath, val_value_t *edit_value, const xmlChar *edit_operation, const xmlChar *insert_where, const xmlChar *insert_point, boolean skip_cb)

Create a new edit based on edit_value.

if its NULL or invalid the error will be generated.

Move or insertion OP available. Skip callbacks for added edits option is available.

Only allowed for Set Hooks or Post Set Hook, the rest are ignored.

Parameters
  • scb -- session invoking the edit

  • msg -- incoming commit rpc_msg_t in progress

  • txcb -- transaction in progress

  • defpath -- XPath path of object instance

  • edit_value -- val_value_t

    representing newnode in transaction

    only needed for create, merge, replace, not delete

  • edit_operation -- <operation string>.

    • "create"

    • "delete"

    • "insert"

    • "merge"

    • "move"

    • "replace"

    • "remove"

  • insert_where -- <insert enum string>.

    • "before"

    • "after"

    • "first"

    • "last"

    • Will be used only if the operations are "move" or "insert". Ignored otherwise.

  • insert_point --

    is a XPath encoded string like the defpath. Only for 'before' or 'after' insert_where paramter. The insert_where must be set to 'before' or 'after' if insert_point specified. Will be used only if the operations are "move" or "insert".

    Ignored otherwise.

    E.g: "/test3[string.1='entry2'][uint32.1='2']"

  • skip_cb -- TRUE if DO NOT invoke callbacks for an edded edit if any. FALSE if SKIP any callback for added edit including Transaction, EDIT1, EDIT2 callbacks Only when target=running

Returns

status

The following code snippet from agt/agt_val_silcall.c shows the usage of this function:

agt_sil_added_edit_t *edit =
    (agt_sil_added_edit_t *)dlq_firstEntry(edded_editQ);
for (; edit && (res == NO_ERR);
       edit = (agt_sil_added_edit_t *)dlq_nextEntry(edit)) {

    res =
        agt_val_add_edit_max(scb,
                             msg,
                             txcb,
                             edit->path,
                             edit->editval,
                             edit->editop,
                             edit->where,
                             edit->point,
                             edit->skipcb);
    if (res != NO_ERR) {
        log_error("\nError: failed to add edit from subsystem '%s' "
                  "defpath '%s' (%s)\n",
                  subsys_id,
                  edit->path,
                  get_error_string(res));
    }
}

SIL-SA Add Edit API

The SIL-SA version of the Add Edit API has a different function template since the transaction control block is not available in the SIL-SA code. The complete functionality of the Add Edit API is still available and all the edits will be added the same way as in SIL code.

The following function template definition is used for SIL-SA Add Edit API callback functions:

status_t sil_sa_add_edit(const xmlChar *defpath, val_value_t *edit_value, const xmlChar *edit_operation, const xmlChar *insert_where, const xmlChar *insert_point, boolean skip_cb)

Add an edit to the current transaction from SIL-SA.

Save an added edit in the SIL-SA Control block for further hook-response processing

Move or insertion OP available. Skip callbacks for added edits option is available.

Only allowed for Set Hooks or Post Set Hook, the rest are ignored.

See also

db_api_edit_full2

Parameters
  • defpath -- XPath path of object instance

  • edit_value -- val_value_t

    representing newnode in transaction

    only needed for create, merge, replace, not delete

  • edit_operation -- <operation string>

    • "create"

    • "delete"

    • "insert"

    • "merge"

    • "move"

    • "replace"

    • "remove"

  • insert_where -- <insert enum string> (NULL if not used)

    • "before"

    • "after"

    • "first"

    • "last"

    • Will be used only if the operations are "move" or "insert".

      Ignored otherwise.

  • insert_point --

    is a XPath encoded string like the defpath. Only for 'before' or 'after' insert_where paramter. The insert_where must be set to 'before' or 'after' if insert_point specified. Will be used only if the operations are "move" or "insert".

    Ignored otherwise.

    E.g: "/test3[string.1='entry2'][uint32.1='2']"

  • skip_cb -- TRUE if DO NOT invoke callbacks for an edded edit if any. FALSE if SKIP any callback for added edit including Transaction, EDIT1, EDIT2 callbacks Only when target=running

Returns

status

The following code snippet shows how this API may be used,

if (newval &&
    !xml_strcmp(VAL_NAME(newval), (const xmlChar *)"ports")) {

    const xmlChar *edit_operation = (const xmlChar *)"delete";
    const xmlChar *insert_point = NULL;
    const xmlChar *insert_where = NULL;
    boolean skip_cb = FALSE;

    val_value_t *testval = sil_sa_get_data(NCX_CFGID_RUNNING,
                                           defpath,
                                           &res2);
    if (testval) {
        log_info("\n+++ Got testval %s (%s)",
                 VAL_NAME(testval),
                 get_error_string(res));
    }

    val_value_t *testval2 = sil_sa_get_data(NCX_CFGID_RUNNING,
                                            defpath2,
                                            &res2);
    if (testval2) {
        log_info("\n+++ Got testval2 %s (%s)",
                 VAL_NAME(testval2),
                 get_error_string(res));
    }

    if (testval) {
        res =
            sil_sa_add_edit(defpath,
                            NULL,               // edit_value
                            edit_operation,
                            insert_where,
                            insert_point,
                            skip_cb);
    }
}

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 -- address of return status

Return values

*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 -- address of return status

Return values

*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!

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

Returns

the status of the operation.

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:

  • Validate Complete

  • Apply Complete

  • Commit Complete

  • Rollback Complete

The Commit Completeness Callbacks are not regular SIL callbacks. Unlike an EDIT1 or EDIT2 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.

Note

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.

  • Use these callbacks instead of using SIL Priority!

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

Validate Complete

The Validate Complete function is the user/system callback that is invoked after the Validate Phase has been processed during 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.

  • The Validate Complete callback is only called after the commit operation for the Validate phase has finished not after the commit for the specific module or edit is done.

  • 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 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> 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> operation, the callback function will be called and provide the access to the candidate and running datastores.

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

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.

Commit Complete Callback

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


    return NO_ERR;

}  /* apply_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 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 */

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

Retval *buff

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