If an application needs to perform additional actions and manipulations with datastore(s) in the same transaction during <commit> operation, there are mechanisms implemented in the form of API functions called

  • Validate Complete callback
  • Apply Complete callback
  • Commit Complete callback
  • Rollback Complete callback


These callbacks are called Commit Completeness callbacks and they are In-Transaction type callbacks for SIL.

For SIL-SA version refer to How do I use Commit Completeness Callbacks for SIL-SA?


Manipulation with configuration data are only allowed for Set Hook callbacks. If  Commit Completeness callbacks are trying to adjust configuration data via add_edit() callback, the operation will be skipped. However, this action will not generate an error, just a warning message.


The Commit Completeness callbacks will not be invoked in case the server is run with active:writable:running capability.


The Commit Completeness callbacks are NOT part of the yangdump-sdk code generation. After the make_sil_dir_pro script is run, callbacks will not be generated automatically. All the In-Transaction callbacks are optional and merely intend to provide more efficient way to manipulate with the database and provide an access to the database at the specific phase, transaction, and edit.




The following function template definitions are used for Commit Completeness callback functions:


/* Typedef of the validate_complete callback */
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 apply_complete callback */
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 callback */ 
typedef status_t 
    (*agt_commit_complete_cb_t)(agt_commit_type_t commit_type);

/* Typedef of the rollback_complete callback */
typedef status_t
  (*agt_cb_rollback_complete_t)(ses_cb_t *scb,
                                rpc_msg_t *msg,
                                val_value_t *candidate,
                                val_value_t *running);


The registration for Commit Completeness callbacks is done during the Initialization Phase 1 before the startup configuration is loaded into the running configuration database and before running configurations are loaded. Initialization function with the registration of all the types of Commit Completeness callbacks may look as follows:


/******************************************************************** 
* FUNCTION interfaces_init
* 
* initialize the server instrumentation library.
* Initialization Phase 1
* 
*********************************************************************/ 
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; 
    }

   ...

    /* register apply complete callback */ 
    res = 
        agt_cb_apply_complete_register(apply_compl_callback); 
    if (res != NO_ERR) { 
        return res; 
    }

   ...

    /* register commit complete callback */ 
    res = 
        agt_commit_complete_register(EXAMPLE_MODNAME, 
                                     commit_compl_callback); 
    if (res != NO_ERR) { 
        return res; 
    }

   ...

    /* register rollback complete callback */ 
    res = 
        agt_cb_rollback_complete_register(rollback_compl_callback); 
    if (res != NO_ERR) { 
        return res; 
    }

   ...
}


Now after the registration, whenever the specific Phase is done for the <commit> operation, the callback function will be called and provide the access to the candidate and running datastores. If there are multiple callbacks registered (multiple callbacks of the same type), all the registered callbacks will be called one after another right after the specific Phase is done during the <commit> operation.

The following example code illustrates how the specific Commit Completeness callback can be cleaned up. The callbacks clean up is getting done during Cleanup Phase.

The unregistered function for the Commit Complete callback is slightly different then for other Phases. Note, that it is passing module name to the unregister function.


/******************************************************************** 
* FUNCTION  interfaces_cleanup
*    cleanup the server instrumentation library 
* 
********************************************************************/ 
void interfaces_cleanup (void)
{
  ...
    /* Unregister Validate Complete callback */ 
    agt_cb_validate_complete_unregister(validate_compl_callback); 

  ...

    /* Unregister Apply Complete callback */ 
    agt_cb_apply_complete_unregister(apply_compl_callback); 

  ...

    /* Unregister Commit Complete callback */ 
     agt_commit_complete_unregister(EXAMPLE_MODNAME);

  ...

    /* Unregister Rollback Complete callback */ 
    agt_cb_rollback_complete_unregister(rollback_compl_callback); 

  ...
}



The Commit Completeness functions are the user/system callbacks that are invoked when the specific 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 callback is object independent and module independent which means you don't have to link it to a specific object as it is done for EDIT or GET callbacks. The callback is intended to allow manipulations with the running and candidate configurations (equivalent to completion of validation phase during <commit> operation).

The callback is only called after commit operation for the specific phase has finished not after the validate/apply/commit for the specific module or edit is done.


The register function for the Commit Complete callback is slightly different then for other Phases. Note, that it is passing module name to the registering function, which means that only one active callback per SIL code can be assigned. If you register one callback and then trying to register another for the same module, the first one will be deactivated and the second will become active. This registered callback function will be called after all changes to the candidate database have been committed. If there are multiple callbacks registered (for different modules), all the registered callbacks will be called one after another right after the Commit Phase during the commit operation is done.



Commit Completeness Callback Function Examples


Let us go through simple examples that will illustrate how to utilize Commit Completeness callbacks.


Consider that there are four modules loaded into the server and each having separate SIL code and you would like to register four different callbacks to be called when validation of respective module is completed on the commit. When module number one is modified and a commit operation is performed, only callback registered in module one's SIL code should be called. In this way each module owner can write independent callback for his module, without side effects to other module callbacks.


The Commit Completeness callbacks are not regular SIL callbacks, they cannot not be called based on the specific module or object or any other parameter. They can be named as “global” callbacks. They can only be called after commit operation for the specific phase has finished not after the commit for the specific module or object is done. For the Commit Completeness callbacks the server has no control of identifying what module and when the specific edit was done for the specific module. The edit can be as complex as having multiple modules modifications at the same time. It may reference other modules as a leafref or augment, and those modules can also be triggered during the same edit. If keep in mind that before you execute the commit operation it is allowed to modify candidate datastore as many times as needed. Then for the Commit Completeness callbacks it is impossible to tell when the Commit Completeness callback should be triggered for a particular module. Thus, the Commit Completeness callbacks are called only after the actual completeness of a specific phase, Apply, Validate, Commit Phases.


In the considering scenario with four SIL codes for four different modules you don't have to register four Commit Completeness callbacks for each module. You may register just one callback for each corresponding phase that will take care of the commit operation. If you want to have a callback that will be invoked when a specific module or object is getting modified, then you should consider regular EDIT callbacks.


The following example code illustrates how the Validate Complete callback may look like.


/******************************************************************** 
* FUNCTION   validate_compl_callback
* 
* Validate Complete callback 
* 
* Max Callbacks: 4 (set by AGT_CB_MAX_TRANSACTION_CBFN) 
* 
* 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 res;

}  /* validate_compl_callback */


Assuming there are multiple modules loaded and many modules are modified. Now, when a commit command triggers the Validate Complete Callback and it is getting invoked it means that all validations for all the modules and all edits are completed at this point. Similarly, appropriate callbacks are called for all phases - when commit operation for that phase is finished for all modules and edits. In order to efficiently detect if configurations have changed, there are multiple compare APIs that compares configuration of running and candidate datastores. 

To detect if the configurations for specific module has changed there is no very efficient way. However, it is possible, but will require couple loops that will have to loop through the whole running config and check every node for changes against candidate config. For this scenario, it would be better to register Transaction Hook callback for a specific node(s) in the specific module. In this case you will have direct access to the node that is changing.


The following example code illustrates how the Apply Complete callback may look like.


/******************************************************************** 
* FUNCTION   apply_compl_callback
* 
* Apply Complete callback 
* 
* Max Callbacks: 4 (set by AGT_CB_MAX_TRANSACTION_CBFN) 
* 
* 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 */


The following example code illustrates how the Commit Complete callback may look like.


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

}  /* apply_compl_callback */