Operational Data

Operational data is data that is set as “config false” in the YANG file. Operational state and statistics are common forms of operational data. These nodes are supported within SIL code in three ways:

  1. object template based “GET2” callback

  2. value node based static operational data

  3. value node based "GET1" virtual operational data

GET1 Callback

In almost all cases it is better to use GET2 instead of GET1 callbacks!

This section describes how to manage non-configurable or just operational data, what type of callbacks are available for non-configurable nodes and how to utilize these type of callbacks.

Operational data nodes are supported within SIL code in three ways:

  1. value node based virtual data (GET1 callback)

  2. value node based static data (GET1 callback)

  3. object template based GET2 callback

GET1 Callback Function

The following function template definition is used for GET1 callback functions:

typedef status_t (*getcb_fn_t)(ses_cb_t *scb, getcb_mode_t cbmode, const val_value_t *virval, val_value_t *dstval)

GET1 Callback function for agent node get handler.

Param scb

session that issued the get (may be NULL) can be used for access control purposes

Param cbmode

reason for the callback

Param virval

place-holder node in the data model for this virtual value node

Param dstval

pointer to value output struct

Retval *dstval

should be filled in, depending on the callback reason

Return

status

The 'scb' pointer represents a session control block structure that is defined in the ncx/ses.h. This control block is primarily used for error reporting.

The 'cbmode' enumeration identifies the callback mode for the GET request, that is defined in the ncx/getcb.h. This control block specifies what retrieval type has this GET callback. GET1 callback should have GETCB_GET_VALUE type, if the type is different, the error should be reported, as specified in the examples later in this section.

The 'virval' pointer represents a data node as a place-holder node in the data model for this virtual value node. The GET1 callback function pointer is stored in this virtual value.

The 'dstval' pointer represents the data node that is being filled in. The GET1 callback is expected to fill in this value node and set the value.

Create a Virtual Node to Use GET1

The GET1 callback design is only supported for SIL code (running in the netconfd-pro process). A virtual data node has to be installed within the <running> datastore for every instance required. The GET1 callback is only supported for operational data. The server expects all configuration data to be a plain val_value_t node without a GET1 callback.

A GET1 callback is required for each "config false" leaf. In the example YANG module, the "observed-speed" leaf needs a GET1 callback.

GET1 Leaf Initialization

val_value_t *agt_make_virtual_leaf(obj_template_t *parentobj, const xmlChar *leafname, getcb_fn_t callbackfn, status_t *res)

make a val_value_t struct for a specified virtual leaf or leaf-list

This is a SIL only API for creating a GET1 callback.

Use GET2 callbacks instead! GET1 is deprecated!

!!! This function works on all value node types !!! !!! Check for leaf or leaf-list removed !!!

Parameters
  • parentobj -- parent object to find child leaf object

  • leafname -- name of leaf to find (namespace hardwired)

  • callbackfn -- get callback function to install

  • res -- address of return status

Return values

*res -- return status

Returns

malloced value struct or NULL if some error

/********************************************************************
* FUNCTION create_readonly_leaf
*
* Make read-only child nodes
* Path: /interfaces/interface/observed-speed
*
* INPUTS:
*     parentval == the parent struct to use for new child nodes
*
* RETURNS:
*     error status
********************************************************************/
static status_t
    create_readonly_leaf (val_value_t *parentval)
{
    status_t res = NO_ERR;

    /* add /interfaces/interface/observed-speed */
    val_value_t *childval =
        agt_make_virtual_leaf(VAL_OBJ(parentval),
                              (const xmlChar *)"observed-speed",
                              observed_speed_get,
                              &res);
    if (childval != NULL) {
        res = val_child_add(childval, parentval);
        if (res != NO_ERR) {
            val_free_value(childval);
        }
    }

    return res;

} /* create_readonly_leaf */

GET1 Callback Example

The following example shows the GET1 callback that will be called when the value for the "observed-speed" leaf is needed.

GET1 Callback Example

status_t val_set_simval_obj(val_value_t *val, obj_template_t *obj, const xmlChar *valstr)

Set an initialized val_value_t as a simple type.

Set a pre-initialized val_value_t as a simple type from an object template instead of individual fields Calls val_set_simval with the object settings

This API should be used by GET1 callbacks where 'retval' has been allocated but not initialized yet

Parameters
  • val -- value struct to set

  • obj -- object template to use

  • valstr -- simple value encoded as a string to set

Returns

status

/********************************************************************
* FUNCTION  observed_speed_get
*
* Callback function for agent node get handler
*
* Fill in 'dstval' contents
*
********************************************************************/
static status_t
    observed_speed_get (ses_cb_t *scb,
                            getcb_mode_t cbmode,
                            const val_value_t *virval,
                            val_value_t *dstval)
{
    status_t res = NO_ERR;

    /* remove the next line if scb is used */
    (void)scb;

    /* remove the next line if virval is used */
    (void)virval;

    if (cbmode != GETCB_GET_VALUE) {
        return ERR_NCX_OPERATION_NOT_SUPPORTED;
    }

    /* set the speed var here */
    const xmlChar *speed = (const xmlChar *)"1000";
    res = val_set_simval_obj(dstval, dstval->obj, speed);

    return res;

} /* observed_speed_get */

GET1 Retrieval Example

The client might send the following <get> request:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <interfaces xmlns="http://yumaworks.com/ns/interfaces">
        <interface>
          <name>supported</name>
        </interface>
      </interfaces>
    </filter>
  </get>
</rpc>

The server may reply with:

<rpc-reply message-id="101"
           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
   <interfaces xmlns="http://yumaworks.com/ns/interfaces">
     <interface>
       <name>supported</name>
       <observed-speed>1000</observed-speed>
     </interface>
   </interfaces>
</rpc-reply>

GET2 Callback

A GET2 callback function is usually mapped to a single data node. The server will issue “get” and “getnext” requests via this API, and the callback code will return value nodes for the data it is managing. These data nodes are not maintained in the server Data Tree. They are not cached in the server.

This API uses 1 callback per object template. The server will issue “get” and “getnext” requests via the API, and the callback code will return value nodes for the data is is managing. These data nodes are not maintained in the server data tree. They are not cached in the server.

Local SIL and remote SIL-SA callbacks are supported for operational data. These callbacks are expected to return some or all of the terminal child nodes (leaf, leaf-list, anyxml) when invoked.

GET2 callbacks for "conventional" Configuration data are not supported at this time. If NMDA is used, then GET2 callbacks for configuration data is supported for the <operational> datastore.

Which Nodes Have GET2 Callbacks

Complex child nodes are expected to have their own GET2 callbacks registered. The GET2 callbacks are registered with an API similar to the EDIT callback registration.

  • The following node types are expected to have GET2 callbacks registered:

    • choice: expected to return the name of the active case and terminal nodes from the active case

    • container: expected to return child terminal nodes

    • list: expected to return list keys and maybe other terminal nodes

  • Callbacks for terminal nodes are allowed, but only if their parent is a config=true node.

  • The server will expect configuration data nodes to be present in the target datastore.

  • If the retrieval request includes config=false nodes, then the server will check each child node that is config=false for a GET2 callback function.

  • If it exists, then it will be used for retrieval of that data node.

  • If not, the current config=true node will be checked for child nodes (static or dynamic operational data).

GET2 Callback Function

The following function template definition is used for GET2 callback functions:

typedef status_t (*getcb_fn2_t)(ses_cb_t *scb, xml_msg_hdr_t *msg, getcb_get2_t *get2cb)

GET2 PRODUCER FUNCTION.

Callback function for server object handler get2 callback Used to provide main and/or subsystem retrieval of instances of a specific named object

Param scb

session control block making the request

Param msg

incoming XML message header

Param get2cb

get2 control block for this callback request

Retval return_keyQ

is full of any new keys added for this entry &#8212; only if 'obj' is a list

Retval return_valQ

is filled with malloced val_value_t nodes If object tested is a choice the a backptr to a constant string containing the case name that is active

Return

status NO_ERR if executed OK and found OK

ERR_NCX_NO_INSTANCE warning if no instance found

GET2 Control Block

The 'getcb_get2_t' structure contains all the input, output, and state data used for GET2 transactions.

struct getcb_get2_t

GET2 control block.

Public Members

dlq_hdr_t qhdr

queue header

xmlChar *txid_str

transaction ID string

obj_template_t *obj

object template containing this callback

val_nodetest_fn_t testfn

value node test function (may be obsolete for get2)

dlq_hdr_t keyQ

Q of malloced val_value_t; 1 entry for each key leaf includes the ancestor keys and keys for the current list (if applicable) If the set of keys for the current list is incomplete, then the provided keys are 'fixed'.

dlq_hdr_t matchQ

Q of malloced val_value_t; 1 entry for each content-match leaf in the subtree or XPath filter; these leafs only apply to the current object.

dlq_hdr_t selectQ

Q of malloced getcb_get2_select_t; 1 entry for each child select node of the object in this get2cb these select nodes only apply to the current object if the 'select_only' flag is set then this queue is used.

