The following article illustrates how to utilize the EDIT1 callbacks in examples.


The server supports 2 modes of database editing callbacks. The original mode EDIT1 is designed to invoke data node callbacks at the leaf level.  This means that each altered leaf will cause a separate SIL callback.  If no leaf callbacks are present, then the parent nodewill be invoked multiple times.

The EDIT2 mode is “list-based” or “container-based” instead. The EDIT2 callbacks are out of the scope of this article.


The following function template definition is used for EDIT1 andEDIT2 callback functions:


/* Typedef of the EDIT1/2 callback functions */
typedef status_t 
    (*agt_cb_fn_t) (ses_cb_t  *scb, 
                    rpc_msg_t *msg, 
                    agt_cbtyp_t cbtyp, 
                    op_editop_t  editop, 
                    val_value_t  *newval, 
                    val_value_t  *curval);


The EDIT1 callback function is hooked into the server with the agt_cb_register_callback function, described below. The SIL code generated by yangdump-pro uses this function to register a single callback function for all callback phases.

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.

Initialization function with the EDIT1 callbacks registration may look as follows.


Register EDIT1 Callback


extern status_t 
    agt_cb_register_callback (const xmlChar *modname,
                              const xmlChar *defpath,
                              const xmlChar *version,
                              agt_cb_fn_t cbfn);


Parameter
Description
modname
Module name string that defines this object node.
defpath
Absolute path expression string indicating which node the callback function is for.
version
If non-NULL, indicates the exact module version expected.
cbfn
The callback function address.  This function will be used for all callback phases.


EDIT1 Callback Cleanup


extern void
    agt_cb_unregister_callbacks(const xmlChar *modname,
                                const xmlChar *defpath);



Parameter
Description
modname
Module name string that defines this object node.
defpath
Absolute path expression string indicating which node the callback function is for.

If you register a callback for a specific object,your SIL code should unregister it during the cleanup phase, that is being called any time the server is shutting down. Also, it is getting called during a restart and reload procedure.



NOTE:

The unregister function can be called just once for a specific object. It will unregister EDIT1, EDIT2, and even GET2 callbacks for the object. However, if it is called multiple times for the same object, it will return with NO errors.

All other callbacks that the object may hold should be unregistered separately.




In the example below,the callback code forces Rollback Phase if a new value of an “interface” list is not acceptable. Otherwise an agent can process to the next step and run device instrumentation as required. 

A new list validation, in this example, is done during “commit” phase. A new value is already written to the datastore (value is getting written during apply phase) which means the server will have to reverse the edit. The server will automatically delete just created new list element from the datastore and restore the state to the initial state, to thestate before the edit.



/********************************************************************
* FUNCTION  edit1_callback_example
* 
* Callback function for server object handler 
* Used to provide a callback sub-mode for 
* a specific named object
*
* Path: /interfaces/interface
* Add object instrumentation in COMMIT phase.
* 
********************************************************************/
static status_t
    edit1_callback_example (ses_cb_t *scb,
              rpc_msg_t *msg,
              agt_cbtyp_t cbtyp,
              op_editop_t editop,
              val_value_t *newval,
              val_value_t *curval)
{
    status_t res = NO_ERR;
    val_value_t *errorval = (curval) ? curval : newval;
    const xmlChar *errorstr = (errorval) ? NCX_NT_VAL : NCX_NT_NONE;

    /* try to find a key value of the /interfaces list to validate */
    val_value_t *child_val = NULL; 
    if (newval) { 
        child_val = 
            val_find_child(newval, 
                           EXAMPLE_MODNAME, 
                           (const xmlChar *)"name"); 

        if (child_val && typ_is_string(VAL_BTYPE(child_val))) { 
            log_info("\ncallback for %s editop, test child name=%s", 
                op_editop_name(editop), VAL_STR(child_val)); 
        } 
    }

    switch (cbtyp) {
    case AGT_CB_VALIDATE:
        /* description stmt validation here */
        break;
    case AGT_CB_APPLY:
        /* database manipulation done here */
        break;
    case AGT_CB_COMMIT:
        /* device instrumentation done here */
        switch (editop) {
        case OP_EDITOP_LOAD:
            interface_enabled = TRUE;
            break;
        case OP_EDITOP_MERGE:
            break;
        case OP_EDITOP_REPLACE:
            break;
        case OP_EDITOP_CREATE:
            interface_enabled = TRUE;

             /* Force Rollback if the key value is not acceptable */
            if (newval && child_val && typ_is_string(VAL_BTYPE(child_val)) && 
                !xml_strcmp(VAL_STR(child_val), (const xmlChar *)"not-supported"))  { 

                log_info("\nKey value is not supported for %s editop, name=%s", 
                   op_editop_name(editop), VAL_STR(child_val)); 

                /* Validation failed if a key value is not supported */ 
                errorval = child_val;
                res = ERR_NCX_OPERATION_NOT_SUPPORTED; 
            } else {
                /* Run device instrumentation here  */
            }
            break;
        case OP_EDITOP_DELETE:
            interface_enabled = FALSE;
            break;
        default:
            res = SET_ERROR(ERR_INTERNAL_VAL);
        }

        break;
    case AGT_CB_ROLLBACK:
        /* undo device instrumentation here */
        break;
    default:
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }

    /* if error: set the res, errorstr, and errorval parms */
    if (res != NO_ERR) {
        agt_record_error(scb,
           &msg->mhdr,
           NCX_LAYER_CONTENT,
           res,
           NULL,
           NCX_NT_STRING,
           errorstr,
           NCX_NT_VAL,
           errorval);
    }
    
    return res;

} /* edit_callback_example */


Now, let us go through the most imminent parts of the above example. In the following part of the above code example we are trying to find “name” key node of the edited “interface” list.


  ...

    val_value_t *child_val = NULL; 
    if (newval) { 
        child_val = 
            val_find_child(newval, 
                           EXAMPLE_MODNAME, 
                           (const xmlChar *)"name"); 

        if (child_val && typ_is_string(VAL_BTYPE(child_val))) { 
            log_info("\ncallback for %s editop, test child name=%s", 
                op_editop_name(editop), VAL_STR(child_val)); 
        } 
    }

  ...


val_find_child API function finds the specified child node. Alternatively, val_find_child_fast, val_find_child_obj,  val_find_child_que, etc API functions could be used to retrieve the desired value.

typ_is_string API function checks if the base type is a simple string touse this string later for logging. Alternatively, typ_is_enum, typ_is_number, etc API functions could be used to check the desired type.

VAL_BTYPE() and VAL_STR() macros are used to access val_value_t structure and get the information about its base type and get string value. If the type of the value would be an integer, for example, VAL_INT32, VAL_UINT16, etc.macros could be used to retrieve the actual set value of it.

In the following part of the above code example we are trying to validate previously retrieved key value. If the provided in the <edit-config> key value is not acceptable as specified below, then the status_t res pointer will be set to ERR_NCX_OPERATION_NOT_SUPPORTED enumeration value, that would signal to record an error and rollback the <edit-config> operation.


  ...

         /* Force Rollback if the key value is not acceptable */
            if (newval && child_val && typ_is_string(VAL_BTYPE(child_val)) && 
                !xml_strcmp(VAL_STR(child_val), (const xmlChar *)"not-supported"))  { 

                log_info("\nKey value is not supported for %s editop, name=%s", 
                   op_editop_name(editop), VAL_STR(child_val)); 

                /* Validation failed if a key value is “not-supported” */ 
                errorval = child_val;
                res = ERR_NCX_OPERATION_NOT_SUPPORTED; 
            } else {
                /* Run device instrumentation here  */
            }
  ...