DB-API Subsystem
The DB-API subsystem provides a special datastore editing interface for subsystems to access the YANG-based data controlled by the main server. It can be used by network management components within the device that operate in addition to the netconfd-pro server.
The DB-API service provides the ability for a subsystem to send a configuration edit to the main server. The YControl protocol is used to manage the connection and communication with the main server.
DB-API runs on an external process on the same system as netconfd-pro to send database requests to the server.
The yumaworks-db-api.yang YANG module contains message definitions for the DB-API subsystem.
DB-API Usage
The db-api-app
example application is provided to show
usage of some of the database and system access APIs available
in the DB-API library.
It is usually installed in /usr/share/yumapro/src/db-api-app/main.c
.
Warning
There are some usage limitations for the DB-API library:
Only the API functions in 'db-api' library are supported.
Only simple single-threaded usage as shown in the 'db-api-app' is supported.
Multi-threaded applications that use the DB-API library are not supported.
DB-API functions are not thread-safe.
The DB-API subsystem will not work if used within the 'netconfd-pro' application. This is not supported.
DB-API Interface Functions
The DB-API service uses the YControl subsystem similar to the SIL-SA service. The same YControl callback interface is used for handling database access messages.
Refer to db-api/db_api.h
for complete details.
Setup
db_register_service: Initialize the DB-API service with the YControl subsystem
db_api_service_ready: Check if the DB-API service is ready to send an edit
Single Edit Patch
db_api_send_edit: Send an edit request to the main server. The SIL and SIL-SA callbacks will not be invoked for this edit.
Only the main server datastore will be updated to contain the requested edit.
The main server will return an “ok” message if the edit was accepted or an “error” message if the edit was rejected for some reason.
The resulting running datastore must pass any YANG validation checks that apply to the changed data somehow.
db_api_send_edit_ex: Same as db_api_send_edit, but with more parameters
db_api_send_edit_full: Same as db_api_send_edit_ex, but with more parameters
db_api_send_edit_full2: Same as db_api_send_full, but with a skip-sil parameter added
Multi-Edit Patch
db_api_start_patch: start a multi-edit patch request
db_api_add_edit: add an edit to the YANG Patch (called 1 – N times)
db_api_send_patch: send the YANG Patch request to the server'
db_api_free_patch: free the memory used by a YANG Patch request
Get Configuration Data
db_api_send_getconfig: send a config request to the server. The running configuration will be retrieved and saved in the specified file.
Get Filtered Configuration or Operational Data
db_api_send_getfilter: send a retrieval request to the server. The running configuration and optionally the operational state will be retrieved and saved in the specified file. An XPath filter can be used to return a subset of the data.
db_api_send_getfilter_cb: Same features as db_api_send_getfilter except a callback function is used instead of a file to save the results.
Check Status
db_api_check_edit: check if an edit request or <getconfig> request has finished
db_api_check_edit_ex: Similar to db_api_check_edit but returns error string from server (if any)
Maintenance Mode
db_api_send_enter_maintmode: send a <enter-maintmode> request to the server
db_api_send_exit_maintmode: send a <exit-maintmode> request to the server
DB-LOCK-TEST Mode
db_api_request_local_db_lock: send a <db-lock> request to the server
db_api_release_local_db_lock: send a <db-unlock> request to the server
Set Log Level
db_api_send_set_loglevel: send a <set-log-level> request to the server
SubRPC
db_api_send_subrpc_request: send a <subrpc-request> message to the server
DB-API Application Structure
A DB-API application is designed to be integrated into another application or used as the main program. It consists of 3 parts:
Initialization
Main Loop
Cleanup
The initialization step is required to load the proper YANG modules and other data structures. The subsystem needs to connect to the main netconfd-pro server and register.
The 'YControl' transport library handles all low level interactions with the main server. After the YControl transport layer has been initialized, the DB-API service must register with the main server.
The first phase initialization is post-CLI and pre-database load. The second phase initialization is post-database load.
After initialization the main loop is used to check for network IO from the main server. First the main loop must wait until the DB-API service is ready.
Refer to the Initialization and Cleanup section for details on the following functions:
ycontrol_init
ycontrol_init2
ycontrol_check_io
ycontrol_cleanup
-
status_t db_api_register_service(void)
Register the DB-API service with the YControl layer.
Must be called before any other DB-API function can be used.
- Returns:
status
-
boolean db_api_service_ready(void)
Check if the DB-API service is done initializing, and is now ready to send requests to the server.
- Returns:
TRUE if ready state; FALSE otherwise
-
boolean ycontrol_shutdown_now(void)
Check if the YControl subsystem has shut down because a <shutdown-event> was received from the server.
- Returns:
TRUE is YControl has shut down
FALSE if YControl has not shut down
Example main db-api-app Function
Note
The 'db-api-app' example program does not do meaningful edits or other database operations. It is only provided as a template to create real database operations as needed by a subsystem application.
int main (int argc, char **argv)
{
/* 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();
}
/* load extra YANG modules only if needed */
if (res == NO_ERR) {
res = load_yang_modules();
if (res != NO_ERR) {
log_error("\nError: load YANG modules failed (%s)",
get_error_string(res));
// continue on without the YANG modules !!!
res = NO_ERR;
}
}
useconds_t usleep_val = 100000; // 100K micro-sec == 1/10 sec
boolean done = FALSE;
/* 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 (res != NO_ERR) {
continue;
}
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;
} else if (db_api_service_ready() && test_done) {
const xmlChar *error_msg = NULL;
/* check the test edit */
res = db_api_check_edit_ex(&error_msg);
if (res == NO_ERR) {
done = TRUE;
}
} // else wait for service ready
}
/* 5) cleanup the control layer before exit */
ycontrol_cleanup();
return (int)res;
} /* main */
Using YANG Modules in DB-API Applications
The db-api-app only loads a minimum set of YANG modules for internal use. In order for the XML parser and other tools to correctly represent YANG data, the YANG modules should be loaded into the application before any requests are sent to the server.
Note
A sil-sa-app or combo-app will automatically load all the YANG modules used by the SIL-SA application.
A db-api-app must manually load YANG modules in the load_yang_modules function in main.c
db-api-app Load Modules Example
First the 'ncxmod' H file must be included
#include "ncxmod.h"
The second stage init phase is enhanced to add additional YANG modules. This code must be placed after the 'ycontrol_init2' section.
You can decide if the application should continue or not if the YANG modules were not loaded.
if (res == NO_ERR) {
res = load_yang_modules();
if (res != NO_ERR) {
log_error("\nError: load YANG modules failed (%s)",
get_error_string(res));
// continue on without the YANG modules !!!
res = NO_ERR;
}
}
A new function called 'load_yang_modules' has been added to db-api-app/main.c. In this example application the 'ietf-interfaces' and 'iana-if-type' modules are loaded.
/********************************************************************
* FUNCTION load_yang_modules
*
* A DB-API application needs to load any YANG modules
* it needs. Only the most basic YANG modules used for
* YControl and db-api control messages are loaded by default
*
* RETURNS:
* status: some error to halt the program or else NO_ERR to continue
*********************************************************************/
static status_t
load_yang_modules (void)
{
/* example: ietf-interfaces
* the module pointer could be saved if needed
* but it is not required
*/
ncx_module_t *retmod = NULL;
status_t res =
ncxmod_load_module((const xmlChar *)"ietf-interfaces",
NULL, // revision
NULL, // savedevQ
&retmod);
if (res != NO_ERR) {
return res;
}
res = ncxmod_load_module((const xmlChar *)"iana-if-type",
NULL, // revision
NULL, // savedevQ
&retmod);
if (res != NO_ERR) {
return res;
}
return NO_ERR;
} /* load_yang_modules */
Database Edit APIs
The DB-API service can be used to send datastore edits from the system to the netconfd-pro server. This is usually done because configuration can come from CLI, SNMP, or other internal system components.
There are several editing APIs. APIs cannot be changed after they are published, so many incremental versions of the "send_edit" APIs exist
The file db-api.h
must be included in the application
to access these API functions.
Function |
Description |
---|---|
db_api_send_edit |
Edit 1 target node with a basic operation and XML value |
db_api_send_edit_ex |
Add patch-id and system edit flag parameters |
db_api_send_edit_full |
Add insert parameters for user-ordered lists |
db_api_send_edit_full2 |
Same as db_api_send_full, but with a skip-sil parameter added |
db_api_check_edit |
Check if a send-edit transaction has completed |
db_api_check_edit_ex |
Check if a send-edit has completed and get error message |
Note
Most edits from DB-API to the server are done to synchronize the server with edits already applied to the system, so the default for the 'skip_sil' parameter is 'true'.
If the DB-API edit needs to be applied to the system then the 'skip_sil' parameter must be set to 'false'.
Any edit to a server controlled data model (e.g. NACM or cert-usermap) must set the 'skip_sil' parameter to 'false'.
Simple Database Edits
The "send_edit" functions are used to make a single patch to the configuration.
Note
DB-API edit functions are not YANG-aware and require complete and valid XML input to send to the server in an edit request.
-
status_t db_api_send_edit_full2(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, boolean skip_sil)
Create a YANG Patch edit request and send it to the DB-API service on the main server.
This patch can have exactly one edit. Provides full access to all 1-shot send_edit parameters.
THIS API MUST ONLY BE USED WITHIN A YCONTROL SUBSYSTEM.
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>" * 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>"
See also
db_api_send_edit
- Parameters:
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.
NULL to ignore this parameter
insert_where -- <insert enum string>
"before"
"after"
"first"
"last"
NULL to ignore this parameter
skip_sil --
TRUE to skip the SIL and SIL-SA callbacks on the main server
FALSE to invoke the SIL and SIL-SA callbacks for this transaction
- Returns:
status
The DB-API application should wait until the server completes the edit operation before continuing on with more DB-API operations. The main application can keep working. It does not have to block while waiting for a DB-API to complete.
-
status_t db_api_check_edit_ex(const xmlChar **errstr)
Check on the status of an edit in progress.
Get the error string if any
- Parameters:
errstr -- [out] address of return error string (may be NULL)
*errstr pointer to error string if return value is not NO_ERR and there is a response from the server that had an error string
- Returns:
status
ERR_NCX_NOT_FOUND if final status and no message response is pending
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
db-api Send Edit Parameters
There are 3 main parameters that are used in most send edit operations:
operation_str
This parameter must be provided to set the edit operation.
The values supported are defined by the YANG Patch Media Type RFC 8072#section2.2
The value
merge
is the safest to add or modify configuration data, since it does not require the db-api-app to know if the target exists or not.The value
remove
is the safest to remove configuration data, since it is not an error if the target does not exist.
path_str
This parameter must be provided to set the edit operation.
The value
/
for the root is permitted. This value must be used to add or modify top-level objects. This value cannot be used for a 'delete' or 'remove' operation.Identifies the complete instance of the node to be patched.
This string should not have prefixes unless they are needed.
path_str = (const xmlChar *)"/nacm/groups";
If a prefix is used then it must match the YANG module name for the module that defines the node.
path_str = (const xmlChar *)"/ietf-interfaces:interfaces";
If the data node in the next segment of the path expression is from the same module as its parent, then the module name prefix must not be present.
path_str = (const xmlChar *)"/ietf-interfaces:interfaces/interface";
If the data node in the next segment of the path expression is from a different module as its parent, then the module name prefix should be present.
path_str = (const xmlChar *)"/ietf-interfaces:interfaces/interface/mymodule:itf-extension";
Key are encoded as path segments, same as the old YANG-API protocol.
path_str = (const xmlChar *)"/ietf-interfaces:interfaces/interface/eth0";
value_str
This parameter must be
NULL
for a 'delete' or 'remove' operation.This parameter must be provided for any operation other than 'delete' or 'remove'.
This is a string representing the YANG data for the patch edit.
XML without namespaces should be used if possible.
If the 'path_str' is set to the root
/
then the top-level node in the 'value_str' must be "<config>".If XML namespaces and 'xmlns' attributes are used, they must be used correctly. Invalid XML will get rejected by the server.
const xmlChar *path_str = (const xmlChar *)"/"; const xmlChar *value_str = (const xmlChar *) "<config>" "<int8.1 xmlns='http://netconfcentral.org/ns/test'>22</int8.1>" "<int16.1 xmlns='http://netconfcentral.org/ns/test'>11</int16.1>" "<int32.1 xmlns='http://netconfcentral.org/ns/test'>1000</int32.1>" "</config>";
This XML string represents a child node of the parent node specified in the 'path_str' parameter.
db-api Send Edit Examples
Example db-api-app send_edit from db-api-app/main.c Most of the example edits require the YANG module test.yang to be loaded in the server.
db-api-app send_test_edit Function
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function.
* The module test.yang needs to be loaded for this to work
*********************************************************************/
static void
send_test_edit (void)
{
/* EXAMPLE EDIT:
* TBD: replace with parameters from command line
*/
/* set 3 top-level leafs at once with test module */
const xmlChar *path_str = (const xmlChar *)"/";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<config>"
"<int8.1 xmlns='http://netconfcentral.org/ns/test'>22</int8.1>"
"<int16.1 xmlns='http://netconfcentral.org/ns/test'>11</int16.1>"
"<int32.1 xmlns='http://netconfcentral.org/ns/test'>1000</int32.1>"
"</config>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
const xmlChar *insert_point = NULL;
const xmlChar *insert_where = NULL;
status_t res =
db_api_send_edit_full(path_str,
operation_str,
value_str,
patch_id_str,
system_edit,
insert_point,
insert_where);
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 */
Example: Add a new interface named 'test11'
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *path_str = (const xmlChar *)"/interfaces";
const xmlChar *value_str = (const xmlChar *)
"<interfaces xmlns='urn:ietf:params:xml:ns:yang:ietf-interfaces'>"
" <interface>"
" <name>test11</name>"
" <type xmlns:ianaift='urn:ietf:params:xml:ns:yang:iana-if-type'>"
"ianaift:ethernetCsmacd</type>"
" </interface>"
"</interfaces>";
Example: Remove a NACM group named 'admin2'
const xmlChar *operation_str = (const xmlChar *)"remove";
const xmlChar *path_str = (const xmlChar *)"/nacm/groups/group/admin2";
const xmlChar *value_str = NULL;
Database Edits Using YANG Patch
In order to edit multiple data nodes at once, the 'YANG Patch' edit method defined in RFC 8072 is used.
There are 4 steps needed to use this database editing method:
start patch
add edit to patch (repeat as needed)
send patch edit request to the server
cleanup the patch
-
status_t db_api_start_patch(const xmlChar *patch_id_str, boolean system_edit, yang_patch_cb_t **retcb)
Start a YANG Patch request that can have multiple edits.
* Steps to use: * 1) db_api_start_patch * 2 .. N-2) db_api_add_edit * N-1) db_api_send_patch * N) db_api_free_patch
See also
See also
See also
- Parameters:
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
retcb -- [out] address of return control block
*retcb the patch control block in progress
Must free with ab_api_free_patch!!
- Returns:
status
-
status_t db_api_start_patch2(const xmlChar *patch_id_str, boolean system_edit, boolean skip_sil, const xmlChar *comment, yang_patch_cb_t **retcb)
Start a YANG Patch request that can have multiple edits with complete parameters (YPW-1826)
* Steps to use: * 1) db_api_start_patch2 * 2 .. N-2) db_api_add_edit * N-1) db_api_send_patch * N) db_api_free_patch
See also
See also
See also
- Parameters:
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
skip_sil --
TRUE to skip SIL callbacks (default)
FALSE to invoke SIL callbacks for this patch
comment --
string to override default 'DB-API patch'
NULL to use the default comment
retcb -- [out] address of return control block
*retcb the patch control block in progress
Must free with ab_api_free_patch!!
- Returns:
status
-
status_t db_api_add_edit(yang_patch_cb_t *pcb, const xmlChar *edit_id_str, const xmlChar *edit_target, const xmlChar *edit_operation, const xmlChar *edit_xml_value, const xmlChar *insert_point, const xmlChar *insert_where)
Create an edit request and add to YANG Patch in progress.
If return NO_ERR then a new edit is added to
pcb
- Parameters:
pcb -- patch control block in progress
edit_id_str -- index value for the edit
edit_target -- edit target path string
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))
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
-
status_t db_api_send_patch(yang_patch_cb_t *pcb)
Send a previously created patch request.
This does not free the pcb! Must call db_api_free_patch after this call
- Parameters:
pcb -- patch control block in progress
- Returns:
status
-
void db_api_free_patch(yang_patch_cb_t *pcb)
Free a patch control block.
- Parameters:
pcb -- patch control block in progress
This example edit function is from db-api-app/main.c
/********************************************************************
* FUNCTION send_complex_test_edit
*
* This is an example send edit function.
* It uses multiple APIs to build a patch with 3 edits
* The module test.yang needs to be loaded for this to work
*********************************************************************/
static void
send_complex_test_edit (void)
{
const xmlChar *patch_id_str = (const xmlChar *)"complex-P1";
boolean system_edit = TRUE;
yang_patch_cb_t *pcb = NULL;
/* change to 1 to use db_api_start_patch version */
#if 0
/* Step 1: create a patch control block
* Use db_api_start_patch or db_api_start_patch2
* to set 2 additional fields
*/
status_t res =
db_api_start_patch(patch_id_str,
system_edit,
&pcb);
#else
/* Step 1 using db_api_start_patch2 to show additional fields
* The default for skip_sil is TRUE so use this API if the
* server SIL or SIL-SA callbacks should be invoked for this edit
* The comment field default is 'DB-API patch'. Set to NULL
* to use the default or set plain string as comment
*/
status_t res =
db_api_start_patch2(patch_id_str,
system_edit,
FALSE, // skip_sil
(const xmlChar *)"my first patch", // comment
&pcb);
#endif // 0 : pick start patch API
if (pcb == NULL) {
log_error("\nCreate patch failed (%s)\n", get_error_string(res));
return;
}
const xmlChar *edit_id_str = (const xmlChar *)"edit1";
const xmlChar *edit_target_str = (const xmlChar *)"/int8.1";
const xmlChar *edit_operation_str = (const xmlChar *)"replace";
const xmlChar *edit_xml_value = (const xmlChar *)
"<int8.1 xmlns='http://netconfcentral.org/ns/test'>44</int8.1>";
const xmlChar *insert_point = NULL;
const xmlChar *insert_where = NULL;
/* Step 2: add edit1 */
res = db_api_add_edit(pcb,
edit_id_str,
edit_target_str,
edit_operation_str,
edit_xml_value,
insert_point,
insert_where);
if (res != NO_ERR) {
log_error("\nAdd edit1 failed for complex test edit (%s)\n",
get_error_string(res));
db_api_free_patch(pcb);
return;
}
/* Step 3: add edit2 */
edit_id_str = (const xmlChar *)"edit2";
edit_target_str = (const xmlChar *)"/int16.1";
edit_operation_str = (const xmlChar *)"remove";
edit_xml_value = NULL;
res = db_api_add_edit(pcb,
edit_id_str,
edit_target_str,
edit_operation_str,
edit_xml_value,
insert_point,
insert_where);
if (res != NO_ERR) {
log_error("\nAdd edit2 failed for complex test edit (%s)\n",
get_error_string(res));
db_api_free_patch(pcb);
return;
}
/* Step 4: add edit3 */
edit_id_str = (const xmlChar *)"edit3";
edit_target_str = (const xmlChar *)"/uint32.1";
edit_operation_str = (const xmlChar *)"merge";
edit_xml_value = (const xmlChar *)
"<uint32.1 xmlns='http://netconfcentral.org/ns/test'>400</uint32.1>";
res = db_api_add_edit(pcb,
edit_id_str,
edit_target_str,
edit_operation_str,
edit_xml_value,
insert_point,
insert_where);
if (res != NO_ERR) {
log_error("\nAdd edit3 failed for complex test edit (%s)\n",
get_error_string(res));
db_api_free_patch(pcb);
return;
}
/* Step 4: send patch request */
res = db_api_send_patch(pcb);
if (res != NO_ERR) {
log_error("\nSend complex test edit failed (%s)\n",
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend complex test edit OK\n");
}
/* step 5: free the patch control block */
db_api_free_patch(pcb);
} /* send_complex_test_edit */
Database Retrieval APIs
There are 3 API functions to retrieve YANG data from the server:
Function |
Description |
---|---|
db_api_send_getconfig |
Retrieve the entire <running> datastore and save to a file. |
db_api_send_getfilter |
Retrieve a subset of the <running> datastore using filter parameters and save to a file. |
db_api_send_getfilter_cb |
Retrieve a subset of the <running> datastore using filter parameters and use a callback to inspect the results instead of saving to a file |
Database Retrieval With File Save
-
status_t db_api_send_getconfig(const xmlChar *filespec, boolean withdef)
Create a <getconfig> request and send it to the main server.
See also
- Parameters:
filespec -- file specification to contain the XML instance document retrieved from the server
withdef -- TRUE to get with defaults; FALSE to leave out defaults
- Returns:
status
/********************************************************************
* FUNCTION send_test_getconfig
*
* Send a <getconfig> request
*********************************************************************/
static void send_test_getconfig (const char *filespec,
boolean withdef,
boolean with_state,
const char *xpath_filter)
{
status_t res = NO_ERR;
if (xpath_filter || with_state) {
res = db_api_send_getfilter((const xmlChar *)filespec,
withdef,
!with_state,
(const xmlChar *)xpath_filter);
} else {
res = db_api_send_getconfig((const xmlChar *)filespec, withdef);
}
if (res != NO_ERR) {
log_error("\nSend test getconfig failed (%s)\n",
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test getconfig OK\n");
}
} /* send_test_getconfig */
-
status_t db_api_send_getfilter(const xmlChar *filespec, boolean withdef, boolean get_config, const xmlChar *xpath_filter)
Retrieve data from the server with complete parameters.
Create a <getconfig> request and send it to the main server. An XPath filter can be sent as a parameter
- Parameters:
filespec -- file specification to contain the XML instance document retrieved from the server
withdef -- TRUE to get with defaults; FALSE to leave out defaults
get_config -- TRUE for config only; FALSE for get (config + state)
xpath_filter -- XPath expression (may be nULL)
- Returns:
status
This API can be used from the 'db-api-app' application:
db-api-app [--subsys=<subsys-id>] [<logging-parameters>]
--getconfig [--withdef] --filespec=/path/to/output.xml
[--with-state] [--xpath-filter=xpath-expression]
Filtered Retrieval Example
The db_api_send_getfilter function is similar to db_api_send_getconfig, but with extended parameters.
This example retrieves the RESTCONF monitoring state information:
> db-api-app --getconfig --withdef --with-state --xpath-filter=/restconf-state --filespec=test.xml
The server might reply:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://yumaworks.com/ns/yumaworks-db-api">
<restconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring">
<capabilities>
<capability>urn:ietf:params:restconf:capability:depth:1.0</capability>
<capability>urn:ietf:params:restconf:capability:with-defaults:1.0</capability>
<capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability>
<capability>urn:ietf:params:restconf:capability:fields:1.0</capability>
<capability>urn:ietf:params:restconf:capability:replay:1.0</capability>
<capability>urn:ietf:params:restconf:capability:filter:1.0</capability>
<capability>urn:ietf:params:restconf:capability:yang-patch:1.0</capability>
</capabilities>
<streams>
<stream>
<access>
<encoding>xml</encoding>
<location>http://localhost/restconf/stream</location>
</access>
<description>default RESTCONF event stream</description>
<name>RESTCONF</name>
<replay-log-creation-time>2018-06-05T16:17:40Z</replay-log-creation-time>
<replay-support>true</replay-support>
</stream>
</streams>
</restconf-state>
</config>
> db-api-app --getconfig --with-state --xpath-filter=--xpath-filter="/interfaces-state/interface[name='lo']" --filespec=test.xml
The server might reply (if the ietf-interfaces and iana-if-type modules are loaded):
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://yumaworks.com/ns/yumaworks-db-api">
<interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"
xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>lo</name>
<type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type>
<admin-status>up</admin-status>
<oper-status>up</oper-status>
<if-index>1</if-index>
<phys-address>00:00:00:00:00:00</phys-address>
<speed>0</speed>
<statistics>
<in-octets>69769829</in-octets>
<in-unicast-pkts>288048</in-unicast-pkts>
<in-multicast-pkts>0</in-multicast-pkts>
<in-discards>0</in-discards>
<in-errors>0</in-errors>
<out-octets>69769829</out-octets>
<out-unicast-pkts>288048</out-unicast-pkts>
<out-discards>0</out-discards>
<out-errors>0</out-errors>
</statistics>
</interface>
</interfaces-state>
</config>
JSON Retrieval Example
Starting in 22.10-3 the db-api subsystem supports saving the result as a JSON file. The module names are not present since the YANG modules are not loaded into the db-api-app.
> db-api-app --getconfig --with-state --xpath-filter=--xpath-filter="/interfaces-state/interface[name='lo']" --filespec=test.json
The server might reply:
{
"yumaworks-db-api:config": {
"interfaces-state": {
"interface": {
"name":"lo",
"type":"ianaift:softwareLoopback",
"admin-status":"up",
"oper-status":"up",
"if-index":"1",
"phys-address":"00:00:00:00:00:00",
"speed":"0",
"statistics": {
"in-octets":"69796024",
"in-unicast-pkts":"288180",
"in-multicast-pkts":"0",
"in-discards":"0",
"in-errors":"0",
"out-octets":"69796024",
"out-unicast-pkts":"288180",
"out-discards":"0",
"out-errors":"0"
}
}
}
}
}
If the YANG modules are loaded, as explained in the db-api-app Load Modules Example, then the JSON parser will be aware of the modules and print the proper YANG to JSON encoding for the objects.
The same request if modules were loaded might return the following reply:
{
"yumaworks-db-api:config": {
"ietf-interfaces:interfaces-state": {
"interface": [
{
"name":"lo",
"type":"iana-if-type:softwareLoopback",
"admin-status":"up",
"oper-status":"up",
"if-index":1,
"phys-address":"00:00:00:00:00:00",
"speed":"0",
"statistics": {
"in-octets":"70265359",
"in-unicast-pkts":"290541",
"in-multicast-pkts":"0",
"in-discards":0,
"in-errors":0,
"out-octets":"70265359",
"out-unicast-pkts":"290541",
"out-discards":0,
"out-errors":0
}
}
]
}
}
}
Database Retrieval With Data Response Callback
There is a new DB-API callback that allows the server data response to be accessed as an internal 'val_value_t' data tree.
This is intended for custom application use only.
The default callback simply uses val_dump_value to display the data.
A cookie can be passed to this callback
The callback is invoked after the server responds or the request times out
Refer to db-api-app/main.c for a complete example.
-
typedef void (*db_api_dataresp_cbfn_t)(status_t res, val_value_t *val, void *cookie)
user function callback template for processing the val_value_t tree for the server data response.
Used with the db_api_getfilter_cb API.
The callback is not expected to return a value.
It is called once at the conclusion of the get operation.
Type name: db_api_dataresp_cbfn_t
- Param res:
result of the get request; if error then val will be NULL
- Param val:
value node representing the 'config' root node received from the server for the corresponding get request
- Param cookie:
cookie param passed to db_api_getfilter_cb
There is a new version of the 'getfilter' API that accepts a callback parameter instead of a filespec parameter:
-
status_t db_api_send_getfilter_cb(db_api_dataresp_cbfn_t cbfn, void *cookie, boolean withdef, boolean get_config, const xmlChar *xpath_filter)
Retrieve data from the server with user callback.
Info on processing val_value_t trees, refer to [Data Trees] (https://docs.yumaworks.com/en/latest/ypserver/yang-data-nodes.html)
Create a <getconfig> request and send it to the main server.
An XPath filter can be sent as a parameter.
The callback function is used to process the internal val_value_t
Use this API instead of db_api_getfilter to avoid disk usage
- Parameters:
cbfn -- Data Response Callback Function to use
cookie -- parameter to pass to callback function after reply received
withdef -- TRUE to get with defaults; FALSE to leave out defaults
get_config -- TRUE for config only; FALSE for get (config + state)
xpath_filter -- XPath expression (may be nULL)
- Returns:
status; if any error then callback will not be invoked
Data Response Callback Example
The example Data Response Callback can be found in db-api-app/main.c:
static void dataresp_cbfn (status_t res,
val_value_t *val,
void *cookie)
{
(void)cookie;
if (val != NULL) {
log_info("\nGot data response:\n");
val_dump_value(val, 0, DINFO);
log_info_append("\n");
} else {
log_info("\nGot error response %d (%s)",
res, get_error_string(res));
}
} /* dataresp_cbfn */
The example code also includes a 'send' function that uses this callback:
/********************************************************************
* FUNCTION send_test_getconfig_cb
*
* Send a <getconfig> request with a callback instead of file save
*********************************************************************/
static void send_test_getconfig_cb (db_api_dataresp_cbfn_t cbfn,
void *cookie,
boolean withdef,
boolean with_state,
const char *xpath_filter)
{
status_t res =
db_api_send_getfilter_cb(cbfn,
cookie,
withdef,
!with_state,
(const xmlChar *)xpath_filter);
if (res != NO_ERR) {
log_error("\nSend test getconfig with callback failed (%s)\n",
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test getconfig with callback OK\n");
}
} /* send_test_getconfig_cb */
Subsystem RPC Requests
This API allows the subsystem to send any RPC request to the server. This should be considered as a last resort low-level API if none of the other DB-APIs can be used.
-
status_t db_api_send_subrpc_request(const xmlChar *user_id, const xmlChar *rpc_modname, const xmlChar *rpc_name, const xmlChar *in_filespec, const xmlChar *out_filespec)
Create a <subrpc-request> request and send it to the main server.
- Parameters:
user_id -- user name to run RPC on server; NULL system user
rpc_modname -- module name containing the RPC or NULL to scan all modules for the first match of rpc_name
rpc_name -- name of the RPC (must be present)
in_filespec -- file specification to contain the XML instance document for the rpc-method element (if needed)
out_filespec -- [out] file specification to contain the XML instance document retrieved from the server (must be present)
out_filespec file named by out_filespec created if operation succeeded.
- Returns:
status
Subsystem RPC Request Example
The <subrpc-request> message is used by a subsystem to send an RPC Operation Request to the server.
The <subrpc-response> message is used by the server to send an RPC reply message to the subsystem that sent the subrpc-request message.
The rpc1.yang module is used for this example:
module rpc1 {
namespace "http://www.yumaworks.com/ns/rpc1";
prefix r1;
revision "2020-02-25";
rpc rpc1 {
input {
leaf A {
type string;
}
leaf B {
type int32;
}
}
output {
leaf C {
type string;
}
leaf D {
type int32;
}
}
}
} // module rpc1
The input file rpc1.xml:
<rpc1 xmlns="http://www.yumaworks.com/ns/rpc1">
<A>test</A>
<B>11</B>
</rpc1>
The code to send the RPC request to the server:
#include "dp_api.h"
const xmlChar *user_id = (const xmlChar *)"admin1";
const xmlChar *rpc_modname = (const xmlChar *)"rpc1";
const xmlChar *rpc_name = (const xmlChar *)"rpc1";
const xmlChar *in_filename = (const xmlChar *)"rpc1.xml";
const xmlChar *out_filename = (const xmlChar *)"rpc1-out.xml";
status_t res =
db_api_send_subrpc_request(user_id,
rpc_modname,
rpc_name,
in_filename,
out_filename);
The output file rpc1-out.xml:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="http://www.yumaworks.com/ns/rpc1">
<C>test1</C>
<D>14</D>
</rpc-reply>
Subsystem Action Request Example
A YANG Action Request is just a special form of the RPC operation. The same format is used here as in the NETCONF protocol for the YANG action RPC.
The ex-action.yang module is used for the example:
module ex-action {
yang-version 1.1;
namespace "http://netconfcentral.org/ns/ex-action";
prefix exa;
import ietf-yang-types { prefix yang; }
revision 2020-03-06;
list server {
key name;
leaf name {
type string;
description "Server name";
}
action reset {
input {
leaf reset-msg {
type string;
description "Log message to print before server reset";
}
}
output {
leaf reset-finished-at {
type yang:date-and-time;
description "Time the reset was done on the server";
}
}
}
}
}
The act1.xml input file:
<action xmlns="urn:ietf:params:xml:ns:yang:1">
<server xmlns="http://netconfcentral.org/ns/ex-action">
<name>test1</name>
<reset>
<reset-msg>this is a reset message</reset-msg>
</reset>
</server>
</action>
The code to send the action request to the server:
#include "dp_api.h"
const xmlChar *user_id = (const xmlChar *)"admin1";
const xmlChar *rpc_modname = (const xmlChar *)"ex-action";
const xmlChar *rpc_name = (const xmlChar *)"reset";
const xmlChar *in_filename = (const xmlChar *)"act1.xml";
const xmlChar *out_filename = (const xmlChar *)"act1-out.xml";
status_t res =
db_api_send_subrpc_request(user_id,
rpc_modname,
rpc_name,
in_filename,
out_filename);
The output file act1-out.xml:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="http://netconfcentral.org/ns/ex-action">
<reset-finished-at>2020-03-11T19:50:30Z</reset-finished-at>
</rpc-reply>