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

The 'db-api-app' application contains example operations only.
It is not intended to be a functional application like sil-sa-app.

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

  • db_api_get_last_msg_id: Get the last message ID sent by the DB-API system, introduced in release 24.10-1.

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

To track the message ID of the most recent DB-API operation, use:

uint32 db_api_get_last_msg_id(void)

API to get the last message ID sent by the db-api system.

Returns:

message ID (0 is not initialized)

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);
    uint32 msgid = db_api_get_last_msg_id();

    if (res != NO_ERR) {
        log_error("\nSend test edit with msgid=%u failed %s %s = %s (%s).\n",
                  msgid,
                  operation_str, path_str, value_str,
                  get_error_string(res));
    } else if (LOGDEBUG) {
        log_debug("\nSend test edit with msgid=%u OK  %s %s = %s.\n",
                  msgid,
                  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

db_api_add_edit

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

db_api_add_edit

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.

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>