An empty selectQ means only keys should be returned (for a list) For a choice the active case must be set. For a P-container, the callback has to return NO_ERR instead of ERR_NCX_NO_INSTANCE

The get2 callback can ignore the select_only flag and selectQ The extra returned values will be ignored by the caller. Q of getcb_get2_select_t

getcb_mode_t cbmode

reason for the callback (get or getnext)

uint32 max_entries

max instances to get 0 for all entries, 1 .

. N for specific max

uint32 max_levels

0 for all levels, 1 .

. N for max max_levels forced to 1 if testmode=TRUE

boolean oper_data

TRUE to get operational data.

boolean config_data

TBD: TRUE to get configuration data not supported yet! All config must be in the cfg->root val_value_t tree.

boolean expand_varexpr

variable expressions: TRUE if a varexpr node should be expanded; FALSE if printed as an expression

boolean keys_only

keys-only: TRUE if only the key leafs are desired from list objects; FALSE if normal retrieval

boolean select_only

select: TRUE if only the selectQ child nodes are desired from the parent for this callback; FALSE if no select mode retrieval

boolean with_defaults

with_defaults: TRUE if default nodes should be returned this is needed for operational data because the server does not process when-stmts on operational data, so the instrumentation has to return operational defaults if they are requested

getcb_api_mode_t api_mode

api_mode: consumer API callback mode

boolean with_origin

get-request with-origin flag

val_value_t *start_add_key

first key val node that was moved from the return_keyQ to the keyQ for nested nodes to process; needs to be removed after each nested list is processed

boolean acmtest_result

acmtest result used for NACM check

boolean last_sibling

flag to track JSON state

boolean isfirst_nokey

flag to track JSON state

boolean first_llsibling

leaf-list siblings

boolean last_llsibling

leaf-list siblings

boolean islast

Used only for AIO RESTCONF processing.

also used for JSON subtree proc

boolean isfirst

also used for JSON subtree proc

boolean aio_done

AIO processing is completed.

boolean first_child

Used for JSON subtree proccessing.

first child done

boolean first_sibling

first sibling done

boolean force_lastsibling

force JSON state

boolean force_lastsib_value

force JSON state

boolean force_array_obj

force JSON array output

boolean finish_list

Used for CBOR processing to know if the special finish-list callback mode is needed.

boolean more_data

set by the callback function if there are more instances that follow the specified instance; this is a global flag that applies to the entire response

dlq_hdr_t getbulkQ

set by the callback function if this is a list callback and multiple entries are returned; this is a global queue that applies to the entire response

Q of getcb_get2_getbulk_t

xmlChar *active_case_modname

set by a choice test_mode callback to return the name of the active case; If the active_case_modname is NULL then the module name of the parent choice-stmt will be used

xmlChar *active_case

name of the active case

dlq_hdr_t return_keyQ

Q of malloced val_value_t.

dlq_hdr_t return_valQ

Q of malloced val_value_t.

val_value_t return_val

if just 1 instance is returned, then the return_val will be used; if the btyp is set to something other than NCX_BT_NONE, then the server will use this value; otherwise 1 or more malloced val_value_t structs are expected in the return_valQ

THIS STRUCT MUST NOT BE USED IN GETBULK MODE

dlq_hdr_t responseQ

if this is a request that causes multiple responses then the responseQ will have each response get2cb

boolean match_test_done

content-match done flag ignored unless the matchQ is non-empty If TRUE.

indicates that the callback performed the requested content-match tests and the returned output is post-filter If FALSE, then the content-match tests were not done

boolean wrote_some_lists

If TRUE then Last list failed BUT some of the entries were written successfully.

So need to write comma after this list object.

val_value_t *parent_val

save parent backtrs for when-stmt processing of GET2 data

struct getcb_get2_t_ *parent_cb

backptr to parent CB

dlq_hdr_t return_aioQ

Q of malloced val_value_t.

Used for sil-aio-get2 extension only. Callbacks will fill in data into this Queue. The callback will be called only once and the server will expect the return_aioQ to be filled in with the whole set of children.

ncx_nmda_ds_t nmda_ds

save NMDA datastore for GET operational

ncx_nmda_origin_t nmda_origin

caller will set the return nmda_origin

ncx_msg_encoding_t aio_encoding

In AIO GET2 callback is used with JSON/XML buffers the encoding will represent corresponding encoding type.

xmlChar *aio_return_buff

AIO XML or JSON malloced buffer from callback to use.

Must be freed after it is converted to val value.

ncx_sm_mpid_t *sm_mpid

If schema-mount is in use then the MPID is set for GET2 callbacks that have a ancestor that is a rootcb (MPID) This is a backpointer NOT a malloced struct.

boolean sm_mpid_malloced

The MPID will be malloced on the SIL-SA side (TRUE) otherwise set to FALSE if the sm_mpid is a backptr.

void *user_data_ref

User Data Reference Can be used by GET2 callbacks to store and reference a pointer during the GET2 callback walk through lists and data structures.

  • Initially set to NULL for the first get2cb created during a walk

  • Up to the GET2 callback to set this field using the GETCB_GET2_USER_DATA_REF() macro

  • The server will copy the value from the parent get2cb when creating a child get2cb during a tree traversal

  • The pointer must not be malloced and stored only here. There is no way to cleanup this pointer and the GET2 walk may exit on error at any time

uint32 user_data_index

User Data Index Can be used by GET2 callbacks to store and reference an index during the GET2 callback walk through lists and data structures.

  • Many code scrubbers do not like casting pointers as numbers

  • Initially set to 0 for the first get2cb created during a walk

  • Up to the GET2 callback to set this field using the GETCB_GET2_USER_DATA_INDEX() macro

  • The server will copy the value from the parent get2cb when creating a child get2cb during a tree traversal

This data structure contains the fields used by the server and the callback to exchange data.

  • getcb Request Fields:

    • set by 'getcb' before the GET2 callback is invoked

    • txid_str: GET2 transaction IF string

      • GETCB_GET2_TXID_STR(cb) access macro

    • obj: pointer to obj_template_t for the object being retrieved

      • GETCB_GET2_OBJ(cb) access macro

    • testfn: test filter function for checking nodes before processing (may be NULL)

      • GETCB_GET2_TESTFN access macro

    • keyQ: queue of val_value_t structs representing the key leafs for the request. These are keys for the ancestor-or-self lists for this object.

      • Fixed keys are identified using the VAL_IS_FIXED_VALUE(keyval) macro

      • The GET2 callback must only return entries matching the provided key values

      • If no value is provided for a key then the first value is requested

      • GETCB_GET2_KEYQ(cb) access macro

      • GETCB_GET2_FIRST_KEY(cb) macro to return the first val_value_t in the queue

      • GETCB_GET2_NEXT_KEY(cb, cur) macro to return the next val_value_t in the queue

    • matchQ: queue of val_value_t structs representing non-key leaf content-match nodes

      • These are always simple child nodes of the object identified by 'obj'

      • The content-match test is optional to implement by the GET2 callback

      • If the content-match tests have been performed by the GET2 callback, then the return flag is set with the GETCB_GET2_CONTENT_MATCH_DONE() macro

      • All entries in the matchQ must match the entry being returned. If not, then an ERR_NCX_NO_INSTANCE status is returned instead

      • GETCB_GET2_MATCHQ(cb) access macro

      • GETCB_GET2_FIRST_MATCH(cb) macro to return the first val_value_t in the queue

      • GETCB_GET2_NEXT_MATCH(cb, cur) macro to return the next val_value_t in the queue

    • cbmode: the GET2 callback mode

      • GETCB_GET_VALUE for get-exact

      • GETCB_GETNEXT_VALUE for get-next

      • GETCB_GET2_CBMODE(cb) access macro

    • max_entries: maximum number of values to return (for getnext mode)

      • 0 == return all entries

      • 1 – N == return at most this number of entries

        • Currently set to 1 by the server

      • GETCB_GET2_MAX_ENTRIES(cb) access macro

    • max_levels: maximum depth of subtrees parameter to determine if the maximum level has been reached

      • GETCB_GET2_MAX_LEVELS(cb) access macro

    • oper_data: true if config=false data should be returned

      • GETCB_GET2_OPER_DATA(cb) access macro

    • config_data: true if config=true data should be returned

      • GETCB_GET2_CONFIG_DATA(cb) access macro

    • expand_varexpr: true is variable expressions should be expanded; false to print the variable instead

      • GETCB_GET2_EXPAND_VAREXPR(cb) access macro

    • keys_only: true if only list keys should be returned; false for all terminal nodes

      • GETCB_GET2_KEYS_ONLY(cb) access macro

    • with_defaults: true if default nodes should be returned; false if they are not requested

      • GETCB_GET2_WITH_DEFAULTS(cb) access macro

    • api_mode: the consumer walker callback API mode

      • GETCB_API_MODE_NORMAL: entries walked with BEGIN, TERM, END

      • GETCB_API_MODE_1SHOT: entry called with 1SHOT, no child nodes walked

      • GETCB_GET2_API_MODE(cb) access macro

  • getcb Response Fields:

    • Set by the GET2 callback before it returns

    • more_data: set to true if there are more instances of this object to retrieve; false otherwise

    • GETCB_GET2_MORE_DATA(cb) access macro

    • match_test_done: set to true if the GET2 callback performed the request content match test(s); false if not or no match tests requested

      • GETCB_GET2_MATCH_TEST_DONE(cb) access macro

    • active_case_modname: name of the module for the module namespace of the active case; set to NULL for the same module name as the choice

      • Ignored unless the 'obj' type is 'choice'

      • GETCB_GET2_ACTIVE_CASE_MODNAME(cb) access macro

    • active_case: name of the active case

      • Ignored unless the 'obj' type is 'choice'

      • GETCB_GET2_ACTIVE_CASE(cb) access macro

    • return_keyQ: queue of val_value_t structs representing the key leafs of the returned value

      • Values from the keyQ can be transferred to this queue

      • GETCB_GET2_RETURN_KEYQ(cb) access macro

      • GETCB_GET2_FIRST_RETURN_KEY(cb) macro to return the first val_value_t in the queue

      • GETCB_GET2_NEXT_RETURN_KEY(cb, cur) macro to return the next val_value_t in the queue

    • return_valQ: queue of val_value_t structs representing the non-key terminal nodes of the returned value

      • Only the first value is used at this time, except for leaf-list nodes

      • Should be empty if the 'keys-only' flag is set in the request

      • GETCB_GET2_RETURN_VALQ(cb) access macro

      • GETCB_GET2_FIRST_RETURN_VAL(cb) macro to return the first val_value_t in the queue

      • GETCB_GET2_NEXT_RETURN_VAL(cb, cur) macro to return the next val_value_t in the queue

    • return_val: inline val_value_t to return a single leaf

      • Can be used instead of the return_valQ to avoid mallocing a val_value_t

      • GETCB_GET2_RETURN_VAL(cb) access macro

      • GETCB_GET2_RETURN_VAL_SET(cb) macro to test if the value is set

    • return_aioQ: queue of val_value_t structure representing the All In One (AIO) nodes of the returned value

      • GETCB_GET2_RETURN_AIOQ(cb) access macro

      • GETCB_GET2_FIRST_RETURN_AIO_VAL(cb) macro to return the first val_value_t in the queue

      • GETCB_GET2_NEXT_RETURN_AIO_VAL(cur) macro to return the next val_value_t in the queue

    • aio_return_buff: AIO XML or JSON malloced buffer from callback to use instead of val_value_t structures

      • GETCB_AIO_BUFFER(cb) access macro

      • GETCB_AIO_ENCODING(cb) macro to access AIO callback encoding

  • getcb User State Fields:

    • There are two new user fields that are available starting in 21.10-6.

      • The server will copy the user state data from parent control block to child control block when traversing a subtree with GET2 callbacks.

      • The GET2 callback can use these fields to store implementation-specific state data to make the GET2 lookup more efficient.

    • user_data_ref: user state data pointer

      • void * pointer

      • Access with macro GETCB_GET2_USER_DATA_REF

    • user_data_index: user state data index

      • uint32

      • Access with macro GETCB_GET2_USER_DATA_INDEX

