Added top-level namespaces when pruning XML tree for client rpc calls and restconf GET

Added new xmlns_set_all()
This commit is contained in:
Olof hagsand 2022-08-26 13:29:06 +02:00
parent 9be83d6c7e
commit ad7232d1ad
13 changed files with 83 additions and 23 deletions

View file

@ -525,7 +525,6 @@ with_defaults(cxobj *xe, cxobj *xret) {
} }
} }
} }
ok:
retval = 0; retval = 0;
done: done:
return retval; return retval;

View file

@ -116,7 +116,6 @@ api_data_get2(clicon_handle h,
int i; int i;
cxobj *x; cxobj *x;
int ret; int ret;
char *namespace = NULL;
cvec *nsc = NULL; cvec *nsc = NULL;
char *attr; /* attribute value string */ char *attr; /* attribute value string */
netconf_content content = CONTENT_ALL; netconf_content content = CONTENT_ALL;
@ -124,7 +123,8 @@ api_data_get2(clicon_handle h,
cxobj *xtop = NULL; cxobj *xtop = NULL;
cxobj *xbot = NULL; cxobj *xbot = NULL;
yang_stmt *y = NULL; yang_stmt *y = NULL;
char *defaults = NULL; char *defaults = NULL;
cvec *nscd = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -266,16 +266,11 @@ api_data_get2(clicon_handle h,
switch (media_out){ switch (media_out){
case YANG_DATA_XML: case YANG_DATA_XML:
for (i=0; i<xlen; i++){ for (i=0; i<xlen; i++){
char *prefix;
x = xvec[i]; x = xvec[i];
/* Some complexities in grafting namespace in existing trees to new */ if (xml_nsctx_node(x, &nscd) < 0)
prefix = xml_prefix(x); goto done;
if (xml_find_type_value(x, prefix, "xmlns", CX_ATTR) == NULL){ if (xmlns_set_all(x, nscd) < 0)
if (xml2ns(x, prefix, &namespace) < 0) goto done;
goto done;
if (namespace && xmlns_set(x, prefix, namespace) < 0)
goto done;
}
if (clixon_xml2cbuf(cbx, x, 0, pretty, -1, 0) < 0) /* Dont print top object? */ if (clixon_xml2cbuf(cbx, x, 0, pretty, -1, 0) < 0) /* Dont print top object? */
goto done; goto done;
} }
@ -305,6 +300,8 @@ api_data_get2(clicon_handle h,
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xpath) if (xpath)
free(xpath); free(xpath);
if (nscd)
xml_nsctx_free(nscd);
if (nsc) if (nsc)
xml_nsctx_free(nsc); xml_nsctx_free(nsc);
if (xtop) if (xtop)

View file

@ -194,8 +194,6 @@ cvec *nscache_get_all(cxobj *x);
int nscache_set(cxobj *x, char *prefix, char *ns); int nscache_set(cxobj *x, char *prefix, char *ns);
int nscache_clear(cxobj *x); int nscache_clear(cxobj *x);
int nscache_replace(cxobj *x, cvec *ns); int nscache_replace(cxobj *x, cvec *ns);
int xmlns_set(cxobj *x, char *prefix, char *ns);
cxobj *xml_parent(cxobj *xn); cxobj *xml_parent(cxobj *xn);
int xml_parent_set(cxobj *xn, cxobj *parent); int xml_parent_set(cxobj *xn, cxobj *parent);
#ifdef XML_PARENT_CANDIDATE #ifdef XML_PARENT_CANDIDATE

View file

@ -63,6 +63,8 @@ int xml_nsctx_cbuf(cbuf *cb, cvec *nsc);
int xml2ns(cxobj *x, char *localname, char **ns); int xml2ns(cxobj *x, char *localname, char **ns);
int xml2ns_recurse(cxobj *x); int xml2ns_recurse(cxobj *x);
int xmlns_set(cxobj *x, char *prefix, char *ns);
int xmlns_set_all(cxobj *x, cvec *nsc);
int xml2prefix(cxobj *xn, char *ns, char **prefixp); int xml2prefix(cxobj *xn, char *ns, char **prefixp);
#endif /* _CLIXON_XML_NSCTX_H */ #endif /* _CLIXON_XML_NSCTX_H */

View file

@ -500,6 +500,7 @@ clicon_rpc_get_config(clicon_handle h,
uint32_t session_id; uint32_t session_id;
int ret; int ret;
yang_stmt *yspec; yang_stmt *yspec;
cvec *nscd = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
@ -554,12 +555,20 @@ clicon_rpc_get_config(clicon_handle h,
} }
} }
if (xt && xd){ if (xt && xd){
/* Sync namespaces, ie explicitly set all xmlns attributes to xd */
if (xml_nsctx_node(xd, &nscd) < 0)
goto done;
if (xml_rm(xd) < 0) if (xml_rm(xd) < 0)
goto done; goto done;
if (xmlns_set_all(xd, nscd) < 0)
goto done;
xml_sort(xd); /* Ensure attr is first */
*xt = xd; *xt = xd;
} }
retval = 0; retval = 0;
done: done:
if (nscd)
cvec_free(nscd);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (xerr) if (xerr)
@ -858,7 +867,7 @@ clicon_rpc_get(clicon_handle h,
cvec *nsc, /* namespace context for filter */ cvec *nsc, /* namespace context for filter */
netconf_content content, netconf_content content,
int32_t depth, int32_t depth,
char *defaults, char *defaults,
cxobj **xt) cxobj **xt)
{ {
int retval = -1; int retval = -1;
@ -871,6 +880,7 @@ clicon_rpc_get(clicon_handle h,
uint32_t session_id; uint32_t session_id;
int ret; int ret;
yang_stmt *yspec; yang_stmt *yspec;
cvec *nscd = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
@ -932,12 +942,20 @@ clicon_rpc_get(clicon_handle h,
} }
} }
if (xt && xd){ if (xt && xd){
/* Sync namespaces, ie explicitly set all xmlns attributes to xd */
if (xml_nsctx_node(xd, &nscd) < 0)
goto done;
if (xml_rm(xd) < 0) if (xml_rm(xd) < 0)
goto done; goto done;
if (xmlns_set_all(xd, nscd) < 0)
goto done;
xml_sort(xd); /* Ensure attr is first */
*xt = xd; *xt = xd;
} }
retval = 0; retval = 0;
done: done:
if (nscd)
cvec_free(nscd);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (xerr) if (xerr)
@ -993,6 +1011,7 @@ clicon_rpc_get_pageable_list(clicon_handle h,
uint32_t session_id; uint32_t session_id;
int ret; int ret;
yang_stmt *yspec; yang_stmt *yspec;
cvec *nscd = NULL;
if (datastore == NULL){ if (datastore == NULL){
clicon_err(OE_XML, EINVAL, "datastore not given"); clicon_err(OE_XML, EINVAL, "datastore not given");
@ -1072,12 +1091,20 @@ clicon_rpc_get_pageable_list(clicon_handle h,
} }
} }
if (xt && xd){ if (xt && xd){
/* Sync namespaces, ie explicitly set all xmlns attributes to xd */
if (xml_nsctx_node(xd, &nscd) < 0)
goto done;
if (xml_rm(xd) < 0) if (xml_rm(xd) < 0)
goto done; goto done;
if (xmlns_set_all(xd, nscd) < 0)
goto done;
xml_sort(xd); /* Ensure attr is first */
*xt = xd; *xt = xd;
} }
retval = 0; retval = 0;
done: done:
if (nscd)
cvec_free(nscd);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (xerr) if (xerr)

View file

@ -77,6 +77,7 @@
#include "clixon_handle.h" #include "clixon_handle.h"
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_vec.h" #include "clixon_xml_vec.h"
#include "clixon_data.h" #include "clixon_data.h"
#include "clixon_text_syntax_parse.h" #include "clixon_text_syntax_parse.h"

View file

@ -597,6 +597,42 @@ xmlns_set(cxobj *x,
return retval; return retval;
} }
/*! Given an xml node x and a namespace context, add namespace xmlns attributes to x
*
* As a side-effect, the namespace cache is set
* Check if already there
* @param[in] x XML tree
* @param[in] nsc Namespace context
* @note you need to do an xml_sort(x) after the call
*/
int
xmlns_set_all(cxobj *x,
cvec *nsc)
{
int retval = -1;
char *ns;
char *pf;
cg_var *cv = NULL;
while ((cv = cvec_each(nsc, cv)) != NULL){
pf = cv_name_get(cv);
/* Check already added */
if (pf != NULL) /* xmlns:<prefix>="<uri>" */
ns = xml_find_type_value(x, "xmlns", pf, CX_ATTR);
else{ /* xmlns="<uri>" */
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
}
if (ns)
continue;
ns = cv_string_get(cv);
if (ns && xmlns_set(x, pf, ns) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get prefix of given namespace recursively /*! Get prefix of given namespace recursively
* @param[in] xn XML node * @param[in] xn XML node
* @param[in] namespace Namespace * @param[in] namespace Namespace

View file

@ -89,7 +89,7 @@ expectpart "$($clixon_cli -1 -f $cfg -l o debug backend 1)" 0 "^$"
# Exercise debug code # Exercise debug code
new "get and put config using restconf" new "get and put config using restconf"
expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:table":{"parameter":{"name":"local0","value":"foo"}}}')" 0 "HTTP/$HVER 200" '<data/>' "HTTP/$HVER 201" expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:table":{"parameter":{"name":"local0","value":"foo"}}}')" 0 "HTTP/$HVER 200" "<data $DEFAULTONLY/>" "HTTP/$HVER 201"
# In freebsd, backend dies in stop_restconf below unless sleep # In freebsd, backend dies in stop_restconf below unless sleep
sleep $DEMSLEEP sleep $DEMSLEEP

View file

@ -146,11 +146,11 @@ fi
# XXX ftest har \n # XXX ftest har \n
# Only compare relevant data line # Only compare relevant data line
echo -n "<data>">> $ftest echo -n "<data $DEFAULTONLY>">> $ftest
cat $fdataxml >> $ftest cat $fdataxml >> $ftest
echo -n "</data>" >> $ftest echo -n "</data>" >> $ftest
# -i (ignore case) dont always work properly # -i (ignore case) dont always work properly
sed '/<data>/!d' $foutput > $foutput2 sed "/<data $DEFAULTONLY>/!d" $foutput > $foutput2
# Strip potential newlines, curl seems to leave trailing newlines on some platforms/versions # Strip potential newlines, curl seems to leave trailing newlines on some platforms/versions
tr -d "\n\r" < $foutput2 > $foutput tr -d "\n\r" < $foutput2 > $foutput

View file

@ -161,11 +161,11 @@ if [ $r -ne 0 ]; then
fi fi
# Only compare relevant data line # Only compare relevant data line
echo -n "<data>">> $ftest echo -n "<data $DEFAULTONLY>">> $ftest
cat $fdataxml >> $ftest cat $fdataxml >> $ftest
#echo "</data> " >> $ftest #echo "</data> " >> $ftest
echo -n "</data>" >> $ftest echo -n "</data>" >> $ftest
sed '/<data>/!d' $foutput > $foutput2 sed "/<data $DEFAULTONLY>/!d" $foutput > $foutput2
mv $foutput2 $foutput mv $foutput2 $foutput
ret=$(diff -i $ftest $foutput) ret=$(diff -i $ftest $foutput)

View file

@ -206,7 +206,7 @@ expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "H
#--------------- Multiple request in single TCP tests #--------------- Multiple request in single TCP tests
new "Multiple requests: GET + POST using --next" new "Multiple requests: GET + POST using --next"
expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}')" 0 "HTTP/$HVER 200" '<data/>' "HTTP/$HVER 201" expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}')" 0 "HTTP/$HVER 200" "<data $DEFAULTONLY/>" "HTTP/$HVER 201"
new "Multiple requests: POST + POST" # XXX Do for HTTP/1 ALSO new "Multiple requests: POST + POST" # XXX Do for HTTP/1 ALSO
expectpart "$(curl $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"local1","type":"regular"}}' --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"local2","type":"regular"}}')" 0 "HTTP/$HVER 201" "localhost/restconf/data/example:cont1/interface=local1" "localhost/restconf/data/example:cont1/interface=local2" expectpart "$(curl $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"local1","type":"regular"}}' --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"local2","type":"regular"}}')" 0 "HTTP/$HVER 201" "localhost/restconf/data/example:cont1/interface=local1" "localhost/restconf/data/example:cont1/interface=local2"

View file

@ -252,7 +252,7 @@ new "PATCH on root resource extra c" # merge extra/c
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"ietf-restconf:data":{"example-jukebox:extra":"c"}}')" 0 "HTTP/$HVER 204" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"ietf-restconf:data":{"example-jukebox:extra":"c"}}')" 0 "HTTP/$HVER 204"
new "GET check" # XXX: "data" should probably be namespaced? new "GET check" # XXX: "data" should probably be namespaced?
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data?content=config -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<extra xmlns="http://example.com/ns/example-jukebox">c</extra>' '<data>' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data?content=config -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<extra xmlns="http://example.com/ns/example-jukebox">c</extra>' "<data $DEFAULTONLY>"
new "Add empty leaf" new "Add empty leaf"
expectpart "$(curl -u andy:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/data -H 'Content-Type: application/yang-data+json' -d '{"example-system:system":{"extraleaf":""}}')" 0 "HTTP/$HVER 201" expectpart "$(curl -u andy:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/data -H 'Content-Type: application/yang-data+json' -d '{"example-system:system":{"extraleaf":""}}')" 0 "HTTP/$HVER 201"

View file

@ -479,7 +479,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPR
"HTTP/$HVER 200" \ "HTTP/$HVER 200" \
"Content-Type: application/yang-data+xml" \ "Content-Type: application/yang-data+xml" \
"Cache-Control: no-cache" \ "Cache-Control: no-cache" \
'<interface xmlns="http://example.com/ns/interfaces"><name>eth1</name><mtu wd:default="true">1500</mtu><status wd:default="true">ok</status></interface>' '<interface xmlns="http://example.com/ns/interfaces" xmlns:wd="urn:ietf:params:xml:ns:netconf:default:1.0"><name>eth1</name><mtu wd:default="true">1500</mtu><status wd:default="true">ok</status></interface>'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then