The DB-API database variant allows an external process to initiate database edits which are processed by the server as user or system edits.

The DB-API functions described in this article 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.

The DB-API subsystem is getting registered and started by db-api-app application.



 Configuration Properties


The DB-API subsystem represents following 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 netconfd-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

  • The db-api-app application is 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


CLI Parameters


db-api-app CLI parameters can be entered in any order, and have the form \fB--name[=value]\fP

  • --count: parameter can be used to specify number of times an edit or getconfig request will be sent to the server
  • --db-lock-change-state-time: this parameter can be used to change the default frequency of sending <db-lock> and <db-unlock> requests for --db-lock-test
  • --db-lock-test: used to start the DB-API in a special test mode. The server must be started with --with-db-lock=true
    flag. The server will send <db-lock> and <db-unlock> requests to the db-api subsystem
  • --db-lock-start-wait-time: used to change the default wait time before sending the first <db-lock> request to the server for --db-lock-test. Default value is 30 seconds
  • --enter: will cause an <enter-maintmode> request to be sent to the server
  • --exit: will cause an <exit-maintmode> request to be sent to the server
  • --filespec: the --filespec parameter is required if the --getconfig parameter is present or if the --subrpc parameter is  present. Specifies the file location to save the <config> response from the server
  • --getconfig: will cause a <getconfig> request to be sent to the server
  • --log-level,  --log and other logging parameters are also supported
  • --in-filespec: The --in-filespec parameter is optional if the --subrpc parameter is present. This specifies the XML file location to read the RPC input in the same form as used in the <rpc> element for NETCONF
  • --set-log-level: will cause a <set-log-level> request to be sent to the server
  • --subrpc: The --subrpc parameter is used to send an RPC request to the server.  This parameter specifies the name of the RPC  operation. If any input is needed then the --in-filespec parameter must be present. The --filespec parameter must be present and specifies the name of the file to store the RPC output from the server. The --user parameter can be used to specify a user name for the RPC operation
  • --subsys-id: specifies the subsystem identifier to use when registering with the netconfd-pro server
  • --user: The --user parameter can be used if the --subrpc parameter is present. It specifies the username for access control purposes that should be used on the server for the RPC operation. If not present then the RPC operation will be invoked as the system user without any access control at all
  • --withdef: cause a <getconfig> request to be sent to the server including the <withdef> parameter set to true
  • --with-state: The --with-state parameter will cause a <getconfig> request to be sent to the server including the <with-state> parameter set  to  true. This will cause all non-configuration data nodes to be returned. If it is missing, the default is not to request these data nodes
  • --xpath-filter: used to add an optional XPath filter to the retrieval operation

DB-API APIs


The DB-API subsystem allows an external process on the same system as netconfd-pro to send database edits to the server. The following list of APIs shows main functions that can be used by the db-api-app:


Simple Edits API:

  • db_api_send_edit: create a YANG Patch edit request and send it to the DB-API service on the main server

  • db_api_send_edit_ex: the same as above but with an option to specify whether the edit is from the system and should bypass access control enforcement

  • db_api_send_edit_full: the same as above but with more options to specify insertion parameters

  • db_api_send_edit_full2: the same as above but with an option to specify whether to skip the SIL and SIL-SA callbacks on the main server or not


/* Simple edits APIs */
status_t
    db_api_send_edit (const xmlChar *edit_target,
                      const xmlChar *edit_operation,
                      const xmlChar *edit_xml_value)

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)

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)

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)

Complex Edits API:

  • db_api_start_patch: start a YANG Patch request that can have multiple edits

  • db_api_start_patch2: start a YANG Patch request that can have multiple edits. Set additional parameters with this API, such as skip SIL callbacks and add comments to the edits

  • db_api_add_edit: create an edit request and add to YANG Patch in progress

  • db_api_send_patch: send a previously created patch request

  • db_api_free_patch: free a patch control block after the complex edit was sent to the server


/* Complex edits APIs */
status_t
    db_api_start_patch (const xmlChar *patch_id_str,
                        boolean system_edit,
                        yang_patch_cb_t **retcb)

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)

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)

status_t
    db_api_send_patch (yang_patch_cb_t *pcb)

void
    db_api_free_patch (yang_patch_cb_t *pcb)

Subsystem RPC and Action requests API:

  • db_api_send_subrpc_request: create a <subrpc-request> request and send it to the main server


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)