GET2 Function Return Data

Each GET2 callback returns a status code:

  • NO_ERR: data is returned

  • ERR_NCX_NO_INSTANCE: the specified data instance does not exist

  • Other status code: some error occurred

The callback is expected to add data to the 'get2cb' via API functions, depending on the object type:

Object

Data Returned

container

Returns the child terminal nodes in the container

list

Returns the keys and perhaps the rest of the child terminal nodes. Set “more_data” to trigger “getnext” until no more instances found

anyxml

Returns the entire anyxml value tree

leaf

Returns the leaf value

leaf-list

Returns all values for the leaf-list

choice

Returns the name of the active case and child terminal nodes in the active case

case

This node type does not have a callback

GET2 Processing Procedure

The server will send a <get-request> server request message to each subsystem that has registered a get2 callback for the specific object.

  • For single-instance nodes (container, leaf, choice, anyxml) only a “get” request will be made.

  • For a leaf-list node, only “get” requests will be made, but all instances of the leaf-list are returned at once (max-entries parameter equals zero).

  • For a list node, a “get” request will be made if the keys are known.

  • A “get” request that contains no keys will cause the first instance to be returned.

  • Entries are ordered by their key values, exactly as specified in the YANG list. If there is no key-stmt defined, then the callback will still return the first entry, except the server will not attempt any sorting if multiple entries are returned (E.g 1 from each subsystem).

  • It is always up to the callback to know what is the “best” next entry, given the keys that are returned.

  • The callback examined the keys (if any) and fills in the return_keys.

  • If the “keys-only” flag is set, then no other terminal nodes are returned.

  • If there are more list entries, then the “more-data” flag is set in the get-response.

  • The server will issue “getnext” requests as needed until the “more-data” flag is not present in the response.

  • The “max-entries” field will be set to 1. It may be set higher in a future release to support getbulk retrieval.

GET2 Callback Initialization and Cleanup

The SIL or SIL-SA phase 1 initialization code is used to register the GET1 callback functions used in the server. The GET1 callback is unregistered during the SIL or SIL-SA cleanup phase.

status_t agt_cb_register_get_callback(const xmlChar *modname, const xmlChar *defpath, const xmlChar *version, getcb_fn2_t get_cbfn)

Register an object specific GET callback function.

Use the same fn for all callback phases all phases will be invoked

Parameters
  • modname -- module that defines the target object for these callback functions

  • defpath -- Xpath with default (or no) prefixes defining the object that will get the callbacks

  • version --

    exact module revision date expected if condition not met then an error will be logged (TBD: force unload of module!)

    NULL means use any version of the module

  • get_cbfn -- address of callback function to use for GET callbacks

Returns

status

Example init1 function:

static status_t init_interfaces (void)
{
    // ...

    status_t res =
        agt_cb_register_get_callback(EXAMPLE_MODNAME,
                                     (const xmlChar *)"/interfaces-state",
                                     EXAMPLE_VERSION,
                                     get2_container_interfaces);
    if (res != NO_ERR) {
        return res;
    }

        // ...

} /* init_interfaces */
void agt_cb_unregister_callbacks(const xmlChar *modname, const xmlChar *defpath)

Unregister all EDIT callback functions for a specific object.

Parameters
  • modname -- module containing the object for this callback

  • defpath -- definition XPath location

Example cleanup function:

void interfaces_cleanup (void)
{
    // ...
        agt_cb_unregister_callbacks(EXAMPLE_MODNAME, (const xmlChar *)"/interfaces-state");
    //...
}

GET2 Callback Container Example

Consider this simplified example, that represents GET2 callback for the “interface-state” container.

container interfaces-state {
  config false;

  leaf in-errors { type uint32; }

  // rest of leafs removed
}

The following SIL code shows a GET2 callback which returns the “interfaces-state” container element with one child leaf node.

void getcb_add_return_val(getcb_get2_t *get2cb, val_value_t *val)

Add a return val to a get2cb return_valQ.

Important API needed by GET2 callbacks to add return values

Parameters
  • get2cb -- get2 control block to use

  • val -- value to add; pass off memory; will be cleaned when the get2cb is freed

The 'agt_make_leaf2' API is used in this example.

val_value_t *agt_make_leaf2(obj_template_t *parentobj, const xmlChar *modname, const xmlChar *leafname, const xmlChar *leafstrval, status_t *res)

make a val_value_t struct for a specified leaf or leaf-list

Similiar to agt_make_leaf except this API allows augmented child nodes to be accessed.

See also

agt_make_leaf

Parameters
  • parentobj -- parent object to find child leaf object

  • modname -- name of module defining leaf (may be NULL to pick parent)

  • leafname -- name of leaf to find (namespace hardwired)

  • leafstrval -- string version of value to set for leaf

  • res -- address of return status

Return values

*res -- return status

Returns

malloced value struct or NULL if some error

/********************************************************************
* FUNCTION  get2_container_interfaces
*
* Path: /interfaces-state
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming XML message header
*   get2cb == get2 control block for this callback request
*
* OUTPUTS:
*   return_valQ is filled with malloced val_value_t nodes
*
* RETURNS:
*    status:
*      NO_ERR if executed OK and found OK
*      ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
    get2_container_interfaces (ses_cb_t *scb,
                               xml_msg_hdr_t *msg,
                               getcb_get2_t *get2cb)
{
    (void)scb;
    (void)msg;

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

    /* get the template for the “interfaces-state” container */
    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
    status_t res = NO_ERR;

    /* pretend leaf 'in-errors' is present */
    val_value_t *retval =
                agt_make_leaf2(obj,
                           EXAMPLE_MODNAME,
                           (const xmlChar *)"in-errors",
                           (const xmlChar *)"8",
                           &res);
    if (retval) {
        getcb_add_return_val(get2cb, retval);
    } else {
        return res;
    }

    return res;

}  /* get2_container_interfaces */

