Consider this simplified example, that represents 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; } } ... } ...
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 as other lists, containers, or choice nodes must have their own callbacks functions.
Please note that your GET2 callback function is responsible to decide what list entry to return. The decision should be made based on the following logic:
GET2 callback function logic for a list could look as follows:
... /* check validity of keys present */ if (getnext) { /* adjust the key to find the next instance after * the specified value */ if (!name_present) { /* return the first instance */ } else { /* find the next instance to retrieve based on the current present key name */ } } else { if (name_present) { /* match a specified instance based on the current present key name */ } else { /* return the first instance */ } } /***** ADD RETURN KEYS AND ANY OTHER OPTIONAL LEAFS ****/ ...
The following C API code exemplifies a simple GET2 callback function for the data model, which returns the “interface” list element with an additional leaf elements when it is requested.
/******************************************************************** * 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 */
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="101" 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="101" 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-name</name> </interface> <interface> <ip>10.10.10.3/16</ip> <name>interface-name</name> </interface> </interfaces-state> </rpc-reply>
NOTE:
In case your YANG definition for a list does not define any keys at all, GET and GETNEXT will not provide any keys values, and it will be up to you to decide what list entry to return upon each GETNEXT request form the server. The more_data flag will signal the server when it should stop asking for more getnext entries.
GET2 callback handling with no any keys defined could look as follows:
... /* This list has no keys defined; return entry somehow * no need to call getcb_add_return_key */ /* For GETNEXT, set the more_data flag true if not sure */ boolean more_data = TRUE; res = get_the_list_item(&item_temp); if (res != NO_ERR) { more_data = FALSE; GETCB_GET2_MORE_DATA(get2cb) = more_data; return res; } GETCB_GET2_MORE_DATA(get2cb) = more_data; ...