Filter retrieval API:

  • db_api_send_getfilter: create a <getconfig> request and send it to the main server. An XPath filter can be sent as a parameter

  • db_api_send_getconfig: create a <getconfig> request and send it to the main server


status_t
    db_api_send_getconfig (const xmlChar *filespec,
                           boolean withdef)

status_t
    db_api_send_getfilter (const xmlChar *filespec,
                           boolean withdef,
                           boolean get_config,
                           const xmlChar *xpath_filter)

Set log level API:

  • db_api_send_set_loglevel: create a <set-log-level> request and send it to the main server


status_t
    db_api_send_set_loglevel (const char *dlevel)

Utility API:

  • db_api_register_service: register services with the YControl layer

  • db_api_service_ready: make sure the service is in ready state

  • db_api_check_edit: check on the status of an edit in progress

  • db_api_check_edit_ex: check on the status of an edit in progress. Get the error string if any

  • ycontrol_check_io: check for input/output and return an <errcode> on ERROR



/* Utility APIs */
status_t
    db_api_register_service (void)

boolean
    db_api_service_ready (void)

status_t
    db_api_check_edit (void)

status_t
    db_api_check_edit_ex (const xmlChar **errstr)

status_t
    ycontrol_check_io (void)


db_api_register_service


This API registers DB-API service with the Ycontrol layer. The following steps are involved during the registration:

  • attempt to load the yumaworks-db-api YANG module
  • attempt to load the yumaworks-attrs extension YANG module
  • register the DB-API service with the YControl layer
    • Register "Start" callback function. When the DB-API service starts the first step is to send a register-request
    • Register "Stop" callback function. Shutdown the DB-API libraries and any transaction in progress
    • Register "Receive" callback function. Handles incoming <db-api> messages
    • Register "Shutdown" callback function. Cleans up profile state

db_api_service_ready


This API checks if DB-API service ready to handle the next request. I twill return TRUE if the state of the service is DB_API_ST_READY. The following status enumerations are possible:

  • DB_API_ST_START: will be set when the DB-API service starts just before it sends a <register-request> message
  • DB_API_ST_STOPPED: will be set when the DB-API service in Shutdown state
  • DB_API_ST_ERROR: will be set when the DB-API handles incoming messages from the server for an <edit-request>, <getconfig>, <register-request> and got an <error> message instead of <ok>
  • DB_API_ST_WAIT_REGISTER: will be set when the DB-API sends a <register-request> message to the server to signal that it waits for the server reply on that message
  • DB_API_ST_READY: will be set when the DB-API handles incoming messages from the server for an <edit-request>, <getconfig>, <register-request> and got an <ok> message
  • DB_API_ST_WAIT_EDIT: will be set when the DB-API sends an <edit-request> message to signal that it waits for the server reply on that message
  • DB_API_ST_WAIT_GETCONFIG: will be set when the DB-API sends an <getconfig> message to signal that it waits for the server reply on that message

db_api_check_edit


This API checks on the status of an edit operation in progress. In may return following results:

  • ERR_NCX_NOT_FOUND: will be set when the final status of an <edit-request> operation is known but no message response was received. The <edit-request> was sent but the server never replied on this request, or the <edit-request> failed even before the request was sent
  • ERR_NCX_SKIPPED: will be set when the final status of an <edit-request> operation is not known yet. The <edit-request> is still in progress
  • NO_ERR: will be set when the last-completed <edit-request> operation was completed with an OK response. The <edit-request> was completed successfully
  • <errcode>: will be set when the last-completed <edit-request> operation was completed with an ERROR response. The <edit-request> FAILED to complete successfully

ycontrol_check_io


This API checks on input/output subsystem loop for the YControl manager and process messages as they are ready/arrived.

It is re

It may return following results:

  • NO_ERR: will be set when the check was successful
  • <errcode>: will be set when the check returns an ERROR. May be due to following
    • attempt to connect to the server failed
    • non-recoverable error occurred



DB-API Examples