To ensure that the GET2 callbacks are working as expected an application may retrieve running configuration and device state information as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example" />
    </filter>
  </get>
</rpc>

The server may reply with:

<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
     <in-errors>8</in-errors>
   </interfaces-state>
</rpc-reply>

GET2 Callback List Example

Consider this simplified example, that demonstrates a GET2 callback for the “interface” list.

container interfaces-state {
  config false;

  list interface {
    key "ip";

    leaf ip {
      type inet:ip-prefix;
    }

    leaf name {
      type string;
      mandatory true;
    }
  }
}

The GET2 callbacks for “list” elements must fill in the return key values. All additional leafs are optional to fill in. Any other descendant complex elements, such other lists, containers, or choice nodes must have their own callbacks functions.

void getcb_add_return_key(getcb_get2_t *get2cb, val_value_t *val)

Add a return keyval to a get2cb return_keyQ.

This is an important GET2 callback API for a list node The list must add return keys in order to continue the walk through all list entries

Parameters
  • get2cb -- get2 control block to use

  • val -- value to add; pass off memory here


/********************************************************************
* FUNCTION  get2_list_interface
*
* Path: /interfaces-state/interface
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming XML message header
*   get2cb == get2 control block for this callback request
*
* OUTPUTS:
*   return_valQ is filled with malloced val_value_t nodes
*   return_keyQ is filled with any new keys added for this entry
*
* RETURNS:
*    status:
*      NO_ERR if executed OK and found OK
*      ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
    get2_list_interface (ses_cb_t *scb,
                         xml_msg_hdr_t *msg,
                         getcb_get2_t *get2cb)
{
    (void)scb;
    (void)msg;

    uint32 max_entries = GETCB_GET2_MAX_ENTRIES(get2cb);
    (void)max_entries;

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

    /* get the list and child objects */
    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);

    obj_template_t *key_obj =
        obj_find_child(obj,
                       EXAMPLE_MODNAME,
                       (const xmlChar *)"ip");

    /* init leaf values for the list */
    const xmlChar *name = NULL;
    const xmlChar *ret_name = NULL;
    boolean name_fixed = FALSE;

    /* check the keyQ for the specific list instance requested */
    val_value_t *name_key = getcb_find_key(get2cb, key_obj);

    /* pretend there are 3 values */
#define IP1 (const xmlChar *)"10.10.10.1/16"
#define IP2 (const xmlChar *)"10.10.10.2/16"
#define IP3 (const xmlChar *)"10.10.10.3/16"

    /* specific list instance requested, check if it is valid */
    if (name_key && VAL_STR(name_key)) {
        name = VAL_STR(name_key);
        int32 ret = xml_strcmp(name, IP2);
        if (ret > 0) {
            return ERR_NCX_NO_INSTANCE;
        }

	 /* specific list instance requested, set the flag */
        name_fixed = VAL_IS_FIXED_VALUE(name_key);
    }

    /* set to true if there are more instances of this object to retrieve;
     * false otherwise
     */
    boolean moreflag = TRUE;

    /* check validity of keys present */
    if (getnext) {
        if (name_fixed) {
            return ERR_NCX_NO_INSTANCE;
        }

        /* adjust the key to find the next entry after
         * the specified value
         */
        if (!name) {
            ret_name = IP1; // return first entry [0]
        } else {
            /* find the correct entry to retrieve */
            int32 ret = xml_strcmp(name, IP1);
            if (ret < 0) {
                ret_name = IP1;
            } else if (ret == 0) {
                ret_name = IP2;
            } else {
                /* check IP2 */
                ret = xml_strcmp(name, IP2);
                if (ret < 0) {
                    ret_name = IP2;
                } else if (ret == 0) {
                    ret_name = IP3;
                    moreflag = FALSE;
                } else {
                    /* assume IP3 */
                    ret_name = IP3;
                    moreflag = FALSE;
                }
            }
        }
    } else {
        if (name) {
            /* get a specified instance */
            boolean name_ok = FALSE;
            if (!xml_strcmp(name, IP1)) {
                name_ok = TRUE;
            } else if (!xml_strcmp(name, IP2)) {
                name_ok = TRUE;
            } else if (!xml_strcmp(name, IP3)) {
                name_ok = TRUE;
                moreflag = FALSE;
            }
            if (name_ok) {
                ret_name = name;
            } else {
                return ERR_NCX_NO_INSTANCE;
            }
        } else {
            // else no keys == get-first
            ret_name = IP1;
        }
    }

    GETCB_GET2_MATCH_TEST_DONE(get2cb) = FALSE;

    if (ret_name == NULL) {
        return ERR_NCX_NO_INSTANCE;
    }

    GETCB_GET2_MORE_DATA(get2cb) = moreflag;

    status_t res = NO_ERR;

    /* if we get here then the index is valid
     *
     * create a return_keyQ node for the name key value
     */
    val_value_t *return_val =
        agt_make_leaf2(obj,
                       EXAMPLE_MODNAME,
                       (const xmlChar *)"ip",
                       ret_name,
                       &res);
    if (res == NO_ERR) {
        if (name_fixed) {
            VAL_SET_FIXED_VALUE(return_val);
        }
        getcb_add_return_key(get2cb, return_val);
    }

    /* Also, pretend leaf 'name' is present
     * Here, might be another key name validation that will dictate what “name”
     * value to use based on key value.
     * Note, “name” leaf is a mandatory=true leaf, so it must be present.
     */
    val_value_t *retval =
		agt_make_leaf2(obj,
				  EXAMPLE_MODNAME,
                            (const xmlChar *)"name",
                            (const xmlChar *)"interface-name",
                            &res);
    if (retval) {
        getcb_add_return_val(get2cb, retval);
    } else {
        return res;
    }

    return NO_ERR;

}  /* get2_list_interface */

Refer to the GET2 Access Functions section for other available functions to access GET2 control block.

To ensure that the GET2 callbacks are working as expected an application can retrieve running configuration and device state information as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
        <interface>
          <ip>10.10.10.1/16</ip>
            </interface>
      </interfaces-state>
    </filter>
  </get>
</rpc>

The server may reply with:

<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
    <interface>
      <ip>10.10.10.1/16</ip>
      <name>interface-name</name>
    </interface>
  </interfaces-state>
</rpc-reply>

If no key value is specified in the request or the request subtree filter target is “interface-state” parent container, then the server will try to output all the interfaces and its children:

<?xml version="1.0" encoding="UTF-8"?>
 <rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
   <get>
     <filter type="subtree">
       <interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example" />
     </filter>
   </get>
 </rpc>

The server may reply with:

<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
    <interface>
      <ip>10.10.10.1/16</ip>
      <name>interface-name</name>
    </interface>
    <interface>
      <ip>10.10.10.2/16</ip>
      <name>interface-name2</name>
    </interface>
    <interface>
      <ip>10.10.10.3/16</ip>
      <name>interface-name3</name>
    </interface>
  </interfaces-state>
</rpc-reply>

GET2 Callback Choice Example

A GET2 callback function for a YANG Choice is expected to return the name of the active choice, and any terminal nodes from the active case. It can also return ERR_NCX_NO_INSTANCE if no active case is set.

Consider this simplified example, that represents GET callback for the node type “choice”.

choice type {
   case interface {          // active case
     leaf interface {
       type if:interface-ref;
     }
   }

   case case-network {
     leaf next-hop-host {
       type inet:ip-address;
     }
   }
 }
status_t getcb_set_active_case(getcb_get2_t *get2cb, const xmlChar *active_case_modname, const xmlChar *active_case)

Set the active case to the specified object.

Parameters
  • get2cb -- get2 control block to use

  • active_case_modname -- module name to set

  • active_case -- case name to set

Returns

status

The following C API code shows a simple GET2 callback function for the data model, which returns the name of the active case and child terminal nodes in the active case when it is requested.


/********************************************************************
* FUNCTION  get2_choice_type
*
* Path: /type
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming XML message header
*   get2cb == get2 control block for this callback request
*
* OUTPUTS:
*   return_valQ is filled with malloced val_value_t nodes
*   the a backptr to a constant string containing the case
*   name that is active
*
* RETURNS:
*    status:
*      NO_ERR if executed OK and found OK
*      ERR_NCX_NO_INSTANCE warning if no instance found
*
********************************************************************/
static status_t
    get2_choice_type (ses_cb_t *scb,
                      xml_msg_hdr_t *msg,
                      getcb_get2_t *get2cb)
{
    (void)scb;
    (void)msg;
    status_t res = NO_ERR;

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

    /* get the active case
     * callback based on instances in the system
     * It may be NULL if no active case
     */
    const xmlChar *active_case_modname = NULL;
    const xmlChar *active_case = (const xmlChar *)"interface";

    /* Set the active case to the specified object */
    res = getcb_set_active_case(get2cb,
                                active_case_modname,
                                active_case);

    if (active_case == NULL) {
        return res;
    }

    /* get the template for the active case */
    obj_template_t *choice_obj = GETCB_GET2_OBJ(get2cb);
    if (choice_obj == NULL) {
        return ERR_NCX_NOT_FOUND;
    }

    obj_template_t *case_obj =
        obj_find_child(choice_obj,
                       active_case_modname,
                       active_case);
    if (case_obj == NULL) {
        return ERR_NCX_NOT_FOUND;
    }

    /* return all the leaf and leaf-list instances that are child nodes
     * of the active case.
     */
    val_value_t *chval = NULL;

    if (!xml_strcmp(active_case, (const xmlChar *)"interface")) {
        /* pretend leaf 'interface' is present */
        chval = agt_make_leaf2(case_obj,
                               active_case_modname,
                               (const xmlChar *)"interface",
                               (const xmlChar *)"ethernet1/1/1",
                               &res);
        if (chval) {
            getcb_add_return_val(get2cb, chval);
        } else {
            return res;
        }
    } else {
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }

    return res;

}  /* get2_choice_type */

