From bab3b5ad5698a4f8bd562cc4020c746d656ded42 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 4 Mar 2020 17:31:52 +0100 Subject: [PATCH] * Added yang population of data in clicon_rpc_get[_config] --- apps/cli/cli_show.c | 17 -------- apps/restconf/restconf_methods_get.c | 2 - lib/src/clixon_proto_client.c | 42 +++++++++++++++++-- lib/src/clixon_xml_map.c | 60 ++++++++++++++++++---------- test/test_augment.sh | 21 +++++----- test/test_restconf_listkey.sh | 34 +++++++++++----- 6 files changed, 114 insertions(+), 62 deletions(-) diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 6eb7dbb6..bdbb0085 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -433,7 +433,6 @@ cli_show_config1(clicon_handle h, cvec *argv) { int retval = -1; - int ret; char *db; char *formatstr; char *xpath; @@ -496,13 +495,6 @@ cli_show_config1(clicon_handle h, clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } - /* Some formats (eg cli) require yang */ - if ((ret = xml_spec_populate(xt, yspec, &xerr)) < 0) - goto done; - if (ret == 0){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); - goto done; - } /* Print configuration according to format */ switch (format){ case FORMAT_XML: @@ -688,7 +680,6 @@ cli_show_auto1(clicon_handle h, cvec *argv) { int retval = 1; - int ret; yang_stmt *yspec; char *api_path_fmt; /* xml key format */ // char *api_path = NULL; /* xml key */ @@ -743,18 +734,10 @@ cli_show_auto1(clicon_handle h, if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) goto done; } - if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } - /* Some formats (eg cli) require yang */ - if ((ret = xml_spec_populate(xt, yspec, &xerr)) < 0) - goto done; - if (ret == 0){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); - goto done; - } if ((xp = xpath_first(xt, nsc, "%s", xpath)) != NULL) /* Print configuration according to format */ switch (format){ diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index d2bc9556..479082f6 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -204,8 +204,6 @@ api_data_get2(clicon_handle h, goto done; goto ok; } - if (xml_spec_populate(xret, yspec, NULL) < 0) - goto done; /* We get return via netconf which is complete tree from root * We need to cut that tree to only the object. */ diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index b4371911..347c77fe 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -343,7 +343,7 @@ clicon_rpc_generate_error(cxobj *xerr, * @endcode * @see clicon_rpc_get * @see clicon_rpc_generate_error - * @note the netconf return message us yang populated, but returned data is not + * @note the netconf return message is yang populated, as well as the return data */ int clicon_rpc_get_config(clicon_handle h, @@ -357,10 +357,13 @@ clicon_rpc_get_config(clicon_handle h, struct clicon_msg *msg = NULL; cbuf *cb = NULL; cxobj *xret = NULL; + cxobj *xerr = NULL; cxobj *xd; cg_var *cv = NULL; char *prefix; uint32_t session_id; + int ret; + yang_stmt *yspec; if (session_id_check(h, &session_id) < 0) goto done; @@ -394,9 +397,21 @@ clicon_rpc_get_config(clicon_handle h, /* Send xml error back: first check error, then ok */ if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL) xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL) + else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){ if ((xd = xml_new("data", NULL, NULL)) == NULL) goto done; + } + else{ + yspec = clicon_dbspec_yang(h); + if ((ret = xml_spec_populate(xd, yspec, &xerr)) < 0) + goto done; + if (ret == 0){ + if ((xd = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + clicon_err(OE_XML, ENOENT, "Expected rpc-error tag but none found(internal)"); + goto done; + } + } + } if (xt){ if (xml_rm(xd) < 0) goto done; @@ -406,6 +421,8 @@ clicon_rpc_get_config(clicon_handle h, done: if (cb) cbuf_free(cb); + if (xerr) + xml_free(xerr); if (xret) xml_free(xret); if (msg) @@ -674,7 +691,7 @@ clicon_rpc_unlock(clicon_handle h, * @endcode * @see clicon_rpc_get_config which is almost the same as with content=config, but you can also select dbname * @see clicon_rpc_generate_error - * @note the netconf return message us yang populated, but returned data is not + * @note the netconf return message is yang populated, as well as the return data */ int clicon_rpc_get(clicon_handle h, @@ -688,11 +705,14 @@ clicon_rpc_get(clicon_handle h, struct clicon_msg *msg = NULL; cbuf *cb = NULL; cxobj *xret = NULL; + cxobj *xerr = NULL; cxobj *xd; char *username; cg_var *cv = NULL; char *prefix; uint32_t session_id; + int ret; + yang_stmt *yspec; if (session_id_check(h, &session_id) < 0) goto done; @@ -733,9 +753,21 @@ clicon_rpc_get(clicon_handle h, /* Send xml error back: first check error, then ok */ if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL) xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL) + else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){ if ((xd = xml_new("data", NULL, NULL)) == NULL) goto done; + } + else{ + yspec = clicon_dbspec_yang(h); + if ((ret = xml_spec_populate(xd, yspec, &xerr)) < 0) + goto done; + if (ret == 0){ + if ((xd = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + clicon_err(OE_XML, ENOENT, "Expected rpc-error tag but none found(internal)"); + goto done; + } + } + } if (xt){ if (xml_rm(xd) < 0) goto done; @@ -745,6 +777,8 @@ clicon_rpc_get(clicon_handle h, done: if (cb) cbuf_free(cb); + if (xerr) + xml_free(xerr); if (xret) xml_free(xret); if (msg) diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 5eb383a4..944d765d 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1313,7 +1313,11 @@ populate_self_top(cxobj *xt, * err; * @endcode * @note For subs to anyxml nodes will not have spec set - * @see xml_spec_populate_rpc for incoming rpc + * There are several functions in the API family + * @see xml_spec_populate_rpc for incoming rpc + * @see xml_spec_populate_parent Not top-level and parent is properly yang populated + * @see xml_spec_populate0 If the calling xml object should also be populated + * @see xml_spec_populate0_parent */ int xml_spec_populate(cxobj *xt, @@ -1357,9 +1361,15 @@ xml_spec_populate_parent(cxobj *xt, if (ret == 0) failed++; } - retval = (failed==0) ? 1 : 0; + if (failed) + goto fail; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; + } /*! Find yang spec association of tree of XML nodes @@ -1378,18 +1388,23 @@ xml_spec_populate0(cxobj *xt, if ((ret = populate_self_top(xt, yspec, xerr)) < 0) goto done; - if (ret == 1){ - xc = NULL; /* Apply on children */ - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0) - goto done; - if (ret == 0) - failed++; - } + if (ret == 0) + goto fail; + xc = NULL; /* Apply on children */ + while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { + if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0) + goto done; + if (ret == 0) + failed++; } - retval = (failed==0) ? 1 : 0; + if (failed) + goto fail; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } /*! Find yang spec association of tree of XML nodes @@ -1408,19 +1423,22 @@ xml_spec_populate0_parent(cxobj *xt, if ((ret = populate_self_parent(xt, xerr)) < 0) goto done; if (ret == 0) - failed++; - else if (ret != 1){ /* 1 means anyxml parent */ - xc = NULL; /* Apply on children */ - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0) - goto done; - if (ret == 0) - failed++; - } + goto fail; + xc = NULL; /* Apply on children */ + while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { + if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0) + goto done; + if (ret == 0) + failed++; } - retval = (failed==0) ? 1 : 0; + if (failed) + goto fail; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } /*! Given an XML node, build an xpath to root, internal function diff --git a/test/test_augment.sh b/test/test_augment.sh index c736d56f..8b0c28a9 100755 --- a/test/test_augment.sh +++ b/test/test_augment.sh @@ -167,14 +167,16 @@ fi new "waiting" wait_backend -new "kill old restconf daemon" -sudo pkill -u $wwwuser -f clixon_restconf +if [ $RC -ne 0 ]; then + new "kill old restconf daemon" + sudo pkill -u $wwwuser -f clixon_restconf -new "start restconf daemon" -start_restconf -f $cfg + new "start restconf daemon" + start_restconf -f $cfg -new "waiting" -wait_restconf + new "waiting" + wait_restconf +fi # mandatory-leaf See RFC7950 Sec 7.17 new "netconf set interface with augmented type and mandatory leaf" @@ -240,7 +242,6 @@ EOF new "restconf POST augment multi-namespace path e2 (middle path)" expectpart "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 '' - new "restconf GET augment multi-namespace top" expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}' @@ -253,8 +254,10 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:int new "restconf GET augment multi-namespace cross level 2" expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example-augment:reference-bandwidth":23}' -new "Kill restconf daemon" -stop_restconf +if [ $RC -ne 0 ]; then + new "Kill restconf daemon" + stop_restconf +fi if [ $BE -eq 0 ]; then exit # BE diff --git a/test/test_restconf_listkey.sh b/test/test_restconf_listkey.sh index 4da53336..b5daf7ae 100755 --- a/test/test_restconf_listkey.sh +++ b/test/test_restconf_listkey.sh @@ -80,21 +80,36 @@ fi new "waiting" wait_backend -new "kill old restconf daemon" -sudo pkill -u $wwwuser -f clixon_restconf +if [ $RC -ne 0 ]; then + new "kill old restconf daemon" + sudo pkill -u $wwwuser -f clixon_restconf -new "start restconf daemon" -start_restconf -f $cfg + new "start restconf daemon" + start_restconf -f $cfg -new "waiting" -wait_restconf + new "waiting" + wait_restconf +fi new "restconf PUT add whole list entry" expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"0"}}')" 0 '' +# GETs to ensure you get list [] in JSON +new "restconf GET whole list entry" +expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/)" 0 '{"list:c":{"a":[{"b":"x","c":"y","nonkey":"0"}]}} + ' + +new "restconf GET list entry" +expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y)" 0 '{"list:a":[{"b":"x","c":"y","nonkey":"0"}]} + ' + new "restconf PUT add whole list entry XML" expecteq "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d 'xxxy0' http://localhost/restconf/data/list:c/a=xx,xy)" 0 '' +new "restconf GET list entry two XXX shouldnt be allowed" +expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a/b)" 0 '{"list:b":["x","xx"]} + ' + new "restconf PUT change whole list entry (same keys)" expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"z"}}')" 0 '' @@ -149,9 +164,10 @@ expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http:// new "restconf PUT change list+leaf-list entry (expect fail)" expectpart "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y/f=u -d '{"list:f":"w"}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}' - -new "Kill restconf daemon" -stop_restconf +if [ $RC -ne 0 ]; then + new "Kill restconf daemon" + stop_restconf +fi if [ $BE -eq 0 ]; then exit # BE