NMDA

The server supports some Network Management Datastore Architecture (NMDA) functionality. This must be enabled by setting the --with-nmda CLI parameter to "true".

If NMDA is enabled then the ietf-netconf-nmda.yang module will be loaded and the <get-data> operation will be available.

NMDA Support

The following components of NMDA are supported by the server:

  • RFC 8342: NMDA

    • ietf-datastores module

    • ietf-origin module

  • RFC 8525: YANG Library

    • /yang-library subtree supported

  • RFC 8526: NETCONF Extensions for the NMDA

    • <get-data> operation

  • RFC 8527: RESTCONF Extensions to Support the NMDA

    • These resources {+restconf}/ds/<datastore> are available

    • Introduced in the 23.10T-3 release

The following components of NMDA are not supported:

  • NETCONF <edit-data> operation

    • No useful functionality unless and until dynamic datastores supported

Instrumentation Changes for NMDA

The main difference for SIL code developers is that the server now supports GET2 callback functions for 'config=true' data nodes. The operational values of configuration data nodes must be provided by SIL or SIL-SA GET2 callback functions.

If the --sil-nmda and --sil-get2 flags are used with any of the 'make_sil_dir*' scripts, then yangdump-pro will generate code for NMDA. This consists of GET2 callbacks for the 'config=true' nodes, register callbacks, and unregister callbacks for these new callbacks.

There are only 2 minor differences between a GET2 callback for the operational values of configuration data and regular GET2 callbacks:

  1. datastore parameter: The NMDA datastore being accessed is provided within the GET2 control block parameter (get2cb). Only the <operational> datastore will be accessed using GET2 but in the future other datastores will be able to use GET2 callbacks as well.

  2. origin return value: The GET2 callback is expected to set the NMDA origin property for the returned data. This can be set for a P-container, list, leaf, or leaf-list. The server will use this meta-data to support the <get-data> operation.

The return origin value is set 2 ways:

  • GETCB_GET2_ORIGIN macro to set origin for the list or P-container

  • 'val_set_origin' function used to set the origin for a child node returned with 'getcb_add_return_val' function.

NMDA APIs For GET2 Callbacks

The NMDA Datastore Enumeration is defined in ncx/ncxtypes.h:

enum ncx_nmda_ds_t

internal enumerations for standard NMDA datastores

Values:

enumerator NCX_NMDA_DS_NONE

not set

enumerator NCX_NMDA_DS_CANDIDATE

candidate datastore

enumerator NCX_NMDA_DS_RUNNING

running datastore

enumerator NCX_NMDA_DS_STARTUP

startup datastore

enumerator NCX_NMDA_DS_INTENDED

intended datastore

enumerator NCX_NMDA_DS_OPERATIONAL

operational datastore

The NMDA Origin Enumeration is defined in ncx/ncxtypes.h:

enum ncx_nmda_origin_t

internal enumerations for standard NMDA origins

Values:

enumerator NCX_NMDA_ORIGIN_NONE

not set

enumerator NCX_NMDA_ORIGIN_INTENDED

intended origin

enumerator NCX_NMDA_ORIGIN_DYNAMIC

dynamic origin

enumerator NCX_NMDA_ORIGIN_SYSTEM

system-set origin

enumerator NCX_NMDA_ORIGIN_LEARNED

learned origin

enumerator NCX_NMDA_ORIGIN_DEFAULT

set by default origin

enumerator NCX_NMDA_ORIGIN_UNKNOWN

unknown origin

The NMDA GET2 function is expected to set the "origin" property before returning.

void val_set_origin(val_value_t *val, ncx_nmda_origin_t origin)

Set the NMDA value origin.

Parameters:
  • val -- datastore val to set the origin for

  • origin -- origin enum to set

The 'ncx_nmda_params_t' struct contains the NMDA <get-data> state during a GET2 operation. This structure should not need to be accessed directly

struct ncx_nmda_params_t

internal NMDA get-data state parameters; rest of parameter are stored directly in the xml_msg_hdr_t using existing params

Public Members

dlq_hdr_t origin_filterQ

Q of ncx_nmda_origin_filter_t.

boolean origin_filter_set

T: origin filter is being used.

boolean origin_filter_negated

T: origin filter is negated.

boolean config_filter_set

config content filter used

boolean config_filter

content filter (config or non-config)

boolean with_origin

add origin attributes

ncx_nmda_ds_t nmda_ds

NMDA datastore to use.

ncx_cfg_t cfg_ds

internal config ID to use

Example NMDA GET2 Function


