By default, the server expects GET2 callbacks to retrieve only one instance at a time. That's, only one list instance is getting return at a time.

After the first instance is return the server will call the callback function again to get the next instance. It will iterate until

the callback function specifies that there is no more instance available.


For most scenarios, it is not critical to follow this procedure. However, assume your implementation cannot use this approach.

Assume, you have to retrieve all the instances within a single GET2 callback call. In this case the regular standard GET2 callback function will not fulfill all the requirements.


The following article describes how to implement bulk get retrieval within a single GET2 callback function call.


Consider this simplified example, that represents GET2 callback for the “active” list.


    container bulkget {                 // separate get2 callback
      config false;

      list active {                     // separate get2 callback
        key "id";

        leaf id {
          type uint32;
        }

        leaf boolean {
          type boolean;
        }
      }
    }



NOTE:

The GET2 callbacks for “list” elements DO require to fill in the list key value. 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.


The following C API code exemplifies a bulk get GET2 callback function for the data model, which returns N "active” list elements with an additional leaf elements when it is requested.


/********************************************************************
* FUNCTION  get2_list_bulkget
*
* Path: /bulkget/active
*
* 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_bulkget (ses_cb_t *scb,
                                 xml_msg_hdr_t *msg,
                                 getcb_get2_t *get2cb)
{
    (void)scb;
    (void)msg;
    status_t res = NO_ERR;

    /* current object to use */
    obj_template_t *get2obj = GETCB_GET2_OBJ(get2cb);

    /* max number of entries to process within a single call
     * can be changed by netconfd-pro CLI parameter "--max-getbulk 15"
     */
    uint32 max_entries = GETCB_GET2_MAX_ENTRIES(get2cb);

    /* will be FALSE until max_etries is reached */
    boolean getnext = FALSE;

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

    /* If the set of keys for the current list is incomplete,
     * then the provided keys are 'fixed'
     */
    boolean key_fixed = 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);
    }

    uint32 aid = 0;
    uint32 ret_aid = 0;

    obj_template_t *key_obj =
        obj_find_child(get2obj,
                       GET2_TEST_MOD_NAME,
                       (const xmlChar *)"id");

    val_value_t *key_val = getcb_find_key(get2cb, key_obj);

    /* the implementation can decide how many entries to
     * return if the --max-getbulk parameter is set to zero
     */
    if (max_entries == 0) {
        max_entries = 10;
    }

    uint32 loop_count = 0;
    boolean done = FALSE;
    while (!done) {

        if (key_val) {
            /* When a key is provided.
             * In case of the GET request on a specific
             * entry (E.g. ../active[id='3']) the value will be available here.
             * In case of a regular GET request on a list with no key(s).
             * For the first run the value will be unavailable, so
             * the return value should be set to the first entry.
             * for the next runs this value will be available
             * to get the next entry of the list
             */
            aid = VAL_UINT32(key_val);

            key_fixed = VAL_IS_FIXED_VALUE(key_val);
            if (LOGDEBUG2) {
                if (key_fixed) {
                    log_debug2("\nget2-list: got fixed key aid=%d", aid);
                } else {
                    log_debug2("\nget2-list: got key aid=%d", aid);
                }
            }
        }

        boolean bulk_mode = !key_fixed;

        if (LOGDEBUG3) {
            log_debug3("\nget2-list: %s params aid=%d",
                       (getnext) ? "GETNEXT" : "GET", aid);
            if (bulk_mode) {
                log_debug3_append(" (BULK:max %u)", max_entries);
            }
        }

        /* check validity of keys present */
        if (getnext || (bulk_mode && (loop_count > 0))) {
            /* check key fixed value; should not happen */
            if (key_fixed) {
                return ERR_NCX_NO_INSTANCE;
            }

            if (!key_val) {
                ; /* return the first entry [0] */
            } else {
                /* set up the next value */
                ret_aid++;
            }

            /* in case the specific list entry requested, ../active[id='3'] */
        } else if (!loop_count && key_val && !getnext) {
            /* return specified entry [aid], match entry based
             * on the specified key value
             */
            if (aid > 5) {
                /* apply limitations and validation for the specified key,
                 * E.g. cannot accces entry > 5
                 */
                return ERR_NCX_NO_INSTANCE;
            } else {
                ret_aid = aid;
            }
        }

        if (LOGDEBUG2) {
            log_debug2("\nget2-list: start %s for ret_aid=%d",
                       (getnext) ? "GETNEXT" : "GET", ret_aid);
        }

        moreflag = TRUE;
        /* Ex: no more then 5 entries */
        if (ret_aid == 5) {
            moreflag = FALSE;
        }

        GETCB_GET2_MORE_DATA(get2cb) = moreflag;
        GETCB_GET2_MATCH_TEST_DONE(get2cb) = FALSE;


        /* Make up a return val_value node based on the found value */
        val_value_t *ret_key_val = val_new_value();
        if (ret_key_val == NULL) {
            return ERR_INTERNAL_VAL;
        }

        val_init_from_template(ret_key_val, key_obj);
        VAL_UINT32(ret_key_val) = ret_aid;
        if (key_fixed) {
            VAL_SET_FIXED_VALUE(ret_key_val);
        }
        getcb_add_return_key(get2cb, ret_key_val);

        if ((res != NO_ERR) || (key_fixed)) {
            done = TRUE;
        } else {
            getcb_finish_getbulk_entry(get2cb);

            /* ++loop_count < max_entries MUST be here to handle
             * specific entry request, if GET on ../active[id='3'], the
             * max_entries flag will be set to 1, as a result the
             * loop will be ended right after the specified entry
             * has matched desired list entry
             */
            if ((++loop_count < max_entries)  && moreflag &&
                ret_key_val) {

                if (!key_fixed) {
                    key_val = ret_key_val;
                }
            } else {
                done = TRUE;
            }
        }
    }

    return res;

}  /* get2_list_bulkget */


As a result, the server will generate all 5 entries within a single GET2 callback function call. Based on the callback function code the JSON output on a RESTCONF GET request on "/bulkget" container should look as follows:


> curl http://restconf-dev/restconf/data/bulkget -H "Accept:application/yang-data+json" 

{
    "get2-test:bulkget": {
        "active": [
            {
                "id": 0
            },
            {
                "id": 1
            },
            {
                "id": 2
            },
            {
                "id": 3
            },
            {
                "id": 4
            },
            {
                "id": 5
            }
        ]
    }
}