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