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: 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
  • --set-log-level: will cause a <set-log-level> request to be sent to the server
  • --subsys-id: specifies the subsystem identifier to use when registering with the netconfd-pro server
  • --withdef: cause a <getconfig> request to be sent to the server including the <withdef> parameter set to true
  • --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:

  • db_api_register_service: register services with the Ycontrol layer

  • db_api_service_ready: make sure the service is in ready state

  • 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

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

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

For more example on how to INSERT or MOVE with help of db-api-app refer to How do I insert or move list entries?

The following functions declarations can be used for reference:


extern status_t
    db_api_register_service (void);


extern boolean
    db_api_service_ready (void);


extern status_t
    db_api_send_edit (const xmlChar *edit_target,
                      const xmlChar *edit_operation,
                      const xmlChar *edit_xml_value);


extern status_t
    db_api_send_edit_ex (const xmlChar *edit_target,
                         const xmlChar *edit_operation,
                         const xmlChar *edit_xml_value,
                         const xmlChar *patch_id_str,
                         boolean system_edit);

extern status_t
    db_api_send_edit_full (const xmlChar *edit_target,
                           const xmlChar *edit_operation,
                           const xmlChar *edit_xml_value,
                           const xmlChar *patch_id_str,
                           boolean system_edit,
                           const xmlChar *insert_point,
                           const xmlChar *insert_where);

extern 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)

extern status_t
    db_api_check_edit (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 following function exemplifies how to send the MERGE operation to the server:


/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function.
*
*********************************************************************/
static void
    send_test_edit (void)
{
    /* mef-cfm test */
    const xmlChar *path_str = (const xmlChar *)"/maintenance-domain/mdTest";
    const xmlChar *operation_str = (const xmlChar *)"merge";
    const xmlChar *value_str = (const xmlChar *)
        "<cfm:maintenance-domain xmlns:cfm='http://metroethernetforum.org/"
        "ns/yang/mef-cfm'><cfm:id>mdTest</cfm:id><cfm:name-type>character"
        "-string</cfm:name-type><cfm:name>mdTestName</cfm:name><cfm:md-le"
        "vel>4</cfm:md-level><cfm:mhf-creation>none</cfm:mhf-creation>"
        "<cfm:id-permission>none</cfm:id-permission></cfm:maintenance"
        "-domain>";

    const xmlChar *patch_id_str = NULL;
    boolean system_edit = FALSE;
    status_t res = db_api_send_edit_ex(path_str,
                                       operation_str,
                                       value_str,
                                       patch_id_str,
                                       system_edit);
    if (res != NO_ERR) {
        log_error("\nSend test edit failed %s %s = %s (%s)\n",
                  operation_str, path_str, value_str,
                  get_error_string(res));
    } else if (LOGDEBUG) {
        log_debug("\nSend test edit OK  %s %s = %s\n",
                  operation_str, path_str, value_str);
    }

}  /* send_test_edit */



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 *)"/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;

    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);
    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);
    }

    /* 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
            // could ignore or shut down YControl IO loop
            done = TRUE;
        }

        // 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 (complex_edit) {
                /* send the complex test edit */
                send_complex_test_edit();
            } 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 (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.