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 netconfdpro running config in memory startupcfg.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 netconfdpro startupcfg.xml running config in memory NVstore 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 netconfdpro 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 netconfdpro running startupcfg.xml config in memory DBAPI 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
© Copyright 2024 Paperzz