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.
Starting in 25.10-3: Entire module is supported
NMDA Support
The following components of NMDA are supported by the server:
RFC 8342: Network Management Datastore Architecture (NMDA)
ietf-datastores.yang module
ietf-origin.yang module
-
/yang-librarysubtree supported
RFC 8526: NETCONF Extensions for the NMDA
<edit-data> operation
<get-data> operation
<lock> operation augmentation
<unlock> operation augmentation
<validate> operation augmentation
RFC 8527: RESTCONF Extensions to Support the NMDA
These resources {+restconf}/ds/<datastore> are available
Introduced in the 23.10T-3 release
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:
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.
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
-
enumerator NCX_NMDA_DS_NONE
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
-
enumerator NCX_NMDA_ORIGIN_NONE
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
-
dlq_hdr_t origin_filterQ
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);