The Module Load callback is a user/system callback that is invoked when a YANG module is loaded into the server. This callback is only invoked for main modules, not submodules.


The following function template definition is used for Module Load callback functions:


/* Typedef of the callback */ 
typedef void (*ncx_load_cbfn_t) (ncx_module_t *mod);

Where, MOD pointer represents a module control block structure that is defined in the /netconf/src/ncx/ncxtypes.h. This control block is a representation of one module or submodule during and after parsing. It provides access to the module specific information, such as module name, revision, prefix, and other module specific information. Note, all the fields in this structure should NOT be changed and if accessed, should NOT be accessed directly. This control block ideally should be used only for getting more information about the loaded module, not for alteration of any of its fields.


The ncx_set_load_callback function is used to declare the Module Load callback. The registration can be done during the Initialization Phase 2 after the running configuration has been loaded from the startup file.


extern status_t
    ncx_set_load_callback (ncx_load_cbfn_t cbfn);


Initialization function with the Module Load callback registration may look as follows:


/******************************************************************** 
* FUNCTION interfaces_init2
* 
* initialize the server instrumentation library.
* Initialization Phase 2
* 
*********************************************************************/ 
static status_t 
     interfaces_init2 (void) 
{ 
  ...

    /* register load module callback */ 
    res = ncx_set_load_callback(add_module_callback); 
    if (res != NO_ERR) { 
        return res; 
    }

  ...
}


The following example code illustrates how the Module Load callback can be cleaned up. The callbacks cleanup is getting done during module Cleanup Phase.


/******************************************************************** 
* FUNCTION  interfaces_cleanup
*    cleanup the server instrumentation library 
* 
********************************************************************/ 
void interfaces_cleanup (void)
{
  ...
    ncx_clear_load_callback(add_module_callback);

  ...
}

Let us go through simple examples that will illustrate how to utilize the Module Load callbacks. In this example, the callback code will generate val_value_tree that will represent a new just loaded module and will add it to the static (config=false) node any time a new module is loaded to the server. In this example we are going to use “ietf-yang-library” YANG data model.


The following example code illustrates how the Module Load callback may look like. 


static val_value_t *mymodules_val;

/******************************************************************** 
* FUNCTION add_module_callback 
* 
* Run an instrumentation-defined function 
* for a 'module-loaded' event 
* 
* INPUTS: 
*   mod == module that was added to the registry 
* 
********************************************************************/ 
static void add_module_callback (ncx_module_t *mod) 
{ 
    status_t res = NO_ERR; 

    val_value_t *module = make_module_val(mod, mymodule_obj, &res); 
    if (module == NULL) { 
        log_error("\nError: could not make val_value_t for module"); 
    } else { 
        res = val_child_add(module, mymodules_val); 
        if (res != NO_ERR) {
            val_free_value(module);
         }
    } 

} /* add_module_callback */


In the following part of the above code example we are constructing a val_value tree based on the ncx_module_t information provided by the Module Load event. Any time a new module is loaded into the server, this callback function will be called with MOD pointer, that contains the complete information about just loaded module. Based on this information, this callback function will construct the static data.


	...

	val_value_t *module = make_module_val(mod, mymodule_obj, &res);

	...

The following functions may be used to obtain required information from the loaded module:

  • ncx_get_modname() - Get the main module name

  • ncx_get_mod_nsid() -  Get the main module namespace ID

  • ncx_get_modversion() - Get the module version

  • ncx_get_modnamespace() - Get the module namespace URI



The make_module_val function constructs a static data based on the module information. After a val_value tree for the module is constructed, the module value is added to the mymodules_val parent.

The mymodules_val pointer represents a parent of the module list, which is a container “modules”.


/******************************************************************** 
* FUNCTION make_module_val 
* 
* make a val_value_t struct for a 'mod' module 
* 
* Path: /modules/module 
* 
* RETURNS: 
*     error status 
********************************************************************/ 
static val_value_t * 
        make_module_val (ncx_module_t *mod, 
                         obj_template_t *module_obj, 
                         status_t *res)
{ 
    *res = NO_ERR; 

    /* create /module node */ 
    val_value_t *module_val = val_new_value(); 
    if (!module_val) { 
        *res = ERR_INTERNAL_MEM; 
        return NULL; 
    } 
    val_init_from_template(module_val, module_obj); 

    /* create list key /module/name */    
    val_value_t *childval = 
        agt_make_leaf(module_obj, 
                      (const xmlChar *)"name", 
                      ncx_get_modname(mod), 
                      res); 
    if (!childval) { 
        val_free_value(module_val); 
        return NULL; 
    } 
    *res = val_child_add(childval, module_val); 
    if (*res != NO_ERR) {
         val_free_value(childval);
         val_free_value(module_val);
         return NULL;
    }

    /* create list key module/revision */ 
    childval = 
        agt_make_leaf(module_obj, 
                      (const xmlChar *)"revision", 
                      ncx_get_modversion(mod), 
                      res); 
    if (!childval) { 
        val_free_value(module_val); 
        return NULL; 
    } 
    *res = val_child_add(childval, module_val);
    if (*res != NO_ERR) {
         val_free_value(childval);
         val_free_value(module_val);
         return NULL;
    }

    /* generate the internal index Q chain */ 
    *res = val_gen_index_chain(module_obj, module_val); 
    if (*res != NO_ERR) { 
        log_error("\nError: could not generate index chain (%s)", 
            get_error_string(*res)); 
        val_free_value(module_val); 
        return NULL; 
    } 

    return module_val;

} /* add_module_callback */


In the above code example, we constructed a list entry with two leafs - “name” and “revision” based on the loaded module information. Make sure to generate index chain after you construct a list entry to let the server to normalize and populate list indexes.


  ...

    val_value_t *module = make_module_val(mod, mymodule_obj, &res); 
    if (module == NULL) { 
        log_error("\nError: could not make val_value_t for module"); 
    } else { 
        res = val_child_add(module, mymodules_val); 
        if (res != NO_ERR) {
            val_free_value(module);
        }
    } 

  ...


To summarize, after we created the list entry based on the loaded module information and generated the index chain, we can add this list entry to the parent container mymodules_val with help of val_child_add function.