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