Setting Object User Flags

The YANG compiler built into the netconfd-pro server has some yp-system level callback APIs that allow YANG data structures to be customized to assist SIL automation or improve processing performance.

  • The obj_template_t structure contains a 'uflags' field that is not used by the server. It is intended for SIL callbacks to use by using the 'OBJ_USER_FLAGS' macro.

  • The SIL code does not need to use the callback APIs described in this section to set the user flags, but a common use for these flags is to check for YANG metadata in YANG data nodes and set user flags based on this information.

  • This feature allows the object template for YANG data nodes to be examined, and the user flags to be set as needed.

  • The server uses binary flags to represent information parsed from YANG metadata. This is more efficient than searching the 'appinfoQ' data structures each time the data is needed.

  • There is a special field in the obj_template_t structure to allow vendor-specific flag definitions for an object template. This field is 32 bits wide and initialized to zero when the object template is created.

There is an API that is called when the object is parsed.

Field definition: uint32 uflags;

Access Macro: OBJ_USER_FLAGS(obj)

Object Callback to Set User Flags

This API is called for all objects during YANG module parsing. The callback may wish to use filters such as 'obj_is_data_db(obj)' to limit scanning to only database objects.

typedef void (*ncx_yang_obj_cbfn_t)(ncx_module_t *mod, struct obj_template_t_ *obj)

user function callback template when a YANG object is parsed by yang_obj.c.

This API is invoked at the end of the resolve phase if the status is NO_ERR It is skipped if the object has errors detected at the time

ncx_yang_obj_cbfn_t

Run an instrumentation-defined function for a 'object parsed' event

Param mod:

module that is being parsed now

Param obj:

object being parsed

The registration function needs to be called from the yp-system initialization callbacks. This callback should be installed before any SIL-related YANG modules are loaded.

The registration functions are defined in ncx/ncx.h

status_t ncx_set_yang_obj_callback(ncx_yang_obj_cbfn_t cbfn)

Set the callback function for a YANG object parse event.

Parameters:

cbfn -- callback function to set

Returns:

status

void ncx_clear_yang_obj_callback(ncx_yang_obj_cbfn_t cbfn)

Clear the callback function for a parse-object event.

Parameters:

cbfn -- callback function to find and clear

YANG Object Template Callback Example

This example callback function checks for a proprietary YANG extension within a YANG object.

/***********  Example YANG Object Template Callback **********/

/*
 * Assume YANG module foo exists with extension acme1
 *
 * module foo {
 *   prefix f;
 *   ...
 *   extension acme1 { ... }
 *
 * The extension is used inside object definitions. E.g:
 *
 *    leaf X {
 *      f:acme1;
 *      type string;
 *    }
 *
 * Assume there is a vendor bit defined for the user flags field
 */

#define FL_ACME_1  0x1

Example Callback Function:

/* user function callback template when a YANG object is
 * parsed by yang_obj.c. This API is invoked at the
 * end of the resolve phase if the status is NO_ERR
 * It is skipped if the object has errors detected at the time
 *
 * ncx_yang_obj_cbfn_t
 *
 *  Run an instrumentation-defined function
 *  for a 'object parsed' event
 *
 * INPUTS:
 *   mod == module that is being parsed now
 *   obj == object being parsed
 */
void
    example_obj_template_cbfn (ncx_module_t *mod,
                               struct obj_template_t_ *obj)
{
    /* optional: use the module to check certain module names to
     * pre-filter the callback
     */
    (void)mod;

    /* get the appinfoQ for the object */
    dlq_hdr_t *appinfoQ = obj_get_appinfoQ(obj);
    if (appinfoQ == NULL) {
        return;   // error!
    }

    /* check the object template appinfoQ to see if the vendor
     * extensions are present
     */
    ncx_appinfo_t *appinfo =
        ncx_find_appinfo(appinfoQ,
                         (const xmlChar *)"f",
                         (const xmlChar *)"acme1");
    if (appinfo) {
        OBJ_USER_FLAGS(obj) |= FL_ACME_1;
    }

}