The URI (path_str parameter) is yang-api encoded [https://tools.ietf.org/html/draft-bierman-netconf-yang-api-01]. It is not RESTCONF encoded [RFC8040].
So, to specify the list entry you should use "/", e.g.: 
"/interfaces/interface/vlan1" instead of RESTCONF specific list encoding
 "=", which would be "/interfaces/interface=vlan1"


Use module name (NOT PREFIX) to identify specific node in the URI (path_str parameter) that belongs to a specific module,  e.g.:
"/ietf-interfaces:interfaces/ietf-interfaces:interface/vlan1"



The following function exemplifies how to create a new List entry with CREATE operation:


/********************************************************************
* 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)
{
    /* create a new list entry */
    const xmlChar *path_str = (const xmlChar *)"/interface-example:interfaces/interface/keyvalue";
    const xmlChar *operation_str = (const xmlChar *)"create";
    const xmlChar *value_str = NULL;

    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)\n",
                  operation_str, path_str,
                  get_error_string(res));
    } else if (LOGDEBUG) {
        log_debug("\nSend test edit OK  %s %s \n",
                  operation_str, path_str);
    }

}  /* send_test_edit */


OR the MERGE operation can be used as well to create a new list as follows:


/********************************************************************
* 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)
{
    /* Create a new list entry and an extra node at the same time */
    const xmlChar *path_str = (const xmlChar *)"/interface-example:interfaces/interface/keyvalue";
    const xmlChar *operation_str = (const xmlChar *)"merge";
    const xmlChar *value_str = (const xmlChar *)
        "   <interface xmlns='http://netconfcentral.org/ns/interface-example'>"
        "       <name>keyvalue</name>"
        "       <description >somestr</description>"
        "   </interface>";

    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 */


The following function exemplifies how to send the MERGE operation to the server to create a top level complex object:


