YumaPro API Quick Start Guide

YumaPro API Quick Start Guide
YANG-Based Unified Modular Automation Tools
Common API Guide
Version 16.10-10
YumaPro API Quick Start Guide
Table of Contents
1 Preface............................................................................................................................................5
1.1 Legal Statements......................................................................................................................5
1.2 Additional Resources................................................................................................................5
1.2.1 WEB Sites.................................................................................................................................... 5
1.2.2 Mailing Lists.................................................................................................................................. 6
1.3 Conventions Used in this Document........................................................................................6
2 YumaPro API Overview...................................................................................................................7
2.1 API Overview............................................................................................................................7
2.2 Terminology..............................................................................................................................9
3 YANG Data Examples...................................................................................................................10
3.1 Object Tree Ordering..............................................................................................................10
3.2 YANG Object Tree Handling Examples..................................................................................11
3.2.1 Find Top-Level Object................................................................................................................. 11
3.2.2 Access Module Pointer............................................................................................................... 11
3.2.3 Match Any Object........................................................................................................................ 12
3.2.4 Check For Child Nodes............................................................................................................... 13
3.2.5 Find a Specific Child Node..........................................................................................................14
3.2.6 Find Choice or Case Child Node................................................................................................14
3.2.7 Get First Child............................................................................................................................. 15
3.2.8 Get Next Child............................................................................................................................ 15
3.2.9 Process All Child Nodes............................................................................................................. 16
3.2.10 Process Ancestor Nodes.......................................................................................................... 16
3.3 YANG Data Tree Handling Examples.....................................................................................18
3.3.1 Leaf Examples............................................................................................................................ 18
3.3.2 Leaf-list Examples...................................................................................................................... 20
3.3.3 Container Examples................................................................................................................... 21
3.3.4 List Examples............................................................................................................................. 24
4 Database Access...........................................................................................................................27
4.1 Common CLI Parameters.......................................................................................................27
4.1.1 Startup Options........................................................................................................................... 27
4.2 Database Deployment Variants..............................................................................................28
4.2.1 Local internal database............................................................................................................... 28
4.2.2 Local internal database + NV-store Hook....................................................................................29
4.2.3 Local internal database + no-NV-store........................................................................................30
4.2.4 Local internal database + External Edits.....................................................................................31
4.3 DB-API Example.....................................................................................................................32
5 Database Transaction Callbacks..................................................................................................37
5.1 EDIT1 and EDIT2 Callbacks..................................................................................................39
5.1.1 Error Handling for Edit Transactions...........................................................................................40
5.1.2 EDIT1 Callback Initialization and Cleanup..................................................................................43
5.1.3 EDIT1 Callback Function Example.............................................................................................45
5.1.4 EDIT2 Callback Initialization and Cleanup..................................................................................48
5.1.5 EDIT2 Callback Function Example.............................................................................................50
5.1.6 EDIT2 Utility Functions............................................................................................................... 59
5.2 GET1 Callback.......................................................................................................................61
5.2.1 GET1 Callback Examples. Virtual data.......................................................................................62
5.2.2 GET1 Callback Examples for Static Data...................................................................................64
5.3 GET2 Callback.......................................................................................................................70
5.3.1 GET2 Callback Initialization and Cleanup...................................................................................72
Version 16.10-10
Page 2
YumaPro API Quick Start Guide
5.3.2 GET2 Callback Function Examples............................................................................................73
5.3.3 GET2 Access Macros................................................................................................................. 89
5.3.4 GET2 Access Functions............................................................................................................. 91
6 In-Transaction Callbacks...............................................................................................................93
6.1 Callback Invocation Order......................................................................................................95
6.2 Set Hook Callback..................................................................................................................97
6.2.1 Set Hook Callback Initialization and Cleanup..............................................................................98
6.2.2 Set Hook Callback Function Examples.....................................................................................100
6.3 Transaction Hook Callback...................................................................................................110
6.3.1 Transaction Hook Callback Initialization and Cleanup...............................................................111
6.3.2 Transaction Hook Callback Function Example..........................................................................112
6.4 Transaction Start Callback...................................................................................................114
6.4.1 Transaction Start Callback Initialization and Cleanup...............................................................114
6.4.2 Transaction Start Callback Function Examples.........................................................................116
6.5 Transaction Complete Callback............................................................................................118
6.5.1 Transaction Complete Callback Initialization and Cleanup........................................................118
6.5.2 Transaction Complete Callback Function Example...................................................................119
6.6 Set Order Hook Callback......................................................................................................120
6.6.1 Set Order Hook Callback Initialization and Cleanup.................................................................120
6.6.2 Set Order Hook Callback Function Examples...........................................................................122
6.7 Add Edit API..........................................................................................................................126
6.8 Get Data API.........................................................................................................................127
6.9 Validate Complete Callback..................................................................................................128
6.9.1 Validate Complete Callback Initialization and Cleanup.............................................................129
6.10 Apply Complete Callback...................................................................................................131
6.10.1 Apply Complete Callback Initialization and Cleanup...............................................................131
6.11 Commit Complete Callback................................................................................................134
6.11.1 Commit Complete Callback Initialization and Cleanup............................................................134
6.11.2 Commit Completeness Callback Function Example................................................................137
7 System Callbacks........................................................................................................................140
7.1 Candidate Reload Callback..................................................................................................141
7.1.1 Candidate Reload Callback Example........................................................................................141
7.2 Module Load Callback..........................................................................................................143
7.2.1 Module Load Callback Initialization and Cleanup......................................................................143
7.2.2 Module Load Callback Function Example.................................................................................144
7.3 Module Unload Callback......................................................................................................148
7.3.1 Module Unload Callback Initialization and Cleanup..................................................................148
7.3.2 Module Unload Callback Function Example..............................................................................149
7.4 NV-Load Callback.................................................................................................................153
7.4.1 NV-Load Callback Initialization and Cleanup............................................................................153
7.4.2 NV-Load Callback Function Example........................................................................................154
7.5 NV-Save Callback.................................................................................................................156
7.5.1 NV-Save Callback Initialization and Cleanup............................................................................156
7.5.2 NV-Save Callback Function Example.......................................................................................157
7.6 Config Replay Callback........................................................................................................159
7.7 Periodic Timer Service..........................................................................................................160
7.7.1 Timed Config Replay Example..................................................................................................161
8 RPC Operation Callbacks...........................................................................................................163
8.1 RPC Callback Initialization and Cleanup..............................................................................164
8.2 RPC Callback Function Examples.......................................................................................166
Version 16.10-10
Page 3
YumaPro API Quick Start Guide
8.2.1 RPC Validate Callback Function...............................................................................................166
8.2.2 RPC Invoke Callback Function.................................................................................................168
Version 16.10-10
Page 4
YumaPro API Quick Start Guide
1 Preface
1.1 Legal Statements
Copyright 2009 - 2012, Andy Bierman, All Rights Reserved.
Copyright 2012 - 2017, YumaWorks, Inc., All Rights Reserved.
1.2 Additional Resources
This document assumes you have successfully set up the software as described in one or both of the printed documents:
YumaPro Installation Guide
Other documentation includes:
YumaPro Quickstart Guide
YumaPro netconfd-pro Manual
YumaPro yangcli-pro Manual
YumaPro yangdiff-pro Manual
YumaPro yangdump-pro Manual
YumaPro Developer Manual
YumaPro yp-system API Guide
YumaPro yp-show API Guide
To obtain additional support you may contact YumaWorks technical support department:
[email protected]
1.2.1
•
•
WEB Sites
YumaWorks
◦
http://www.yumaworks.com
◦
Offers support, training, and consulting for YumaPro.
Netconf Central
◦
http://www.netconfcentral.org/
▪
•
Free information on NETCONF and YANG, tutorials, on-line YANG module validation and
documentation database
Yang Central
◦
http://www.yang-central.org
◦
Free information and tutorials on YANG, free YANG tools for download
Version 16.10-10
Page 5
YumaPro API Quick Start Guide
•
•
•
NETCONF Working Group Wiki Page
◦
http://trac.tools.ietf.org/wg/netconf/trac/wiki
◦
Free information on NETCONF standardization activities and NETCONF implementations
NETCONF WG Status Page
◦
http://tools.ietf.org/wg/netconf/
◦
IETF Internet draft status for NETCONF documents
libsmi Home Page
◦
http://www.ibr.cs.tu-bs.de/projects/libsmi/
◦
Free tools such as smidump, to convert SMIv2 to YANG
1.2.2
•
•
Mailing Lists
NETCONF Working Group
◦
http://www.ietf.org/html.charters/netconf-charter.html
◦
Technical issues related to the NETCONF protocol are discussed on the NETCONF WG mailing list. Refer to
the instructions on the WEB page for joining the mailing list.
NETMOD Working Group
◦
http://www.ietf.org/html.charters/netmod-charter.html
◦
Technical issues related to the YANG language and YANG data types are discussed on the NETMOD WG
mailing list. Refer to the instructions on the WEB page for joining the mailing list.
1.3 Conventions Used in this Document
The following formatting conventions are used throughout this document:
Documentation Conventions
Convention
Version 16.10-10
Description
--foo
CLI parameter foo
<foo>
XML parameter foo
foo
netconfd-pro command or parameter
$FOO
Environment variable FOO
$$foo
netconfd-pro global variable foo
some text
Example script
some text
Plain text
Page 6
YumaPro API Quick Start Guide
2 YumaPro API Overview
This section gives a quick overview for those who want to get started with the netconfd-pro server and all the API that it
provides without reading the entire manual first. It is assumed that the appropriate programs have been installed and
configured correctly.
2.1 API Overview
This section describes the basic API used in the netconfd-pro server.
YumaPro tool provides numerous APIs that can be used to effectively perform desired tasks, improve efficiency, refine
auto-generated code, and extend default functionality.
Version 16.10-10
Page 7
YumaPro API Quick Start Guide
YumaPro provides the following APIs:
•
Transaction Management functions that provide control on the transaction and can be invoked within, before, and
after the transaction.
•
YANG Object Tree manipulation functions, so that object properties do not have to be accessed directly.
•
YANG Data Tree manipulation functions provide access functions to the data nodes.
•
Error Handling functions provide various alternative ways to record an error.
•
Notification functions provide control on notifications.
•
XPATH Handling/Validation functions can be used to manipulate XPATH expressions.
•
Extension Access functions allow to manipulate with custom YANG extensions.
•
Timer Service functions allow some SIL code that may need to be called at periodic intervals to check system
status, update counters, and/or perhaps send notifications.
•
SIL Utility functions provide control on SIL libraries and allows to use them more efficiently.
•
System Callback functions allow to creating and use the general system SIL library.
•
Access Control functions allow to configure and enforce ACM.
•
SYSLOG and Log functions allow to customize logging.
•
Database Access functions allow to validate, manipulate, and manage database.
•
YCONTROL Interface functions provide communication service between sub-agents and the main server.
•
DB-API Interface provide database interface functions to edit the internal database from another internal process..
YUMAWORKS RESERVES THE RIGHT TO CHANGE INTERNAL DATA STRUCTURES
AT ANY TIME WITHOUT NOTICE! API FUNCTIONS ARE STABLE, NOT DATA STRUCTURES!
Version 16.10-10
Page 8
YumaPro API Quick Start Guide
2.2 Terminology
Data Tree
The Data Tree is a representation of some subset of all possible object
instances that a server is maintaining within a configuration database or other
structure. Each Data Tree starts with a Root container, and any child nodes
represent top-level YANG module data nodes that exist within the server.
Object Tree
The Object Tree is a tree representation of all the YANG module RPC, data
definition, and notification statements. It starts with a Root container. This is
defined with a YANG container statement which has an ncx:root extension
statement within it. The <config> parameter within the <edit-config> operation
is an example of an object node which is treated as a Root container. Each
configuration database maintained by the server (e.g., <candidate> and
<running>) has a Root container value node as its top-level object.
Root container
The Root container is defined with a YANG container statement which has an
ncx:root extension statement within it. The Root container does not have any
child nodes defined in it within the YANG file. However, the YumaPro tools
will treat this special container as if any top-level YANG data node is allowed
to be a child node of the Root container type.
Version 16.10-10
Page 9
YumaPro API Quick Start Guide
3 YANG Data Examples
This section describes the basic design and demonstrates and exemplifies how to handle the YANG Object Tree and the
corresponding Data Tree that represents instances of various object nodes that the client or the server can create.
The Object Tree is a tree representation of all the YANG module RPC, data definition, and notification statements. It starts
with a Root container. This is defined with a YANG container statement which has an ncx:root extension statement within
it. The <config> parameter within the <edit-config> operation is an example of an object node which is treated as a Root
container. Each configuration database maintained by the server (e.g., <candidate> and <running>) has a Root container
value node as its top-level object.
A Root container does not have any child nodes defined in it within the YANG file. However, the YumaPro tools will treat
this special container as if any top-level YANG data node is allowed to be a child node of the Root container type.
The Data Tree is a representation of some subset of all possible object instances that a server is maintaining within a
configuration database or other structure.
Each Data Tree starts with a Root container, and any child nodes represent top-level YANG module data nodes that exist
within the server.
Each configuration database maintains its own copy (and version) of the Data Tree. There is only one Object Tree,
however, and all Data Trees use the same Object Tree for reference.
Not all object types have a corresponding node within a Data Tree. Only 'real' data nodes are present. Object nodes that are
used as meta-data to organize the Object Tree (e.g., choice, augment) are not present.
3.1 Object Tree Ordering
Top-Level Data Nodes are derived from:
•
data-def-stmt
•
rpc-stmt
•
notification-stmt
The YANG object tree is maintained in “schema order” wherever that is defined.
•
Top-Level Data Nodes:
◦
◦
•
These nodes are sorted:
▪
Primary Key: local-name
▪
Secondary Key: module-name
The top-level YANG objects from all loaded modules are present in the object tree (except if removed by
status=obsolete or deviation=not-supported)
Child Data Nodes, RPC and Notification Nodes
◦
Child nodes defined in the current module are in schema order (order they appear in the YANG module
◦
Augmenting child nodes are in no defined order. They will be after all the real child nodes and the order may
change depending on how the augmenting modules are loaded.
Version 16.10-10
Page 10
YumaPro API Quick Start Guide
▪
DO NOT DEPEND ON THE ORDER OF AUGMENTING DATA NODES
3.2 YANG Object Tree Handling Examples
In this section we will go through examples on how to utilize different type of the Object Trees, what API functions can be
used for specific purpose, and when you should consider one API function to another.
Let us go through the most prominent Object Tree handling examples, that are often used in SIL callbacks.
There are various API functions that you can use to locate desired objects, they differ widely by available information that
you have prior the search and by desired search precision.
3.2.1
Find Top-Level Object
Assume you know the name of the target object and your goal is to obtain its object template. Assume the object is a top
level object in the YANG module, in this case you cannot use obj_find_child or analogous API function that are using
parent object to locate a child object. In this scenario you have two options, use module structure to locate desired objects
or search through all the modules and try to match the object name string.
If you know the module where the target object is located, the following retrieval function can be used:
...
/* get the top object node */
obj_template_t *topobj =
obj_find_template_top(mod,
module_name,
object_name);
if (!topobj) {
res = ERR_NCX_DEF_NOT_FOUND;
}
...
3.2.2
Access Module Pointer
If you are developing code in the SIL code, the module usually is getting loaded to the server during the Initialization Phase
1 before the startup configuration is loaded into the running configuration database, and before the running configuration is
loaded. The following code illustrates how the module pointer can be obtained during the module load procedure:
/********************************************************************
* FUNCTION init_function
Version 16.10-10
Page 11
YumaPro API Quick Start Guide
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
init_function (const xmlChar *modname,
const xmlChar *revision)
{
...
res = ncxmod_load_module(modname,
revision,
agt_get_savedevQ(),
mod);
if (res != NO_ERR) {
return res;
}
...
}
After we load the module and have the module pointer, we can utilize it to locate top level objects in the module.
Alternatively, if the module already loaded and you want to locate the module in order to use it later to search for object
templates you may use the following API function:
...
ncx_module_t *mod = ncx_find_module(modname, revision);
if (ncmod == NULL) {
res = ERR_NCX_DEF_NOT_FOUND;
}
...
3.2.3
Match Any Object
The second option to locate a top level object is to search through all the modules and try to match the object name string.
The following API functions illustrate how to locate the top level object based on module name:
...
obj_template_t *topobj =
ncx_match_any_object(object_name,
NCX_MATCH_FIRST,
alt_names,
&res);
...
Version 16.10-10
Page 12
YumaPro API Quick Start Guide
In the above example, NCX_MATCH_FIRST parameter dictates the API function to stop search on the first match.
Alternatively, the parameter can be changed to the following options:
Match mode
description
NCX_MATCH_EXACT
Try a case-sensitive exact-length match
NCX_MATCH_EXACT_NOCASE
Try a case-insensitive exact-length match
NCX_MATCH_ONE
Try a case-sensitive partial-name match
NCX_MATCH_ONE_NOCASE
Try a case-insensitive partial-name match
NCX_MATCH_FIRST
Try a case-sensitive first match
NCX_MATCH_FIRST_NOCASE
Try a case-insensitive first match
The alt_names parameter dictates if alternative names should be checked in addition to the YANG node names. If set to
TRUE the check will be applied; if set to FALSE the check will be ignored.
The *res parameter may be set to ERR_NCX_MULTIPLE_MATCHES in case there are multiple matching objects
available. In this case the function will not return any of the object templates. If you want to search with the highest
precision for a specific object you should consider NCX_MATCH_EXACT value and set alt_names to FALSE.
Alternatively, you can use the following extended version of the previous API function:
...
obj_template_t *topobj =
ncx_match_any_object_ex(modname,
object_name,
dataonly,
NCX_MATCH_EXACT,
alt_names,
&res);
...
In the above example, the module name is optional; however it is recommended. If it is present the API function will search
only within specified module and will not try to search through all the modules.
The dataonly parameter specifies whether the search function should try to match only data nodes or not. Set to TRUE if it
should.
The *res parameter may be set to ERR_NCX_MULTIPLE_MATCHES in case there are multiple matching objects
available. In this case the function will not return any of the object templates. If you want to search with the highest
precision for a specific object you should consider NCX_MATCH_EXACT value and set alt_names to FALSE.
3.2.4
Check For Child Nodes
Now, since we know the top level object we can locate any children objects of the current top level object. However, only if
the current object has and may have children.
Version 16.10-10
Page 13
YumaPro API Quick Start Guide
In order to verify that the current object has any available children you may use the following API function:
...
boolean has_children =
obj_has_children(topobj);
...
If there are any accessible nodes within the object the function returns TRUE.
3.2.5
Find a Specific Child Node
The following example code illustrates how to locate a specific child object of the current top level object. The following
API function checks for accessible names only. That means child nodes of choice, case will be present instead of the choice
name or case name:
...
/* get the child obj template */
obj_template_t *child_obj =
obj_find_child(topobj,
modname,
objname);
...
3.2.6
Find Choice or Case Child Node
In case you want to locate choice or case objects as well as other accessible names you may consider to use the following
API function. This function checks not only for accessible names. That means the choice name or case name will be
preferred instead of their child nodes:
...
/* get the child obj template */
obj_template_t *child_obj =
obj_find_child_choice_case(topobj,
modname,
objname);
...
Alternatively, you can use the following extended version of the previous API functions:
Version 16.10-10
Page 14
YumaPro API Quick Start Guide
...
/* get the child obj template */
obj_template_t *child_obj =
obj_find_child_ex(topobj,
modname,
objname,
NCX_MATCH_FIRST,
alt_names,
dataonly,
&res);
...
3.2.7
Get First Child
In addition to the find_child family API functions, you may consider to use get_child API functions. The main difference
is that find functions are trying to match desired object, but get functions merely get the first, next, last, etc objects.
To exemplify, if you want to get the first child object of the current object, the following API function can be used. Note,
that this function skips over augments and uses:
...
obj_template_t *first_obj = obj_first_child(topobj);
...
Similarly, the following API function can be used to locate the first terminal object. Note, that this function also skips over
augments and uses:
...
obj_template_t *first_obj = obj_first_terminal_child(topobj);
...
3.2.8
Get Next Child
In order to get the next, the last, parent or previous child object or the terminal child object the following API functions can
be used:
...
obj_template_t *next_obj = obj_next_child(first_obj);
Version 16.10-10
Page 15
YumaPro API Quick Start Guide
obj_template_t *next_obj = obj_next_terminal_child(first_obj);
obj_template_t *prev_obj = obj_previous_child(next_obj);
obj_template_t *last_obj = obj_last_child(topobj);
obj_template_t *topobj = obj_get_parent(next_obj);
...
3.2.9
Process All Child Nodes
To summarize previous list of API functions, you may construct the following loop to apply custom actions for desired
children objects:
...
/* check all the child nodes */
obj_template_t *chobj = obj_first_child(obj);
for (; chobj; chobj = obj_next_child(chobj)) {
const xmlChar *modname = obj_get_mod_name(chobj);
const xmlChar *objname = obj_get_name(chobj);
/* process objects here */
}
...
3.2.10
Process Ancestor Nodes
In order to loop through ancestor entries and process them as required, the following code may be used:
...
/* go through the ancestor entries and process them as needed */
obj_template_t *testobj = obj_get_parent(chobj);
while (testobj) {
/* process parent objects here */
testobj = obj_get_parent(testobj);
if (testobj && obj_is_root(testobj)) {
testobj = NULL;
}
}
Version 16.10-10
Page 16
YumaPro API Quick Start Guide
...
The obj_is_root API function signals when to stop. It is also possible to have a NULL pointer returned. That indicates you
reached the top Root container. This container should NOT be modified, accessed or changed in any manner.
Version 16.10-10
Page 17
YumaPro API Quick Start Guide
3.3 YANG Data Tree Handling Examples
In this section we will go through examples on how to utilize different type of the Data Trees, what API functions can be
used for specific purpose, and when you should consider one API function to another.
Let us go through the most prominent Data Tree handling examples, that are often used in SIL callbacks.
There are various API functions that you can use to manipulate with desired Data node, they differ widely the operation that
you need to perform. Let us go through simple examples that will illustrate how to manage different YANG node types.
3.3.1
Leaf Examples
The following examples illustrates how to construct a simple leaf data node.
There are multiple ways to construct leaf node depending on what information you have before the construction and what
data type is the node.
The most common and generic API function to construct the data node that does not required to know the type of the node
is demonstrated below. However, you have to find the object template prior the construction:
...
obj_template_t *leafobj =
obj_find_child(parentobj,
modname,
objname);
if (!leafobj) {
/* report an error or do not try to generate the leaf */
return ERR_NCX_DEF_NOT_FOUND;
}
/* construct a leaf val_vale tree regardless of its type */
val_value_t *any_type_leaf =
val_make_simval_obj(leafobj,
(const xmlChar *)"8",
&res);
...
The most beneficial part of this API function is that you do NOT have to investigate the type of the node before the
construction.
However, If you know the type of the node and want to construct the value based on the type, the following example
demonstrates how to accomplish this goal. In this example we are constructing “string” type data node:
...
/* construct a sting type leaf */
val_value_t *string_leaf =
agt_make_leaf(parentobj,
leafname,
(const xmlChar *)"example value",
Version 16.10-10
Page 18
YumaPro API Quick Start Guide
&res);
...
In the example code above, if the PARENTOBJ is a leaf or leaf-list then it will be used directly instead of checking for a
child node.
In order to construct data node with integer types, the following API function can be used:
...
/* construct a uint32 type leaf */
val_value_t *string_leaf =
agt_make_uint_leaf(parentobj,
leafname,
(uint32)32,
&res);
/* construct a uint32 type leaf */
val_value_t *value =
agt_make_int_leaf(parentobj,
leafname,
(int32)32,
&res);
/* construct a uint64 type leaf */
val_value_t *value =
agt_make_uint64_leaf(parentobj,
leafname,
(uint64)64,
&res);
/* construct a int64 type leaf */
val_value_t *value =
agt_make_int64_leaf(parentobj,
leafname,
(int64)64,
&res);
...
In order to construct data node with boolean type, the following API function can be used. The BOOLVAL value is getting
set to TRUE is the value of this node should be TRUE:
...
/* construct a boolean type leaf */
val_value_t *string_leaf =
agt_make_boolean_leaf(parentobj,
modname,
leafname,
boolval,
&res);
...
Version 16.10-10
Page 19
YumaPro API Quick Start Guide
In order to construct data node with empty type, the following API function can be used. The BOOLVAL value is ignored
by this API function:
...
/* construct a boolean type leaf */
val_value_t *string_leaf =
agt_make_empty_leaf(parentobj,
modname,
leafname,
boolval,
&res);
...
3.3.2
Leaf-list Examples
The following examples illustrates how to construct a simple leaf-list data node.
The leaf-list nodes can be constructed the same way as regular leaf nodes. However, there might be multiple instances,
siblings of the same leaf-list node in the Data Tree.
The following example code illustrates how to construct 3 leaf-list siblings using the same API functions as for leaf node:
...
/* construct 3 leaf-list entries */
val_value_t *leaflist_value1 =
val_make_simval_obj(leaflist_obj, (const xmlChar *)"53", &res);
if (!leaflist_value) {
return res;
}
val_value_t *leaflist_value2 =
val_make_simval_obj(leaflist_obj, (const xmlChar *)"30", &res);
if (!leaflist_value) {
return res;
}
val_value_t *leaflist_value3 =
val_make_simval_obj(leaflist_obj, (const xmlChar *)"80", &res);
if (!leaflist_value) {
return res;
}
...
Alternatively, the following example can be used in order to construct multiple leaf-list entries of int32 type node and add
them to the existent container parent:
Version 16.10-10
Page 20
YumaPro API Quick Start Guide
...
status_t res = NO_ERR;
int32 value = 0;
for (value = 10; value <= 15 && res==NO_ERR; value++) {
val_value_t *leaflist_value =
agt_make_int_leaf(parentobj,
leaflistname,
value,
&res);
if (!leaflist_value) {
return res;
}
/* add a new leaf-list entry into target container */
res = val_child_add(leaflist_value, container_value);
if (res != NO_ERR) {
val_free_value(leaflist_value);
}
}
...
As a result, all the leaf-list entries will be added to the parent container. The next section will describe this construction in
more details.
3.3.3
Container Examples
The following examples illustrates how to construct a container data node.
The container nodes can be constructed different ways depending whether it is a top level container or not. If the container
is not a top level container, the following code can be used:
...
/* get the container obj template */
obj_template_t *cont_obj =
obj_find_child(parentobj,
modname,
objname);
if (!cont_obj) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* make a container value */
val_value_t *cont_value = val_new_value();
if (!cont_value) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(cont_value, cont_obj);
...
Version 16.10-10
Page 21
YumaPro API Quick Start Guide
On the other hand, if the container is a top level container, and you are constructing it in order to use as a static or virtual
data within running datastore. If the container represents operational data, the following example can be used:
...
cfg_template_t *runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING);
if (!runningcfg || !runningcfg->root) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* get the top object node */
obj_template_t *topobj =
obj_find_template_top(mod,
module_name,
object_name);
if (!topobj) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* construct top level container */
val_value_t *cont_value = val_new_value();
if (!cont_value) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(cont_value, topobj);
/* handing off the malloced memory here */
res = val_child_add(cont_value, runningcfg->root);
if (res != NO_ERR) {
val_free_value(cont_value);
return res;
}
...
As a result of these two examples, we will have an empty container with no any children. In order to add a child to the
container, the following example that creates multiple leaf-list entries and adds them into parent container can be used:
...
status_t res = NO_ERR;
int32 value = 0;
for (value = 10; value <= 15 && res==NO_ERR; value++) {
val_value_t *leaflist_value =
agt_make_int_leaf(parentobj,
leaflistname,
value,
&res);
if (!leaflist_value) {
return res;
}
/* add a new leaf-list entry to parent container */
res = val_child_add(leaflist_value, cont_value);
if (res != NO_ERR) {
val_free_value(leaflist_value);
Version 16.10-10
Page 22
YumaPro API Quick Start Guide
return res;
}
}
...
In order to construct nested containers, the same steps should be applied twice, as illustrated below:
...
cfg_template_t *runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING);
if (!runningcfg || !runningcfg->root) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* get the top object node */
obj_template_t *topobj =
obj_find_template_top(mod,
module_name,
object_name);
if (!topobj) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* construct top level container */
val_value_t *cont_value = val_new_value();
if (!cont_value) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(cont_value, topobj);
/* handing off the malloced memory here */
status_t res = val_child_add(cont_value, runningcfg→root);
if (res != NO_ERR) {
val_free_value(cont_value);
return res;
}
/* get the next container obj template */
obj_template_t *cont_obj =
obj_find_child(topobj,
modname,
objname);
if (!cont_obj) {
val_free_value(cont_value);
return ERR_NCX_DEF_NOT_FOUND;
}
/* make a next container value */
val_value_t *next_cont_value = val_new_value();
if (!next_cont_value) {
val_free_value(cont_value);
return ERR_INTERNAL_MEM;
}
val_init_from_template(next_cont_value, cont_obj);
res = val_child_add(next_cont_value, cont_value);
if (res != NO_ERR) {
Version 16.10-10
Page 23
YumaPro API Quick Start Guide
val_free_value(next_cont_value);
val_free_value(cont_value);
return res;
}
...
3.3.4
List Examples
The following examples illustrates how to construct a list data node.
The list nodes have several limitations and special handling nuances. For following list summarize the most important:
•
NETCONF does not require entries to be sorted by index. It only requires the order to be maintained if “ordered-by
user” is configured in the YANG module. The server will maintain the order or instances given in the <editconfig> or startup or other sources. It will maintain YANG schema order so the objects will be returned in the
correct order according to the YANG module.
•
val_gen_index_chain MUST be called after all the index leafs have been added to the list. This can be done
before or after any additional child nodes are added to the list.
•
The index (key) nodes should be added first, before any other nodes are added.
There are multiple variants of val_child_add. Sometimes the value is only created so it can be output in XML or JSON,
and not to be stored in the database. The API functions in val_child should be used instead of val.c. The val.c API functions
still works, but the new functions should be used in new code. The new functions can fail with an error, so it is important to
check the return value in server code. The yangcli-pro and compiler programs will use val_child_add_force by default,
since they do not store the data nodes in a database.
Function
Description
val_child_add
Replaces val_add_child and val_add_child_sorted
val_child_add_force
Allows AVL tree insertion to be skipped for duplicates or
incomplete list entries
val_child_insert
Replaces val_insert_child
The following example illustrates how to construct the list data node with multiple children:
/********************************************************************
* FUNCTION create_list_entry3
*
* Make a list entry based on the key
*
* INPUTS:
*
res = return status
*
* RETURNS:
*
val_value_t of listval entry if no error
Version 16.10-10
Page 24
YumaPro API Quick Start Guide
*
else NULL
*
*********************************************************************/
static val_value_t *
create_list_entry(obj_template_t *parentobj,
int32 key,
status_t *res)
{
/* get the obj template for the list obj */
obj_template_t *list_obj =
obj_find_child(parentobj,
(const xmlChar *)”my-module”,
(const xmlChar *)”my-list”);
if (!list_obj) {
*res = ERR_NCX_DEF_NOT_FOUND;
return NULL;
}
/* make the list node */
val_value_t *list_value = val_new_value();
if (!list_value) {
*res = ERR_INTERNAL_MEM;
return NULL;
}
val_init_from_template(list_value, list_obj);
/* make key leaf entry */
val_value_t *child =
agt_make_int_leaf(list_obj,
keyname,
key,
res);
if (!child) {
val_free_value(list_value);
return NULL;
}
*res = val_child_add(child, list_value);
if (*res != NO_ERR) {
val_free_value(child);
val_free_value(list_value);
return NULL;
}
/* make an extra leaf entry */
child =
agt_make_uint_leaf(list_obj,
(const xmlChar *)”my-other-leaf”,
(uint32)32,
res);
if (!child) {
val_free_value(list_value);
return NULL;
}
*res = val_child_add(child, list_value);
if (*res != NO_ERR) {
val_free_value(child);
val_free_value(list_value);
return NULL;
}
/* generate the internal index Q chain */
*res = val_gen_index_chain(list_obj, list_value);
if (*res != NO_ERR) {
Version 16.10-10
Page 25
YumaPro API Quick Start Guide
val_free_value(list_value);
return NULL;
}
return list_value;
} /* create_list_entry3 */
This above function is an example function and can be changed according to the required goal. By using this function, we
create one list entry that we can now add to the parent or repeat the same steps but with different key value in order to
generate another list entry. The following example illustrates how to generate multiple list entries using the sample function
above and add them to parent container value:
...
/* malloced and construct list values */
status_t res = NO_ERR;
int32 key = 0;
for (key = 10; key <= 15; key++) {
val_value_t *list_value =
create_list_entry(container_obj,
key,
&res);
if (!list_value) {
return res;
}
/* add a new list entry into target container */
res = val_child_add(list_value, contvalue);
if (res != NO_ERR) {
val_free_value(list_value);
return res;
}
}
...
As a result we generated container with 5 list entries.
Version 16.10-10
Page 26
YumaPro API Quick Start Guide
4 Database Access
This document describes the configuration and deployment options for the configuration database used by the netconfd-pro
server.
Information included:
•
Database related CLI parameters
•
Deployment Variants
◦
Use-Case description
◦
System integration description
◦
Related Callback APIs
4.1 Common CLI Parameters
4.1.1
Startup Options
There are 3 startup parameter variants, using the YANG 'start' choice statement.
•
choice of 3 leafs:
◦
--startup
◦
--factory-default
◦
--no-startup
The –startup=filespec CLI parameter can be used to specify the NV-storage file. The default filespec is
$HOME/.yumapro/startup-cfg.xml
The parameter –factory-startup can be used to load an empty configuration instead of the stored configuration. If the
default configuration is used (startup-cfg.xml) then it is reset to factory default values.
The parameter –no-startup can be used to load an empty configuration instead of the stored configuration. The default
configuration (startup-cfg.xml) is not affected by this parameter.
Version 16.10-10
Page 27
YumaPro API Quick Start Guide
4.2 Database Deployment Variants
There are many ways to configure and build the server for storing and retrieving datastore contents.
•
Local internal database: configuration in memory and NV-storage in netconfd-owned XML file
•
Local internal database + NV-store hook: configuration in memory and NV-storage via callback functions, and
transferred to/from the server as an XML file
•
Local internal database + no-NV-store: configuration in memory and NV-storage is managed by the yp-system
and/or SIL code. The server will not attempt to load or store the configuration to non-volatile storage
•
Local internal database + external edits: your external process communicates with the netconfd-pro process to
initiate external edits (e.g., initiated from legacy CLI or internal database)
4.2.1
Local internal database
netconfd­pro
running
config
in memory
startup­cfg.xml
This is the default database configuration and provides complete management of all database storage and management. No
extra APIs or CLI parameters are required.
Configuration properties:
•
Tree-based data structures in memory for run-time transaction processing
•
This is the canonical database
•
There is no other database
•
The non-volatile load and store is done by netconfd-pro using the –startup filespec to store the configuration
•
The file is encoded as an XML instance document containing 1 element named 'data'
•
No YANG default leafs are stored in this file
Version 16.10-10
Page 28
YumaPro API Quick Start Guide
4.2.2
Local internal database + NV-store Hook
netconfd­pro
startup­cfg.xml
running
config
in memory
NV­store hook
This database variant allows the vendor/system code to manage the non-volatile configuration. The system control is
constrained to the lreplacement of the functions that load and store the configuration file.
This variant requires that the NV-store APIs (described in the Database Transaction Callbacks section.
Configuration properties:
•
Tree-based data structures in memory for run-time transaction processing
•
This is the canonical database
•
There is no other database
•
The non-volatile load and store is done by netconfd-pro via user callback functions.
•
The NV-store callback functions manage the actual non-volatile representation and storage.
•
The NV-Store callback transfers the configuration to/from netconfd-pro as an XML file
•
YANG default leafs will be treated as explicitly set if contained in the transfer file
Version 16.10-10
Page 29
YumaPro API Quick Start Guide
4.2.3
Local internal database + no-NV-store
netconfd­pro
running
config
in memory
This variant allows either a simple deployment that does not support non-volatile storage of configuration or
a more flexible system-managed non-volatile storage design, using multiple server APIs.
This variant requires that the CLI parameter –no-nvstore be set to 'false'.
The –startup CLI parameter is ignored if –no-nvstore is used.
Various initialization callback APIs and transaction APIs can be used to load and save the non-volatile storage
Configuration properties:
•
Tree-based data structures in memory for run-time transaction processing
•
This is the canonical database
•
There is no other database
•
The non-volatile load and store is done by netconfd-pro via various user callback functions.
•
The internal NV-store and NV-store callback functions are not used
•
The yp-system library or individual SIL libraries can be used to initialize and fill the empty database with
configuration and operational data nodes
•
Transaction hooks can be used to save the running configuration at run-time if any client edit transactions are
processed by the server
Version 16.10-10
Page 30
YumaPro API Quick Start Guide
4.2.4
Local internal database + External Edits
netconfd­pro
running
startup­cfg.xml
config
in memory
DB­API msgs
your process
This database variant allows an external process to initiate database edits, that are processed by the server as user or system
edits.
The DB-API functions described in the next section can be used by the subsystem to edit the server configuration database.
This variant can be used with other variants for processing non-volatile storage. This variant is used to co-exist with a
legacy or canonical database owned by the system.
Configuration properties:
•
Tree-based data structures in memory for run-time transaction processing
•
This may or may not be the canonical database, depending on the non-volatile storage options.
•
There may be another database in the system
•
The server uses its copy in memory as the complete database
•
The DB-API “send_edit” and “send_edit_ex” API functions can be used to transfer configuration from the system
to tnetconfd-pro.
•
Data is transferred in XML
•
Error responses are sent by the server if an edit is not accepted.
•
An error will be returned if a client is already in the process of editing the database
Version 16.10-10
Page 31
YumaPro API Quick Start Guide
•
The db-api-app application in installed in /usr/share/yumapro/src/db-api-app by default, and includes some
examples of initializing the DB-API service and sending edits to the server
4.3 DB-API Example
The DB-API subsystem allows an external process on the same system as netconfd-pro to send database edits to the server.
•
db_api_register_service
•
db_api_service_ready
•
db_api_send_edit
•
db_api_send_edit_ex
•
db_api_send_edit_full
•
db_api_check_edit
extern status_t
db_api_register_service (void);
extern boolean
db_api_service_ready (void);
extern status_t
db_api_send_edit (const xmlChar *edit_target,
const xmlChar *edit_operation,
const xmlChar *edit_xml_value);
extern status_t
db_api_send_edit_ex (const xmlChar *edit_target,
const xmlChar *edit_operation,
const xmlChar *edit_xml_value,
const xmlChar *patch_id_str,
boolean system_edit);
/********************************************************************
* FUNCTION db_api_send_edit_full
*
* Create a YANG Patch edit request and send it to the DB-API service
* on the main server.
*
* The content should represent the intended target resource
* as specified in YANG-API (NOT RESTCONF)
* Only the data resource identifier is provided, not the
* API wrapper identifiers (so this can change when RESTCONF is supported)
* Example leaf:
*
*
edit_target == /interfaces/interface/eth0/mtu
*
edit_value == "<mtu>9000</mtu>
Version 16.10-10
Page 32
YumaPro API Quick Start Guide
*
edit_operation == "merge"
*
patch_id_str == "my-patch-x01'
*
system_edit == true
*
* Example list:
*
*
edit_operation == <operation string>
*
"create"
*
"delete"
*
"insert"
*
"merge"
*
"move"
*
"replace"
*
"remove"
*
*
edit_target == /interfaces/interface/eth0/ipv4
*
edit_value == "<ipv4>
*
<enabled>true</enabled><
*
<forwarding>true</forwarding>
*
<address>204.102.10.4</address>
*
<prefix-length>24</prefix-length>
*
</ipv4>"
*
* INPUTS:
*
edit_target == target resource (YANG-API path expression)
*
edit_operation == edit operation (create merge replace delete remove)
*
edit_xml_value == XML payload in string form, whitespace allowed
*
MAY BE NULL if no value required (delete remove))
*
patch_id_str == string to use as the patch ID
*
== NULL to use the default patch-id field
*
system_edit == TRUE if this edit is from the system and should
*
bypass access control enforcement
*
== FALSE if this edit is from a user and should not
*
bypass access control enforcement
*
insert_point is a string like the target except a different instance
*
of the same list of leaf-list; only for before, after
*
insert_where == <insert enum string>
*
"before"
*
"after"
*
"first"
*
"last"
* RETURNS:
*
status
*********************************************************************/
extern status_t
db_api_send_edit_full (const xmlChar *edit_target,
const xmlChar *edit_operation,
const xmlChar *edit_xml_value,
const xmlChar *patch_id_str,
boolean system_edit,
const xmlChar *insert_point,
const xmlChar *insert_where);
/********************************************************************
* FUNCTION db_api_check_edit
*
* Check on the status of an edit in progress
* RETURNS:
*
status:
*
*
ERR_NCX_NOT_FOUND if final status and no message
*
response is pending
Version 16.10-10
Page 33
YumaPro API Quick Start Guide
*
ERR_NCX_SKIPPED if final status is not known yet
*
NO_ERR if there is a last-completed operation that
*
completed with an OK response
*
<errcode> if there is a last-completed operation that
*
completed with an ERROR response
*********************************************************************/
extern status_t
db_api_check_edit (void);
Example db-api-app send_edit from ap-api-app.c
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function.
* The module ietf-interfaces needs to be loaded for this to work
*********************************************************************/
static void
send_test_edit (void)
{
/* mef-cfm test */
const xmlChar *path_str = (const xmlChar *)"/maintenance-domain/mdTest";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<cfm:maintenance-domain xmlns:cfm='http://metroethernetforum.org/"
"ns/yang/mef-cfm'><cfm:id>mdTest</cfm:id><cfm:name-type>character"
"-string</cfm:name-type><cfm:name>mdTestName</cfm:name><cfm:md-le"
"vel>4</cfm:md-level><cfm:mhf-creation>none</cfm:mhf-creation>"
"<cfm:id-permission>none</cfm:id-permission></cfm:maintenance"
"-domain>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
status_t res = db_api_send_edit_ex(path_str,
operation_str,
value_str,
patch_id_str,
system_edit);
if (res != NO_ERR) {
log_error("\nSend test edit failed %s %s = %s (%s)\n",
operation_str, path_str, value_str,
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test edit OK %s %s = %s\n",
operation_str, path_str, value_str);
}
}
/* send_test_edit */
/********************************************************************
* FUNCTION main
*
* This is an example main function.
*
Version 16.10-10
Page 34
YumaPro API Quick Start Guide
* RETURNS:
*
0 if NO_ERR
*
status code if error connecting or logging into ncxserver
*********************************************************************/
int main (int argc, char **argv)
{
#ifdef MEMORY_DEBUG
mtrace();
#endif
/* 1) setup yumapro messaging service profile */
status_t res = ycontrol_init(argc, argv,
(const xmlChar *)"subsys1");
/* 2) register services with the control layer */
if (res == NO_ERR) {
res = db_api_register_service();
}
/* 3) do 2nd stage init of the control manager (connect to server) */
if (res == NO_ERR) {
res = ycontrol_init2();
}
useconds_t usleep_val = 100000;
boolean done = FALSE;
// 100K micro-sec == 1/10 sec
/* 4) call ycontrol_check_io periodically from the main program
* control loop
*/
#ifdef DB_API_APP_DEBUG
int id = 0;
#endif // DB_API_APP_DEBUG
boolean test_done = FALSE;
while (!done && res == NO_ERR) {
#ifdef DB_API_APP_DEBUG
if (LOGDEBUG3) {
log_debug3("\ndb-api-app: checking ycontrol IO %d", id++);
}
#endif // DB_API_APP_DEBUG
res = ycontrol_check_io();
if (ycontrol_shutdown_now()) {
// YControl has received a <shutdown-event>
// from the server subsystem is no longer active
// could ignore or shut down YControl IO loop
done = TRUE;
}
// Using sleep to represent other program work; remove for real
if (!done && res == NO_ERR) {
(void)usleep(usleep_val);
}
if (db_api_service_ready() && !test_done) {
send_test_edit();
test_done = TRUE;
}
}
Version 16.10-10
Page 35
YumaPro API Quick Start Guide
/* 5) cleanup the control layer before exit */
ycontrol_cleanup();
#ifdef MEMORY_DEBUG
muntrace();
#endif
return (int)res;
}
/* main */
Version 16.10-10
Page 36
YumaPro API Quick Start Guide
5 Database Transaction Callbacks
YumaPro provides pre-transaction, in-transaction and post-transaction APIs to manage behavior of a database edit
transaction, such as its functionality, order, etc. This section illustrates how to utilize transaction callbacks, highlighting
some relevant use cases in detail.
YANG can model state data, as well as configuration data, based on the "config" statement. When a node is tagged with
"config false", its sub-hierarchy is flagged as state data, to be reported using NETCONF's <get> operation, not the <getconfig> operation. When a node has no tag or tagged with “config true”, its sub-hierarchy is flagged as configuration data.
Consider this simplified, but functional, example. Refer to the “Glossary” section for the complete YANG data model:
...
container interfaces {
list interface {
key "name";
leaf name {
type string;
}
leaf-list untagged-ports {
type string;
}
leaf speed {
type enumeration {
enum 10m;
enum 100m;
enum auto;
}
}
leaf observed-speed {
config false;
type uint32;
}
}
}
...
leaf version {
config false;
type string;
}
In this example, three leafs are defined for each interface, a configured “speed” and “untagged-ports” and a nonconfigurable “observed-speed”. The “speed” and “untagged-ports” is configuration, so it can be returned with NETCONF
<get-config> operations and they can be manipulated using <edit-config>. Note that the “observed-speed” is not
configuration and it cannot be manipulated using <edit-config>.
This differentiation is critical and based on it an appropriate callback should be used. For the configuration data there
should be EDIT-like callbacks registered. However, for the state data, GET-like callbacks should be registered.
Version 16.10-10
Page 37
YumaPro API Quick Start Guide
The following transaction management APIs are used to manage an object that is being edited or retrieved. They are used to
provide a callback sub-mode for a specific named object.
API
Description
EDIT-1 callback
agt_cb_fn_t: First generation callbacks are generated by yangdump-sdk
using node-based APIs. An edit function is generated for every node,
including terminal nodes (leaf, leaf-list, anyxml). The GET1 API is
assumed for read-only data, so code to generate virtual read-only nodes
(Make Read Only MRO) is included in an EDIT1 callback. This is the
default in yangdump-sdk and make_sil_* scripts.
EDIT-2 callback
agt_cb_fn_t: Second generation callbacks are generated by yangdump-sdk
using container or list-based APIs. An edit function is generated for
“parent” list and container nodes only. The terminal child nodes are handled
by this callback. Each nested container or list has its own callback. This is
generated in yangdump-sdk or make_sil_* scripts using the –sil-edit2
parameter. The same callback function signature as EDIT1 is used, but the
registration function and procedure is different.
GET-1 callback
getcb_fn_t: Used for <get> and <get-config> operations. GET1 requires
an actual data node in the data tree for each instance that exists. A data node
with a GET1 callback function is called a 'virtual nodes'. It is usually created
in SIL code for operational data. Only supports internal GET callback
GET-2 callback
getcb_fn2_t: Used for the <get> operation. There is 1 GET2 callback for
the object, not each instance in the data tree. GET, GETNEXT, and
GETBULK operations are supported. Optional parameters allow
subtree/XPath filtering to be passed to the GET2 callback for pruning.
Version 16.10-10
Page 38
YumaPro API Quick Start Guide
5.1 EDIT1 and EDIT2 Callbacks
The following sections illustrates how to utilize the EDIT1 and EDIT2 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 node will be invoked multiple times.
The EDIT2 mode is “list-based” or “container-based” instead. The following key aspects define EDIT2 callbacks:
•
In this mode there are no SIL callback functions expected for terminal nodes (leaf, leaf-list, anyxml).
•
The SIL callback for a container or list will be invoked, even if only child terminal nodes are being changed.
•
The parent SIL callback will be invoked only once (per phase) if multiple child nodes are being altered at once
•
The parent node for such an edit is flagged in the “undo” record as a special “edit2_merge”. The edit operation will
be “OP_EDITOP_MERGE”, but the parent node is not being changed
•
The special “edit2_merge” type of edit will have a queue of child_undo records containing info on the child edits.
For example, 1 leaf could be created, another leaf modified, and a third leaf deleted, all in the same edit request.
The child_undo records provide the edit operation and values being changed.
The following function template definition is used for EDIT1 and EDIT2 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);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 per object
•
File: agt_cb.h
•
Template: agt_cb_fn_t
◦
Inputs:
▪
scb == session control block making the request
▪
msg == incoming rpc_msg_t in progress
▪
cbtyp == reason for the callback
▪
editop == edit operation enumeration for the node being edited
▪
newval == object holding the proposed changes to apply to the current config, depending on the editop
value.
Version 16.10-10
Page 39
YumaPro API Quick Start Guide
▪
◦
curval == current values from the <running> or <candidate> configuration, if any. Could be NULL for
create and other operations.
Returns: status_t
Status of the callback function execution
•
Register: agt_cb_register_callback / agt_cb_register_edit2_callback
•
Unregister: agt_cb_unregister_callbacks
Where, the scb parameter represents a session control block structure that is defined in the netconf/src/ncx/ses.h. This
control block is primarily used for error reporting, as described in the example section later. However, can be used for more
advanced actions. It provides access to the session specific information, such as current message input/output encoding,
current session ID information, current protocol information, user name information, peer address information, etc. Note,
almost all the fields in this structure should NOT be changed and accessed directly. This control block ideally should be
used only for getting more information about the current session, not for alteration of any of its fields.
The msg parameter represents the NETCONF Server and Client RPC Request/Reply Message Handler control block that is
defined in the netconf/src/ncx/rpc.h. Similarly to SCB, this control block is primarily used for error reporting, as
described in the example section later. The fields of this control block should NOT be access and changed for other
purposes.
The cbtype parameter represents an enumeration structure of the different server EDIT callback types that is defined in the
netconf/src/agt/agt.h. This control block specifies what Phase is in the process, Validation, Apply, or Commit/Rollback, as
described in the example section later.
The editop parameter represents an enumeration structure of the NETCONF edit-config operation types that is defined in
the netconf/src/ncx/op.h. This control block specifies what operation is in the process, merge, replace, delete, or other, as
described in the example section later.
The newval and curval parameters represent data nodes that are being edited. The example section demonstrates how they
can be utilized.
5.1.1
Error Handling for Edit Transactions
NOTE:
Do not use SET_ERROR() macro to return normal errors. This macro will cause “assert()” to be invoked, halting program
execution. The macro defined in netconf/src/ncx/status.h and it is for flagging internal errors only. This macro must not be
used for normal errors.
The agt_record_error function is one of the main error handling functions used in SIL code.
If a specific error is not recorded by SIL code when an EDIT1 or EDIT2 callback is invoked. then the server will record a
generic error, based on the 'status_t' returned by the SIL code.
In the following part of the above code example we are trying to record an error if some of the previous steps fail. This error
will be sent to the client side signaling that the <edit-config> operation failed to complete.
/********************************************************************
Version 16.10-10
Page 40
YumaPro API Quick Start Guide
* FUNCTION agt_record_error
*
* Generate an rpc_err_rec_t and save it in the msg
*
* INPUTS:
*
scb == session control block
*
== NULL and no stats will be recorded
*
msghdr == XML msg header with error Q
*
== NULL, no errors will be recorded!
*
layer == netconf layer error occured
<error-type>
*
res == internal error code
<error-app-tag>
*
xmlnode == XML node causing error <bad-element>
<error-path>
*
== NULL if not available
*
parmtyp == type of node in 'error_info'
*
error_info == error data, specific to 'res'
<error-info>
*
== NULL if not available (then nodetyp ignored)
*
nodetyp == type of node in 'error_path'
*
error_path == internal data node with the error
<error-path>
*
== NULL if not available or not used
* OUTPUTS:
*
errQ has error message added if no malloc errors
*
scb->stats may be updated if scb non-NULL
*
* RETURNS:
*
none
*********************************************************************/
extern void
agt_record_error (ses_cb_t *scb,
xml_msg_hdr_t *msghdr,
ncx_layer_t layer,
status_t res,
const xml_node_t *xmlnode,
ncx_node_t parmtyp,
const void *error_info,
ncx_node_t nodetyp,
void *error_path);
Example:
...
/* 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);
}
...
Version 16.10-10
Page 41
YumaPro API Quick Start Guide
agt_record_error API function generates an rpc_err_rec_t and save it in the message. Alternatively,
agt_record_error_errinfo, agt_record_warning, agt_record_attr_error, etc API functions could be used to record an
error.
The API functions, used in this code break are the most utilized and prominent functions. These functions are getting used
in the SIL code often.
To summarize, anytime an <edit-config> operation loads specific “interface” configuration to the running datastore, the
callback will be invoked and run.
Assuming the “interfaces” container preexist in the datastore. To invoke the callback the following RPC can be sent:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<candidate/>
</target>
<config>
<interfaces xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<name>supported</name>
</interface>
</interfaces>
</config>
</edit-config>
</rpc>
When the incoming configuration has been safely loaded onto the candidate datastore and validated, it is ready to impact
the running system. If the device supports the :candidate capability, use the <commit> operation to apply candidate
configuration to running datastore. Otherwise, if the device supports :writable-running capability, in the above <editconfig> operation example, change target to running.
The server may reply with the following:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<ok/>
</rpc-reply>
If we change the “interface” name in the previous <edit-config> to “not-supported”, the server will reply with:
<errors xmlns:ncx="http://netconfcentral.org/ns/yuma-ncx"
xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
<error>
<error-type>rpc</error-type>
<error-tag>operation-not-supported</error-tag>
<error-app-tag>no-support</error-app-tag>
Version 16.10-10
Page 42
YumaPro API Quick Start Guide
<error-message xml:lang="en">operation not supported</error-message>
<error-info>
<error-number>273</error-number>
</error-info>
</error>
</errors>
Now that the incoming configuration has been integrated into the running configuration and all the callbacks run their
validation and processed all the nodes, the application needs to gain trust that the change has affected the device in the way
intended without affecting it negatively.
To gain this confidence, the application can run tests of the operational state of the device. The nature of the test is
dependent on the nature of the change and is outside the scope of this document. Depending on the changes that have been
applied on running datastore, these tests may include reachability from the system running the application (using ping),
changes in reachability to the rest of the network (by comparing the device's routing table), or, for instance, inspection of
the particular change (looking for operational evidence of the BGP peer that was just added).
5.1.2
EDIT1 Callback Initialization and Cleanup
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. Assume we are using the same YANG
module (Refer to the “Glossary” section for the complete YANG data model):
Register Callback Function
extern status_t
agt_cb_register_callback (const xmlChar *modname,
const xmlChar *defpath,
const xmlChar *version,
agt_cb_fn_t cbfn);
agt_cb_register_callback
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.
Version 16.10-10
Page 43
YumaPro API Quick Start Guide
Register Callback Example
#define EXAMPLE_MODNAME (const xmlChar *)"ietf-interfaces-example"
#define EXAMPLE_VERSION (const xmlChar *)"2017-01-01"
#define EXAMPLE_DEFPATH (const xmlChar *)"/if:interfaces/if:interface"
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
res =
agt_cb_register_callback(EXAMPLE_MODNAME,
EXAMPLE_DEFPATH,
EXAMPLE_VERSION,
edit_callback_example);
...
}
Callback Cleanup
If you register a callback for a specific object, your SIL code must unregister it during the cleanup phase, that is being
called any time the server is shutting down. Also, it is getting called during restart and reload procedure.
The following example code illustrates how the EDIT1 callback can be cleaned up. The callbacks cleanup is done during
module Cleanup Phase.
extern void
agt_cb_unregister_callbacks (const xmlChar *modname,
const xmlChar *defpath);
agt_cb_unregister_callbacks
Version 16.10-10
Page 44
YumaPro API Quick Start Guide
Parameter
Description
modname
Module name string that defines this object node.
defpath
Absolute path expression string indicating which node
the callback function is for.
Callback Cleanup Example
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
agt_cb_unregister_callbacks (EXAMPLE_MODNAME, EXAMPLE_DEFPATH);
...
}
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.
5.1.3
EDIT1 Callback Function Example
In this example, 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 the state 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
Version 16.10-10
Page 45
YumaPro API Quick Start Guide
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:
Version 16.10-10
Page 46
YumaPro API Quick Start Guide
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 to use this string later for logging. Alternatively,
typ_is_enum, typ_is_number, etc API functions could be used to check the desired type.
Version 16.10-10
Page 47
YumaPro API Quick Start Guide
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 */
}
...
5.1.4
EDIT2 Callback Initialization and Cleanup
The EDIT2 callback function is hooked into the server with the agt_cb_register_edit2_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 EDIT2 callback registration may look as follows:
Register EDIT2 Callback
extern status_t
agt_cb_register_edit2_callback (const xmlChar *modname,
const xmlChar *defpath,
const xmlChar *version,
agt_cb_fn_t cbfn);
agt_cb_register_edit2_callback
Version 16.10-10
Page 48
YumaPro API Quick Start Guide
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.
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
res =
agt_cb_register_edit2_callback(EXAMPLE_MODNAME,
EXAMPLE_DEFPATH,
EXAMPLE_VERSION,
edit2_callback_example);
...
}
EDIT2 Callback Cleanup
extern void
agt_cb_unregister_callbacks (const xmlChar *modname,
const xmlChar *defpath);
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.
Version 16.10-10
Page 49
YumaPro API Quick Start Guide
All other callbacks that the object may hold should be unregistered separately.
The following example code illustrates how the EDIT2 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)
{
...
agt_cb_unregister_callbacks (EXAMPLE_MODNAME, EXAMPLE_DEFPATH);
...
}
5.1.5
EDIT2 Callback Function Example
The following sections illustrates how to utilize the EDIT2 callbacks in examples.
The EDIT2 callback template is the same as the EDIT1 callback. The difference is that there is a queue of child edit
records that may need to be accessed to reliably process the edit requests.
In the following example, EDIT2 callback code gets each child_undo record in order to retrieve the real edited nodes and
the edited operation and merely form the data and prints it to the log. Instead of printing the data an agent could process to
the next step and run device instrumentation as required.
The following example code illustrates how the EDIT2 callback may look like.
/********************************************************************
* FUNCTION edit2_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.
*
********************************************************************/
edit2_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;
Version 16.10-10
Page 50
YumaPro API Quick Start Guide
val_value_t *errorval = (curval) ? curval : newval;
if (LOGDEBUG) {
log_debug("\nEnter edit2_callback_example callback for %s phase",
agt_cbtype_name(cbtyp));
}
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:
/* the edit is not really on this node; need to get
* each child_undo record to get the real edited nodes
* and the edited operations
*/
agt_cfg_transaction_t *txcb = RPC_MSG_TXCB(msg);
agt_cfg_undo_rec_t *child_edit =
agt_cfg_first_child_edit(txcb, newval, curval);
while (child_edit) {
op_editop_t child_editop = OP_EDITOP_NONE;
val_value_t *child_newval = NULL;
val_value_t *child_curval = NULL;
xmlChar *newval_str = NULL;
xmlChar *curval_str = NULL;
agt_cfg_child_edit_fields(child_edit,
&child_editop,
&child_newval,
&child_curval);
if (child_newval) {
newval_str = val_make_sprintf_string(child_newval);
if (newval_str == NULL) {
return ERR_INTERNAL_MEM;
}
}
if (child_curval) {
curval_str = val_make_sprintf_string(child_curval);
if (curval_str == NULL) {
m__free(newval_str);
return ERR_INTERNAL_MEM;
}
}
log_info("\n
child_newval
child_editop
child_newval
child_curval
%s: editop=%s newval=%s curval=%s",
? VAL_NAME(child_newval) : NCX_EL_NULL,
? op_editop_name(child_editop) : NCX_EL_NONE,
? newval_str : NCX_EL_NULL,
? curval_str : NCX_EL_NULL);
/* Force Rollback if the child value is not acceptable */
if (child_newval &&
Version 16.10-10
Page 51
YumaPro API Quick Start Guide
!xml_strcmp(VAL_NAME(child_newval),
(const xmlChar *)"untagged-ports") &&
!xml_strcmp(VAL_STR(child_newval),
(const xmlChar *)"not-supported")) {
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
m__free(newval_str);
m__free(curval_str);
break;
}
/**** process child edits here ****/
m__free(newval_str);
m__free(curval_str);
child_edit = agt_cfg_next_child_edit(child_edit);
}
break;
case OP_EDITOP_REPLACE:
case OP_EDITOP_CREATE:
/* the edit is on this list node and the child editop
* can be treated the same as the parent (even if different)
* the val_value_t child nodes can be accessed and there
* are no child_undo records to use
*/
val_value_t *child_newval = NULL;
child_newval =
val_find_child(newval,
EXAMPLE_MODNAME,
(const xmlChar *)"untagged-ports");
val_value_t *leaflist_val = child_newval;
while (leaflist_val) {
/**** process child leaf-list edits here ****/
leaflist_val = val_next_child_same(leaflist_val);
}
/**** process other child edits here if needed ****/
break;
case OP_EDITOP_DELETE:
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 (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_CONTENT,
res,
Version 16.10-10
Page 52
YumaPro API Quick Start Guide
NULL,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
return res;
} /* edit2_callback_example */
NOTE:
Do not use SET_ERROR() macro to return normal errors. This macro will cause “assert()” to be invoked, halting program
execution. The macro defined in netconf/src/ncx/status.h and it is for flagging internal errors only. This macro must not be
used for normal errors.
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 write a log with the information that the callback is getting
called. This is completely optional and is not required code, the main purpose of this code is to provide fast way to debug
problems.
...
if (LOGDEBUG) {
log_debug("\nEnter edit2_callback_example callback for %s phase",
agt_cbtype_name(cbtyp));
}
...
The agt _cbtype_name API function merely gets the string for the server callback phase to print it out.
NOTE:
If the operation is “merge”, the edit is not really on this node; need to get each child_undo record to get the real edited
nodes and the edited operations.
Otherwise, the edit is on the list node and the child edit operation can be treated the same as the parent (even if different)
the val_value_t child nodes can be accessed and there are no child_undo records to use.
The following part of the above example code is the most important in the EDIT2 callbacks. If the operation is set to
“merge”, it means that some of the children of the current node have been modified. The edit is not really on the current
node, and we need to get each child undo record to get the real edited nodes and the edited operations.
Version 16.10-10
Page 53
YumaPro API Quick Start Guide
In order to do so, we need to loop through the child undo records and retrieve the actual operation and the actual child node
that has been modified.
NOTE:
If the parent operation is “merge” and the actual edit is on the node with default value. The edited node has a “default”
YANG statement, then the actual child edit operation will always be “merge”. The difference will be in the newval, curval
values.
The default children nodes should be handled carefully. If the edited node has “default” YANG statement, then the actual
child edit operation will always be “merge” and curval, newval will always be set. The difference will be in newval,
curval values. The following table illustrates what values will be set for those nodes:
Action
Newval value
Curval value
create default node
new non-default value
default value
modify default
node
new non-default or set
back to default value
old non-default value
delete default node
default value
old non-default value
The following code demonstrates how to loop through the child undo records and retrieve the actual operation and the
actual child nodes. After the retrieval, the code merely logs the edited nodes into the log file and, in addition, it validates a
child value to make sure that the value is appropriate for the node.
...
case OP_EDITOP_MERGE:
agt_cfg_transaction_t *txcb = RPC_MSG_TXCB(msg);
agt_cfg_undo_rec_t *child_edit =
agt_cfg_first_child_edit(txcb, newval, curval);
while (child_edit) {
op_editop_t child_editop = OP_EDITOP_NONE;
val_value_t *child_newval = NULL;
val_value_t *child_curval = NULL;
xmlChar *newval_str = NULL;
xmlChar *curval_str = NULL;
agt_cfg_child_edit_fields(child_edit,
&child_editop,
&child_newval,
&child_curval);
if (child_newval) {
newval_str = val_make_sprintf_string(child_newval);
if (newval_str == NULL) {
return ERR_INTERNAL_MEM;
}
Version 16.10-10
Page 54
YumaPro API Quick Start Guide
}
if (child_curval) {
curval_str = val_make_sprintf_string(child_curval);
if (curval_str == NULL) {
m__free(newval_str);
return ERR_INTERNAL_MEM;
}
}
log_info("\n
child_newval
child_editop
child_newval
child_curval
%s: editop=%s newval=%s curval=%s",
? VAL_NAME(child_newval) : NCX_EL_NULL,
? op_editop_name(child_editop) : NCX_EL_NONE,
? newval_str : NCX_EL_NULL,
? curval_str : NCX_EL_NULL);
/* Force Rollback if the child value is not acceptable */
if (child_newval &&
!xml_strcmp(VAL_NAME(child_newval),
(const xmlChar *)"untagged-ports") &&
!xml_strcmp(VAL_STR(child_newval),
(const xmlChar *)"not-supported")) {
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
m__free(newval_str);
m__free(curval_str);
break;
}
/**** process child edits here ****/
m__free(newval_str);
m__free(curval_str);
child_edit = agt_cfg_next_child_edit(child_edit);
}
break;
...
The following code illustrates how to loop through the children undo records in order to retrieve the actual operation and
the actual edited children:
...
agt_cfg_undo_rec_t *child_edit =
agt_cfg_first_child_edit(txcb, newval, curval);
while (child_edit) {
op_editop_t child_editop = OP_EDITOP_NONE;
val_value_t *child_newval = NULL;
val_value_t *child_curval = NULL;
agt_cfg_child_edit_fields(child_edit,
&child_editop,
&child_newval,
Version 16.10-10
Page 55
YumaPro API Quick Start Guide
&child_curval);
/**** process child edits here ****/
child_edit = agt_cfg_next_child_edit(child_edit);
}
...
The agt_cfg_first_child_edit, agt_cfg_next_child_edit, agt_cfg_child_edit_fields API functions get the first, next child
node edit record, and this edit record fields respectively. There is no any alternative functions that could be used to retrieve
the desired child undo records. This loop merely loops trough all the edited children and is trying to retrieve their fields,
such as child new value, current value, and an actual operation that have been applied to the child node.
After we retrieve any children edits, we can process them as required. The following example code demonstrates how to log
the edited nodes into the log file:
...
if (child_newval) {
newval_str = val_make_sprintf_string(child_newval);
if (newval_str == NULL) {
return ERR_INTERNAL_MEM;
}
}
if (child_curval) {
curval_str = val_make_sprintf_string(child_curval);
if (curval_str == NULL) {
m__free(newval_str);
return ERR_INTERNAL_MEM;
}
}
log_info("\n
child_newval
child_editop
child_newval
child_curval
%s: editop=%s newval=%s curval=%s",
? VAL_NAME(child_newval) : NCX_EL_NULL,
? op_editop_name(child_editop) : NCX_EL_NONE,
? newval_str : NCX_EL_NULL,
? curval_str : NCX_EL_NULL);
m__free(newval_str);
m__free(curval_str);
...
The val_make_sprintf_string API function malloc a buffer and fill it with a zero-terminated string representation of the
value node. Make sure that the malloced buffer is freed after you use it. Use m__free to free the malloced buffer.
In the following part of the above code example we are trying to validate previously retrieved child “untagged-ports” node
value. If the provided in the <edit-config> 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.
Version 16.10-10
Page 56
YumaPro API Quick Start Guide
...
if (child_newval &&
!xml_strcmp(VAL_NAME(child_newval),
(const xmlChar *)"untagged-ports") &&
!xml_strcmp(VAL_STR(child_newval),
(const xmlChar *)"not-supported")) {
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
m__free(newval_str);
m__free(curval_str);
break;
}
...
If the operation is not “merge”, it mean that the current <edit-config> is on the “interface” list and not on its children. The
edit is on the list node and the child edit operation can be treated the same as the parent (even if different) the val_value_t
child nodes can be accessed the same way as for EDIT1 callback functions and there are no child_undo records to use. The
following code break illustrates how to access “untagged-ports” leaf-list children that are getting created along with thier
“interface” parent:
...
case OP_EDITOP_CREATE:
val_value_t *child_newval = NULL;
child_newval =
val_find_child(newval,
EXAMPLE_MODNAME,
(const xmlChar *)"untagged-ports");
val_value_t *leaflist_val = child_newval;
while (leaflist_val) {
/**** process child leaf-list edits here ****/
leaflist_val = val_next_child_same(leaflist_val);
}
/**** process other child edits here if needed ****/
break;
...
The val_next_child_same API function gets the next instance of the corresponding child node. This API function is useful
for leaf-list and list in order to get the next sibling in the list or leaf-list.
In the following code we are trying to record an error if some of the previous steps fail. This error will be sent to the client
side signaling that the <edit-config> operation failed to complete.
if (res != NO_ERR) {
agt_record_error(scb,
Version 16.10-10
Page 57
YumaPro API Quick Start Guide
&msg->mhdr,
NCX_LAYER_CONTENT,
res,
NULL,
NCX_NT_STRING,
errorstr,
NCX_NT_VAL,
errorval);
}
The agt_record_error API function generates an rpc_err_rec_t and save it in the message. Alternatively,
agt_record_error_errinfo, agt_record_warning, agt_record_attr_error, etc API functions could be used to record an
error.
The API functions, used in this code break are the most utilized and prominent functions. These functions are getting used
in the SIL code often.
To summarize, anytime an <edit-config> operation creates a new or modifies an existent “interface” list node or its children
the callback will be invoked and run. As described earlier, if the <edit-config> operation creates a new list entry, the
operation will be “create” and any children that are getting created along with its parent should be treated as regular nodes
and should be accessed the same way as for EDIT1 callback. However, if the operation is “merge”, the actual operation and
edit is on children nodes, so the according code and actions should be applied.
Assuming the “interfaces” container and “interface” list preexist in the datastore. However, assume there is not any other
children on this list other than the key child. To invoke the callback with “merge” operation the following RPC can be sent:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<candidate/>
</target>
<config>
<interfaces xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<name>supported</name>
<untagged-ports>port1</untagged-ports>
<untagged-ports>port2</untagged-ports>
<untagged-ports>port3</untagged-ports>
<untagged-ports>port4</untagged-ports>
</interface>
</interfaces>
</config>
</edit-config>
</rpc>
As a result, the server will invoke the callback with “merge” operation; however, the actual operation is “create” and the
actual edit is on “untagged-ports” leaf-list. So, you will have to loop trough the children and retrieve the actual edits.
When the incoming configuration has been safely loaded onto the candidate datastore and validated, it is ready to impact
the running system. If the device supports the :candidate capability, use the <commit> operation to apply candidate
Version 16.10-10
Page 58
YumaPro API Quick Start Guide
configuration to running datastore. Otherwise, if the device supports :writable-running capability, in the above <editconfig> operation example, change target to running.
This RPC and the callback function that it triggers will pass the validation and should not trigger any Rollback, so the
server may reply with the following:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<ok/>
</rpc-reply>
If we add another leaf-list entry to the <edit-config> RPC with the “not-supported” value, then the server should reply with
the following error (based on the SIL callback validation that we created previously) and trigger Rollback phase where it
should reverse any previously applied edits and restore the state to the state that was before the edit:
<errors xmlns:ncx="http://netconfcentral.org/ns/yuma-ncx"
xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
<error>
<error-type>rpc</error-type>
<error-tag>operation-not-supported</error-tag>
<error-app-tag>no-support</error-app-tag>
<error-message xml:lang="en">operation not supported</error-message>
<error-info>
<error-number>273</error-number>
</error-info>
</error>
</errors>
Now that the incoming configuration has been integrated into the running configuration and all the callbacks run their
validation and processed all the nodes, the application needs to gain trust that the change has affected the device in the way
intended without affecting it negatively.
To gain this confidence, the application can run tests of the operational state of the device. The nature of the test is
dependent on the nature of the change and is outside the scope of this document. Depending on the changes that have been
applied on running datastore, these tests may include reachability from the system running the application (using ping),
changes in reachability to the rest of the network (by comparing the device's routing table), or, for instance, inspection of
the particular change (looking for operational evidence of the BGP peer that was just added).
5.1.6
EDIT2 Utility Functions
EDIT 2 mode provides extended edit functionality and thus more ways to manage specific edit.
There are EDIT 2 mode specific high-level Transaction access and management utilities in netconf/src/agt/agt_cfg.h.
These functions access the lower-level functions to provide simpler functions for common transaction management tasks.
The following table highlights available functions:
Version 16.10-10
Page 59
YumaPro API Quick Start Guide
API
Description
agt_cfg_first_child_edit()
Get the first child node edit record for a given transaction.
agt_cfg_next_child_edit()
Get the child edit fields from the undo record.
agt_cfg_child_edit_fields()
Get the child edit fields from the specified undo record.
agt_cfg_first_child_undo()
Get the first child undo record for EDIT2 mode
agt_cfg_next_child_undo()
Get the next child undo record
Version 16.10-10
Page 60
YumaPro API Quick Start Guide
5.2 GET1 Callback
This section describes how to manage non-configurable or just operational data, what type of callbacks are available for
non-configurable nodes and how to utilize these type of callbacks.
Operational data nodes are supported within SIL code in three ways:
1.
value node based virtual data (GET1 callback)
2.
value node based static data (GET1 callback)
3.
object template based GET2 callback
The following function template definition is used for GET1 callback functions:
typedef status_t
(*getcb_fn_t) (ses_cb_t *scb,
getcb_mode_t cbmode,
const val_value_t *virval,
val_value_t *dstval);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 per object
•
File: getcb.h
•
Template: getcb_fn_t
◦
Inputs:
▪
scb == session that issued the get (may be NULL) , can be used for access control purposes
▪
cbmode == reason for the callback
▪
virval == place-holder node in the data model for this virtual value node
▪
dstval == pointer to value output struct
◦
Outputs: fill may be adjusted depending on callback reason. Dstval should be filled in, depending on the
callback reason.
◦
Returns: status_t:
Status of the callback function execution.
•
Register: none
•
Unregister: none
Where, SCB pointer represents a session control block structure that is defined in the netconf/src/ncx/ses.h. This control
block is primarily used for error reporting. However, it also provides access to the session specific information, such as
current message input/output encoding, current session ID information, current protocol information, user name
information, peer address information, etc. Note, all the fields in this structure should NOT be changed and accessed
directly. This control block ideally should be used only for getting more information about the current session, not for
alteration of any of its fields.
Version 16.10-10
Page 61
YumaPro API Quick Start Guide
CBMODE pointer represents an enumeration structure of the different server GET callback retrieval types that is defined in
the netconf/src/ncx/getcb.h. This control block specifies what retrieval type has this GET callback. GET1 callback should
have GETCB_GET_VALUE type, if the type is different, the error should be reported, as specified in the examples later in
this section.
VIRVAL pointer represents a data node as a place-holder node in the data model for this virtual value node. It is defined in
the netconf/src/ncx/getcb.h. This control block specifies what operation is in the process, merge, replace, delete, or other,
as described in the example section later.
DSTVAL pointer represent data nodes that is being filled in. The example section later demonstrates how it can be utilized.
5.2.1
GET1 Callback Examples. Virtual data
A common SIL callback function to use is a virtual node 'get' function. A virtual node can be either a configuration or nonconfiguration node, but is more likely to be a non-configuration node, such as a counter or hardware status object. Use
virtual operational data when the data is stored in the running system somewhere and the values need to be retrieved from
the system in order to fill the YANG data nodes in the <rpc-reply>.
This type of callbacks also called - GET1 callbacks and they can be used during SIL initialization phase or during the
<edit-config> operation.
To exemplify, when the incoming configuration for /interfaces/interface has been safely loaded onto the device and
validated and you want read-only leafs to be populated at the same time, then the callback function may be implemented as
follows. In this example we use already existent EDIT1 callback where we extend it with a GET1 callback function:
The function that is responsible for the GET1 callback may look as follows:
/********************************************************************
* FUNCTION create_readonly_leaf
*
* Make read-only child nodes
* Path: /interfaces/interface/observed-speed
*
* INPUTS:
*
parentval == the parent struct to use for new child nodes
*
* RETURNS:
*
error status
********************************************************************/
static status_t
create_readonly_leaf (val_value_t *parentval)
{
status_t res = NO_ERR;
/* add /interfaces/interface/observed-speed */
val_value_t *childval =
agt_make_virtual_leaf( VAL_OBJ(parentval),
(const xmlChar *)"observed-speed",
observed_speed_get,
&res);
if (childval != NULL) {
res = val_child_add(childval, parentval);
if (res != NO_ERR) {
val_free_value(childval);
}
Version 16.10-10
Page 62
YumaPro API Quick Start Guide
return res;
} /* create_readonly_leaf */
The following example shows a simple get callback function for the same data model, which returns the “observed-speed”
element when it is requested.
/********************************************************************
* FUNCTION observed_speed_get
*
* Callback function for agent node get handler
*
* Fill in 'dstval' contents
*
********************************************************************/
static status_t
observed_speed_get ( ses_cb_t *scb,
getcb_mode_t cbmode,
const val_value_t *virval,
val_value_t *dstval)
{
status_t res = NO_ERR;
/* remove the next line if scb is used */
(void)scb;
/* remove the next line if virval is used */
(void)virval;
if (cbmode != GETCB_GET_VALUE) {
return ERR_NCX_OPERATION_NOT_SUPPORTED;
}
/* set the speed var here */
const xmlChar *speed = (const xmlChar *)"1000";
res =
val_set_simval_obj( dstval,
dstval->obj,
speed);
return res;
} /* observed_speed_get */
In these examples, the following commonly used API functions were implemented:
•
agt_make_virtual_leaf: Make a val_value_t structure for a specified virtual leaf or leaf-list. Alternatively,
agt_add_top_virtual API function could be used if there is need to make a val_value_t structure for a specified
virtual top-level data node. Refer to the section named “SIL Utility Functions” in this guide for more details.
•
val_set_simval_obj: Set an initialized val_value_t as a simple type. Refer to the section named “Access
Functions” in this guide for more available API and details.
The above list illustrates only the most utilized and prominent functions used in the example. These functions are getting
used in the SIL code very often. To find more information on the not listed API functions refer to the corresponding .h file.
Version 16.10-10
Page 63
YumaPro API Quick Start Guide
To summarize, anytime an <edit-config> operation loads specific “interface” list node configuration to the specific target
configuration, the virtual “observed-speed” node will be generated.
To ensure that the state data was successfully generated an application may retrieve running configuration and device state
information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces xmlns="http://yumaworks.com/ns/interfaces">
</interface>
<name>supported</name>
</interface>
</interfaces>
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces xmlns="http://yumaworks.com/ns/interfaces">
</interface>
<name>supported</name>
<observed-speed>1000</observed-speed>
</interface>
</interfaces>
</rpc-reply>
5.2.2
GET1 Callback Examples for Static Data
Let us go through simple examples that will illustrate how to utilize the GET1 callbacks based on static data and populate
this static data during SIL initialization phase, so it will be available right after the module and its SIL are loaded into the
server. Consider this simplified, but functional, example. Refer to the “Glossary” section for the complete YANG data
model:
...
container interfaces {
list interface {
key "name";
Version 16.10-10
Page 64
YumaPro API Quick Start Guide
leaf name {
type string;
}
leaf speed {
type enumeration {
enum 10m;
enum 100m;
enum auto;
}
}
leaf observed-speed {
config false;
type uint32;
}
}
leaf count {
config false;
type unit32;
}
}
...
leaf version {
config false;
type string;
}
In this example we have two operational data nodes, an “observed-speed” and a “version” leaf. We just illustrated how to
register the GET1 callback for the “observed-speed” leaf. Now, let us register the GET1 callback for the “version” static
data leaf.
The concept is the same as in the previous example; however, the GET1 callback is getting registered and initialized during
the SIL initialization phase.
During the Initialization Phase 1, the server will load the module:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
ncx_module_t *interface_mod = NULL;
/* load the module */
status_t res =
ncxmod_load_module(EXAMPLE_MODNAME,
NULL,
agt_get_savedevQ(),
Version 16.10-10
Page 65
YumaPro API Quick Start Guide
&interface_mod);
if (res != NO_ERR) {
return res;
}
...
}
After the startup configuration is loaded into the running configuration database, all the stage 2 initialization routines are
called. These are needed for modules which add read-only “static” data nodes to the tree containing the running
configuration. SIL modules may use their 'init2' function to create factory default configuration nodes (which can be saved
for the next reboot).
The Initialization Phase 2 function that creates factory default “static” configuration nodes with help of the GET1 callbacks
may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* Initialize the server instrumentation library.
* Called after running config is loaded
*
* Initialization Phase 2
*
*********************************************************************/
static status_t
interfaces_init 2 (void)
{
status_t res= NO_ERR;
/* get the running configuration datastore */
cfg_template_t *runningcfg = cfg_get_config_id(NCX_CFGID_RUNNING);
if (!runningcfg || !runningcfg->root) {
return ERR_INTERNAL_VAL;
}
/* get the top object node */
obj_template_t *topobj =
obj_find_template_top(interface_mod,
EXAMPLE_MODNAME,
(const xmlChar *)"version");
if (!topobj) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* add /version virtual node */
val_value_t *version = val_new_value();
if (!version) {
return ERR_INTERNAL_MEM;
}
val_init_virtual(version, get_version, topobj);
/* handing off the malloced memory here */
res = val_child_add(version, runningcfg->root);
if (res != NO_ERR) {
val_free_value(version).
}
Version 16.10-10
Page 66
YumaPro API Quick Start Guide
return res;
} /* interfaces_init */
The obj_find_template_top API returns obj_template_t structure or NULL if some error; however, this function should
be used only for the top level objects. Similarly, the following extended function can be used.
...
/* Get the server profile struct */
agt_profile_t *profile = agt_get_profile();
/* get the top object node */
obj_template_t *topobj =
ncx_match_any_object_ex(EXAMPLE_MODNAME,
(const xmlChar *)"interfaces",
TRUE,
profile->agt_match_names,
profile->agt_alt_names,
&res);
if (!topobj) {
return ERR_NCX_DEF_NOT_FOUND;
}
...
Alternatively, if you know the object template that defines the object template for the parent node and would like to find
specific child node object of that parent, the following API can be used:
...
obj_template_t *leafobj =
obj_find_child(topobj,
EXAMPLE_MODNAM,
(const xmlChar *)"count");
if (!leafobj) {
return ERR_NCX_DEF_NOT_FOUND;
}
...
The following example shows a simple GET1 callback function for the same data model, which creates factory default
“static” configuration for the “observed-speed” node.
/********************************************************************
* FUNCTION get_version
*
Version 16.10-10
Page 67
YumaPro API Quick Start Guide
* Callback function for agent node get handler
*
* Fill in 'dstval' contents
*
********************************************************************/
static status_t
get_version ( ses_cb_t *scb,
getcb_mode_t cbmode,
const val_value_t *virval,
val_value_t *dstval)
{
status_t res = NO_ERR;
/* remove the next line if scb is used */
(void)scb;
/* remove the next line if virval is used */
(void)virval;
if (cbmode != GETCB_GET_VALUE) {
return ERR_NCX_OPERATION_NOT_SUPPORTED;
}
/* set the speed var here */
const xmlChar *version = (const xmlChar *)"Version1.1";
res =
val_set_simval_obj(dstval,
dstval->obj,
version);
return res;
} /* get_version */
Now, the static “version” node is generated during the SIL initialization phase and will available right after the server boots.
To ensure that the static data was successfully generated an application can retrieve running configuration and device state
information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get/>
</rpc>
The server should reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces xmlns="http://yumaworks.com/ns/interfaces">
</interface>
<name>supported</name>
<observed-speed>1000</observed-speed>
Version 16.10-10
Page 68
YumaPro API Quick Start Guide
</interface>
</interfaces>
<version>Version1.1</version>
</rpc-reply>
Version 16.10-10
Page 69
YumaPro API Quick Start Guide
5.3 GET2 Callback
The GET2 callback functions use 1 callback per object template. The server will issue “get” and “getnext” requests via this
API, and the callback code will return value nodes for the data it is managing. These data nodes are not maintained in the
server Data Tree. They are not cached in the server.
Configuration data is not supported at this time. These callbacks are expected to return some or all of the terminal child
nodes (leaf, leaf-list, anyxml) when invoked.
NOTE:
GET2 callbacks never return child nodes that are complex nodes. Complex child nodes are expected to have their own get2
callbacks registered.
Configuration data is not supported for the GET2 callbacks.
The following function template definition is used for GET2 callback functions:
typedef status_t
(*getcb_fn2_t) (ses_cb_t *scb,
xml_msg_hdr_t *msg,
getcb_get2_t *get2cb);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 per object
•
Files: getcb.h, agt_cb.h
•
Template: getcb_fn2_t
◦
Inputs:
▪
scb == session control block making the request
▪
msg == incoming XML message header
▪
get2cb == get2 control block for this callback request
◦
Outputs: return_keyQ is full of any new keys added for this entry -- only if 'obj' is a list return_valQ is filled
with malloced val_value_t nodes. If object tested is a choice the a backptr to a constant string containing the
case name that is active.
◦
Returns: status_t:
Status of the callback function execution. NO_ERR if executed OK and found OK.
ERR_NCX_NO_INSTANCE warning if no instance found
•
Register: agt_cb_register_get_callback
•
Unregister: agt_cb_unregister_callbacks
Version 16.10-10
Page 70
YumaPro API Quick Start Guide
Complex child nodes are expected to have their own get2 callbacks registered. The GET2 callbacks are registered with an
API similar to the EDIT callback registration.
The following node types are expected to have GET2 callbacks registered:
•
choice: expected to return the name of the active case and terminal nodes from the active case
•
container: expected to return child terminal nodes
•
list: expected to return list keys and maybe other terminal nodes
Callbacks for terminal nodes are allowed, but only if their parent is a config=true node. The server will expect
configuration data nodes to be present in the target datastore. If the retrieval request includes config=false nodes, then the
server will check each child node that is config=false for a GET2 callback function. If it exists, then it will be used for
retrieval of that data node. If not, the current config=true node will be checked for child nodes (static or dynamic
operational data).
Each GET2 callback returns a status code
•
NO_ERR: data is returned
•
ERR_NCX_NO_INSTANCE: the specified data instance does not exist
•
Other status code: some error occurred
YANG Node Type
GET2 Callback Summary
container
Returns the child terminal nodes in the container
list
Returns the keys and perhaps the rest of the child terminal nodes. Set
“more_data” to trigger “getnext” until no more instances found
anyxml
Returns the entire anyxml value tree
leaf
Returns the leaf value
leaf-list
Returns all values for the leaf-list
choice
Returns the name of the active case and child terminal nodes in the active case
case
This node type does not have a callback
Procedure
The server will send a <get-request> server request message to each subsystem that has registered a get2 callback for the
specific object.
•
For single-instance nodes (container, leaf, choice, anyxml) only a “get” request will be made.
•
For a leaf-list node, only “get” requests will be made, but all instances of the leaf-list are returned at once (maxentries parameter equals zero).
•
For a list node, a “get” request will be made if the keys are known. A “get” request that contains no keys will cause
the first instance to be returned. Entries are ordered by their key values, exactly as specified in the YANG list. If
Version 16.10-10
Page 71
YumaPro API Quick Start Guide
there is no key-stmt defined, then the callback will still return the first entry, except the server will not attempt any
sorting if multiple entries are returned (i.e. 1 from each subsystem). It is always up to the callback to know what is
the “best” next entry, given the keys that are returned.
The callback examined the keys (if any) and fills in the return_keys. If the “keys-only” flag is set, then no other
terminal nodes are returned. If there are more list entries, then the “more-data” flag is set in the get-response. The
server will issue “getnext” requests as needed until the “more-data” flag is not present in the response. The “maxentries” field will be set to 1. It may be set higher in a future release to support getbulk retrieval.
5.3.1
GET2 Callback Initialization and Cleanup
The following example code illustrates how the GET2 callbacks can be registered. The callbacks registration is getting
done during Initialization Phase 1.
/********************************************************************
* FUNCTION agt_cb_register_get_callback
*
* Register an object specific GET callback function
* use the same fn for all callback phases
* all phases will be invoked
*
*
* INPUTS:
*
modname == module that defines the target object for
*
these callback functions
*
defpath == Xpath with default (or no) prefixes
*
defining the object that will get the callbacks
*
version == exact module revision date expected
*
if condition not met then an error will
*
be logged (TBD: force unload of module!)
*
== NULL means use any version of the module
*
get_cbfn == address of callback function to use for GET callbacks
*
* RETURNS:
*
status
*********************************************************************/
extern status_t
agt_cb_register_get_callback (const xmlChar *modname,
const xmlChar *defpath,
const xmlChar *version,
getcb_fn2_t get_cbfn);
Example:
/********************************************************************
* FUNCTION init_interfaces
*
* initialize the server instrumentation library
*
*********************************************************************/
static status_t
Version 16.10-10
Page 72
YumaPro API Quick Start Guide
init_interfaces (void)
{
...
res = agt_cb_register_get_callback(EXAMPLE_MODNAME,
(const xmlChar *)"/interfaces-state",
EXAMPLE_VERSION,
get2_container_interfaces);
if (res != NO_ERR) {
return res;
}
...
} /* init_interfaces */
The following example code illustrates how the GET2 callbacks can be cleaned up. The callbacks Cleanup is getting done
during Cleanup Phase.
NOTE:
The unregister function is the same as for EDIT callbacks. It is not required to unregister GET2 callbacks for an object if
an unregister function has already been called for the same object but with EDIT callback. However, if it is called multiple
times for the same object, it will return with no errors.
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
agt_cb_unregister_callbacks (EXAMPLE_MODNAME, (const xmlChar *)"/interfaces-state");
...
}
5.3.2
GET2 Callback Function Examples
The following sections illustrates how to utilize the GET2 callback in examples. Let us go through simple examples that
will illustrate how to utilize the GET2 callbacks for the specific YANG node types.
Container example
Version 16.10-10
Page 73
YumaPro API Quick Start Guide
Consider this simplified example, that represents GET2 callback for the “interface-state” container. Refer to the “Glossary”
section for the complete YANG data model:
...
container interfaces-state {
config false;
leaf in-errors { type uint32; }
...
}
...
NOTE:
The GET2 callbacks for “container” elements DO NOT required to fill in any children elements. All additional leafs are
optional to fill in. Any other descendant complex elements, such as lists, other containers, and choice elements must have
their own callbacks functions.
The following C API code exemplifies a simple get callback function for the data model, which returns the “interfacesstate” container element with an additional leaf element when it is requested.
/********************************************************************
* FUNCTION get2_container_interfaces
*
* Path: /interfaces-state
*
* INPUTS:
*
scb == session control block making the request
*
msg == incoming XML message header
*
get2cb == get2 control block for this callback request
*
* OUTPUTS:
*
return_valQ is filled with malloced val_value_t nodes
*
* RETURNS:
*
status:
*
NO_ERR if executed OK and found OK
*
ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
get2_container_interfaces (ses_cb_t *scb,
xml_msg_hdr_t *msg,
getcb_get2_t *get2cb)
{
(void)scb;
(void)msg;
/* check the callback mode type */
Version 16.10-10
Page 74
YumaPro API Quick Start Guide
getcb_mode_t cbmode = GETCB_GET2_CBMODE(get2cb);
switch (cbmode) {
case GETCB_GET_VALUE:
break;
case GETCB_GETNEXT_VALUE:
return ERR_NCX_NO_INSTANCE;
default:
return SET_ERROR(ERR_INTERNAL_VAL);
}
/* get the template for the “interfaces-state” container */
obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
status_t res = NO_ERR;
/* pretend leaf 'in-errors' is present */
val_value_t *retval =
agt_make_leaf2(obj,
EXAMPLE_MODNAME,
(const xmlChar *)"in-errors",
(const xmlChar *)"8",
&res);
if (retval) {
getcb_add_return_val(get2cb, retval);
} else {
return res;
}
/* choice and other complex nodes will be checked in a separate call */
return res;
}
/* get2_container_interfaces */
The agt_make_leaf2 API returns malloced value structure or NULL if some error. Alternatively, if you know the object
template that defines the leaf node that you want to generate, the following more generic API can be used:
...
obj_template_t *leafobj =
obj_find_child(obj,
EXAMPLE_MODNAM,
(const xmlChar *)"in-errors");
if (!leafobj) {
/* report an error or do not try to generate the leaf, just skip it */
return ERR_NCX_DEF_NOT_FOUND;
}
/* pretend leaf 'in-errors' is present */
val_value_t *retval =
val_make_simval_obj(leafobj,
(const xmlChar *)"8",
&res);
...
Version 16.10-10
Page 75
YumaPro API Quick Start Guide
In the scenario above, make sure that the leaf object template was found. If it was not found, you may report an error
immediately or ignore that node and skip the node generation process. However, do not generate the leaf node, if the object
template was not found.
NOTE:
In the SIL, when you fill the data in get2cb with help of val_make_simval_obj() or analogous APIs, You don’t need to free
the buffer because the server does clean up get2cb.
To ensure that the GET2 callbacks are working as expected an application may retrieve running configuration and device
state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<in-errors>8</in-errors>
</interfaces-state>
</rpc-reply>
Now let us go through simple examples that will illustrate how to utilize the GET2 callbacks for “list” nodes.
List example
Consider this simplified example, that represents GET callback for the “interface” list. Refer to the “Glossary” section for
the complete YANG data model:
...
container interfaces-state {
config false;
Version 16.10-10
Page 76
YumaPro API Quick Start Guide
list interface {
key "ip";
leaf ip {
type inet:ip-prefix;
}
leaf name {
type string;
mandatory true;
}
}
...
}
...
NOTE:
The GET2 callbacks for “list” elements DO require to fill in the list key value. All additional leafs are optional to fill in.
Any other descendant complex elements, such other lists, containers, or choice nodes must have their own callbacks
functions.
The following C API code exemplifies a simple GET2 callback function for the data model, which returns the “interface”
list element with an additional leaf elements when it is requested.
/********************************************************************
* FUNCTION get2_list_interface
*
* Path: /interfaces-state/interface
*
* INPUTS:
*
scb == session control block making the request
*
msg == incoming XML message header
*
get2cb == get2 control block for this callback request
*
* OUTPUTS:
*
return_valQ is filled with malloced val_value_t nodes
*
return_keyQ is filled with any new keys added for this entry
*
* RETURNS:
*
status:
*
NO_ERR if executed OK and found OK
*
ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
get2_list_interface (ses_cb_t *scb,
xml_msg_hdr_t *msg,
getcb_get2_t *get2cb)
Version 16.10-10
Page 77
YumaPro API Quick Start Guide
{
(void)scb;
(void)msg;
uint32 max_entries = GETCB_GET2_MAX_ENTRIES(get2cb);
(void)max_entries;
boolean getnext = FALSE;
/* check the callback mode type */
getcb_mode_t cbmode = GETCB_GET2_CBMODE(get2cb);
switch (cbmode) {
case GETCB_GET_VALUE:
break;
case GETCB_GETNEXT_VALUE:
getnext = TRUE;
break;
default:
return SET_ERROR(ERR_INTERNAL_VAL);
}
/* get the list and child objects */
obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
obj_template_t *key_obj =
obj_find_child(obj,
EXAMPLE_MODNAME,
(const xmlChar *)"ip");
/* init leaf values for the list */
const xmlChar *name = NULL;
const xmlChar *ret_name = NULL;
boolean name_fixed = FALSE;
/* check the keyQ for the specific list instance requested */
val_value_t *name_key = getcb_find_key(get2cb, key_obj);
/* pretend there are 3
#define IP1 (const xmlChar
#define IP2 (const xmlChar
#define IP3 (const xmlChar
values */
*)"10.10.10.1/16"
*)"10.10.10.2/16"
*)"10.10.10.3/16"
/* specific list instance requested, check if it is valid */
if (name_key && VAL_STR(name_key)) {
name = VAL_STR(name_key);
int32 ret = xml_strcmp(name, IP2);
if (ret > 0) {
return ERR_NCX_NO_INSTANCE;
}
/* specific list instance requested, set the flag */
name_fixed = VAL_IS_FIXED_VALUE(name_key);
}
/* set to true if there are more instances of this object to retrieve;
* false otherwise
*/
boolean moreflag = TRUE;
/* check validity of keys present */
if (getnext) {
if (name_fixed) {
return ERR_NCX_NO_INSTANCE;
Version 16.10-10
Page 78
YumaPro API Quick Start Guide
}
/* adjust the key to find the next entry after
* the specified value
*/
if (!name) {
ret_name = IP1; // return first entry [0]
} else {
/* find the correct entry to retrieve */
int32 ret = xml_strcmp(name, IP1);
if (ret < 0) {
ret_name = IP1;
} else if (ret == 0) {
ret_name = IP2;
} else {
/* check IP2 */
ret = xml_strcmp(name, IP2);
if (ret < 0) {
ret_name = IP2;
} else if (ret == 0) {
ret_name = IP3;
moreflag = FALSE;
} else {
/* assume IP3 */
ret_name = IP3;
moreflag = FALSE;
}
}
}
} else {
if (name) {
/* get a specified instance */
boolean name_ok = FALSE;
if (!xml_strcmp(name, IP1)) {
name_ok = TRUE;
} else if (!xml_strcmp(name, IP2)) {
name_ok = TRUE;
} else if (!xml_strcmp(name, IP3)) {
name_ok = TRUE;
moreflag = FALSE;
}
if (name_ok) {
ret_name = name;
} else {
return ERR_NCX_NO_INSTANCE;
}
} else {
// else no keys == get-first
ret_name = IP1;
}
}
GETCB_GET2_MATCH_TEST_DONE(get2cb) = FALSE;
if (ret_name == NULL) {
return ERR_NCX_NO_INSTANCE;
}
GETCB_GET2_MORE_DATA(get2cb) = moreflag;
status_t res = NO_ERR;
/* if we get here then the index is valid
Version 16.10-10
Page 79
YumaPro API Quick Start Guide
*
* create a return_keyQ node for the name key value
*/
val_value_t *return_val =
agt_make_leaf2(obj,
EXAMPLE_MODNAME,
(const xmlChar *)"ip",
ret_name,
&res);
if (res == NO_ERR) {
if (name_fixed) {
VAL_SET_FIXED_VALUE(return_val);
}
getcb_add_return_key(get2cb, return_val);
}
/* Also, pretend leaf 'name' is present
* Here, might be another key name validation that will dictate what “name”
* value to use based on key value.
* Note, “name” leaf is a mandatory=true leaf, so it must be present.
*/
val_value_t *retval =
agt_make_leaf2(obj,
EXAMPLE_MODNAME,
(const xmlChar *)"name",
(const xmlChar *)"interface-name",
&res);
if (retval) {
getcb_add_return_val(get2cb, retval);
} else {
return res;
}
return NO_ERR;
}
/* get2_list_interface */
Refer to the section named “GET2 Access Functions” for other available functions to access GET2 control block.
To ensure that the GET2 callbacks are working as expected an application can retrieve running configuration and device
state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<ip>10.10.10.1/16</ip>
</interface>
</interfaces-state>
</filter>
</get>
</rpc>
Version 16.10-10
Page 80
YumaPro API Quick Start Guide
The server may reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<ip>10.10.10.1/16</ip>
<ip>interface-name</ip>
</interface>
</interfaces-state>
</rpc-reply>
If no key value is specified in the request or the request subtree filter target is “interface-state” parent container, then the
server will try to output all the interfaces and its children:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<ip>10.10.10.1/16</ip>
<ip>interface-name</ip>
</interface>
<interface>
<ip>10.10.10.2/16</ip>
<ip>interface-name</ip>
</interface>
<interface>
<ip>10.10.10.3/16</ip>
<ip>interface-name</ip>
</interface>
</interfaces-state>
</rpc-reply>
Now let us go through simple examples that will illustrate how to utilize the GET2 callbacks for “choice” node.
Version 16.10-10
Page 81
YumaPro API Quick Start Guide
Choice example
Consider this simplified example, that represents GET callback for the node type “choice”. Refer to the “Glossary” section
for the complete YANG data model:
...
choice type {
case interface {
// active case
leaf interface {
type if:interface-ref;
}
}
case case-network {
leaf next-hop-host {
type inet:ip-address;
}
}
}
...
The following C API code exemplifies a simple get callback function for the data model, which returns the name of the
active case and child terminal nodes in the active case when it is requested.
/********************************************************************
* FUNCTION get2_choice_type
*
* Path: /type
*
* INPUTS:
*
scb == session control block making the request
*
msg == incoming XML message header
*
get2cb == get2 control block for this callback request
*
* OUTPUTS:
*
return_valQ is filled with malloced val_value_t nodes
*
the a backptr to a constant string containing the case
*
name that is active
*
* RETURNS:
*
status:
*
NO_ERR if executed OK and found OK
*
ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
get2_choice_type (ses_cb_t *scb,
xml_msg_hdr_t *msg,
getcb_get2_t *get2cb)
{
(void)scb;
Version 16.10-10
Page 82
YumaPro API Quick Start Guide
(void)msg;
status_t res = NO_ERR;
/* check the callback mode type */
getcb_mode_t cbmode = GETCB_GET2_CBMODE(get2cb);
switch (cbmode) {
case GETCB_GET_VALUE:
break;
case GETCB_GETNEXT_VALUE:
return ERR_NCX_NO_INSTANCE;
default:
return SET_ERROR(ERR_INTERNAL_VAL);
}
/* get the active case
* callback based on instances in the system
* It may be NULL if no active case
*/
const xmlChar *active_case_modname = NULL;
const xmlChar *active_case = (const xmlChar *)"interface";
/* Set the active case to the specified object */
res = getcb_set_active_case(get2cb,
active_case_modname,
active_case);
if (active_case == NULL) {
return res;
}
/* get the template for the active case */
obj_template_t *choice_obj = GETCB_GET2_OBJ(get2cb);
if (choice_obj == NULL) {
return ERR_NCX_NOT_FOUND;
}
obj_template_t *case_obj =
obj_find_child(choice_obj,
active_case_modname,
active_case);
if (case_obj == NULL) {
return ERR_NCX_NOT_FOUND;
}
/* return all the leaf and leaf-list instances that are child nodes
* of the active case.
*/
val_value_t *chval = NULL;
if (!xml_strcmp(active_case, (const xmlChar *)"interface")) {
/* pretend leaf 'interface' is present */
chval = agt_make_leaf2(case_obj,
active_case_modname,
(const xmlChar *)"interface",
(const xmlChar *)"ethernet1/1/1",
&res);
if (chval) {
getcb_add_return_val(get2cb, chval);
} else {
return res;
}
} else {
res = SET_ERROR(ERR_INTERNAL_VAL);
Version 16.10-10
Page 83
YumaPro API Quick Start Guide
}
return res;
}
/* get2_choice_type */
NOTE:
Note that any leaf or leaf-list nodes that are in nested “choice” element within the active case are not returned . These are
considered complex objects and the caller will ask for nested choice, list, and container nodes in a separate call .
Refer to the section named “GET2 Access Functions” for other available functions to access GET2 control block.
To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state
information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interface xmlns="http://yumaworks.com/ns/interfaces" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface xmlns="http://yumaworks.com/ns/interfaces">ethernet1/1/1 </interface>
</rpc-reply>
Now let us go through simple examples that will illustrate how to utilize the GET2 callbacks for the leaf and leaf-list nodes.
Leaf and leaf-list examples
Consider this simplified example, that represents GET callback for the “leaf” and “leaf-list” nodes. Refer to the “Glossary”
section for the complete YANG data model:
Version 16.10-10
Page 84
YumaPro API Quick Start Guide
...
leaf get2-leaf {
config false;
type int32;
}
leaf-list get2-leaf-list {
config false;
type int32;
}
...
NOTE:
Note that only leaf or leaf-list nodes that are top level nodes required a callback. Any other simple type nodes DOES NOT
required callback and can be generated within their parent callback invocation.
The following C API code exemplifies a simple get callback function for the data model, which returns the leaf value when
it is requested.
/* start value for leaf /get2-leaf */
static int32 leaf_val = 42;
/********************************************************************
* FUNCTION get2_leaflist_type
*
* Path: /get2-leaf
*
* INPUTS:
*
scb == session control block making the request
*
msg == incoming XML message header
*
get2cb == get2 control block for this callback request
*
* OUTPUTS:
*
return_valQ is filled with malloced val_value_t nodes
*
* RETURNS:
*
status:
*
NO_ERR if executed OK and found OK
*
ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
get2_leaflist_type (ses_cb_t *scb,
xml_msg_hdr_t *msg,
getcb_get2_t *get2cb)
{
(void)scb;
(void)msg;
/* check the callback mode type */
getcb_mode_t cbmode = GETCB_GET2_CBMODE(get2cb);
Version 16.10-10
Page 85
YumaPro API Quick Start Guide
switch (cbmode) {
case GETCB_GET_VALUE:
break;
case GETCB_GETNEXT_VALUE:
return ERR_NCX_NO_INSTANCE;
default:
return SET_ERROR(ERR_INTERNAL_VAL);
}
/* get the real value from the system here.
* But in this example just increment the value by 1
*/
int32 retval = leaf_val++;
/* return the leaf in the static return_val struct */
obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
val_value_t *return_val = GETCB_GET2_RETURN_VAL(get2cb);
val_init_from_template(return_val, obj);
VAL_INT32(return_val) = retval;
return NO_ERR;
}
/* get2_leaflist_type */
Refer to the section named “GET2 Access Functions” for other available functions to access GET2 control block.
In the above example, the code generates the value based on the static value that is incremented after each retrieval. An
agent can generate the value based on the real device statistical data or based on the system's statistical data, etc.
NOTE:
In the example, the code demonstrates how the value can be set using val.h macros, VAL_INT32. However, it is more
reliable and recommended to use val.h APIs as agt_make_leaf2, or specific API for a specific YANG type, such as
agt_make_int_leaf2. Or the most generic val_make_simval_obj() that creates and set a val_value_t as a simple type from
an object template instead of individual fields.
To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state
information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<get2-leaf xmlns="http://yumaworks.com/ns/interfaces" />
</filter>
</get>
</rpc>
The server may reply with:
Version 16.10-10
Page 86
YumaPro API Quick Start Guide
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get2-leaf xmlns="http://yumaworks.com/ns/interfaces">42</get2-leaf>
</rpc-reply>
Now let us go through simple examples that will illustrate how to utilize the GET2 callbacks for the leaf-list nodes.
The following C API code exemplifies a simple GET2 callback function for the same data model, which returns the leaf-list
values when requested.
/********************************************************************
* FUNCTION get2_leaflist_type
*
* Path: /get2-leaf-list
*
* INPUTS:
*
scb == session control block making the request
*
msg == incoming XML message header
*
get2cb == get2 control block for this callback request
*
* OUTPUTS:
*
return_valQ is filled with malloced val_value_t nodes
*
* RETURNS:
*
status:
*
NO_ERR if executed OK and found OK
*
ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
get2_leaflist_type (ses_cb_t *scb,
xml_msg_hdr_t *msg,
getcb_get2_t *get2cb)
{
(void)scb;
(void)msg;
status_t res = NO_ERR;
/* check the callback mode type */
getcb_mode_t cbmode = GETCB_GET2_CBMODE(get2cb);
switch (cbmode) {
case GETCB_GET_VALUE:
break;
case GETCB_GETNEXT_VALUE:
return ERR_NCX_NO_INSTANCE;
default:
return SET_ERROR(ERR_INTERNAL_VAL);
}
obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
/* always return all the leaf-list instances
* 3 entries [53, 67, 92]
Version 16.10-10
Page 87
YumaPro API Quick Start Guide
*/
val_value_t *retval =
val_make_simval_obj(obj, (const xmlChar *)"53", &res);
if (retval) {
getcb_add_return_val(get2cb, retval);
}
if (res == NO_ERR) {
retval = val_make_simval_obj(obj, (const xmlChar *)"67", &res);
if (retval) {
getcb_add_return_val(get2cb, retval);
}
}
if (res == NO_ERR) {
retval = val_make_simval_obj(obj, (const xmlChar *)"92", &res);
if (retval) {
getcb_add_return_val(get2cb, retval);
}
}
return res;
}
/* get2_leaflist_type */
In the above example, the code generates the value based on the static value that does not fluctuate after each retrieval. An
agent can generate the value based on the real device statistical data or based on the system's statistical data, etc.
To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state
information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">53</get2-leaf-list>
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">67</get2-leaf-list>
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">92</get2-leaf-list>
</rpc-reply>
Version 16.10-10
Page 88
YumaPro API Quick Start Guide
5.3.3
GET2 Access Macros
GET 2 mode provides extended retrieval functionality and thus more ways to manage specific retrieval.
There are GET 2 mode specific high-level Transaction access and management utilities in ncx/getcb.h. These functions
access the lower-level functions to provide simpler functions for common transaction management tasks.
The following table highlights the access macros available:
Macro
Description
GETCB_GET2_TXID_STR(G)
Access transaction ID as a string value.
GETCB_GET2_OBJ(G)
Access object template of the value. Pointer to obj_template_t
for the object being retrieved.
GETCB_GET2_TESTFN(G)
Access test function that is used for filtering. Test filter function
for checking nodes before processing.
GETCB_GET2_KEYQ(G)
Access KeyQ. Queue of val_value_t structures representing the
key leafs for the request. These are keys for the ancestor-or-self
lists for this object. Fixed keys are identified using the
VAL_IS_FIXED_VALUE(keyval) macro. The get2 callback
must only return entries matching the provided key values. If no
value is provided for a key then the first value is requested
GETCB_GET2_FIRST_KEY(G)
Access macro to return the first val_value_t in the queue.
GETCB_GET2_NEXT_KEY(G,C)
Access macro to return the next val_value_t in the queue.
GETCB_GET2_MATCHQ(G)
Access MatchQ. Queue of val_value_t structs representing
non-key leaf content-match nodes. These are always simple
child nodes of the object identified by 'obj'. The content-match
test is optional to implement by the get2 callback.
GETCB_GET2_FIRST_MATCH(G)
Access macro to return the first val_value_t in the queue.
GETCB_GET2_NEXT_MATCH(G,C)
Access macro to return the next val_value_t in the queue.
GETCB_GET2_SELECTQ(G)
Access SelectQ. Q of malloced getcb_get2_select_t; 1 entry
for each child select node of the object in this get2cb these
select nodes only apply to the current object if the 'select_only'
flag is set then this queue is used.
GETCB_GET2_FIRST_SELECT(G)
Access macro to return the first getcb_get2_select_t in the
queue.
GETCB_GET2_NEXT_SELECT(G,C)
Access macro to return the next getcb_get2_select_t in the
queue.
GETCB_GET2_RETURN_KEYQ(G)
Access Return_keyQ. Queue of val_value_t structures
representing the key leafs of the returned value.
GETCB_GET2_FIRST_RETURN_KEY(G)
Access macro to return the first val_value_t in the queue.
GETCB_GET2_NEXT_RETURN_KEY(G,C)
Access macro to return the next val_value_t in the queue.
GETCB_GET2_RETURN_VALQ(G)
Access Return_valQ. Queue of val_value_t structures
Version 16.10-10
Page 89
YumaPro API Quick Start Guide
Macro
Description
representing the non-key terminal nodes of the returned value.
Only the first value is used at this time, except for leaf-list
nodes. Should be empty if the 'keys-only' flag is set in the
request.
GETCB_GET2_FIRST_RETURN_VAL(G)
Access macro to return the first val_value_t in the queue.
GETCB_GET2_NEXT_RETURN_VAL(G,C)
Access macro to return the next val_value_t in the queue.
GETCB_GET2_GETBULKQ(G)
Access getbulkQ. Queue of the getcb_get2_getbulk_t
structures. Set by the callback function if this is a list callback
and multiple entries are returned; this is a global queue that
applies to the entire response.
GETCB_GET2_FIRST_GETBULK(G)
Access macro to return the first getcb_get2_getbulk_t in the
queue.
GETCB_GET2_NEXT_GETBULK(G,C)
Access macro to return the next getcb_get2_getbulk_t in the
queue.
GETCB_GET2_CBMODE(G)
Access value for the callback mode.
GETCB_GET2_MAX_ENTRIES(G)
Access value for the maximum number of values to return (for
getnext mode).
GETCB_GET2_MAX_LEVELS(G)
Access value for the maximum depth of subtrees parameter to
determine if the maximum level has been reached.
GETCB_GET2_OPER_DATA(G)
Access operational data flag value. True if config=false data
should be returned.
GETCB_GET2_CONFIG_DATA(G)
Access configuration data flag value. True if config=true data
should be returned.
GETCB_GET2_EXPAND_VAREXPR(G)
Access variable expressions flag value. True is variable
expressions should be expanded; false to print the variable
instead.
GETCB_GET2_KEYS_ONLY(G)
Access keys only flag value. True if only list keys should be
returned; false for all terminal nodes.
GETCB_GET2_WITH_DEFAULTS(G)
Access with-defaults flag value. True if default nodes should be
returned; false if they are not requested.
GETCB_GET2_API_MODE(G)
Access api mode enumeration value. The consumer walker
callback API mode.
GETCB_GET2_MORE_DATA(G)
Access more data flag value. Set to true if there are more
instances of this object to retrieve; false otherwise.
GETCB_GET2_ACTIVE_CASE_MODNAME(G)
Access active case string value. Name of the module for the
module namespace of the active case; set to NULL for the same
module name as the choice. Ignored unless the 'obj' type is
'choice'.
GETCB_GET2_ACTIVE_CASE(G)
Access name of the active case value. Ignored unless the 'obj'
type is 'choice'.
GETCB_GET2_MATCH_TEST_DONE(G)
Access match test done flag value. Set to true if the get2
callback performed the request content match test(s); false if not
or no match tests requested.
Version 16.10-10
Page 90
YumaPro API Quick Start Guide
Macro
Description
GETCB_GET2_RETURN_VAL(G)
Access in-line val_value_t to return a single leaf. Can be used
instead of the return_valQ to avoid mallocing a val_value_t.
GETCB_GET2_RETURN_VAL_SET(G)
Macro to test if the value is set.
5.3.4
GET2 Access Functions
GET 2 mode provides extended retrieval functionality and thus more ways to manage specific retrieval.
There are GET 2 mode specific high-level Transaction access and management utilities in ncx/getcb.h. These functions
access the lower-level functions to provide simpler functions for common transaction management tasks.
The following table highlights the functions available:
API
Description
getcb_add_return_val()
Add a return val to a get2cb return_valQ.
getcb_find_return_val()
Find a return val in the get2cb return_valQ.
getcb_find_return_val2()
Find a return val in the get2cb return_valQ . Use NSID, NAME instead of
object pointer.
getcb_find_next_return_val()
Find the next matching return val in the get2cb return_valQ.
getcb_find_key()
Find an input keyval in the get2cb keyQ.
getcb_find_key_str()
Find an input keyval in the get2cb keyQ . USE string parameters.
getcb_find_match()
Find an input keyval in the get2cb matchQ.
getcb_add_key()
Add a keyval to a get2cb keyQ.
getcb_add_match()
Add a match node to a get2cb matchQ.
getcb_add_select()
Add a select node to a get2cb matchQ.
getcb_add_return_key()
Add a return keyval to a get2cb return_keyQ .
getcb_find_return_key()
Find a return keyval in the get2cb return_keyQ.
getcb_find_return_key2()
Find a return keyval in the get2cb return_keyQ . Use NSID, NAME instead
of object pointer.
Version 16.10-10
Page 91
YumaPro API Quick Start Guide
API
Description
getcb_move_return_keys()
Move the return keys to the keyQ replacing the nodes in the keyQ if
already there.
getcb_undo_move_return_keys()
Move back the return keys from the keyQ.
getcb_clean_return_data()
Clean the return data in the return_val and return_valQ.
getcb_handle_acmtest()
Check the access control and testfn callback for a node that would have it
skipped because the write_full_check_val function is skipped.
getcb_new_keyval()
Create a new GET2 keyval holder.
getcb_new_keyval2()
Create a new Get2 keyval holder using val backptr.
getcb_free_keyval()
Free a GET2 keyval.
getcb_clean_keyvalQ()
Free all the Get2 keyvals from a dlq_hdr.
getcb_dump_get2cb()
Print the interesting fields in a get2cb.
getcb_need_get2()
check if the node has a get2 callback or in a choice/case subtree that has
get2 callback.
getcb_need_get2_ex()
check if the node has a get2 callback or in a choice/case subtree that has
get2 callback. Also, returns address of return choice count and top choice
object template.
getcb_first_requested_child()
Check if the specified object has any terminal nodes that need to be returned
for a get2 request. Return the first one .
The get2cb will be examined:
- boolean flags
- select-node queue
Any content-match nodes will be returned but no filtering of these objects
will be done .
Any key leaf nodes will be skipped; Processing of key leafs is done first and
is never skipped
getcb_next_requested_child()
Check if the specified object has any more terminal nodes that need to be
returned for a get2 request. Return the next one
getcb_set_active_case()
Set the active case to the specified object.
Version 16.10-10
Page 92
YumaPro API Quick Start Guide
6 In-Transaction Callbacks
In-transaction capabilities are being added to the netconfd-pro server API to allow an application to update the
configuration data and perform additional actions while the transaction is in progress. This can be a vital solution to
implement many services in switch/router management operations.
If an application needs to write more data in data store in the same transaction, there is a mechanism implemented in the
form of API function called Set Hook.
If an application needs to perform additional validations, security check or any other manipulations with datastore in the
same transaction, there are mechanisms implemented in the form of API functions called Transaction Hook, Start
Transaction, and Complete Transaction.
The hook function has access to the transaction, so it can modify other objects in the transaction as necessary. These
additional edits are added to the end of the edit request and If the sil-priority is set then the order SIL callbacks are invoked
will be based on the numeric priority value instead.
The Set Hook is a way for the application to manipulate with datastore in the same transaction. For example, whenever the
specific node is created, the hook registered on that node gets called and the specific list can be updated with additional list
entry or entries.
Version 16.10-10
Page 93
YumaPro API Quick Start Guide
If an application needs to perform additional actions and manipulations with datastore in the same transaction during
<commit> operation, there are mechanisms implemented in the form of API functions called Validate Complete, Apply
Complete, Commit Complete callbacks. These three callbacks are called Commit Completeness callbacks.
Manipulation with configuration data are only allowed for Set Hook callbacks. If Transaction Hook, Start/Complete
Transaction, or 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 in the log with log-level
equals debug2.
The netconfd-pro server uses 3 phases database editing model with In-Transaction APIs, as defined in section 6 of the
YumaPro Developer Manual.
•
Validate phase
•
Apply phase
•
Commit or rollback phase
However, this model is enhanced with new callbacks and validation mechanisms to support all the In-Transactions APIs.
NOTE:
In-Transaction callbacks are NOT part of the yangdump-sdk code generation. After the make_sil_dir_pro script is run, InTransaction callbacks will not be generated. All the In-Transaction callbacks are optional and merely intend to provide more
efficient way to manipulate with the database and give an access to the database at the specific phase, transaction, and edit.
Version 16.10-10
Page 94
YumaPro API Quick Start Guide
6.1 Callback Invocation Order
This section gives a quick overview for the callbacks invocation order.
Assume we register all the possible callbacks and the edit is complex as possible. The possible invocation order may look as
follows, processing on the candidate datastore (before the commit):
1.
Transaction Start
2.
Set Order Hook
3.
Set Hook and then Set Order Hook for the added edit if any
4.
SIL Callbacks (only during Validation Phase)
5.
Transaction Complete
The possible invocation order during the commit operation may look as follows, processing on the running datastore (after
the commit):
1.
Transaction Start
2.
Set Order Hook
3.
SIL Callbacks (Validate Phase)
4.
Validate Complete
5.
SIL Callbacks (Apply Phase)
6.
Apply Complete
7.
SIL Callbacks (Commit Phase)
8.
Transaction Hook
9.
Commit Complete
10. Transaction Complete
The commit operation is not available if the default target is set to running. Thus, the possible invocation order during the
edit operation may look as follows, processing on the running datastore only (the same invocation order will be during
Load, Reload and Restart operations):
1.
Transaction Start
2.
Set Order Hook
3.
Set Hook and then Set Order Hook for the added edit if any
4.
SIL Callbacks (Validate Phase)
5.
SIL Callbacks (Apply Phase)
6.
SIL Callbacks (Commit Phase)
7.
Transaction Hook
8.
Transaction Complete
The possible invocation order during the validate operation may look as follows, processing on the running datastore only:
Version 16.10-10
Page 95
YumaPro API Quick Start Guide
1.
Transaction Start
2.
Set Order Hook
3.
SIL Callbacks (Validate Phase)
4.
Transaction Complete
Set Hook and Transaction Hook callbacks will not be invoke during explicit validate command and in case of the
following netconfd-pro parameters are set to TRUE:
--sil-validate-candidate (only relevant to Set Hook)
--sil-skip-load
Version 16.10-10
Page 96
YumaPro API Quick Start Guide
6.2 Set Hook Callback
A Set Hook is a function that is invoked within the transaction when an object is modified. When the netconfd-pro server
has been configured to provide a candidate configuration, Set Hook code will be invoked when changes are done to the
<candidate> configuration if the –target=candidate parameter is used. If --target=running then the set hook will be
invoked at the start of the transaction on the running datastore. This callback will be invoked before a EDIT1 or EDIT2
callback for the same object. Refer to netconfd development manual for more information.
Set Hook can be invoked during the Load; However, it is not allowed to add new edits. So, callback are treated as
Transaction Hook style hooks and can perform only validation tasks and cannot edit datastore. Any add_edit() during
Load will be ignored, it is not an error just skip the call and go back with NO_ERR status.
The following function template definition is used for Set Hook callback functions:
/* Typedef of the agt_hook_cb callback */
typedef status_t
(*agt_cb_hook_t)(ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 Set Hook callback per object
•
File: agt_cb.h
•
Template: agt_cb_hook_t
◦
Inputs:
▪
scb == session control block making the request
▪
msg == incoming rpc_msg_t in progress
▪
txcb == transaction control block in progress
▪
editop == edit operation enumeration for the node being edited
▪
newval == value holding the proposed changes to apply to the current config, depending on the editop
value.
▪
curval == current values from the <running> or <candidate> configuration, if any. Could be NULL for
create and other operations.
◦
Outputs: none
◦
Returns: status_t:
Status of the callback function execution
•
Register: agt_cb_hook_register
•
Unregister: agt_cb_hook_unregister
Version 16.10-10
Page 97
YumaPro API Quick Start Guide
6.2.1
Set Hook Callback Initialization and Cleanup
A Set Hook database edit callback function is hooked into the server with the agt_cb_hook_register function, described
below.
The agt_cb_hook_register function is used to declare the callback as a specific style callback. 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.
extern status_t
agt_cb_hook_register (const xmlChar *defpath,
agt_hook_fmt_t format,
agt_hook_type_t type,
agt_cb_hook_t cbfn);
agt_cb_hook_register
Parameter
Description
defpath
Absolute path expression string indicating which node
the callback function is for.
format
different hook formats dictates specific hook
functionality
type
different hook types dictates hook invocation point
cbfn
address of callback function to use
NOTE:
Set Hook callbacks will not be invoke during explicit validate command and in case of the following netconfd-pro
parameters are set to TRUE: --sil-validate-candidate or --sil-skip-load.
Consider the same data model as defined in the “Glossary” section. Initialization function with the registration of Set Hook
callback type may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
Version 16.10-10
Page 98
YumaPro API Quick Start Guide
...
/* Register an object specific Set Hook callback */
res =
agt_cb_hook_register((const xmlChar *)”/if:trigger”,
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback_edit);
...
}
The format parameter is important when you want to specify how Set Hook callbacks will be invoked. There are two
options available for this parameter:
•
AGT_HOOKFMT_NODE: Set the type of the callback to this value if You want to make sure that the callback
will be invoked only when you modify the node that registered the callback but not any of its children.
•
AGT_HOOKFMT_SUBTREE: If the format is set to this value, the callback will be invoked if you edit children
as well.
The type parameter is important when you want to set the type of callback. There are two options available for this
parameter, either Set Hook or Transaction Hook callback:
•
AGT_HOOK_TYPE_SETHOOK: Set the type of the callback to this value if You want to register Set Hook
callback.
•
AGT_HOOK_TYPE_TRANSACTION: Set the type of the callback to this value if You want to register
Transaction Hook callback.
The following example code illustrates how hook-based callbacks can be cleaned up. The callbacks cleanup is getting done
during module Cleanup Phase.
extern void
agt_cb_hook_unregister (const xmlChar *defpath);
agt_cb_hook_unregister
Parameter
defpath
Description
Absolute path expression string indicating which node
the callback function is for.
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
agt_cb_hook_unregister((const xmlChar *)"/if:trigger");
Version 16.10-10
Page 99
YumaPro API Quick Start Guide
...
}
NOTE:
It is possible to register different callbacks for the same object, for instance, there can be Set Hook and Transaction Hook
callbacks registered for the same object, as well as there can be EDIT or GET2 callbacks for the same object. However, in
order to cleanup callbacks, unregister them, you need to run different cleanup functions. The EDIT and GET2 callbacks
have their own unregister functions and hook-based callbacks have their own unregister function.
6.2.2
Set Hook Callback Function Examples
The following sections illustrates how to utilize the Set Hook callback in examples.
NOTE:
Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Hook or other In-Transaction
callbacks call add_edit() the operation will be ignored. It will not return an error, just ignore add_edit calls.
Let us go through simple examples that will illustrate how to utilize the Set Hook callbacks for the specific purposes.
First we need a YANG module. Consider this simplified, but functional, example. Refer to the “Glossary” section for the
complete YANG data model:
container interfaces {
list interface {
key "name";
leaf name {
type string;
}
leaf speed {
type enumeration {
enum 10m;
enum 100m;
enum auto;
}
}
leaf hook-node {
type uint32;
}
}
leaf status {
type string;
}
}
Version 16.10-10
Page 100
YumaPro API Quick Start Guide
...
leaf trigger {
type string;
}
Add a new node
As specified in the previous section, Set Hook was registered for the “trigger” leaf element. Thus, whenever the node
/if:trigger is edited, the callback function will be called and additional specific data can be updated or populated with
desired values.
In this example, we will generate an extra “interface” list entry with key value equal to “vlan1” when a “trigger” node is
getting edited with a specific value equal to “add-edit”.
The callback function may look as follows:
/********************************************************************
* FUNCTION sethook_callback_edit
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set Hook:
*
trigger: edit /if:trigger
*
add_edit:
*
add nodes: populate 1 list entry with name=vlan1
*
*
path: /if:interfaces/if:interface[if:name=vlan1]
*
*********************************************************************/
static status_t
sethook_callback_edit (ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval)
{
log_debug("\nEnter SET Hook callback");
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
const xmlChar *defpath =
(const xmlChar *)"/if:interfaces";
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
case OP_EDITOP_CREATE:
/* add a new edit if the "/if:trigger" value is "add-edit" */
if (newval &&
!xml_strcmp(VAL_STR(newval), (const xmlChar *)"add-edit")) {
Version 16.10-10
Page 101
YumaPro API Quick Start Guide
/* find object template of the desired node */
obj_template_t *targobj =
ncx_match_any_object_ex(EXAMPLE_MODNAME,
(const xmlChar *)"interfaces",
FALSE,
NCX_MATCH_FIRST,
FALSE,
&res);
if (!targobj) {
return ERR_NCX_INVALID_VALUE;
}
/* create edit_value container value */
val_value_t *editval = val_new_value();
if (editval == NULL) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(editval, targobj);
/* malloce and construct list value, for more
* examples refer to libhooks-test/src/hooks-test.c library
*/
uint32 key = 11;
val_value_t *list_value = create_list_entry(VAL_OBJ(editval),
key,
&res);
if (!list_value) {
val_free_value(editval);
return res;
}
/* add a new list entry */
res = val_child_add(list_value, editval);
if (res != NO_ERR) {
val_free_value(list_value);
} else {
/* add a new edit, MERGE on defpath with 1 new list entry */
res = agt_val_add_edit(scb,
msg,
txcb,
defpath,
editval,
OP_EDITOP_MERGE);
}
/* clean up the editval */
val_free_value(editval);
}
break;
case OP_EDITOP_DELETE:
break;
default:
res = SET_ERROR(ERR_INTERNAL_VAL);
}
if (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_CONTENT,
res,
NULL,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
Version 16.10-10
Page 102
YumaPro API Quick Start Guide
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
return res;
}
/* sethook_callback_edit */
As a result, whenever some north bound agent edit the /if:trigger with a specific value, the callback is invoked and
additionally creates a new interface /if:interfaces/if:interface[if:name=value]
The function that is responsible for the list entry creation may look as follows:
/********************************************************************
* FUNCTION create_list_entry
*
* Make a list entry based on the key
*
* INPUTS:
*
parentobj == parent object template to use
*
keyname == value of the key
*
res = return status
*
* RETURNS:
*
val_value_t of listval entry if no error
*
else NULL
*
*********************************************************************/
static val_value_t *
create_list_entry(obj_template_t *parentobj,
const xmlChar *keyname,
status_t *res)
{
/* get the obj template for the list obj */
obj_template_t *list_obj =
obj_find_child(parentobj,
EXAMPLE_MODNAME,
(const xmlChar *)"interface");
if (!list_obj) {
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
/* add all the /container/list nodes */
val_value_t *list_value = val_new_value();
if (!list_value) {
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
val_init_from_template(list_value, list_obj);
/* make key leaf entry */
val_value_t *child =
agt_make_leaf(list_obj,
(const xmlChar *)"name",
keyname,
Version 16.10-10
Page 103
YumaPro API Quick Start Guide
res);
if (!child) {
val_free_value(list_value);
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
*res = val_child_add(child, list_value);
if (*res != NO_ERR) {
val_free_value(list_value);
return NULL;
}
/* make some extra leaf entries */
child =
agt_make_uint_leaf(list_obj,
(const xmlChar *)"hook-node",
(uint32)1000,
res);
if (!child) {
val_free_value(list_value);
*res = ERR_NCX_INVALID_VALUE;
return NULL;
}
*res = val_child_add(child, list_value);
if (*res != NO_ERR) {
val_free_value(child);
val_free_value(list_value);
return NULL;
}
/* generate the internal index Q chain */
*res = val_gen_index_chain(list_obj, list_value);
if (*res != NO_ERR) {
log_error("\nError: could not generate index chain (%s)",
get_error_string(*res));
val_free_value(list_value);
return NULL;
}
return list_value;
} /* create_list_entry */
NOTE:
When you are constructing a list node make sure that a key node is getting created first, and then all other children are
getting added. Also, make sure that you use val_gen_index_chain API function after you construct the list, so it will be
normalized and all required fields will be set accordingly.
To ensure that the data added by add_edit was successfully generated an application can retrieve running configuration and
device state information. The server should reply with:
Version 16.10-10
Page 104
YumaPro API Quick Start Guide
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces xmlns="http://yumaworks.com/ns/interfaces">
</interface>
<name>vlan1</name>
<observed-speed>1000</observed-speed>
</interface>
</interfaces>
<trigger>add-edit</trigger>
</rpc-reply>
Delete node
As specified earlier, Set Hook was registered for the “trigger” leaf element. Thus, whenever the node /if:trigger is edited,
the callback function will be called and additional specific data can be updated or populated with desired values.
In this example, we will delete just added /if:interface container with all its children when a “trigger” node is getting
deleted.
The callback function may look as follows:
/********************************************************************
* FUNCTION sethook_callback_edit
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set Hook:
*
trigger: delete /trigger
*
add_edit:
*
delete nodes: delete the whole container
*
*
path: /if:interfaces
*
*********************************************************************/
static status_t
sethook_callback_edit (ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval)
{
log_debug("\nEnter SET Hook callback");
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
const xmlChar *defpath =
(const xmlChar *)"/if:interfaces";
switch (editop) {
case OP_EDITOP_LOAD:
break;
Version 16.10-10
Page 105
YumaPro API Quick Start Guide
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
case OP_EDITOP_CREATE:
break;
case OP_EDITOP_DELETE:
/* delete the interfaces container if the curval of 'trigger' node is
* "delete-all"
*/
if (curval &&
!xml_strcmp(VAL_STR(curval), (const xmlChar *)"delete-all")) {
res = agt_val_add_edit(scb,
msg,
txcb,
defpath,
NULL, // editval
editop);
}
break;
default:
res = SET_ERROR(ERR_INTERNAL_VAL);
}
if (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_CONTENT,
res,
NULL,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
return res;
}
/* sethook_callback_edit */
As a result, the server will deletes the whole /if:interfaces container if “trigger” value is set to 'delete-all' and some north
bound agent attempts to DELETE /trigger node.
To ensure that the data deleted by add_edit was successfully deleted an application can retrieve running configuration and
device state information. The server should reply with:
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<data></data>
</rpc-reply>
Update node
Consider the same data model as for the previous examples; however, now we will register callback for the top level
/if:interfaces container element. In this example, we will populate an extra leafs when the container node is created.
Version 16.10-10
Page 106
YumaPro API Quick Start Guide
Register a Set Hook to the /if:interfaces container.
/* Register an object specific Set Hook callback */
res = agt_cb_hook_register((const xmlChar*)"/if:interface",
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_SETHOOK,
sethook_callback_edit);
Now, when user wants to create the container, the callback function will be called and container can be updated with desired
additional values.
NOTE:
Callback format should be AGT_HOOKFMT_NODE in order to invoke this callback only for a new container edit. If the
format is AGT_HOOKFMT_SUBTREE, the callback will be invoked if you edit children as well. In this case newval
will not represent container value and cannot not be used to construct a new updated edit value.
The callback function may look as follows:
/********************************************************************
* FUNCTION sethook_callback_edit
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Set Hook:
*
trigger: create /interfaces
*
create container and populate extra nodes
*
*
add_edit effect: populate extra nodes within the container
*
when /interfaces is created
*
*********************************************************************/
static status_t
sethook_callback_edit (ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval)
{
log_debug("\nEnter SET Hook callback");
status_t res = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
/* defpath specified target root */
const xmlChar *defpath =
(const xmlChar *)"/if:interfaces";
Version 16.10-10
Page 107
YumaPro API Quick Start Guide
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
break;
case OP_EDITOP_CREATE:
/* populate a new container with several leafs */
if (newval) {
/* use newval value to write a new edits */
val_value_t *editval =
val_clone_config_data(newval, &res);
if (editval == NULL) {
return ERR_INTERNAL_MEM;
}
/* malloced and construct some leaf values */
val_value_t *child =
agt_make_leaf(VAL_OBJ(editval),
(const xmlChar *)"status",
(const xmlChar *)"extra-leaf",
&res);
if (!child) {
val_free_value(editval);
return ERR_NCX_INVALID_VALUE;
}
res = val_child_add(child, editval);
if (res != NO_ERR) {
val_free_value(child);
} else {
/* add a new edit on defpath and populate new entries */
res = agt_val_add_edit(scb,
msg,
txcb,
defpath,
editval,
OP_EDITOP_MERGE);
}
/* clean up the editval */
val_free_value(editval);
}
break;
case OP_EDITOP_DELETE:
break;
default:
res = SET_ERROR(ERR_INTERNAL_VAL);
}
if (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_CONTENT,
res,
NULL,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
Version 16.10-10
Page 108
YumaPro API Quick Start Guide
return res;
}
/* sethook_callback_edit */
As a result, whenever some north bound agent creates an /interfaces container the callback is invoked and additionally
creates a new leafs within this container.
Version 16.10-10
Page 109
YumaPro API Quick Start Guide
6.3 Transaction Hook Callback
A Transaction Hook is a function that is invoked within the transaction when an object is modified. The Transaction
Hook is similar to the Set Hook except this callback is invoked just before the data is committed to the running datastore.
This callback will be invoked after EDIT1 or EDIT2 callbacks for the same object. Note that the Transaction hook is not
allowed to change anything, It is not allowed to call agt_val_add_edit, or to alter the edits or the datastore in any way.
Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Hook callbacks calls
agt_val_add_edit, the operation will be ignored. Do not return error just ignore add_edit calls.
The following function template definition is used for Transaction Hook callback functions:
/* Typedef of the agt_hook_cb callback */
typedef status_t
(*agt_cb_hook_t)(ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 Transaction Hook callback per object
•
File: agt_cb.h
•
Template: agt_cb_hook _t
◦
Inputs:
▪
scb == session control block making the request
▪
msg == incoming rpc_msg_t in progress
▪
txcb == transaction control block in progress
▪
editop == edit operation enumeration for the node being edited
▪
newval == value holding the proposed changes to apply to the current config, depending on the editop
value.
▪
curval == current values from the <running> or <candidate> configuration, if any. Could be NULL for
create and other operations.
◦
Outputs: none
◦
Returns: status_t:
Status of the callback function execution
•
Register: agt_cb_hook_register
•
Unregister: agt_cb_hook_unregister
Version 16.10-10
Page 110
YumaPro API Quick Start Guide
6.3.1
Transaction Hook Callback Initialization and Cleanup
A Transaction Hook database edit callback function is hooked into the server with the agt_cb_hook_register function,
described below.
The agt_cb_hook_register function is used to declare the callback as a specific style callback. 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.
NOTE:
The Transaction Hook callbacks will not be invoke during explicit validate command and in case --sil-skip-load
netconfd-pro parameters is set to TRUE
Consider the same data model as defined in the “Glossary” section. Initialization function with the registration of
Transaction Hook callback type may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
/* Register an object specific Transaction Hook callback */
res =
agt_cb_hook_register((const xmlChar *)”/if:trigger”,
AGT_HOOKFMT_NODE,
AGT_HOOK_TYPE_TRANSACTION,
sethook_callback_edit);
...
}
agt_cb_hook_register
Parameter
Description
defpath
Absolute path expression string indicating which node
the callback function is for.
format
different hook formats dictates specific hook
functionality
type
different hook types dictates hook invocation point
Version 16.10-10
Page 111
YumaPro API Quick Start Guide
Parameter
cbfn
6.3.2
Description
address of callback function to use
Transaction Hook Callback Function Example
The following sections illustrates how to utilize the Transaction Hook callback in examples.
NOTE:
Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Hook or other In-Transaction
callbacks call add_edit() the operation will be ignored. It will not return an error, just ignore add_edit calls.
Consider the same data model as for the previous examples; however, now we will register Transaction Hook callback for
the top level /if:interfaces container element. In this example, we will validate the container node.
Now, whenever the 'interfaces' node is edited, the callback function will be called and perform specific validation actions.
The callback function may look as follows:
/********************************************************************
* FUNCTION transhook_callback
*
* Callback function for server object handler
* Used to provide a callback for a specific named object
*
* Transaction-Hook:
*
trigger: DELETE /interface
*
effect:
*
- if testval node exist the CREATE operation will be denied
*
- if testval is ‘deny-delete’, the operation will be denied
*
********************************************************************/
static status_t
transhook_callback (ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval)
{
log_debug("\nEnter Transaction-Hook callback");
status_t res = NO_ERR;
status_t res2 = NO_ERR;
val_value_t *errorval = (curval) ? curval : newval;
const xmlChar *defpath =
(const xmlChar *)"/if:interfaces/if:status";
/* find a test node and validate its value */
val_value_t *testval =
agt_val_get_data(txcb->cfg_id,
defpath,
Version 16.10-10
Page 112
YumaPro API Quick Start Guide
&res2);
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
case OP_EDITOP_REPLACE:
case OP_EDITOP_CREATE:
/* deny an edit, if the test exist */
if (testval) {
res = ERR_NCX_ACCESS_DENIED;
}
break;
case OP_EDITOP_DELETE:
/* deny an edit, if the test value set to “deny-delete” */
if (testval &&
!xml_strcmp(VAL_STR(testval), (const xmlChar *)"deny-delete") {
res = ERR_NCX_ACCESS_DENIED;
} else {
res2 = NO_ERR;
}
break;
default:
res = SET_ERROR(ERR_INTERNAL_VAL);
}
if (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_CONTENT,
res,
NULL,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
return res;
}
/* hooks_transhook_edit */
NOTE:
Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Hook callbacks call add_edit() the
operation will be ignored. It will not return an error, just ignore add_edit calls.
So whenever some north bound agent edits an /if:interfaces node the callback is invoked and additionally validates the
/if:interfaces/if:status node. Based on this validation, the operation can be denied.
Version 16.10-10
Page 113
YumaPro API Quick Start Guide
6.4 Transaction Start Callback
The Transaction Start function is the user/system callback that is invoked before any changes to the candidate database
will be committed.
The following function template definition is used for Transaction Start callback functions:
/* Typedef of the trans_start callback */
typedef status_t
(*agt_cb_trans_start_t)(agt_cfg_transaction_t *txcb);
Callback Template
•
Type: User Callback
•
Max Callbacks: 4 (set by AGT_CB_MAX_TRANSACTION_CBFN)
•
File: agt_cb.h
•
Template: agt_cb_trans_start_t
◦
Inputs:
▪
txcb == transaction control block in progress
•
Outputs: none
•
Returns: status_t:
Status of the callback function execution. If an error is returned the transaction will be terminated.
•
Register: agt_cb_trans_start_register
•
Unregister: agt_cb_trans_start_unregister
6.4.1
Transaction Start Callback Initialization and Cleanup
The Transaction Start callback function is hooked into the server with the agt_cb_trans_start_register function,
described below.
extern status_t
agt_cb_trans_start_register (agt_cb_trans_start_t cbfn);
The register function is used to declare a specific callback function for Transaction Start callbacks. 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 registration of the Transaction Start callback type may look as follows:
Version 16.10-10
Page 114
YumaPro API Quick Start Guide
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
/* Register a Transaction Start callback. */
res = agt_cb_trans_start_register(transaction_start);
if (res != NO_ERR) {
return res;
}
...
}
The following example code illustrates how the callback can be cleaned up. The callback cleanup is getting done during
module Cleanup Phase.
extern void
agt_cb_trans_start_unregister (agt_cb_trans_start_t cbfn);
extern void
agt_cb_trans_complete_unregister (agt_cb_trans_complete_t cbfn);
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
/* Unregister a Transaction Start callback */
agt_cb_trans_start_unregister(transaction_start);
/* Unregister a Transaction Complete callback */
agt_cb_trans_complete_unregister(transaction_complete);
...
}
Version 16.10-10
Page 115
YumaPro API Quick Start Guide
6.4.2
Transaction Start Callback Function Examples
The following sections illustrates how to utilize the Transaction Start callback in examples.
In this example, the Transaction Start callback function applies multiple actions prior the transaction is actually started.
The purpose of this function is to provide more validation options and more flexible and easier development. This callback
function may deny the start of the transaction if some specific check is unmet, in this example, if the node “trigger” is
present in the datastore then the server will deny the transaction. In addition, in this example, the callback function updates
a comment string of this transaction, it may send a custom notifications, or write a system or other log entries.
The following example code illustrates how the Transaction Start callback may look like.
/********************************************************************
* FUNCTION transaction_start
*
* Start Transaction callback
* The Start Transaction function is the user/system
* callback that is invoked before any changes to the
* candidate database will be committed.
*
* INPUTS:
*
txcb == transaction control block in progress
*
* RETURNS:
*
status
********************************************************************/
static status_t
transaction_start (agt_cfg_transaction_t *txcb)
{
log_debug("\nEnter transaction_start callback");
status_t res = NO_ERR;
/* deny the start of transaction if some specific check is unmet. E.g:
* if there is a specific node present in the datastore
*/
val_value_t *val =
agt_val_get_data(txcb->cfg_id,
(const xmlChar *)"/if:trigger",
&res);
if (val) {
res = ERR_NCX_ACCESS_DENIED;
}
/* update a comment string of this transaction */
txcb->comment = (const xmlChar *)"special transaction";
/* send custom notifications */
/* write a sys, audit, vendor specific, etc log entries */
return res;
}
/* transaction_start */
Version 16.10-10
Page 116
YumaPro API Quick Start Guide
NOTE:
Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Start or Complete callbacks call
add_edit() the operation will be ignored. It will not return an error, just ignore add_edit calls.
Version 16.10-10
Page 117
YumaPro API Quick Start Guide
6.5 Transaction Complete Callback
The Transaction Complete function is the user/system callback that is invoked after the transactions has been processed.
The following function template definition is used for Transaction Complete callback functions:
/* Typedef of the trans_complete callback */
typedef void
(*agt_cb_trans_complete_t)(agt_cfg_transaction_t *txcb)
Callback Template
•
Type: User Callback
•
Max Callbacks: 4 (set by AGT_CB_MAX_TRANSACTION_CBFN)
•
File: agt_cb.h
•
Template: agt_cb_trans_complete_t
◦
Inputs:
▪
txcb == transaction control block to check
•
Outputs: none
•
Returns: none
Status of the callback function execution
•
Register: agt_cb_trans_complete_register
•
Unregister: agt_cb_trans_complete_unregister
6.5.1
Transaction Complete Callback Initialization and Cleanup
The Transaction Complete callback function is hooked into the server with the agt_cb_trans_complete_register
function, described below.
The register function is used to declare a specific callback function for Transaction Complete callbacks. 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 registration of the Transaction Complete callback type may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
Version 16.10-10
Page 118
YumaPro API Quick Start Guide
interfaces_init (void)
{
...
/* Register a Transaction Complete callback. */
res = agt_cb_trans_complete_register(transaction_complete);
if (res != NO_ERR) {
return res;
}
...
}
6.5.2
Transaction Complete Callback Function Example
The following sections illustrates how to utilize the Transaction Complete callback in examples.
In this example, the Transaction Complete callback function applies multiple actions prior the transaction is actually
completed. The purpose of this function is to provide more validation options and more flexible and easier development.
This callback function may send a custom notifications, or write a system or other log entries.
The following example code illustrates how the Transaction Complete callback may look like.
/********************************************************************
* FUNCTION transaction_complete
*
* Complete Transaction callback
* The Transaction Complete function is the
* user/system callback that is invoked after
* the transactions has been processed.
*
* INPUTS:
*
txcb == transaction control block in progress
*
* RETURNS:
*
status
********************************************************************/
static status_t
transaction_complete (agt_cfg_transaction_t *txcb)
{
log_debug("\nEnter transaction_complete callback");
/* send custom notifications */
/* write a sys, audit, vendor specific, etc log entries */
return res;
}
/* transaction_complete */
Version 16.10-10
Page 119
YumaPro API Quick Start Guide
6.6 Set Order Hook Callback
Callback function for server list object handler used to provide a callback for a specific list instance object .
The Set Order Hook is invoked in document order for each edited instance of the specified object. The callback returns the
desired secondary SIL priority for the specific list instance. This callback is invoked once per edited instance and after any
Set Hook is called for the object and instance.
The following function template definition is used for Set Order Hook callback functions:
/* Typedef of the callback */
typedef uint8
(*agt_cb_order_hook_t) (agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval,
status_t *res);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 per object
•
File: agt_cb.h
•
Template: agt_cb_order_hook_t
◦
Inputs:
▪
txcb == transaction control block to check
▪
editop == edit operation enumeration for the node being edited
▪
newval == value holding the proposed changes to apply to the current config, depending on the editop
value.
▪
curval == current container values from the <running> or <candidate> configuration, if any. Could be
NULL for create and other operations.
▪
res == address of return status
•
Outputs: status of callback; status == error will cause the transaction to be terminated and rollback started
•
Returns: the secondary SIL priority to assign to the object instance
•
Register: agt_cb_order_hook_register
•
Unregister: agt_cb_order_hook_unregister
6.6.1
Set Order Hook Callback Initialization and Cleanup
The Set Order Hook callback function is hooked into the server with the agt_cb_order_hook_register function, described
below.
Version 16.10-10
Page 120
YumaPro API Quick Start Guide
extern status_t
agt_cb_order_hook_register (const xmlChar *defpath,
agt_cb_order_hook_t cbfn);
The register function is used to declare a specific callback function for the Set Order Hook callback. 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 registration of the Set Order Hook callback may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
/* Register an object specific Set-Order-Hook callback function */
res = agt_cb_order_hook_register((const xmlChar*)"/if:interface/if:interface",
set_order_hook);
if (res != NO_ERR) {
return res;
}
...
}
Now, whenever the /if:interface/if:interface list is getting modified, the callback function will be invoked and desired
secondary SIL priority for the list instance can be specified. As a result, the server will apply list of edits based on this
priority, the lower priority number is set to the instance the earlier the server will apply the edit.
The following example code illustrates how the callbacks can be cleaned up. The callbacks cleanup is getting done during
module Cleanup Phase.
extern void
agt_cb_order_hook_unregister (const xmlChar *defpath);
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
Version 16.10-10
Page 121
YumaPro API Quick Start Guide
...
/* Unregister an object specific Set-Order-Hook callback function */
agt_cb_order_hook_unregister((const xmlChar*)"/if:interface/if:interface");
...
}
6.6.2
Set Order Hook Callback Function Examples
The following sections illustrates how to utilize the Set Order Hook callback in examples.
The Set Order Hook callback function is the user/system callback that is used to provide an access for a specific list
instance object and modify its secondary SIL priority.
NOTE:
The Set Order Hook callback function can be registered only for “list” YANG objects.
In this example, the Set Order Hook callback function applies priority to a list edit based on a key value. The purpose of
this function is to prioritize edits and give more flexible and easier development.
The following example code illustrates how the Set Order Hook callback may look like:
/********************************************************************
* FUNCTION set_order_hook
*
* Callback function is
* used to provide a callback for a specific named object
*
* This callback is invoked once per instance before any
* Set Hook is called for the object and instance.
*
* INPUTS:
*
txcb == transaction control block in progress
*
editop == edit operation enumeration for the node being edited
*
newval == container object holding the proposed changes to
*
apply to the current config, depending on
*
the editop value. Will not be NULL.
*
curval == current container values from the <running>
*
or <candidate> configuration, if any. Could be NULL
*
for create and other operations.
*
res == address of return status
* OUTPUTS:
*
*res == status of callback; status == error will cause the
*
transaction to be terminated and rollback started
* RETURNS:
*
the secondary SIL priority to assign to the object instance
********************************************************************/
static uint8
Version 16.10-10
Page 122
YumaPro API Quick Start Guide
set_order_hook (agt_cfg_transaction_t *txcb,
op_editop_t editop,
val_value_t *newval,
val_value_t *curval,
status_t *res)
{
(void)txcb;
(void)editop;
(void)curval;
uint8 retprior = 0;
/* Set the priority only if the operation is not delete and if
* a new value has keys, that will be compared later
*/
if (newval && val_has_index(newval)) {
/* Get the first index entry, if any for this value node */
const val_index_t *c1 = val_get_first_index(newval);
if (!c1) {
return ERR_INTERNAL_VAL;
}
if (!xml_strcmp(VAL_STR(c1->val), (const xmlChar *)"vlan1")) {
log_debug("\n Setting Priority to 140 for index:%s \n",
VAL_STR(c1->val));
retprior = 100;
} else if (!xml_strcmp(VAL_STR(c1->val), (const xmlChar *)"ethernet1/1/10")) {
log_debug("\n Setting Priority to 160 for index:%s \n",
VAL_STR(c1->val));
retprior = 150;
} else {
log_debug("\n Setting Priority to 130 for index:%s \n",
VAL_STR(c1->val));
retprior = 200;
}
}
return retprior;
}
/* set_order_hook */
So, whenever some north bound agent edits an /if:interfaces/if:interface node the callback is invoked and additionally
assigns desired priority to this edit. Note, that in the example above, the priority will be assigned only if there is a new
value. It will be assigned only if the edit operation is not “delete” or “remove” because during the edits for those operations
the server does not allocate a new value. Use a current value for any comparisons or validations during “delete” or
“remove” edit operations.
To exemplify, if the north bound agent is creating 3 interfaces with different key values at the same time using the following
<edit-config> RPC:
<edit-config>
<target>
<candidate/>
Version 16.10-10
Page 123
YumaPro API Quick Start Guide
</target>
<default-operation>merge</default-operation>
<test-option>set</test-option>
<config>
<interfaces>
<interface>
<name>ethernet1/1/1</name>
</interface>
<interface>
<name>vlan1</name>
</interface>
<interface>
<name>ethernet1/1/10</name>
</interface>
</interfaces>
</config>
</edit-config>
By the regular server logic, the server would validate/apply/commit these interfaces in order they are specified in the edit.
However, using the Set Order Hook the server now will apply these “interfaces” based on their priority assigned in the
callback function. The first list instance that will be processed by the server will be the “interface” with the key value set to
“vlan1” since it has the highest priority - “100”.
So, the edits order would look as follows, from the server's logging:
***** start commit phase on candidate for session 5, transaction 1234358 *****
Start full commit of transaction 1234358: 3 edits on candidate config
edit-transaction 1234358: on session 5 by --@::1
message-id: -trace-id: -datastore: candidate
operation: create
target: /if:interfaces/if:interface[if:name="vlan1"]
comment: none
edit-transaction 1234358: on session 5 by --@::1
message-id: -trace-id: -datastore: candidate
operation: create
target: /if:interfaces/if:interface[if:name="ethernet1/1/10"]
comment: none
edit-transaction 1234358: on session 5 by --@::1
message-id: -trace-id: -datastore: candidate
operation: create
target: /if:interfaces/if:interface[if:name="ethernet1/1/1"]
comment: none
The Set Order Hook callback can be extremely useful in cooperation with the Set Hook callback or by itself. The power to
prioritize all the edits in the transaction provides wide spectrum of utilization. The fact that the Set Order Hook is getting
invoked before SIL or any other callbacks for the same object provides possibility to customize the transaction as desired.
Version 16.10-10
Page 124
YumaPro API Quick Start Guide
Version 16.10-10
Page 125
YumaPro API Quick Start Guide
6.7 Add Edit API
The Add Edit API is used to add an edit to the transaction in progress.
Manipulation with datastore are only allowed for Set Hook callbacks. If Transaction Hook or Start/Complete
Transaction callbacks call add_edit() the operation will be ignored. Do not return error just ignore add_edit calls.
/* FUNCTION agt_val_add_edit */
status_t
agt_val_add_edit(ses_cb_t *scb,
rpc_msg_t *msg,
agt_cfg_transaction_t *txcb,
const xmlChar *defpath,
val_value_t *edit_value,
op_editop_t editop);
API Template
•
Type: Server Utility Function
•
File: agt_val.h
•
Template: agt_val_add_edit
◦
Inputs:
▪
scb == session control block making the request
▪
msg == incoming rpc_msg_t in progress
▪
txcb == transaction control block in progress
▪
defpath == XPath expression specifying the data instance to add
▪
edit_value == string containing the XML or JSON value to use in the edit. Can be NULL if editop is
OP_EDITOP_DELETE or OP_EDITOP_REMOVE
▪
editop == edit operation enumeration for the edit being added
◦
Outputs: none
◦
Returns: status_t:
Status of the operation;
▪
ERR_INTERNAL_MEM can be returned if a malloc fails
▪
ERR_NCX_DEF_NOT_FOUND can be returned if the XPath expression contains unknown or ambiguous
object names
Version 16.10-10
Page 126
YumaPro API Quick Start Guide
6.8 Get Data API
The Get Data API is used to retrieve data nodes from the server.
If the XPath expression matches multiple nodes, then only the first instance is returned. The exact instance is
implementation-dependent if the data is “ordered-by system” and the –system-sorted parameter is set to false.
/* FUNCTION agt_val_get_data */
const val_value_t *
agt_val_get_data (ncx_cfg_t cfgid,
const xmlChar *defpath,
status_t *retres);
API Template
•
Type: Server Utility Function
•
File: agt_val.h
•
Template: agt_val_get_data
◦
◦
Inputs:
▪
cfgid == datastore enumeration to use (candidate or running)
▪
defpath == XPath expression specifying the data instance to add
▪
retres == address of return status
Outputs:
▪
◦
*retres == set to the return status
Returns: const *val_value_t
▪
Read only pointer to the requested data
▪
Data cannot be saved and used later, can only be used immediately after the call to agt_val_get_data
▪
ERR_INTERNAL_MEM can be returned if a malloc fails
▪
ERR_NCX_DEF_NOT_FOUND can be returned if the XPath expression contains unknown or ambiguous
object names
Version 16.10-10
Page 127
YumaPro API Quick Start Guide
6.9 Validate Complete Callback
The Validate Complete function is the user/system callback that is invoked after the Validate Phase has been processed
during the <commit> operation.
If the Validate Complete 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 Validate Complete callback is object independent and module independent which means you don't have to link it to
the 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>).
NOTE:
The <commit> operation is not available if the default target is set to <running>, if the server is run with active
:writable:running capability.
The callback is only called after commit operation for the specific phase has finished not after the validate for the specific
module or edit is done.
The following function template definition is used for Validate Complete 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);
Callback Template
•
Type: User Callback
•
Max Callbacks: 4 (set by AGT_CB_MAX_TRANSACTION_CBFN)
•
File: agt_cb.h
•
Template: agt_cb_validate_complete
◦
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
•
Outputs: none
•
Returns: Status of the callback function execution
Version 16.10-10
Page 128
YumaPro API Quick Start Guide
•
Register: agt_cb_validate_complete_register
•
Unregister: agt_cb_validate_complete_unregister
6.9.1
Validate Complete Callback Initialization and Cleanup
The Validate Complete callback is object independent and module independent which means you don't have to link it to
the specific object as it is done for EDIT or GET callbacks.
NOTE:
The Validate Complete callback is only called after the commit operation for the Validate phase has finished not after the
commit for the specific module or edit is done.
The Validate Complete callback function is hooked into the server with the agt_cb_validate_complete_register function,
as described below.
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 registration of the
Validate Complete callback type 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;
}
...
}
Version 16.10-10
Page 129
YumaPro API Quick Start Guide
agt_cb_validate_complete_register
Parameter
cbfn
Description
The callback function address. This function will be
used for the callback invocation
Now, whenever the Validation 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, all the registered callbacks will be
called one after another right after the Validation Phase during the commit operation is done.
The following example code illustrates how the Validate Complete 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)
{
...
/* Unregister Validate Complete callback */
agt_cb_validate_complete_unregister(validate_compl_callback);
...
}
agt_cb_validate_complete_unregister
Parameter
cbfn
Version 16.10-10
Description
The callback function address. This function will be
cleaned up
Page 130
YumaPro API Quick Start Guide
6.10 Apply Complete Callback
The Apply Complete function is the user/system callback that is invoked after the Apply Phase has been processed during
the <commit> operation.
If Apply Complete 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 Apply Complete callback is object independent and module independent which means you don't have to link it to the
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
apply phase during <commit>).
The callback is only called after commit operation for the specific phase has finished not after the apply for the specific
module or edit is done.
The following function template definition is used for Apply Complete callback functions:
/* 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);
Callback Template
•
Type: User Callback
•
Max Callbacks: 4 (set by AGT_CB_MAX_TRANSACTION_CBFN)
•
File: agt_cb.h
•
Template: agt_cb_apply_complete
◦
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
•
Outputs: none
•
Returns: Status of the callback function execution
•
Register: agt_cb_apply_complete_register
•
Unregister: agt_cb_apply_complete_unregister
6.10.1
Apply Complete Callback Initialization and Cleanup
Version 16.10-10
Page 131
YumaPro API Quick Start Guide
The Apply Complete callback is object independent and module independent which means you don't have to link it to the
specific object as it is done for EDIT or GET callbacks.
NOTE:
The Apply Complete is only called after the commit operation for the Apply Phase has finished not after the commit for
the specific module or edit is done.
The Apply Complete callback function is hooked into the server with the agt_cb_apply_complete_register function, as
described below.
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 registration of the
Apply Complete callback type may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
/* register apply complete callback */
res =
agt_cb_apply_complete_register(apply_compl_callback);
if (res != NO_ERR) {
return res;
}
...
}
Now, whenever the Apply 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, all the registered callbacks will be
called one after another right after the Apply Phase during the commit operation is done.
agt_cb_apply_complete_register
Parameter
cbfn
Version 16.10-10
Description
The callback function address. This function will be
used for the callback invocation
Page 132
YumaPro API Quick Start Guide
The following example code illustrates how the Apply callbacks can be cleaned up. The callback cleanup is getting done
during module Cleanup Phase.
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
/* Unregister Apply Complete callback */
agt_cb_apply_complete_unregister(apply_compl_callback);
...
}
agt_cb_apply_complete_unregister
Parameter
cbfn
Version 16.10-10
Description
The callback function address. This function will be
cleaned up
Page 133
YumaPro API Quick Start Guide
6.11 Commit Complete Callback
The Commit Complete callback is a user/system callback that is invoked when the <commit> operation completes without
errors or the internal <replay-config> operation completes without errors.
If a Commit Complete callback fails the status of the failing operation is returned immediately and no further Commit
Complete callbacks are called.
The callback is only called after commit operation for the specific phase has finished not after the commit for the specific
module or edit is done.
The following function template definition is used for Apply Complete callback functions:
/* Typedef of the callback */
typedef status_t
(*agt_commit_complete_cb_t)(agt_commit_type_t commit_type);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 per SIL
•
File: agt/agt_commit_complete.h
•
Template: agt_commit_complete_cb_t
◦
Inputs:
▪
commit_type == The type of commit that was just completed:
AGT_COMMIT_TYPE_NORMAL: <commit> operation was completed
AGT_COMMIT_TYPE_REPLAY: <replay-commit> operation was completed
•
Outputs: none
•
Returns: status_t:
Status of the callback function execution
•
Register: agt_commit_complete_register
•
Unregister: agt_commit_complete_unregister
6.11.1
Commit Complete Callback Initialization and Cleanup
The Commit Complete callback is object independent and module independent which means you don't have to link it to
the specific object as it is done for EDIT or GET callbacks.
NOTE:
Version 16.10-10
Page 134
YumaPro API Quick Start Guide
The Commit Complete callback is only called after the commit operation for the Commit Phase has finished not after the
commit for the specific module or edit is done.
The specific Commit Complete callback function is hooked into the server with the agt_cb_commit_complete_register
function, as described below.
extern status_t
agt_commit_complete_register (const xmlChar *modname,
agt_commit_complete_cb_t cb);
agt_commit_complete_register
Parameter
Description
modname
Module name of the module registering the callback
cbfn
The callback function address. This function will be
used for the callback invocation
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 registration of the Commit Complete callback may look as described below. 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. It will be called right after the commit operation is done.
Initialization function with the registration of the specific Commit Complete callback type may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
/* register commit complete callback */
res =
agt_commit_complete_register(EXAMPLE_MODNAME,
Version 16.10-10
Page 135
YumaPro API Quick Start Guide
commit_compl_callback);
if (res != NO_ERR) {
return res;
}
...
}
Now, whenever the Commit Phase is done for the commit operation, the callback function will be called.
The following example code illustrates how the Commit Complete callback can be cleaned up. The callback cleanup is
getting done during module Cleanup Phase.
extern void
agt_commit_complete_unregister (const xmlChar *modname);
agt_commit_complete_unregister
Parameter
modname
Description
Module name of the module unregistering the callback
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
/* Unregister Apply Complete callback */
agt_commit_complete_unregister(EXAMPLE_MODNAME);
...
}
The unregister function for the Commit Complete callback is slightly different then for other Phases. Note, that it is
passing module name to the unregistering function.
Version 16.10-10
Page 136
YumaPro API Quick Start Guide
6.11.2
Commit Completeness Callback Function Example
Let us go through simple examples that will illustrate how to utilize Commit Completeness callbacks.
Consideration:
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.
Solution:
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.
The server has no control of identifying what module and when the specific edit is 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. E.g: edit on the first module's node, then edit on the second module's node, then again edit on node from the first
module, etc. Then it would be almost impossible to tell when the Validation Complete callback should be triggered for a
particular module. Only if the server would sort edits based on module which will significantly affect performance or limit
number of edits at the same time to one, which is not possible.
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, that described in the section named “Main Transaction Callback Examples”.
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
********************************************************************/
Version 16.10-10
Page 137
YumaPro API Quick Start Guide
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)
Version 16.10-10
Page 138
YumaPro API Quick Start Guide
{
(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 */
Version 16.10-10
Page 139
YumaPro API Quick Start Guide
7 System Callbacks
The callback functions described in this section are invoked as part of a normal server operation.
They can be registered from the yp-system library module or a SIL module. Some internal functions are documented as
well. These are needed by the system and multiple callbacks are not supported.
Refer to the file /usr/share/yumapro/src/libsystem/src/example-system.c for examples of the callbacks in this section.
API
Description
Candidate Reload callback
cfg_reload_candidate_cb_t: There can be one callback registered to be
invoked each time the candidate datastore is reloaded from the running
datastore, such as the <discard-changes> operation.
Module Load callback
ncx_load_cbfn_t: There can be multiple callbacks registered to be invoked
when a module is loaded into the server. A transaction to set module
defaults will also be generated, in addition to this callback.
Module Unload callback
ncx_unload_cbfn_t: There can be multiple callbacks registered to be
invoked when a module is unloaded from the server. A transaction to
remove module configuration data will also be generated, in addition to this
callback.
Configuration Replay Start or Finish
callback
agt_replay_fn_t: There can be one callback registered that is called at the
start and again at the end of a configuration replay transaction.
NV-Load callback
agt_nvload_fn_t: There can be one callback registered that is invoked when
the server needs to retrieve the configuration contents from non-volatile
storage.
NV-Save callback
agt_nvsave_fn_t: There can be one callback registered that is invoked when
when some config needs to be saved to Non-Volatile storage.
Config Replay callback
agt_replay_fn_t: The callback is invoked when a configuration replay is
started or finished.
Periodic Timer Service
agt_timer_fn_t: The timer callback function is expected to do a short
amount of work, and not block the running process. The function returns
zero for a normal exit, and -1 if there was an error and the timer should be
destroyed.
Version 16.10-10
Page 140
YumaPro API Quick Start Guide
7.1 Candidate Reload Callback
The Candidate Reload callback is an internal system callback that is used to clear some cached validation data when the
running configuration is loaded into to candidate datastore.
The following function template definition is used for Candidate Reload callback functions:
/* Typedef of the callback */
typedef void (*cfg_reload_candidate_cb_t) (void);
Callback Template
•
Type: Internal use only
•
Max Callbacks: 1
•
File: ncx/cfg.h
•
Template: cfg_reload_candidate_cb_t
◦
Inputs: none
◦
Outputs: none
◦
Returns: none
•
Register: cfg_register_reload_candidate_cb
•
Unregister: none
7.1.1
Candidate Reload Callback Example
There can be one callback registered to be invoked each time the candidate datastore is reloaded from the running datastore,
such as the <discard-changes> operation. Or anytime the candidate is filled in from startup, running, or in line.
If the server is run with active :writable-running capability the callback will not be available since the candidate
configuration datastore is not available when the capability is active.
The cfg_register_reload_candidate_cb function is used to declare the callback. The registration is done during the
Initialization Phase 2 after the running configuration has been loaded from the startup file.
extern void
cfg_register_reload_candidate_cb (cfg_reload_candidate_cb_t cbfn);
Initialization function with the Candidate Reload callback registration may look as follows:
/********************************************************************
* FUNCTION do_init2
*
Version 16.10-10
Page 141
YumaPro API Quick Start Guide
* Call the Post Running Config Set Initialization Functions
*
*********************************************************************/
static status_t
do_init2 (boolean startup_loaded)
{
...
/* initialize the reload_candidate callback function */
cfg_register_reload_candidate_cb(agt_val_clean_cached_results);
...
}
Version 16.10-10
Page 142
YumaPro API Quick Start Guide
7.2 Module Load Callback
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);
Callback Template
•
Type: Internal and User Callback
•
Max Callbacks: NCX_MAX_LOAD_CALLBACKS (8: ncx/ncxconst.h)
•
File: ncx/ncx.h
•
Template: ncx_load_cbfn_t (ncx/ncxtypes.h)
◦
Inputs:
▪
mod == the internal module structure for the module that was just loaded
◦
Outputs: none
◦
Returns: none
•
Register: ncx_set_load_callback
•
Unregister: ncx_clear_load_callback
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.
7.2.1
Module Load Callback Initialization and Cleanup
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:
Version 16.10-10
Page 143
YumaPro API Quick Start Guide
/********************************************************************
* 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);
...
}
7.2.2
Module Load Callback Function Example
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;
/********************************************************************
Version 16.10-10
Page 144
YumaPro API Quick Start Guide
* 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”.
Version 16.10-10
Page 145
YumaPro API Quick Start Guide
/********************************************************************
* 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 */
Version 16.10-10
Page 146
YumaPro API Quick Start Guide
*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.
Version 16.10-10
Page 147
YumaPro API Quick Start Guide
7.3 Module Unload Callback
The Module Unload callback is a user/system callback that is invoked when a YANG module is unloaded from the server.
This callback is only invoked for main modules, not submodules.
The following function template definition is used for Module Unload callback functions:
/* Typedef of the callback */
typedef void (*ncx_unload_cbfn_t) (ncx_module_t *mod);
Callback Template
•
Type: Internal and User Callback
•
Max Callbacks: NCX_MAX_LOAD_CALLBACKS (8: ncx/ncxconst.h)
•
File: ncx/ncx.h
•
Template: ncx_unload_cbfn_t (ncx/ncxtypes.h)
◦
Inputs:
▪
mod == the internal module structure for the module that is being unloaded
◦
Outputs: none
◦
Returns: none
•
Register: ncx_set_unload_callback
•
Unregister: ncx_clear_unload_callback
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 unloaded module, not for alteration of any of its fields.
7.3.1
Module Unload Callback Initialization and Cleanup
The ncx_set_unload_callback function is used to declare the Module Unload callback. The registration can be done
during the Initialization Phase 2 after the running configuration has been loaded from the startup file.
Initialization function with the Module Unload callback registration may look as follows:
/********************************************************************
* FUNCTION interfaces_init2
*
* initialize the server instrumentation library.
* Initialization Phase 2
Version 16.10-10
Page 148
YumaPro API Quick Start Guide
*
*********************************************************************/
static status_t
interfaces_init2 (void)
{
...
/* register unload module callback */
res = ncx_set_unload_callback(remove_module_callback);
if (res != NO_ERR) {
return res;
}
...
}
The following example code illustrates how the Module Unload 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_unload_callback(remove_module_callback);
...
}
7.3.2
Module Unload Callback Function Example
Let us go through simple examples that will illustrate how to utilize the Module Unload callbacks. In this example, the
callback code will find a val_value tree, based on the information from MOD pointer, find a tree and remove it from the
static data list. In this example we are still using the same “ietf-yang-library” YANG data model.
The following example code illustrates how the Module Unload callback may look like.
/********************************************************************
* FUNCTION remove_module_callback
*
* Run an instrumentation-defined function
* for a 'module-unload' event
*
* INPUTS:
*
mod == module that was added to the registry
*
********************************************************************/
static void remove_module_callback (ncx_module_t *mod)
Version 16.10-10
Page 149
YumaPro API Quick Start Guide
{
remove_module(mod);
} /* add_module_callback */
In the remove_module() function we are removing a val_value tree based on the ncx_module_t information provided by
the Module Load event. Any time a new module is unloaded from the server, this callback function will be called with
MOD pointer, that contains the complete information about just unloaded module. Based on this information, this callback
function will find and remove the static data associated with this module.
/********************************************************************
* FUNCTION remove_module
*
* Remove a module entry from the static modules list
*
* INPUTS:
*
mod == module to remove
*
********************************************************************/
static void
remove_module (ncx_module_t *mod)
{
xmlns_id_t nsid = val_get_nsid(mymodules_val);
val_value_t *module = val_get_first_child(mymodules_val);
for (; module; module = val_get_next_child(module)) {
val_value_t *checkval =
val_find_child_fast(module, nsid, (const xmlChar *)"name");
/* check the module name, if it match, remove the module from the list */
if (checkval) {
if (xml_strcmp(VAL_STR(checkval), ncx_get_modname(mod))) {
continue;
}
} else {
continue; // error
}
/* check the module revision, if it match, remove the module from the list */
if (ncx_get_modversion(mod)) {
checkval =
val_find_child_fast(module, nsid, (const xmlChar *)"revision");
if (checkval) {
if (xml_strcmp(VAL_STR(checkval), ncx_get_modversion(mod))) {
continue;
}
} else {
continue; // error!
}
}
val_remove_child(module);
val_free_value(module);
return;
}
Version 16.10-10
Page 150
YumaPro API Quick Start Guide
}
/* remove_module */
The following functions may be used to obtain required information from the unloaded 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 mymodules_val pointer represents a parent of the module list.
The following code illustrates how to loop through the mymodules_val parent in order to retrieve all the children –
modules.
...
val_value_t *module = val_get_first_child(mymodules_val);
for (; module; module = val_get_next_child(module)) {
}
...
Once we got a module list entry pointer, we can try to match its child (module name) with a module name of the unloaded
module in order to proceed to the removal. The following code demonstrates how to validate the module name:
...
val_value_t *checkval =
val_find_child_fast(module, nsid, (const xmlChar *)"name");
/* check the module name, if it match, remove the module from the list */
if (checkval) {
if (xml_strcmp(VAL_STR(checkval), ncx_get_modname(mod))) {
continue;
}
} else {
continue; // error
}
...
Version 16.10-10
Page 151
YumaPro API Quick Start Guide
If the module name matched already, we will try to match and the revision date. The revision statement is optional for the
module, so we need to make sure that the module actually has the revision statement prior the revision match test. The
following code demonstrates how to validate the module revision date:
...
/* check the module revision, if it match, remove the module from the list */
if (ncx_get_modversion(mod)) {
checkval =
val_find_child_fast(module, nsid, (const xmlChar *)"revision");
if (checkval) {
if (xml_strcmp(VAL_STR(checkval), ncx_get_modversion(mod))) {
continue;
}
} else {
continue; // error!
}
}
...
After we verify that the unloaded module is in the list, we will remove it from the list:
...
val_remove_child(module);
val_free_value(module);
...
The val_remove_child function removes the child value node from its parent value node, from its parent mymodules_val.
The it is freed with val_free_value.
Version 16.10-10
Page 152
YumaPro API Quick Start Guide
7.4 NV-Load Callback
The NV-Load callback function is a user callback that is invoked when the running configuration needs to be read from
non-volatile storage. A configuration filespec is passed to this callback that contains the configuration that needs to be
saved to non-volatile storage.
The following function template definition is used for NV-Load callback functions:
/* Typedef of the callback */
typedef status_t
(*agt_nvload_fn_t) (ncx_display_mode_t *encoding,
xmlChar **filespec);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1
•
File: agt/agt.h
•
Template: agt_nvload_fn_t
◦
◦
Inputs:
▪
encoding == the address of the return encoding used in the config file: (values other than XML are TBD)
▪
filespec == the address of the return filespec of the configuration loaded from non-volatile storage
Outputs:
▪
Encoding == set to the encoding used in the config file: (values other than XML are TBD)
▪
Filespec == set to a malloced string containing the filespec of the configuration loaded from non-volatile
storage. This must be freed by the called if non-NULL.
Set to NULL if an error returned
Set to NULL if NO_ERR returned to indicate that the factory config is being loaded
•
Returns: status_t:
Status of the callback function execution
•
Register: agt_register_local_nv_handler
•
Unregister: none
7.4.1
NV-Load Callback Initialization and Cleanup
The agt_register_local_nv_handler function is used to declare the NV-Load callback. The registration can be done during
the Initialization Phase 2, after the running configuration has been loaded from the startup file.
Version 16.10-10
Page 153
YumaPro API Quick Start Guide
extern status_t
agt_register_local_nv_handler (agt_nvload_fn_t load_fn,
agt_nvsave_fn_t store_fn);
Initialization function with the NV-Load callback registration may look as follows:
#define EXAMPLECONFIG_SPEC (const xmlChar *)"/tmp/example-config.xml"
/********************************************************************
* FUNCTION interfaces_init2
*
* initialize the server instrumentation library.
* Initialization Phase 2
*
*********************************************************************/
static status_t
interfaces_init2 (void)
{
...
/* register NV-storage handler to load/save config
* uncomment following to enable
*/
res = agt_register_local_nv_handler(nvload_callback,
nvsave_callback);
if (res != NO_ERR) {
return res;
}
...
}
There is no cleanup function for this callback. The cleanup will be done automatically by the server.
7.4.2
NV-Load Callback Function Example
The following example code illustrates how the NV-Load callback may look like.
/********************************************************************
* FUNCTION nvload_callback
*
* this callback is invoked when some config needs to be read
* from non-volatile storage
*
* INPUTS:
*
encoding == address of encoding for the config
*
filespec == address of filespec containing the config that was loaded
*
* OUTPUTS:
*
*encoding == set to the enum for the encoding used in the config
Version 16.10-10
Page 154
YumaPro API Quick Start Guide
*
*filespec == malloced filespec containing the config that was loaded
*
* RETURNS:
*
status; error indicates NV-load failed somehow
*
If return NO_ERR and *filespec == NULL then use the factory config
*
*********************************************************************/
static status_t
nvload_callback (ncx_display_mode_t *encoding,
xmlChar **filespec)
{
log_debug("\nEnter nvload_callback ");
*filespec = NULL;
*encoding = NCX_DISPLAY_MODE_XML;
status_t res = NO_ERR;
if (ncxmod_test_filespec(EXAMPLE_CONFIG_SPEC)) {
/* file exists so copy the filespec */
*filespec = xml_strdup(EXAMPLE_CONFIG_SPEC);
if (*filespec == NULL) {
res = ERR_INTERNAL_MEM;
}
}
return res;
}
/* nvload_callback */
Now, when some configuration needs to be read from Non-Volatile storage this callback function will try to find a file
specification and copy it into the buffer.
NOTE:
If the callback returns NO_ERR and *filespec is empty then use the factory configuration.
NOTE:
Only XML encoding is supported. The server will save the configuration only in XML.
Version 16.10-10
Page 155
YumaPro API Quick Start Guide
7.5 NV-Save Callback
The NV-Save callback function is a user callback that is invoked when the running configuration needs to be saved to nonvolatile storage. A configuration filespec is passed to this callback that contains the configuration that needs to be saved to
non-volatile storage.
The following function template definition is used for NV-Save callback functions:
/* Typedef of the callback */
typedef status_t
(*agt_nvsave_fn_t) (ncx_display_mode_t encoding,
const xmlChar *filespec);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1
•
File: agt/agt.h
•
Template: agt_nvload_fn_t
◦
Inputs:
▪
Encoding == the encoding used in the config file: (values other than XML are TBD)
▪
Filespec == the filespec of the configuration to save to non-volatile storage
◦
Outputs: none
◦
Returns: status_t:
Status of the callback function execution
•
Register: agt_register_local_nv_handler
•
Unregister: none
7.5.1
NV-Save Callback Initialization and Cleanup
The agt_register_local_nv_handler function is used to declare the NV-Save callback. The registration can be done during
the Initialization Phase 2, after the running configuration has been loaded from the startup file.
Initialization function with the NV- Save callback registration may look as follows:
#define EXAMPLECONFIG_SPEC (const xmlChar *)"/tmp/example-config.xml"
/********************************************************************
* FUNCTION interfaces_init2
*
* initialize the server instrumentation library.
* Initialization Phase 2
Version 16.10-10
Page 156
YumaPro API Quick Start Guide
*
*********************************************************************/
static status_t
interfaces_init2 (void)
{
...
/* register NV-storage handler to load/save config
* uncomment following to enable
*/
res = agt_register_local_nv_handler(nvload_callback,
nvsave_callback);
if (res != NO_ERR) {
return res;
}
...
}
There is no cleanup function for this callback. The cleanup will be done automatically by the server.
7.5.2
NV-Save Callback Function Example
The following example code illustrates how the NV-Save callback may look like.
/********************************************************************
* FUNCTION nvsave_callback
*
* this callback is invoked when some config needs to be saved
* to non-volatile storage
*
* INPUTS:
*
encoding == encoding format for the config (xml only allowed value)
*
filespec == filespec containing the config to save
* RETURNS:
*
status; error indicates NV-save failed somehow
*
*********************************************************************/
static status_t
example_nvsave (ncx_display_mode_t encoding,
const xmlChar *filespec)
{
status_t res = NO_ERR;
if (filespec == NULL || *filespec == 0) {
res = ERR_NCX_INVALID_VALUE;
} else if (encoding != NCX_DISPLAY_MODE_XML) {
res = ERR_NCX_INVALID_VALUE;
} else {
res = ncxmod_copy_text_file(filespec, EXAMPLE_CONFIG_SPEC);
}
}
return res;
/* nvsave_callback */
Version 16.10-10
Page 157
YumaPro API Quick Start Guide
Now, when some configuration needs to be saved to Non-Volatile storage this callback function will try to save specified
configuration into specific Non-Volatile storage.
Version 16.10-10
Page 158
YumaPro API Quick Start Guide
7.6 Config Replay Callback
The Config Replay callback is a user callback that is invoked when the replay configuration procedure is started and
finished. A configuration replay is triggered by a user timer callback function.
When the timer function detects that the back-end system process needs to be reloaded with the current configuration, it
needs to call the agt_request_replay() function to cause the server to start the configuration replay procedure.
The following function template definition is used for Config Replay callback functions:
/* Typedef of the callback */
typedef void (*agt_replay_fn_t) (boolean is_start);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1
•
File: agt/agt.h
•
Template: agt_replay_fn_t
◦
Inputs:
▪
is_start == TRUE: the configuration replay procedure is starting. FALSE: the configuration replay
procedure is finished
◦
Outputs: none
◦
Returns: none
•
Register: agt_register_replay_callback
•
Unregister: none
Version 16.10-10
Page 159
YumaPro API Quick Start Guide
7.7 Periodic Timer Service
Some SIL code may need to be called at periodic intervals to check system status, update counters, and/or perhaps send
notifications.
The timer callback function is expected to do a short amount of work, and not block the running process. The function
returns zero for a normal exit, and -1 if there was an error and the timer should be destroyed.
A SIL timer can be set up as a periodic timer or a one-time event.
The timer interval (in seconds) and the SIL timer callback function are provided when the timer is created. A timer can also
be restarted if it is running, and the time interval can be changed as well.
The following table highlights the SIL timer access functions in agt/agt_timer.h:
SIL Timer Access Functions
Function
Description
agt_timer_create
Create a SIL timer.
agt_timer_restart
Restart a SIL timer
agt_timer_delete
Delete a SIL timer
The following function template definition is used for Timer callback functions:
/* Typedef of the callback */
typedef int (*agt_timer_fn_t) (uint32 timer_id,
void *cookie);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1
•
File: agt/agt_timer.h
•
Template: agt_timer_fn_t
◦
Inputs:
▪
timer_id == timer identifier
▪
cookie == context pointer, such as a session control block, passed to agt_timer_set function (may be
NULL)
◦
Outputs: none
◦
Returns: 0 == normal exit.
-1 == error exit, delete timer upon return
Version 16.10-10
Page 160
YumaPro API Quick Start Guide
7.7.1
Timed Config Replay Example
It is possible to replay the entire configuration if the underlying system resets or restarts, but the server process is still
running.
The configuration replay procedure must be triggered by a timer callback function. This function can be registered in the
yp-system library or in a SIL library. The purpose of this callback is to check the health of the underlying system and if it
has restarted and needs to be re-initialized with configuration, then the agt_request_replay() function must be called.
/********************************************************************
* FUNCTION agt_request_replay
*
* Request replay of the running config to SIL modules
* because SIL hardware has reset somehow
*
*********************************************************************/
extern void agt_request_replay (void);
The agt_timer_create function is used to declare the callback. 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 registration of the Periodic Timer callback type may look as follows:
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
/* put your module initialization code here */
uint32 timerid = 0;
/* example will check every 10 seconds */
res = agt_timer_create(10, TRUE, force_replay, NULL, &timerid);
if (res != NO_ERR) {
return res;
}
...
}
The following example code illustrates how the Periodic Timer callback may look like:
Version 16.10-10
Page 161
YumaPro API Quick Start Guide
/********************************************************************
* FUNCTION force_replay
*
* Process the timer expired event
*
* INPUTS:
*
timer_id == timer identifier
*
cookie == context pointer, such as a session control block,
*
passed to agt_timer_set function (may be NULL)
*
* RETURNS:
*
0 == normal exit
*
-1 == error exit, delete timer upon return
********************************************************************/
static int
force_replay (uint32 timer_id,
void *cookie)
{
(void)timer_id;
(void)cookie;
boolean need_replay = FALSE;
// check the system somehow
if (need_replay) {
agt_request_replay();
}
return 0;
}
/* force_replay */
Now, the Config Replay callback will be invoked when a configuration replay is started or finished, which is triggered by a
call to agt_request_replay().
Version 16.10-10
Page 162
YumaPro API Quick Start Guide
8 RPC Operation Callbacks
All RPC operations are data-driven within the server, using the YANG RPC statement for the operation and SIL callback
functions.
Any new protocol operation can be added by defining a new YANG RPC statement in a module, and providing the proper
SIL code.
The agt_rpc_method_t function in agt/agt_rpc.h is used as the callback template for all RPC callback phases.
The following function template definition is used for RPC callback function:
/* Template for RPC server callbacks
* The same template is used for all RPC callback phases
*/
typedef status_t
(*agt_rpc_method_t) (ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode);
Callback Template
•
Type: User Callback
•
Max Callbacks: 1 per object
•
File: agt_rpc.h
•
Template: agt_rpc_method_t
◦
◦
Inputs:
▪
scb == session control block making the request
▪
msg == incoming rpc_msg_t in progress
▪
methnode == XML node for the operation, which can be used in error reporting (or ignored)
Returns: status_t
Return status for the phase; an error in validate phase will cancel Invoke Phase; an rpc-error will be added if an
error is returned and the msg error Q is empty
•
Register:
agt_rpc_register_method
•
Unregister: agt_rpc_unregister_method
Where, scb pointer represents a session control block structure that is defined in the netconf/src/ncx/ses.h. This control
block is primarily used for error reporting, as described in the example section later. However, can be used for more
advanced actions. It provides access to the session specific information, such as current message input/output encoding,
current session ID information, current protocol information, user name information, peer address information, etc. Note,
almost all the fields in this structure should NOT be changed and accessed directly. This control block ideally should be
used only for getting more information about the current session, not for alteration of any of its fields.
The msg pointer represents the NETCONF Server and Client RPC Request/Reply Message Handler control block that is
defined in the netconf/src/ncx/rpc.h. Similarly to SCB, this control block is primarily used for error reporting, as described
in the example section later. However, this control block also provides the RPC input value, as described later in the
examples.
Version 16.10-10
Page 163
YumaPro API Quick Start Guide
The methnode pointer represents the XML node being parsed if this is available. This can used for error reporting if no 'val'
or 'obj' parameters are available (from the request message). This pointer is defined in the netconf/src/ncx/xml_util.h.
8.1 RPC Callback Initialization and Cleanup
The agt_rpc_register_method function in agt/agt_rpc.h is used to provide a callback function for a specific callback phase.
The same function can be used for multiple phases if desired.
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 RPC callbacks registration for the Validate Phase may look as follows. Assume we are
using the same YANG module (Refer to the “Glossary” section for the complete YANG data model):
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
res =
agt_rpc_register_method (EXAMPLE_MODNAME,
(const xmlChar *)"do-example",
AGT_RPC_PH_VALIDATE,
rpc_example_validate);
...
}
Initialization function with the RPC callbacks registration for the Invoke Phase may look as follows. Assume we are using
the same YANG module (Refer to the “Glossary” section for the complete YANG data model):
/********************************************************************
* FUNCTION interfaces_init
*
* initialize the server instrumentation library.
* Initialization Phase 1
*
*********************************************************************/
static status_t
interfaces_init (void)
{
...
Version 16.10-10
Page 164
YumaPro API Quick Start Guide
res =
agt_rpc_register_method (EXAMPLE_MODNAME,
(const xmlChar *)"do-example",
AGT_RPC_PH_INVOKE,
rpc_example_validate);
...
}
agt_rpc_register_method
Parameter
Description
module
The name of the module that contains the rpc statement
method_name
The identifier for the rpc statement
phase
AGT_PH_VALIDATE(0): validate phase
AGT_PH_INVOKE(1): invoke phase
AGT_PH_POST_REPLY(2): post-reply phase
method
The address of the callback function to register
If you register a callback for a specific object, your SIL code must unregister it during the cleanup phase, that is being
called any time the server is shutting down. Also, it is getting called during restart and reload procedure.
The following example code illustrates how the RPC callback can be cleaned up. The callbacks cleanup is getting done
during module Cleanup Phase. This unregister function will clean up callbacks for all the phases and should be called only
once per object.
/********************************************************************
* FUNCTION interfaces_cleanup
*
cleanup the server instrumentation library
*
********************************************************************/
void interfaces_cleanup (void)
{
...
/* Unregister all the RPC SIL callbacks */
agt_rpc_unregister_method(EXAMPLE_MODNAME, (const xmlChar *)"do-example");
...
}
agt_rpc_unregister_method
Parameter
Description
module
The name of the module that contains the rpc statement
method_name
The identifier for the rpc statement
Version 16.10-10
Page 165
YumaPro API Quick Start Guide
8.2 RPC Callback Function Examples
8.2.1 RPC Validate Callback Function
The RPC Validate callback function is optional to use. Its purpose is to validate any aspects of an RPC operation, beyond
the constraints checked by the server engine. Only 1 validate function can register for each YANG RPC statement. The
standard NETCONF operations are reserved by the server engine. There is usually zero or one of these callback functions
for every 'rpc' statement in the YANG module associated with the SIL code.
The yangdump-sdk code generator will create the SIL callback function by default. There will be C comments in the code
to indicate where your additional C code should be added.
The val_find_child function is commonly used to retrieve particular parameters within the RPC input section, which is
encoded as a val_value_t tree. The rpc_user1 and rpc_user2 cache pointers in the rpc_msg_t structure can also be used
to store data in the Validation phase, so it can be immediately available in the Invoke phase.
The agt_record_error function is commonly used to record any parameter or other errors.
In the following example the callback function validates “session id” value and if it is higher then allowed the operation
will report an error and terminate:
/********************************************************************
* FUNCTION rpc_example_validate
*
* RPC validation phase
* All YANG constraints have passed at this point.
*
* INPUTS:
*
see agt/agt_rpc.h for details
*
* RETURNS:
*
error status
********************************************************************/
static status_t
rpc_example_validate (ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode)
{
status_t res = NO_ERR;
val_value_t *errorval = NULL;
if (LOGDEBUG) {
log_debug("\nStart SIL validate rpc <do-example> ");
}
val_value_t *session_id_val = NULL;
uint32 session_id = 0;
val_value_t *inputval = msg->rpc_input;
/* find a session-id child in the RPC input */
Version 16.10-10
Page 166
YumaPro API Quick Start Guide
session_id_val = val_find_child(inputval,
EXAMPLE_MODNAME,
(const xmlChar *)"session-id");
if (session_id_val != NULL && session_id_val->res == NO_ERR) {
session_id = VAL_UINT(session_id_val);
}
if (session_id) {
if (LOGDEBUG) {
log_debug("\nSesion ID %u is not acceptable ", session_id);
}
/* validate the session ID value */
if (session_id > 10) {
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
}
}
if (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_OPERATION,
res,
methnode,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
return res;
} /* rpc_test_augm_validate */
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 retrieve the RPC input value, that is stored in the msg>rpc_input and then obtain a needed “session-id” node from the RPC input in order to validate it later:
...
val_value_t *inputval = msg->rpc_input;
/* find a session-id child in the RPC input */
session_id_val = val_find_child(inputval,
EXAMPLE_MODNAME,
(const xmlChar *)"session-id");
if (session_id_val != NULL && session_id_val->res == NO_ERR) {
session_id = VAL_UINT(session_id_val);
}
...
Version 16.10-10
Page 167
YumaPro API Quick Start Guide
In the following part of the above code example we are trying to validate previously retrieved “session-id” value. If the
provided in the input 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 rterminate
the operation.
...
if (session_id) {
if (LOGDEBUG) {
log_debug("\nSesion ID %u is not acceptable ", session_id);
}
/* validate the session ID value */
if (session_id > 10) {
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
}
}
if (res != NO_ERR) {
agt_record_error(scb,
&msg->mhdr,
NCX_LAYER_OPERATION,
res,
methnode,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval,
(errorval) ? NCX_NT_VAL : NCX_NT_NONE,
errorval);
}
...
8.2.2 RPC Invoke Callback Function
The RPC Invoke callback function is used to perform the operation requested by the client session. Only 1 invoke function
can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is
usually one of these callback functions for every 'rpc' statement in the YANG module associated with the SIL code.
The RPC Invoke callback function is optional to use, although if no invoke callback is provided, then the operation will
have no affect. Normally, this is only the case if the module is tested by an application developer, using netconfd-pro as a
server simulator.
The yangdump-sdk code generator will create this SIL callback function by default. There will be C comments in the code
to indicate where your additional C code should be added.
The val_find_child function is commonly used to retrieve particular parameters within the RPC input section, which is
encoded as a val_value_t tree. The rpc_user1 and rpc_user2 cache pointers in the rpc_msg_t structure can also be used
to store data in the Validation phase, so it can be immediately available in the Invoke phase.
The agt_record_error function is commonly used to record any internal or platform-specific errors.
For RPC operations that return either an <ok> or <rpc-error> response, there is nothing more required of the RPC Invoke
callback function.
Version 16.10-10
Page 168
YumaPro API Quick Start Guide
For operations which return some data or <rpc-error>, the SIL code must do 1 of 2 additional tasks:
•
add a val_value_t structure to the rpc_dataQ queue in the rpc_msg_t for each parameter listed in the YANG rpc
'output' section.
•
set the rpc_datacb pointer in the rpc_msg_t structure to the address of your data reply callback function. See the
agt_rpc_data_cb_t definition in agt/agt_rpc.h for more details.
/********************************************************************
* FUNCTION rpc_test_augm_invoke
*
* RPC invocation phase
* All constraints have passed at this point.
* Call device instrumentation code in this function.
*
* INPUTS:
*
see agt/agt_rpc.h for details
*
* RETURNS:
*
error status
********************************************************************/
static status_t
rpc_test_augm_invoke (ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode)
{
status_t res = NO_ERR;
if (LOGDEBUG) {
log_debug("\nStart SIL invoke rpc <do-example>");
}
val_value_t *session_id_val = NULL;
val_value_t *inputval = msg->rpc_input;
session_id_val = val_find_child(inputval,
RPC_TEST_MOD,
(const xmlChar *)"session-id");
if (session_id_val != NULL && session_id_val->res == NO_ERR) {
;
}
/* remove the next line if scb is used */
(void)scb;
/* remove the next line if methnode is used */
(void)methnode;
/* invoke your device instrumentation code here */
return res;
} /* rpc_test_augm_invoke */
Version 16.10-10
Page 169