Accessing Object Level Appinfo

All of the YANG extensions that are applied to a data definition statement can be accessed by SIL and SIL-SA code during callbacks that have access to the object template (obj_tempalte_t struct). This data can be nested. It is saved in a structure called the 'ncx_appinfo_t'.

There must be a YANG 'extension' statement that corresponds to the appinfo or the YANG syntax will not be valid. This extension can be in any module, including the module using the extension.

Example Appinfo YANG Usage

module mymodule {
  // ...
  prefix mymod;

  extension myinfo {
     description
       "Save my info.
        The mandatory 'parmstr' argument is the info.
        The type is a plain string.";
     argument parmstr;
  }

  container my-container {
    mymod:myinfo "test-plan 9";
    // ...
  }
}

Appinfo Data Structure

The following data structure should not be used directly:

struct ncx_appinfo_t

YANG extension usage entry A nested external statement will produce a tree of ncx_appinfo_t.

Public Members

dlq_hdr_t qhdr

queue header

xmlChar *prefix

external prefix used

xmlChar *name

external statement name

xmlChar *value

argument string (if any)

struct ext_template_t_ *ext

YANG extension found to match this external stmt.

dlq_hdr_t *appinfoQ

nested external statements within this one

boolean isclone

flag to identfy cloned appinfo

ncx_error_t tkerr

file and line info for the compiler

The server will process the YANG modules when they are loaded into server. The appinfo data will be set at this time and not changed after creation.

Appinfo API Functions

The most common usage for appinfo is to find a specific extension and check its value (if any). The 'obj_find_appinfo' function can be used for this purpose:

status_t obj_get_appinfo(obj_template_t *obj, const xmlChar *prefix, const xmlChar *name, const xmlChar **retval)

Get the first (or only) instance of the specified appinfo.

Parameters:
  • obj -- object to check for the appinfo (mandatory)

  • prefix --

    prefix string for the appinfo

    leave NULL unless expecting duplicates

  • name -- name of the appinfo (extension identifier) (mandatory)

  • retval -- [out]

    address of return value (may be NULL)

    *retval == const pointer to the appinfo value string; may be NULL RETURNS status expect NO_ERR if found or ERR_NCX_NOT_FOUND if not found

The queue of ncx_appinfo_t structs can be read directly by using the 'obj_get_appinfoQ' function.

dlq_hdr_t *obj_get_appinfoQ(obj_template_t *obj)

Get the appinfoQ for this obj.

Parameters:

obj -- the specific object to check

Returns:

pointer to the appinfoQ for this object

Appinfo Object Example

In this example the 'my-container' EDIT callback function used to show how the 'myinfo' data can be accessed during the edit callback.

SIL Code Snippet for my-container EDIT

