Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/backend/backend_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ restconf_client_get_capabilities(clixon_handle h,
cprintf(cb, "<capabilities>");
cprintf(cb, "<capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability>");
cprintf(cb, "<capability>urn:ietf:params:restconf:capability:depth:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:restconf:capability:fields:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:restconf:capability:with-defaults:1.0</capability>");
cprintf(cb, "</capabilities>");
if (clixon_xml_parse_string(cbuf_get(cb), YB_PARENT, NULL, &xrstate, NULL) < 0)
Expand Down
84 changes: 84 additions & 0 deletions apps/restconf/restconf_methods_get.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,79 @@
/* Forward */
static int api_data_pagination(clixon_handle h, void *req, char *api_path, int pi, cvec *qvec, int pretty, restconf_media media_out);

/*! Filter top-level data nodes based on "fields" query parameter (RFC 8040 Sec 4.8.3)
*
* Simple implementation: only top-level module:node or module:* selection at data root.
* Removes children of xdata that do not match any entry in the semicolon-separated
* fields list.
* @param[in] xdata XML data root whose children are top-level data nodes
* @param[in] fields_str Value of the "fields" query parameter, e.g. "mod1:node1;mod2:node2"
* @retval 0 OK
* @retval -1 Error
* @see RFC 8040 Sec 4.8.3
*/
static int
restconf_fields_filter(cxobj *xdata,
char *fields_str)
{
int retval = -1;
char *str = NULL;
char *s;
char *saveptr;
char *colon;
cxobj *x;
yang_stmt *ynode;
yang_stmt *ymod;
char *modname;
char *nodename;
int i;
int found;

for (i = xml_child_nr(xdata)-1; i >= 0; i--){
x = xml_child_i(xdata, i);
if (xml_type(x) != CX_ELMNT)
continue;
ynode = xml_spec(x);
ymod = (ynode != NULL) ? ys_module(ynode) : NULL;
modname = (ymod != NULL) ? yang_argument_get(ymod) : NULL;
nodename = xml_name(x);
found = 0;
if ((str = strdup(fields_str)) == NULL){
clixon_err(OE_UNIX, errno, "strdup");
goto done;
}
s = strtok_r(str, ";", &saveptr);
while (s != NULL){
colon = strchr(s, ':');
if (colon != NULL){
*colon = '\0';
if (modname != NULL &&
strcmp(s, modname) == 0 &&
strcmp(colon+1, nodename) == 0)
found = 1;
*colon = ':';
}
else{
if (modname != NULL && strcmp(s, modname) == 0)
found = 1;
}
if (found)
break;
s = strtok_r(NULL, ";", &saveptr);
}
free(str);
str = NULL;
if (!found)
if (xml_purge(x) < 0)
goto done;
}
retval = 0;
done:
if (str)
free(str);
return retval;
}

/*! Generic GET (both HEAD and GET)
*
* According to restconf
Expand Down Expand Up @@ -125,6 +198,7 @@ api_data_get2(clixon_handle h,
cxobj *xtop = NULL;
yang_stmt *y = NULL;
char *defaults = NULL;
char *fields_str = NULL;
cvec *nscd = NULL;
int ret;

Expand Down Expand Up @@ -207,6 +281,11 @@ api_data_get2(clixon_handle h,
clixon_debug(CLIXON_DBG_RESTCONF, "with_defaults=%s", attr);
defaults = attr;
}
/* Check for fields attribute: simple root-level module:node filter, RFC 8040 Sec 4.8.3 */
if ((attr = cvec_find_str(qvec, "fields")) != NULL){
clixon_debug(CLIXON_DBG_RESTCONF, "fields=%s", attr);
fields_str = attr;
}

clixon_debug(CLIXON_DBG_RESTCONF, "path:%s", xpath);
if ((ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret)) < 0){
Expand Down Expand Up @@ -235,6 +314,11 @@ api_data_get2(clixon_handle h,
goto done;
}
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
/* Apply fields filter if requested */
if (fields_str != NULL){
if (restconf_fields_filter(xret, fields_str) < 0)
goto done;
}
switch (media_out){
case YANG_DATA_XML:
if (clixon_xml2cbuf(cbx, xret, 0, pretty, NULL, -1, 0) < 0) /* Dont print top object? */
Expand Down
24 changes: 20 additions & 4 deletions test/test_restconf_jukebox.sh
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,30 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCP

# Maybe this is not correct w [null,null]but I have no good examples
new 'B.3.2. "depth" Parameter depth=3'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}}
'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}}

'

new "restconf DELETE whole datastore"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"

#new 'B.3.3. "fields" Parameter'
new 'B.3.3. "fields" Parameter: preamble (create system and jukebox data)'
expectpart "$(curl $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d '<data xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><system xmlns="http://example.com/ns/example-system"><enable-jukebox-streaming>true</enable-jukebox-streaming></system><jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Fields Artist</name></artist></library></jukebox></data>')" 0 "HTTP/$HVER 204"

new 'B.3.3. "fields" Parameter: system only'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data?fields=example-system:system)" 0 "HTTP/$HVER 200" \
'"example-system:system"' --not-- '"example-jukebox:jukebox"'

new 'B.3.3. "fields" Parameter: jukebox only'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data?fields=example-jukebox:jukebox)" 0 "HTTP/$HVER 200" \
'"example-jukebox:jukebox"' --not-- '"example-system:system"'

new 'B.3.3. "fields" Parameter: two modules'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' "$RCPROTO://localhost/restconf/data?fields=example-system:system%3Bexample-jukebox:jukebox")" 0 "HTTP/$HVER 200" \
'"example-system:system"' '"example-jukebox:jukebox"'

new "restconf DELETE whole datastore (after fields test)"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"

new 'B.3.4. "insert" Parameter'
JSON="{\"example-jukebox:song\":[{\"index\":1,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Rope']\"}]}"
Expand Down Expand Up @@ -251,7 +268,6 @@ new 'B.3.5. "insert/point" leaf-list check order (2,4,3,1)'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'

#new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed
#new 'B.3.3. "fields" Parameter'
#new 'B.3.6. "filter" Parameter'
#new 'B.3.7. "start-time" Parameter'
#new 'B.3.8. "stop-time" Parameter'
Expand Down
4 changes: 2 additions & 2 deletions test/test_yang_with_defaults.sh
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,14 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCP
0 \
"HTTP/$HVER 200" "Content-Type: application/yang-data+json" \
'Cache-Control: no-cache' \
'{"ietf-restconf-monitoring:capabilities":{"capability":\["urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit","urn:ietf:params:restconf:capability:depth:1.0","urn:ietf:params:restconf:capability:with-defaults:1.0"\]}}'
'{"ietf-restconf-monitoring:capabilities":{"capability":\["urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit","urn:ietf:params:restconf:capability:depth:1.0","urn:ietf:params:restconf:capability:fields:1.0","urn:ietf:params:restconf:capability:with-defaults:1.0"\]}}'

new "rfc8040 B.1.3. Retrieve the Server Capability Information xml"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" \
0 \
"HTTP/$HVER 200" "Content-Type: application/yang-data+xml" \
'Cache-Control: no-cache' \
'<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth:1.0</capability><capability>urn:ietf:params:restconf:capability:with-defaults:1.0</capability></capabilities>'
'<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth:1.0</capability><capability>urn:ietf:params:restconf:capability:fields:1.0</capability><capability>urn:ietf:params:restconf:capability:with-defaults:1.0</capability></capabilities>'

new "rfc8040 B.3.9. RESTCONF with-defaults parameter = report-all json"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example:interfaces/interface=eth1?with-defaults=report-all)" \
Expand Down