Note that any leaf or leaf-list nodes that are in nested within other “choice” statements within the active case are not returned . These are considered complex objects and the caller will ask for nested choice, list, and container nodes in a separate call .

container top {
  choice complex-type {
     case interface {          // active case
       choice new-choice {
         leaf interface {
           type if:interface-ref;
         }
         leaf test-interface {
           type if:interface-ref;
         }
       }
     }

     case case-network {
       leaf next-hop-host {
         type inet:ip-address;
       }
     }
   }
 }

In the example above, the GET2 callback for 'complex-choice' would be called first and the active case name interface would be returned without a terminal node.

Then the GET2 callback for 'new-choice' would be invoked and this callback will return the name of the active case (interface or test-interface), and the child leaf for that case.

Refer to the GET2 Access Functions section for other available functions to access GET2 control block.

GET2 Callback Example for To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state information as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <top xmlns="http://yumaworks.com/ns/interfaces" />
    </filter>
  </get>
</rpc>

The server may reply with:

<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <top xmlns="http://yumaworks.com/ns/interfaces">
    <interface>ethernet1/1/1</interface>
  </top>
</rpc-reply>

GET2 Callback Example For Leaf

Consider this simplified example, that represents GET callback for a “leaf” node.

container top {
  leaf get2-leaf {
    config false;
    type int32;
  }
}

}

Note that only leaf or leaf-list nodes that are top level nodes or the parent node is a config=true node will require a GET2 callback.

The following C API code shows a simple GET2 callback function for the data model, which returns the leaf value when it is requested.

/* start value for leaf /get2-leaf */
static int32 leaf_val = 42;

static status_t
    get2_leaf_type (ses_cb_t *scb,
                    xml_msg_hdr_t *msg,
                    getcb_get2_t *get2cb)
{
    (void)scb;
    (void)msg;

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

    /* get the real value from the system here.
     * But in this example just increment the value by 1
     */
    int32 retval = leaf_val++;

    /* return the leaf in the static return_val struct */
    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
    val_value_t *return_val = GETCB_GET2_RETURN_VAL(get2cb);
    val_init_from_template(return_val, obj);

    /* Do not use a VAL_ macro unless sure type is correct */
    if (VAL_TYPE(return_val) == NCX_BT_INT32) {
        VAL_INT32(return_val) = retval;
        return NO_ERR;
    }

    /* object not int32!!! */
    return ERR_NCX_OPERATION_FAILED;

}  /* get2_leaf_type */

Refer to the GET2 Access Functions section for other available functions to access GET2 control block.

In the above example, the code generates the value based on the static value that is incremented after each retrieval. An agent can generate the value based on the real device statistical data or based on the system's statistical data, etc.

To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state information as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <get2-leaf xmlns="http://yumaworks.com/ns/interfaces" />
    </filter>
  </get>
</rpc>

The server may reply with:

<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
   <get2-leaf xmlns="http://yumaworks.com/ns/interfaces">42</get2-leaf>
</rpc-reply>

GET2 Callback Example for Leaf-List

A GET2 callback for a leaf-list is similar to the callback for a leaf, except multiple values can be returned, instead of one value. The server will return the data in the same order as returned by the callback function.

Consider this simplified example, that represents a GET2 callback for a “leaf-list” node.

leaf-list get2-leaf-list {
  config false;
  type int32;
}

The following C API code shows a simple GET2 callback function for the same data model, which returns the leaf-list values when requested.

static status_t
    get2_leaflist_type (ses_cb_t *scb,
                        xml_msg_hdr_t *msg,
                        getcb_get2_t *get2cb)
{
    (void)scb;
    (void)msg;

    status_t res = NO_ERR;

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

    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);

    /* always return all the leaf-list instances
     *  3 entries [53, 67, 92]
     */
    val_value_t *retval =
        val_make_simval_obj(obj, (const xmlChar *)"53", &res);
    if (retval) {
        getcb_add_return_val(get2cb, retval);
    }

    if (res == NO_ERR) {
        retval = val_make_simval_obj(obj, (const xmlChar *)"67", &res);
        if (retval) {
            getcb_add_return_val(get2cb, retval);
        }
    }

    if (res == NO_ERR) {
        retval = val_make_simval_obj(obj, (const xmlChar *)"92", &res);
        if (retval) {
            getcb_add_return_val(get2cb, retval);
        }
    }

    return res;

}  /* get2_leaflist_type */

In the above example, the code generates the value based on the static value that does not fluctuate after each retrieval. An agent can generate the value based on the real device statistical data or based on the system's statistical data, etc.

To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state information as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces" />
    </filter>
  </get>
</rpc>

The server may reply with:

<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">53</get2-leaf-list>
  <get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">67</get2-leaf-list>
  <get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">92</get2-leaf-list>
</rpc-reply>

GET2 Access Functions

There are several GET2 mode specific high-level Transaction access and management utilities in ncx/getcb.h.

The following functions are described elsewhere in this document:

  • getcb_add_return_val

  • getcb_add_return_key

  • getcb_set_active_case

  • getcb_first_requested_child

  • getcb_next_requested_child

  • getcb_add_return_aioQ

val_value_t *getcb_find_return_val(getcb_get2_t *get2cb, obj_template_t *obj)

Find a return val in the get2cb return_valQ.

Parameters
  • get2cb -- get2 control block to use

  • obj -- object type to find

val_value_t *getcb_find_return_val2(getcb_get2_t *get2cb, xmlns_id_t obj_nsid, const xmlChar *obj_name)

Find a return val in the get2cb return_valQ use { NSID, NAME } instead of object pointer.

Parameters
  • get2cb -- get2 control block to use

  • obj_nsid -- object module namespace to find

  • obj_name -- object local-name to find

Returns

pointer to found value or NULL if not found

val_value_t *getcb_find_next_return_val(getcb_get2_t *get2cb, val_value_t *curval)

Find the next matching return val in the get2cb return_valQ.

Parameters
  • get2cb -- get2 control block to use

  • curval -- current value pointer

Returns

next return value or NULL if none

val_value_t *getcb_find_key(getcb_get2_t *get2cb, obj_template_t *obj)

Find an input keyval in the get2cb keyQ.

Use an object template to search the return value queue

Parameters
  • get2cb -- get2 control block to use

  • obj -- key object type to find

Returns

pointer to the found key value or NULL if not found

val_value_t *getcb_find_key_str(getcb_get2_t *get2cb, const xmlChar *modname, const xmlChar *objname)

Find an input keyval in the get2cb keyQ with a string.

Use the object name string to search the return value queue

Parameters
  • get2cb -- get2 control block to use

  • modname -- module name of the key object

  • objname -- object name of the key object

Returns

pointer to the found key value or NULL if not found

val_value_t *getcb_find_match(getcb_get2_t *get2cb, obj_template_t *obj)

Find an input keyval in the get2cb matchQ.

Parameters
  • get2cb -- get2 control block to use

  • obj -- object type to find

Returns

value struct for the match node or NULL if not found

void getcb_add_key(getcb_get2_t *get2cb, val_value_t *val)

Add a keyval to a get2cb keyQ.

This is NOT the return key Q! This is not a user API for a GET2 callback

Parameters
  • get2cb -- get2 control block to use

  • val -- value to add

void getcb_add_match(getcb_get2_t *get2cb, val_value_t *val)

Add a match node to a get2cb matchQ.

This is not a user API for a GET2 callback

Parameters
  • get2cb -- get2 control block to use

  • val -- value to add

status_t getcb_add_select(getcb_get2_t *get2cb, const xmlChar *modname, const xmlChar *objname)

Add a select node to a get2cb matchQ.

This is not a user API for a GET2 callback

Parameters
  • get2cb -- get2 control block to use

  • modname -- module name of the select node

  • objname -- object name of the select node

Returns

status

val_value_t *getcb_find_return_key(getcb_get2_t *get2cb, obj_template_t *obj)

Find a return keyval in the get2cb return_keyQ.

Parameters
  • get2cb -- get2 control block to use

  • obj -- object type to find

Returns

value node for this object or NULL if not found

val_value_t *getcb_find_return_key2(getcb_get2_t *get2cb, xmlns_id_t obj_nsid, const xmlChar *obj_name)

