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.

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.

This application contains example operations only.
It is not intended to be a functional application like sil-sa-app.

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.

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

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

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_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

Simple Database Edits

The "send_edit" functions are used to make a single patch to the configuration.

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)

Create a YANG Patch edit request and send it to the DB-API service on the main server.

Adds insert operation support.

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>"

Uses db_api_send_edit_full2 with the following defaults:

  • skip_sil is TRUE

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

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 -- address of return error string (may be NULL)

Return values

*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

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.

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

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 -- address of return control block

Return values

*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 -- address of return control block

Return values

*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 2 API functions to retrieve YANG data from the server:

db_api_send_getconfig: Used to retrieve the entire <running> datastore

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

db_api_send_getfilter: Used to retrieve configuration data from the <running> datastore or operational data from the system. An XPath filter can be used to select data instead of retrieving all data nodes.

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

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

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>

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 -- file specification to contain the XML instance document retrieved from the server (must be present)

Return values

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>