/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function.
*
*********************************************************************/
static void
    send_test_edit (void)
{
    /* create a top level node; NOTE that hte 'config' must be used
     * in this case.
     */
    const xmlChar *path_str = (const xmlChar *)"/";
    const xmlChar *operation_str = (const xmlChar *)"merge";
    const xmlChar *value_str = (const xmlChar *)
        "<config>"
        "   <interfaces xmlns='http://netconfcentral.org/ns/hooks-test'>"
        "       <interface>"
        "           <name>interface1</name>"
        "          <type>eth0</type>"
        "       </interface>"
        "   </interfaces>"
        "</config>";

    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 */


The following function exemplifies how to send the CREATE operation to the server to create a simple leaf object:


/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function.
*
*********************************************************************/
static void
    send_test_edit (void)
{
    /* create a simple leaf. Note: the value string points to the leaf node */
    const xmlChar *path_str = (const xmlChar *)"/ietf-dhcp:dhcp/server/option/domain-name";
    const xmlChar *operation_str = (const xmlChar *)"create";
    const xmlChar *value_str = (const xmlChar *)
        "<domain-name>benunets.com</domain-name>";

    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 */


The following function exemplifies how to send the DELETE operation to the server:


/********************************************************************
* FUNCTION send_test_edit
*
* This is an example that send delete operation.
*
*********************************************************************/
static void
    send_test_edit (void)
{

    /* NOTE: the path is yang-api encoded [https://tools.ietf.org/html/draft-bierman-netconf-yang-api-01]
     * It is not RESTCONF encoded [RFC8040]
     * So, to specify the list entry you should use "/" instead of RESTCONF
     * specific encoding, which would be "/interfaces/interface=vlan1"
     */
    const xmlChar *path_str = (const xmlChar *)"/ietf-interfaces:interfaces/ietf-interfaces:interface/vlan1";
    const xmlChar *operation_str = (const xmlChar *)"delete";

    /* NOTE: the value in case of deletion is NULL */
    const xmlChar *value_str = NULL;

    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 */


The db-api-app main function example:


/********************************************************************
* FUNCTION main
*
* This is an example main function.
*
* 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

    /* in case db-lock-test is used */
    (void)time(&start_time);
    last_time = 0;
    locked = FALSE;

    const char *subsys = NULL;
    boolean getconfig = false;
    boolean withdef = false;
    boolean with_state = false;
    const char *filespec = NULL;
    const char *xpath_filter = NULL;
    uint32 cnt = 0;
    uint32 max_count = 0;
    uint32 change_time = 7;
    uint32 st_time = 30;
    boolean entermaint = false;
    boolean exitmaint = false;
    const char *dlevel = NULL;
    uint32 maintbits = 0;
    boolean db_lock_test = FALSE;
    boolean edit_full2 = false;
    boolean ycontrol_done = FALSE;
    const char *subrpc = NULL;
    const char *in_filespec = NULL;
    const char *user = NULL;
    uint16 retry_limit = 0;

    status_t res =
        check_cli_parms(argv,
                        &subsys,
                        &getconfig,
                        &withdef,
                        &with_state,
                        &filespec,
                        &xpath_filter,
                        &max_count,
                        &change_time,
                        &st_time,
                        &entermaint,
                        &exitmaint,
                        &dlevel,
                        &maintbits,
                        &db_lock_test,
                        &subrpc,
                        &in_filespec,
                        &user,
                        &retry_limit);
    if (res != NO_ERR) {
        print_usage();
    }

    /* 1) setup yumapro messaging service profile */
    if (res == NO_ERR) {
        if (subsys == NULL) {
            res = ycontrol_init(argc, argv,
                                (const xmlChar *)"subsys1");
        } else {
            res = ycontrol_init(argc, argv,
                                (const xmlChar *)subsys);
        }
        ycontrol_done = TRUE;
    }

    if (getconfig && (filespec==NULL)) {
        res = ERR_NCX_MISSING_PARM;
        print_usage();
    }

    if ((res == NO_ERR) && entermaint && exitmaint) {
        res = ERR_NCX_EXTRA_PARM;
        print_usage();
    }

    /* 2) register services with the control layer */
    if (res == NO_ERR) {
        res = db_api_register_service_ex(db_lock_test);
    }

    /* set the retry limit if provided */
    if ((res == NO_ERR) && (retry_limit > 0)) {
        ycontrol_set_retry_limit(retry_limit);
    }

    /* It is also possible to set the retry_interval but there is
     * no CLI parameter provided for this purposes
     * if (res == NO_ERR) {
     *     ycontrol_set_retry_interval(retry_int_milliseconds);
     * }
     */

    /* 3) do 2nd stage init of the control manager (connect to server) */
    if (res == NO_ERR) {
        res = ycontrol_init2();
    }

    boolean done = FALSE;

    /* pick whether the simple edit or the complex edit is sent */
    boolean complex_edit = FALSE;

    boolean complex_edit2 = 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;
    const xmlChar *error_msg = NULL;

    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
            // need to  shut down YControl IO loop
            done = TRUE;
            continue;
        }

        // Using sleep to represent other program work; remove for real
#ifdef DO_SLEEP
        if (!done && (res == NO_ERR)) {
            useconds_t usleep_val = 100000;  // 100K micro-sec == 1/10 sec
            (void)usleep(usleep_val);
        }
#endif  // DO_SLEEP

        if (db_api_service_ready() && !test_done) {
            if (db_lock_test) {
                check_db_lock_test(st_time, change_time, &done);
                continue;
            } else if (getconfig) {
                send_test_getconfig(filespec,
                                    withdef,
                                    with_state,
                                    xpath_filter);
            } else if (entermaint) {
                send_test_enter_maintmode(maintbits);
            } else if (exitmaint) {
                send_test_exit_maintmode();
            } else if (dlevel) {
                send_test_set_loglevel(dlevel);
            } else if (subrpc) {
                res = send_test_subrpc(subrpc,
                                       in_filespec,
                                       filespec,
                                       user);
            } else if (complex_edit) {
                /* send the complex test edit */
                send_complex_test_edit();
            } else if (complex_edit2) {
                send_complex_test_edit2(cnt);
            } else if (edit_full2) {
                /*send the test for edit with skip_sil*/
                send_test_edit_full2();
            } else {
                /* send the simple test edit */
                send_test_edit();
            }

            test_done = TRUE;
        } else if (db_api_service_ready() && test_done) {
            /* check the test edit */
            res = db_api_check_edit_ex(&error_msg);
            if (res == NO_ERR) {
                log_info("\nTest %u succeeded\n", cnt+1);

                if (cnt < max_count) {
                    cnt++;
                    if (complex_edit2) {
                        log_info("\nStart send complex edit %u", cnt);
                        send_complex_test_edit2(cnt);
                    } else {
                        log_info("\nStart send edit %u", cnt);
                        send_test_edit();
                    }
                } else {
                    done = TRUE;
                }
            } else {
                if (res == ERR_NCX_SKIPPED) {
                    res = ERR_NCX_OPERATION_FAILED;
                }

                log_info("\nTest failed with error: %d '%s'\n\n",
                         res, error_msg ? error_msg :
                         (const xmlChar *)get_error_string(res));

                done = TRUE;
            }
        }
    }

    /* 5) cleanup the control layer before exit */
    if (ycontrol_done) {
        ycontrol_cleanup();
    }

#ifdef MEMORY_DEBUG
    muntrace();
#endif

    return (int)res;

}  /* main */


Refer to /usr/share/yumapro/src/db-api-app/main.c for more information and examples.