Find a return keyval in the get2cb return_keyQ Use { NSID, NAME } instead of object pointer.

Parameters
  • get2cb -- get2 control block to use

  • obj_nsid -- namespace ID of object to find

  • obj_name -- name of object to find

Returns

value node for this return key or NULL if not found

void getcb_move_return_keys(getcb_get2_t *get2cb)

Move the return keys to the keyQ replacing the nodes in the keyQ if already there.

Parameters

get2cb -- get2 control block to use

void getcb_undo_move_return_keys(getcb_get2_t *get2cb)

Move back the return keys from the keyQ.

Parameters

get2cb -- get2 control block to use

void getcb_clean_return_data(getcb_get2_t *get2cb)

Clean the return data in the return_val and return_valQ.

Parameters

get2cb -- get2 control block to clean

status_t getcb_handle_acmtest(ses_cb_t *scb, xml_msg_hdr_t *msg, val_nodetest_fn_t testfn, getcb_get2_t *get2cb)

check the access control and testfn callback for a node that would have it skipped because the write_full_check_val function is skipped

Parameters
  • scb -- session control block

  • msg -- xml_msg_hdr_t in progress

  • testfn -- callback function to use, NULL if not used

  • get2cb -- get2 control block to use

Returns

status: ERR_NCX_SKIPPED if tests fail; NO_ERR if they pass

getcb_keyval_t *getcb_new_keyval(const xmlChar *keyval)

Create a new GET2 keyval holder.

Parameters

keyval -- key value string

Returns

pointer to initialized keyval, or NULL if malloc error

getcb_keyval_t *getcb_new_keyval2(val_value_t *keynode, obj_template_t *keyobj, boolean fixed_value)

Create a new Get2 keyval holder using val backptr.

Parameters
  • keynode -- val_value_t node for key value

  • keyobj -- object template for real object

  • fixed_value -- TRUE if a fixed value for getnext purposes

Returns

pointer to initialized keyval, or NULL if malloc error

void getcb_free_keyval(getcb_keyval_t *keyval)

Free a GET2 keyval.

Parameters

keyval -- keyval to free

void getcb_clean_keyvalQ(dlq_hdr_t *que)

Free all the Get2 keyvals from a dlq_hdr.

Parameters

que -- Q of getcb_keyval_t to clean

void getcb_dump_get2cb(getcb_get2_t *get2cb)

Print the interesting fields in a get2cb.

Parameters

get2cb -- get2 control block to use

boolean getcb_need_get2(obj_template_t *curobj, obj_template_t *reqobj)

check if the node has a get2 callback or in a choice/case subtree that has get2 callback

Parameters
  • curobj -- ceiling object &#8212; data parent of 'reqobj'

  • reqobj -- target object requested that may have get2 cb

Returns

TRUE if get2 applies; FALSE if get2 does not apply

boolean getcb_need_get2_ex(obj_template_t *curobj, obj_template_t *reqobj, uint32 *choicecnt, obj_template_t **top_choice)

check if the node has a get2 callback or in a choice/case subtree that has get2 callback

Parameters
  • curobj -- ceiling object &#8212; data parent of 'reqobj'

  • reqobj -- target object requested that may have get2 cb

  • choicecnt -- address of return choice count

  • top_choice -- address of return top_choice

Return values
  • *choicecnt -- number of implied choices that need to be retrieved

  • *top_choice -- topmost choice that needs to be retrieved

Returns

TRUE if get2 applies; FALSE if get2 does not apply

status_t getcb_add_return_aio_buff(getcb_get2_t *get2cb, const xmlChar *buffer)

Add a return buffer to a get2cb control block and set encoding type.

Parameters
  • get2cb -- get2 control block to use

  • buffer -- buffer used in the callback

Returns

status

GETBULK Support

The netconfd-pro server supports a YANG list iterator operation called <get-bulk>. It is defined in yumaworks-getbulk.yang.

The GET2 callback is used for GETBULK support. There are parameters passed to the callback function that indicate the bulk parameters to use. Data is returned from the callback to the server in a slightly different way than a "plain" GET2 callback that returns one entry at a time.

The maximum number of entries a GET2 callback should return is configured with the --max-getbulk parameter. It is used for the 'max-entries' field, which is passed in the <get-bulk> request.

  • No error is generated in the server if more entries are returned, but extra entries may not be returned to the client.

  • The GET2 callback is not required to return more than one entry.

  • The max-entries field can be ignored by the GET2 callback.

  • The --max-getbulk CLI parameter can be used to change the 'max-entries' value. The default is '10'.

  • The 'agt_sil_getbulk_max' agt_profile field contains the configured value at run-time. This field is used to set the 'max-entries' parameter in the <get-bulk> request.

  • This parameter is only set for YANG list nodes. A leaf-list callback always returns all instances in 1 request. All other data node callback types are expected to return 1 entry.

  • If max-entries is set to zero, then the GET2 callback can return as many entries as desired.

  • Existing GET2 code will check the max_entries filed and return ERR_NCX_OPERATION_FAILED if it is not set to '1'. This code needs to be removed in order to use GETBULK. New SIL code generated with yangdump-sdk does not generate this check on max_entries any more.

In order to use the GETBULK API, a GET2 callback MUST add a call to 'getcb_finish_getbulk_entry' function after the entry is completed. The general code for a GET2 callback stays the same, except that the callback can loop through its code to find the next GETNEXT entry multiple times.

The first instance of a list is requested with a GET request, and none of the keys will be present. This is an indication to the list callback that the first entry is being requested. The GET2 callback can return up to 'max-entries' getbulk instances. The next request will be a GETNEXT request, starting where the last getbulk entry left off.

Processing List Keys for GETNEXT and GETBULK

There are 2 types of keys passed to a GET2 function in a “u_” callback file for a list:

  1. ancestor keys: these key values are always set and always fixed

  2. local keys: these key values are for the target list object.

    1. key value: The value of they key in its base type; Only valid if foo_present flag is true

    2. foo_present: true if the foo local key leaf value is valid

    3. foo_fixed: true if the foo local key leaf value is fixed. This indicates that a content-match filter for that key leaf value is being processed. If true, then the GET2 callback MUST NOT increment the associated key value. Only entries matching the fixed key value are to be returned.

If all of the local keys are set and the associated “foo_fixed” leaf is also set, then the server is requesting a single list entry. Do not return multiple entries in the case, even if max-entries is greater than 1.

GETBULK Operation Examples

This simple example shows how the GETBULK procedure may be supported for a list /top-state/A.

GETBULK Example YANG Module

The following YANG file is used in this example:

module t104 {

  namespace "http://netconfcentral.org/ns/t104";
  prefix t104;
  revision "2015-11-04";

  container top-state {
    config false;
    list A {
      key X;
      leaf X { type int32; }
      leaf Y { type int32; }
      leaf Z { type int32; }
    }
  }

}

GETBULK Example C Files

There are 2 different versions of this test module implemented for SIL and SIL-SA testing.

  • Refer to the SIL-SA file /usr/share/yumapro/src/get2-test/src/get2-test.c in the YumaPro installation.

  • Refer to the SIL file /usr/share/yumapro/src/get2-test-local/src/get2-test.c in the YumaPro installation.

GETBULK Example Callback Function

The following SIL code shows how GETBULK might be implemented for the list /top-state/A.


/********************************************************************
* FUNCTION u_t104_top_state_A_get
*
* Get database object callback for list A
* Path: /top-state/A
* Fill in 'get2cb' response fields
*
* INPUTS:
*     see ncx/getcb.h for details (getcb_fn2_t)
*
* RETURNS:
*     error status
********************************************************************/
status_t u_t104_top_state_A_get (
    getcb_get2_t *get2cb,
    int32 k_top_state_A_X,
    boolean X_fixed,
    boolean X_present)
{
    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);
    }

    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);

    uint32 max_entries = GETCB_GET2_MAX_ENTRIES(get2cb);
    if (max_entries == 0) {
        max_entries = 10;
    }

    obj_template_t *X_obj =
        obj_find_child(obj, NULL, (const xmlChar *)"X");
    obj_template_t *Y_obj =
        obj_find_child(obj, NULL, (const xmlChar *)"Y");
    obj_template_t *Z_obj =
        obj_find_child(obj, NULL, (const xmlChar *)"Z");

    boolean getbulk_mode = !X_fixed;

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