The code generated in 'u_mymodule.c' for the function 'u_mymod_my_container_edit' needs to be edited to use the 'obj_get_appinfo' function.

 /*
  * @brief Edit database object callback (agt_cb_fn_t)\n
  * Path: container /my-container
  *
  * @param scb session control block making the request
  * @param msg message in progress for this edit request
  * @param cbtyp callback type for this callback
  * @param editop the parent edit-config operation type,
  * which is also used for all other callbacks
  * that operate on objects.
  * @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 return status for the phase.
  */
 status_t u_mymod_my_container_edit (
     ses_cb_t *scb,
     rpc_msg_t *msg,
     agt_cbtyp_t cbtyp,
     op_editop_t editop,
     val_value_t *newval,
     val_value_t *curval)
 {
     (void)scb;  /* remove if scb used */
     (void)msg;  /* remove if msg used */
     (void)newval;  /* remove if newval used */
     (void)curval;  /* remove if curval used */

     status_t res = NO_ERR;

     if (LOGDEBUG) {
         log_debug("\nEnter u_mymod_my_container_edit callback for %s phase",
             agt_cbtype_name(cbtyp));
     }

     obj_template_t *obj = NULL;
     if (newval) {
         obj = VAL_OBJ(newval);
     } else if (curval) {
         obj = VAL_OBJ(curval);
     }
     if (obj == NULL) {
         return ERR_NCX_OPERATION_FAILED;
     }

     const xmlChar *name = (const xmlChar *)"myinfo";
     const xmlChar *myinfo = NULL;
     res = obj_get_appinfo(obj,    // obj to search is mandatory
                           NULL,   // prefix can be NULL
                           name,   // appinfo name is mandatory
                           &myinfo);  // return string is mandatory
     if (res == NO_ERR) {
         if (LOGDEBUG) {
             log_debug("\nGot 'myinfo' extension for object '%s' = '%s'",
                       obj_get_name(obj),
                       myinfo);
         }
     }  // else probably ERR_NCX_NOT_FOUND

     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:
             break;
         case OP_EDITOP_MERGE:
             break;
         case OP_EDITOP_REPLACE:
             break;
         case OP_EDITOP_CREATE:
             break;
         case OP_EDITOP_DELETE:
             break;
         default:
             /* USE SET_ERROR FOR PROGRAMMING BUGS ONLY */
             res = SET_ERROR(ERR_INTERNAL_VAL);
         }
         break;
     case AGT_CB_ROLLBACK:
         /* undo device instrumentation here */
         break;
     default:
         /* USE SET_ERROR FOR PROGRAMMING BUGS ONLY */
         res = SET_ERROR(ERR_INTERNAL_VAL);
     }
     return res;

 } /* u_mymod_my_container_edit */

Appinfo Usage During EDIT Callback

The client can sent an <edit-config> operation to cause the EDIT callback to be invoked. In this example, the --target parameter is set to the default 'candidate'.

andy@localhost> merge /my-container

Filling container /my-container:
RPC OK Reply 4 for session 3 [default]:

andy@localhost>

The server log shows the debug trace from the SIL callback function:

 Enter my_container_edit callback for validate phase
 Enter u_mymod_my_container_edit callback for validate phase
 Got 'myinfo' extension for object 'my-container' = 'test-plan 9'
 Start full commit of transaction 755374: 1 edit on candidate config
 edit-transaction 755374: on session 3 by [email protected]
   time: 2024-03-06T01:26:08Z
   message-id: 4
   trace-id: --
   datastore: candidate
   operation: create
   target: /mymod:my-container
   comment: none

 Complete commit OK of transaction 755374 on candidate database

The client will then send a <commit> operation to apply the edit to the "<running>" datastore.

andy@localhost> commit

RPC OK Reply 5 for session 3 [default]:

andy@localhost>

The server log shows the debug trace from the SIL callback function:

 Enter my_container_edit callback for validate phase
 Enter u_mymod_my_container_edit callback for validate phase
 Got 'myinfo' extension for object 'my-container' = 'test-plan 9'
 Enter my_container_edit callback for apply phase
 Enter u_mymod_my_container_edit callback for apply phase
 Got 'myinfo' extension for object 'my-container' = 'test-plan 9'
 Start full commit of transaction 755375: 1 edit on running config
 Enter my_container_edit callback for commit phase
 Enter u_mymod_my_container_edit callback for commit phase
 Got 'myinfo' extension for object 'my-container' = 'test-plan 9'
 edit-transaction 755375: on session 3 by [email protected]
   time: 2024-03-06T01:39:03Z
   message-id: 5
   trace-id: --
   datastore: running
   operation: create
   target: /mymod:my-container
   comment: none

 Complete commit OK of transaction 755375 on running database
 Writing <running> config to file '/home/andy/.yumapro/startup-cfg.xml'
 Generating <netconf-config-change> notification