/********************************************************************
* FUNCTION u_taddress_addresses_address_get
*
* Get database object callback for list address
* NMDA GET2 for Operational Data
* Path: /addresses/address
* Fill in 'get2cb' response fields
*
* INPUTS:
*     see ncx/getcb.h for details (getcb_fn2_t)
*
* RETURNS:
*     error status
********************************************************************/
status_t u_taddress_addresses_address_get (
    getcb_get2_t *get2cb,
    const xmlChar *k_addresses_address_last_name,
    boolean last_name_fixed,
    boolean last_name_present,
    const xmlChar *k_addresses_address_first_name,
    boolean first_name_fixed,
    boolean first_name_present)
{

    if (LOGDEBUG) {
        log_debug("\nEnter u_taddress_addresses_address_get");
    }

    boolean getnext = FALSE;

    /* check the callback mode type */
    getcb_mode_t cbmode = GETCB_GET2_CBMODE(get2cb);
    switch (cbmode) {
    case GETCB_GET_VALUE:
        break;
    case GETCB_GETNEXT_VALUE:
        getnext = TRUE;
        break;
    default:
        return SET_ERROR(ERR_INTERNAL_VAL);
    }


    /* TBD: GET2 callbacks for any datastore; expect operational */
    ncx_nmda_ds_t datastore = GETCB_GET2_DATASTORE(get2cb);
    (void)datastore;  // REMOVE THIS LINE IF datastore USED

    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
    status_t res = NO_ERR;

    uint32 max_entries = GETCB_GET2_MAX_ENTRIES(get2cb);
    (void)max_entries;  // REMOVE THIS LINE IF max_entries USED


    /* For GET, find the entry that matches the key values
     * For GETNEXT, find the entry that matches the next key value
     * If the 'present' flag is false then return first key instance
     * If the 'fixed' flag is true then no GETNEXT advance for the key
     * Create a new return key val_value_t, then getcb_add_return_key */

    /***** ADD RETURN KEYS AND REMOVE THIS COMMENT ****/

    int retidx = -1;

    if (getnext) {
        boolean do_lookup = TRUE;
        if (!last_name_present && !first_name_present) {
            retidx = 0;  // GETNEXT first
            do_lookup = FALSE;
        } else if (last_name_present && first_name_present) {
            if (last_name_fixed && first_name_fixed) {
                // no next entry for fixed keys
                do_lookup = FALSE;
            } // else lookup the next entry of both keys
        } // else lookup the next entry without all keys

        if (do_lookup) {
            retidx = find_next_address(k_addresses_address_last_name,
                                       last_name_fixed,
                                       last_name_present,
                                       k_addresses_address_first_name,
                                       first_name_fixed,
                                       first_name_present);
        }
    } else {
        if (!last_name_present && !first_name_present) {
            retidx = 0;  // GETNEXT first
        } else if (last_name_present && first_name_present) {
            retidx = find_exact_address(k_addresses_address_last_name,
                                        k_addresses_address_first_name);
        } // else cannot lookup the exact entry without all keys
    }

    address_t *a = NULL;
    if (retidx >= 0) {
        a = &addresses[retidx];
        val_value_t *keyval =
            agt_make_leaf2(obj,
                           y_taddress_M_taddress,
                           y_taddress_N_last_name,
                           (const xmlChar *)a->last_name,
                           &res);
        if (keyval) {
            getcb_add_return_key(get2cb, keyval);
        } else {
            return res;
        }

        keyval =
            agt_make_leaf2(obj,
                           y_taddress_M_taddress,
                           y_taddress_N_first_name,
                           (const xmlChar *)a->first_name,
                           &res);
        if (keyval) {
            getcb_add_return_key(get2cb, keyval);
        } else {
            return res;
        }
    }

    if (GETCB_GET2_FIRST_RETURN_KEY(get2cb) == NULL) {
        return ERR_NCX_NO_INSTANCE;
    }

    /* Set origin to the correct enumeration found in ncxtypes.h */
    ncx_nmda_origin_t origin = NCX_NMDA_ORIGIN_INTENDED;
    GETCB_GET2_ORIGIN(get2cb) = origin;

    /* optional: check if any content-match nodes are present */
    boolean match_test_done = FALSE;
    val_value_t *match_val = GETCB_GET2_FIRST_MATCH(get2cb);
    for (; match_val; match_val =
        GETCB_GET2_NEXT_MATCH(get2cb, match_val)) {

        /**** CHECK CONTENT NODES AGAINST THIS ENTRY ***/

    }
    GETCB_GET2_MATCH_TEST_DONE(get2cb) = match_test_done;

    /* For GETNEXT, set the more_data flag to TRUE */
    boolean more_data = FALSE;

    /**** SET more_data FLAG ****/
    if (retidx < MAX_ADDRESS) {
        more_data = TRUE;
    }
    GETCB_GET2_MORE_DATA(get2cb) = more_data;

    /* go through all the requested terminal child objects */
    obj_template_t *childobj =
        getcb_first_requested_child(get2cb, obj);
    for (; childobj; childobj =
        getcb_next_requested_child(get2cb, childobj)) {

        const xmlChar *name = obj_get_name(childobj);

        /* Retrieve the value of this terminal node and
         * add the NMDA origin with val_set_origin and
         * add with getcb_add_return_val */

        val_value_t *retval = NULL;

        if (!xml_strcmp(name, y_taddress_N_street)) {
            /* leaf street (string) */
            retval = val_make_simval_obj(childobj,
                                         (const xmlChar *)a->street,
                                         &res);
        } else if (!xml_strcmp(name, y_taddress_N_city)) {
            /* leaf city (string) */
            retval = val_make_simval_obj(childobj,
                                         (const xmlChar *)a->city,
                                         &res);
        } else if (!xml_strcmp(name, y_taddress_N_zipcode)) {
            /* leaf zipcode (string) */
            retval = val_make_simval_obj(childobj,
                                         (const xmlChar *)a->zipcode,
                                         &res);
            if (retval) {
                val_set_origin(retval, NCX_NMDA_ORIGIN_LEARNED);
            }
        }
        if (retval) {
            getcb_add_return_val(get2cb, retval);
        } else {
            return res;
        }
    }

    return res;

} /* u_taddress_addresses_address_get */

Highlights:

Datastore parameter passed to callback but it can be ignored for now. The value will be ‘operational’

/* GET2 callbacks for any datastore; expect operational */
ncx_nmda_ds_t datastore = GETCB_GET2_DATASTORE(get2cb);
(void)datastore;  // REMOVE THIS LINE IF datastore USED

Return origin value set for the list entry:

/* Set origin to the correct enumeration found in ncxtypes.h */
ncx_nmda_origin_t origin = NCX_NMDA_ORIGIN_INTENDED;
GETCB_GET2_ORIGIN(get2cb) = origin;

Return origin value set for a leaf or leaf-list child node returned within a GET2 callback:

val_set_origin(retval, NCX_NMDA_ORIGIN_LEARNED);