#define MAX_X  42

    int32 X_val = 0;
    if (X_present) {
        X_val = k_top_state_A_X;
    } else {
        X_val = 1;
    }
    if (getnext) {
        ++X_val;
    }
    if ((X_val == 0) || (X_val > MAX_X)) {
        return ERR_NCX_NO_INSTANCE;
    }

    int32 seed_num = X_val * 100;
    uint32 entry_count = 0;
    boolean done = false;
    while (!done) {

        val_value_t *X_return_val = val_new_value();
        if (X_return_val == NULL) {
            return ERR_INTERNAL_MEM;
        }
        val_init_from_template(X_return_val, X_obj);
        VAL_INT32(X_return_val) = X_val;
        getcb_add_return_key(get2cb, X_return_val);

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

        /* 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 true if not sure */
        boolean more_data = (X_val < MAX_X);

        /**** SET more_data FLAG ****/

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

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

            /* real code would find the current list entry
             * and then get the Y and Z leafs out of that entry
             * This code just returns random numbers
             */
            val_value_t *childval = NULL;

            /* leaf Y (int32) */
            if (childobj == Y_obj) {
                childval = val_new_value();
                if (childval == NULL) {
                    return ERR_INTERNAL_MEM;
                }
                val_init_from_template(childval, childobj);
                srand(seed_num++);
                VAL_INT32(childval) = rand();
                getcb_add_return_val(get2cb, childval);
            } else if (childobj == Z_obj) {
                /* leaf Z (int32) */
                childval = val_new_value();
                if (childval == NULL) {
                    return ERR_INTERNAL_MEM;
                }
                val_init_from_template(childval, childobj);
                srand(seed_num++);
                VAL_INT32(childval) = rand();
                getcb_add_return_val(get2cb, childval);
            }
        }

        if (getbulk_mode) {
            getcb_finish_getbulk_entry(get2cb);

            /* make sure both numbers get incremented */
            ++entry_count;
            ++X_val;

            if ((entry_count == max_entries) || (X_val > MAX_X)) {
                done = true;
            }
        } else {
            done = true;
        }
    }

    return NO_ERR;

} /* u_t104_top_state_A_get */

GETBULK KnowledgeBase FAQs

All In One GET2 Callback

This section describes All In One (AIO) GET2 mechanism that allows to call a single GET2 callback for the whole Subtree. An AIO callback can return data to the server in 3 different formats:

AIO GET2 callbacks follow exactly the same template as regular GET2 callbacks, they use the same registration API and are getting invoked the same way for SIL and SIL-SA as regular GET2 callbacks. The main difference is that there will not be any callback invocations and there will not be any callbacks at all for any of AIO node children.

All In One GET2 support:

  • Supported only for container and list nodes

  • YANG extension must be specified for the top AIO node

  • Supported for SIL and SIL-SA

  • Supported for XPath and Subtree filtering

  • Supported for RESTCONF retrieval

  • Supported for <get-bulk> operation

  • Supported for NMDA <get-data> operation for "operational" datastore

  • Auto-generation code support

  • Must be built with --sil-get2 flag during make_sil_dir_pro

The entire subtree would be expected in one retrieval in one callback invocation. That’s if the callback is registered for a container, any complex nodes within this container will not have any extra callbacks, they will be handled by the top AIO container callback.

sil-aio-get2 YANG Extension

With help of a new YANG extension, the server will set specific flags to the objects to identify them as a special case retrieval. This extension would be used in a container or a list to force the server to treat that data subtree as a AIO node for GET2.

The yangdump-sdk auto-generation code does not auto generate callbacks for children of the top All In One parent. The callbacks will be generated only for the top nodes that have the extension specified. The extension definition looks as follows:

extension sil-aio-get2 {
  description
    "Used within a data definition statement to define
     the GET2 retrieval mechanism.
     This extension affects the descendant data nodes.

     This extension can be used in a container or list
     to force the server to treat that data subtree as
     a terminal node for GET2.

     The entire subtree would be expected in one retrieval
     in one callback invocation.

     The entire subtree can be specified in the JSON
     or XML buffer that will be used for return values.
     The server will parse and handle the buffer and process
     retrieval based on the provide JSON or XML encoded buffer.

     The 'parmstr' argument can specify the encoding that will
     be used in the callback. Available options are:
       - xml: XML element in a buffer is expected in return value
       - json: JSON object in a buffer is expected in return value
       - val: val_value_t tree is expected in return value
    ";
  argument parmstr;
}

The following YANG snippet shows how the extension can be used to cause an AIO callback function to be generated for the 'nonconfig-list' data node:

// Need to import yumaworks-extensions
import yumaworks-extensions { prefix ywx; }


// Use ywx:sil-api-get2 statement as needed
container get3-config-cont {
  presence "Presence container";

  list config-list {
    key name;
    leaf name { type string; }

    list nonconfig-list {               // Regular GET2 CB
      config false;
      key name;
      leaf name { type string; }

      list nonconfig-list2 {            // All in One GET2 CB
        ywx:sil-aio-get2 "val";

        key name;
        leaf name { type string; }

        list nonconfig-list3 {          // No callback
          key name;
          leaf name { type string; }

          container int-con {           // No callback
            leaf int-con-leaf { type int32; }
          }
        }
      }
    }
  }
}

All In Once API Functions

The following API functions are used in this example:

obj_template_t *getcb_first_requested_child(getcb_get2_t *get2cb, obj_template_t *parent_obj)

Check if the specified object has any terminal nodes that need to be returned for a get2 request.

Return the first one

The get2cb will be examined:

  • boolean flags

  • select-node queue

Any content-match nodes will be returned but no filtering of these objects will be done

Any key leaf nodes will be skipped; Processing of key leafs is done first and is never skipped

Parameters
  • get2cb -- get2 control block to use

  • parent_obj -- target object usually the obj from get2cb but could be the active case object for a choice callback

Returns

pointer to first requested child terminal object

NULL if none

obj_template_t *getcb_next_requested_child(getcb_get2_t *get2cb, obj_template_t *curchild)

Check if the specified object has any more terminal nodes that need to be returned for a get2 request.

Return the next one

The get2cb will be examined:

  • boolean flags

  • select-node queue

Any content-match nodes will be returned but no filtering of these objects will be done

Any key leaf nodes will be skipped; Processing of key leafs is done first and is never skipped

Parameters
  • get2cb -- get2 control block to use

  • curchild -- current child; start search with next sibling

Returns

pointer to next requested child terminal object

NULL if none

void getcb_add_return_aioQ(getcb_get2_t *get2cb, val_value_t *val)

Add a return val to a get2cb return_aioQ.

Parameters
  • get2cb -- get2 control block to use

  • val -- value to add

All In One List Example

In this example the server will invoke GET2 callbacks only for "nonconfig-list" first and then for AIO "nonconfig-list2" list, and will not invoke any deeper level callbacks.

The server will expect that the AIO "nonconfig-list2" callback will fill in the GET2 control block with the sufficient information about "nonconfig-list2" node children, complex children as well as terminal nodes.

Example C files:

  • Refer to the SIL-SA file /usr/share/yumapro/src/get3-test/src/get3-test.c

  • Refer to the SIL file /usr/share/yumapro/src/get3-test-local/src/get3-test.c

This section illustrates how to use All In One GET2 callbacks for list nodes. Assume we have the same data model as described above then the GET2 callback may look as follow:

/********************************************************************
* FUNCTION get_test_cont_list_list
*
* Get database object callback for list nonconfig-list2
* Path: /get3-nonconfig-cont/nonconfig-list/nonconfig-list2
* Fill in 'get2cb' response fields
*
* sil-aio-get2 extension enabled for this object;
* No callbacks for children will be called
*
* INPUTS:
*     see ncx/getcb.h for details (getcb_fn2_t)
*
* RETURNS:
*     error status
********************************************************************/
static status_t
    get_test_cont_list_list (ses_cb_t *scb,
                             xml_msg_hdr_t *msg,
                             getcb_get2_t *get2cb)
{
    obj_template_t *obj = GETCB_GET2_OBJ(get2cb);
    status_t res = NO_ERR;

    /* create 3 top level lists */
    uint32 key = 0;
    for (key = 1; key < 4; key++) {

        const xmlChar *key1 = NULL;
        if (key == (uint32)1) {
            key1 = (const xmlChar *)"name1";
        } else if (key == (uint32)2) {
            key1 = (const xmlChar *)"name2";
        } else if (key == (uint32)3) {
            key1 = (const xmlChar *)"name3";
        }

        /* make return value that will be added to the return_aioQ */
        val_value_t *retval = val_new_value();
        if (!retval) {
            return ERR_INTERNAL_MEM;
        }
        val_init_from_template(retval, obj);

        /* Use retval for the current callback obj and fill
         * it in with folowing terminal and complex nodes;
         * then add to get2cb with help of getcb_add_return_aioQ API
         */
        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 child node and
             * add it to retval of the current callback node
             */
            if (!xml_strcmp(name, (const xmlChar *)"name")) {
                /* leaf name (string) */
                val_value_t *child =
                    val_make_simval_obj(childobj,
                                        key1,
                                        &res);
                if (child) {
                    res = val_child_add(child, retval);
                    if (res != NO_ERR) {
                        val_free_value(child);
                        val_free_value(retval);
                        return ERR_NCX_INVALID_VALUE;
                    }
                } else {
                    val_free_value(retval);
                    return ERR_NCX_INVALID_VALUE;
                }
            } else if (!xml_strcmp(name, (const xmlChar *)"nonconfig-list3")) {

                /* list nonconfig-list (list) */
                uint32 nextlist_key = 0;
                for (nextlist_key = 1; nextlist_key < 3; nextlist_key++) {

                    const xmlChar *keyname = NULL;
                    if (nextlist_key == (uint32)1) {
                        keyname = (const xmlChar *)"name1";
                    } else if (nextlist_key == (uint32)2) {
                        keyname = (const xmlChar *)"name2";
                    }

                    /* create 5 list entries */
                    val_value_t *listval =
                        create_list_entry(childobj,
                                          (const xmlChar *)"name",
                                          keyname,
                                          &res);
                    if (!listval) {
                        val_free_value(retval);
                        return ERR_INTERNAL_MEM;
                    }

                    /* Use add_force to allow insertion lists with NO keys */
                    res = val_child_add(listval, retval);
                    if (res != NO_ERR) {
                        val_free_value(listval);
                        val_free_value(retval);
                        return ERR_NCX_INVALID_VALUE;
                    }
                }
            }
        }

        if (res == NO_ERR) {
            /* generate the internal index Q chain */
            res = val_gen_index_chain(obj, retval);
            if (res != NO_ERR) {
                val_free_value(retval);
                return res;
            }
        }

        /* Ensure that the generated value is correct and can be added
         * to the return Queue.
         */
        if (retval && res == NO_ERR) {
            res = val_validate_value(retval);
            if (res != NO_ERR) {
                val_free_value(retval);
                return res;
            }
        }

        /* Add created val_value_t to get2cb with help of
         * getcb_add_return_aioQ API; In case of list callbacks,
         * repeat above steps for another list entry
         */
        getcb_add_return_aioQ(get2cb, retval);
    }

    return res;

} /* get_test_cont_list_list */

Below is the example function that was used in the above AIO callback example:


/********************************************************************
* FUNCTION create_list_entry
*
* Make a list entry based on the key
*
* INPUTS:
*   res = return status
*
* RETURNS:
*    val_value_t of listval entry if no error
*    else NULL
*
*********************************************************************/
static val_value_t *
    create_list_entry (obj_template_t *list_obj,
                       const xmlChar *keyname,
                       const xmlChar *keystr,
                       status_t *res)
{
    /* find a key child obejct */
    obj_template_t *key_obj =
        obj_find_child(list_obj,
                       GET3_TEST_MOD,
                       keyname);
    if (!key_obj) {
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }

    val_value_t *list_value = val_new_value();
    if (!list_value) {
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }
    val_init_from_template(list_value, list_obj);

    /* make key leaf entry */
    val_value_t *child =
        val_make_simval_obj(key_obj,
                            keystr,
                            res);
    if (!child) {
        val_free_value(list_value);
        *res = ERR_NCX_INVALID_VALUE;
        return NULL;
    }

    *res = val_child_add(child, list_value);
    if (*res != NO_ERR) {
        val_free_value(child);
        val_free_value(list_value);
        return NULL;
    }

    obj_template_t *notkey_obj =
        obj_find_child(list_obj,
                       GET3_TEST_MOD,
                       (const xmlChar *)"not-keyname");
    if (notkey_obj) {
        /* make NON key leaf entry */
        child =
            val_make_simval_obj(notkey_obj,
                                (const xmlChar *)"some-value",
                                res);
        if (!child) {
            *res = ERR_NCX_INVALID_VALUE;
        }

        if (*res == NO_ERR) {
            *res = val_child_add(child, list_value);
            if (*res != NO_ERR) {
                val_free_value(child);
            }
        }
    }

    obj_template_t *cont_obj =
        obj_find_child(list_obj,
                       GET3_TEST_MOD,
                       (const xmlChar *)"int-con");
    if (cont_obj) {
        /* make return value that will be added to the return_aioQ */
        val_value_t *contval = val_new_value();
        if (!contval) {
            val_free_value(list_value);
            *res = ERR_NCX_INVALID_VALUE;
            return NULL;
        }
        val_init_from_template(contval, cont_obj);

        obj_template_t *leaf_obj =
            obj_find_child(cont_obj,
                           GET3_TEST_MOD,
                           (const xmlChar *)"con-leaf");
        if (leaf_obj) {
            val_value_t *leafval =
                val_make_simval_obj(leaf_obj,
                                    (const xmlChar *)"42",
                                    res);
            if (leafval) {
                *res = val_child_add(leafval, contval);
                if (*res != NO_ERR) {
                    val_free_value(leafval);
                    val_free_value(contval);
                }
            } else {
                val_free_value(contval);
                *res = ERR_NCX_INVALID_VALUE;
            }

            if (*res == NO_ERR) {
                *res = val_child_add(contval, list_value);
                if (*res != NO_ERR) {
                    val_free_value(contval);
                }
            }
        } else {
            val_free_value(contval);
        }
    }

    /* generate the internal index Q chain */
    *res = val_gen_index_chain(list_obj, list_value);
    if (*res != NO_ERR) {
        log_error("\nError: could not generate index chain (%s)",
                  get_error_string(*res));
        val_free_value(list_value);
        return NULL;
    }

    return list_value;

} /* create_list_entry */

The following section in the AIO callback function example is used to ensure that the constructed value is well-formed and can be safely used by the server.

If the value is not a container or a list or if the list value index chain is malformed this function will return an error "invalid value".

status_t val_validate_value(val_value_t *useval)

Verify that the value is valid and can be safely used later Used in the SIL code to ensure that the created value is correct.

Validates that:

  • value and its OBJ is not NULL

  • value is list or container

Parameters

useval -- val value to check

Returns

status

This example shows how this API is used:

/* Ensure that the generated value is correct and can be added
  * to the return Queue.
  */
 if (retval && res == NO_ERR) {
     res = val_validate_value(retval);
     if (res != NO_ERR) {
         val_free_value(retval);
         return res;
     }
 }

After the data has been created, it must be transferred to the GET2 control block so it can be processed by the server.

/* Add created val_value_t to get2cb with help of
 * getcb_add_return_aioQ API; In case of list callbacks,
 * repeat above steps for another list entry
 */
getcb_add_return_aioQ(get2cb, retval);

All In One GET2 Callbacks with XML/JSON Buffers

AIO GET2 callbacks follow exactly the same template as regular GET2 callbacks, they use the same registration API and are getting invoked the same way for SIL and SIL-SA as regular GET2 callbacks. The main difference is that there will not be any callback invocations for descendant children and there will not be any callbacks auto generated at all for any of AIO node children.

Note, the AIO callback is not part of the auto-generated code by default and you will have to add an extension to the desired node that you want to become an AIO callback node. Otherwise, the auto generated code will generate regular GET2 callback for that node(s).

AIO XML Callback Example

The AIO GET2 callback may look as follow for the "/get3-config-cont/config-list/nonconfig-list/nonconfig-list2" list. A real callback would probably not have the data hard-wired in strings. This is shown to keep the example simple.

/********************************************************************
* FUNCTION aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get
*
* Get database object callback for list nonconfig-list2
* Path: list /get3-config-cont/config-list/nonconfig-list/nonconfig-list2
* Fill in 'get2cb' response fields
*
* sil-aio-get2 extension enabled for this object;
* No callbacks for children will be called
*
* XML encoded buffer is expected
*
* INPUTS:
*     see ncx/getcb.h for details (getcb_fn2_t)
*
* RETURNS:
*     error status
********************************************************************/
static status_t
    aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get (
        ses_cb_t *scb,
        xml_msg_hdr_t *msg,
        getcb_get2_t *get2cb)
{
    if (LOGDEBUG) {
        log_debug("\nEnter aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get");
    }

    (void)scb;
    (void)msg;

    status_t res = NO_ERR;

    /* make XML buffer that will be used instead of return val value
     * NOTE if the callback target is a list, the XML buffer must provide
     * parent node in the buffer. There cannot be multiple siblings
     * in XML, it will make XML malformed.
     *
     * If the list is a top level node use the following wrapper:
     * <config xmlns=\"http://netconfcentral.org/ns/yuma-ncx\"></config>
     */
    const xmlChar *xml_buffer = (const xmlChar *)
        "<nonconfig-list xmlns=\"http://yumaworks.com/ns/aio-test\">"
        "<nonconfig-list2>"
        "<name>name1</name><nonconfig-list3>"
        "<name>name1</name></nonconfig-list3><nonconfig-list3>"
        "<name>name2</name></nonconfig-list3></nonconfig-list2>"
        "<nonconfig-list2>"
        "<name>name2</name><nonconfig-list3>"
        "<name>name1</name></nonconfig-list3><nonconfig-list3>"
        "<name>name2</name></nonconfig-list3></nonconfig-list2>"
        "<nonconfig-list2>"
        "<name>name3</name><nonconfig-list3>"
        "<name>name1</name></nonconfig-list3><nonconfig-list3>"
        "<name>name2</name></nonconfig-list3></nonconfig-list2>"
        "</nonconfig-list>";

    /* Add created buffer to return get2cb */
    res = getcb_add_return_aio_buff(get2cb, xml_buffer);

    return res;

} /* aio_test_get3_config_cont_config_list_nonconfig_list_nonconfig_list2_get */