* C-code changes:
  - Replaced stream uri:s w constants
  - Replaced large debug print code with single clicon_log_xml
  - Restconf put and post handling refactored using new parse API
This commit is contained in:
Olof hagsand 2020-02-24 15:25:06 +01:00
parent de3853a126
commit a71c256898
27 changed files with 308 additions and 250 deletions

View file

@ -297,7 +297,6 @@ client_statedata(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "clixon-rfc5277 namespace not found");
goto done;
}
cprintf(cb, "<netconf xmlns=\"%s\"/>", namespace);
if (xml_parse_string2(cbuf_get(cb), YB_TOP, yspec, xret, NULL) < 0)
goto done;
@ -1035,15 +1034,8 @@ from_client_get(clicon_handle h,
if (ret > 0 && (ret = xml_yang_validate_add(h, xret, &xerr)) < 0)
goto done;
if (ret == 0){
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s FAIL: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
#endif
if (debug)
clicon_log_xml(LOG_DEBUG, xret, "VALIDATE_STATE");
if ((xr = xpath_first(xerr, NULL, "//error-tag")) != NULL &&
(xb = xml_body_get(xr))){
if (xml_value_set(xb, "operation-failed") < 0)
@ -1258,7 +1250,7 @@ from_client_create_subscription(clicon_handle h,
struct timeval stop;
cvec *nsc = NULL;
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netmod:notification")) == NULL)
if ((nsc = xml_nsctx_init(NULL, EVENT_RFC5277_NAMESPACE)) == NULL)
goto done;
if ((x = xpath_first(xe, nsc, "//stream")) != NULL)
stream = xml_find_value(x, "body");
@ -1721,7 +1713,7 @@ backend_rpc_init(clicon_handle h)
/* In backend_client.? RPC from RFC 5277 */
if (rpc_callback_register(h, from_client_create_subscription, NULL,
"urn:ietf:params:xml:ns:netmod:notification", "create-subscription") < 0)
EVENT_RFC5277_NAMESPACE, "create-subscription") < 0)
goto done;
/* Clixon RPC */
if (rpc_callback_register(h, from_client_debug, NULL,

View file

@ -125,13 +125,8 @@ clixon_plugin_statedata(clicon_handle h,
if (fn(h, nsc, xpath, x) < 0)
goto fail; /* Dont quit here on user callbacks */
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, x, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s STATE: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__);
#endif
if (xml_spec_populate(x, yspec, NULL) < 0)
goto done;

View file

@ -442,7 +442,7 @@ netconf_notification_cb(int s,
if (clicon_msg_decode(reply, yspec, NULL, &xt) < 0)
goto done;
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netconf:notification:1.0")) == NULL)
if ((nsc = xml_nsctx_init(NULL, NOTIFICATION_RFC5277_NAMESPACE)) == NULL)
goto done;
if ((xn = xpath_first(xt, nsc, "notification")) == NULL)
goto ok;

View file

@ -232,9 +232,7 @@ api_root(clicon_handle h,
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
FCGX_FPrintF(r->out, "\r\n");
if (xml_parse_string("<restconf xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>", NULL, &xt) < 0)
goto done;
if (xml_spec_populate(xt, yspec, NULL) < 0)
if (xml_parse_string("<restconf xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>", yspec, &xt) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");

View file

@ -236,34 +236,34 @@ api_data_write(clicon_handle h,
restconf_media media_out,
int plain_patch)
{
int retval = -1;
int retval = -1;
enum operation_type op;
int i;
cxobj *xdata0 = NULL; /* Original -d data struct (including top symbol) */
cxobj *xdata; /* -d data (without top symbol)*/
cbuf *cbx = NULL;
cxobj *xtop = NULL; /* top of api-path */
cxobj *xbot = NULL; /* bottom of api-path */
yang_stmt *ybot = NULL; /* yang of xbot */
yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
cxobj *xparent;
yang_stmt *yp; /* yang parent */
yang_stmt *yspec;
cxobj *xa;
char *api_path;
cxobj *xret = NULL;
cxobj *xretcom = NULL; /* return from commit */
cxobj *xretdis = NULL; /* return from discard-changes */
cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xe; /* direct pointer into tree, dont free */
char *username;
int ret;
char *namespace = NULL;
char *dname;
int nullspec = 0;
cbuf *cbpath = NULL;
cvec *nsc = NULL;
int i;
cxobj *xdata0 = NULL; /* Original -d data struct (including top symbol) */
cxobj *xdata; /* -d data (without top symbol)*/
cbuf *cbx = NULL;
cxobj *xtop = NULL; /* top of api-path */
cxobj *xbot = NULL; /* bottom of api-path */
yang_stmt *ybot = NULL; /* yang of xbot */
yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
cxobj *xparent;
yang_stmt *yp; /* yang parent */
yang_stmt *yspec;
cxobj *xa;
char *api_path;
cxobj *xret = NULL;
cxobj *xretcom = NULL; /* return from commit */
cxobj *xretdis = NULL; /* return from discard-changes */
cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xe; /* direct pointer into tree, dont free */
char *username;
int ret;
char *namespace = NULL;
char *dname;
cbuf *cbpath = NULL;
cvec *nsc = NULL;
enum yang_bind yb;
clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0);
clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data);
@ -306,13 +306,8 @@ api_data_write(clicon_handle h,
}
#if 0
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XRET: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xret, "%s xret:", __FUNCTION__);
#endif
if (xml_child_nr(xret) == 0){ /* Object does not exist */
if (plain_patch){ /* If the target resource instance does not exist, the server MUST NOT create it. */
@ -370,12 +365,35 @@ api_data_write(clicon_handle h,
*/
if ((xdata0 = xml_new("data0", NULL, NULL)) == NULL)
goto done;
if (xml_copy_one(api_path?xml_parent(xbot):xbot, xdata0) < 0)
goto done;
/* Parse input data as json or xml into xml */
{ /* XXX mv to copy? */
cxobj *xfrom;
cxobj *xac;
xfrom = api_path?xml_parent(xbot):xbot;
if (xml_copy_one(xfrom, xdata0) < 0)
goto done;
xa = NULL;
while ((xa = xml_child_each(xfrom, xa, CX_ATTR)) != NULL) {
if ((xac = xml_new(xml_name(xa), xdata0, NULL)) == NULL)
goto done;
if (xml_copy(xa, xac) < 0) /* recursion */
goto done;
}
}
if (xml_spec(xdata0)==NULL)
yb = YB_TOP;
else
yb = YB_PARENT;
/* Parse input data as json or xml into xml
* Note that in POST (api_data_post) the new object is grafted on xbot, since it is a new
* object. In that case all yang bindings can be made since xbot is available.
* Here the new object replaces xbot and is therefore more complicated to make when parsing.
* Instead, xbots parent is copied into xdata0 (but not its children).
*/
switch (media_in){
case YANG_DATA_XML:
if (xml_parse_string(data, yspec, &xdata0) < 0){
if ((ret = xml_parse_string2(data, yb, yspec, &xdata0, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
@ -386,15 +404,18 @@ api_data_write(clicon_handle h,
goto done;
goto ok;
}
if (ret == 0){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
break;
case YANG_DATA_JSON:
/* Data here cannot cannot be Yang populated since it is loosely
* hanging without top symbols.
* And if it is not yang populated, it cant be translated properly
* from JSON to XML.
* Therefore, yang population is done later after addsub below
*/
if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){
if ((ret = json_parse_str2(data, yb, yspec, &xdata0, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
@ -436,15 +457,6 @@ api_data_write(clicon_handle h,
goto ok;
}
xdata = xml_child_i_type(xdata0, 0, CX_ELMNT);
#if 0
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
#endif
/* If the api-path (above) defines a module, then xdata must have a prefix
* and it match the module defined in api-path
* This does not apply if api-path is / (no module)
@ -568,23 +580,6 @@ api_data_write(clicon_handle h,
xml_purge(xbot);
if (xml_addsub(xparent, xdata) < 0)
goto done;
nullspec = (xml_spec(xdata) == NULL);
/* xbot is already populated, resolve yang for added xdata too */
if (xml_spec_populate0(xdata, yspec, NULL) < 0)
goto done;
if (media_in == YANG_DATA_JSON && nullspec){
/* json2xml decode could not be done above in json_parse,
* need to be done here instead
* UNLESS it is root resource, then json-parse has already done it
*/
if ((ret = json2xml_decode(xdata, &xerr)) < 0)
goto done;
if (ret == 0){
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
}
/* If restconf insert/point attributes are present, translate to netconf */
if (restconf_insert_attributes(xdata, qvec) < 0)
goto done;
@ -864,9 +859,11 @@ api_data_delete(clicon_handle h,
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
xml_prefix_set(xa, NETCONF_BASE_PREFIX);
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
if (xml_namespace_change(xa, NETCONF_BASE_NAMESPACE, NETCONF_BASE_PREFIX) < 0)
goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
/* For internal XML protocol: add username attribute for access control

View file

@ -210,12 +210,8 @@ api_data_get2(clicon_handle h,
* We need to cut that tree to only the object.
*/
#if 0 /* DEBUG */
if (debug){
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, xret, 0, 0, -1);
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
cbuf_free(cb);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xret, "%s xret:", __FUNCTION__);
#endif
/* Check if error return */
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){

View file

@ -129,6 +129,7 @@ api_data_post(clicon_handle h,
int ret;
restconf_media media_in;
int nrchildren0 = 0;
enum yang_bind yb;
clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path);
clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data);
@ -178,11 +179,18 @@ api_data_post(clicon_handle h,
nrchildren0++;
xml_flag_set(x, XML_FLAG_MARK);
}
/* Parse input data as json or xml into xml */
if (xml_spec(xbot)==NULL)
yb = YB_TOP;
else
yb = YB_PARENT;
/* Parse input data as json or xml into xml
* If xbot is top-level (api_path=null) it does not have a spec therefore look for
* top-level (yspec) otherwise assume parent (xbot) is populated.
*/
media_in = restconf_content_type(r);
switch (media_in){
case YANG_DATA_XML:
if (xml_parse_string(data, yspec, &xbot) < 0){
if ((ret = xml_parse_string2(data, yb, yspec, &xbot, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
@ -193,12 +201,18 @@ api_data_post(clicon_handle h,
goto done;
goto ok;
}
if (ret == 0){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
break;
case YANG_DATA_JSON:
/* If xbot is top-level (api_path=null) it does not have a spec therefore look for
* top-level (yspec) otherwise assume parent (xbot) is populated.
*/
if ((ret = json_parse_str(data, yspec, &xbot, &xerr)) < 0){
if ((ret = json_parse_str2(data, yb, yspec, &xbot, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
@ -256,14 +270,8 @@ api_data_post(clicon_handle h,
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
#if 0 /* XXX postpone this, there is something wrong with NETCONF_BASE_NAMESPACE not appearing here
* but later it does due to default handling,... */
if (xml_namespace_change(xa, NETCONF_BASE_NAMESPACE, NETCONF_BASE_PREFIX) < 0)
goto done;
#else
xml_prefix_set(xa, NETCONF_BASE_PREFIX); /* XXX: But this assumes proper namespace set */
#endif
if (ys_module_by_xml(yspec, xdata, &ymoddata) < 0)
goto done;
@ -299,13 +307,8 @@ api_data_post(clicon_handle h,
if (restconf_insert_attributes(xdata, qvec) < 0)
goto done;
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XDATA:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xdata, "%s xdata:", __FUNCTION__);
#endif
/* Create text buffer for transfer to backend */
@ -495,13 +498,8 @@ api_operations_post_input(clicon_handle h,
* <data><input xmlns="urn:example:clixon">...</input></data>
*/
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xdata, "%s xdata:", __FUNCTION__);
#endif
/* Validate that exactly only <input> tag */
if ((xinput = xml_child_i_type(xdata, 0, CX_ELMNT)) == NULL ||
@ -602,13 +600,8 @@ api_operations_post_output(clicon_handle h,
xml_name_set(xoutput, "output");
/* xoutput should now look: <output><x xmlns="uri">0</x></output> */
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xoutput, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XOUTPUT:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xoutput, "%s xoutput:", __FUNCTION__);
#endif
/* Sanity check of outgoing XML
@ -840,20 +833,15 @@ api_operations_post(clicon_handle h,
/* Here xtop is:
<rpc username="foo"><myfn xmlns="uri"><x>42</x></myfn></rpc> */
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xtop, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s 5. Translate input args: %s",
__FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xtop, "%s 5. Translate input args:", __FUNCTION__);
#endif
/* 6. Validate incoming RPC and fill in defaults */
/* 6. Validate outgoing RPC and fill in defaults */
if (xml_spec_populate_rpc(xtop, yspec, NULL) < 0) /* */
goto done;
if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0)
goto done;
if (ret == 0){
if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
@ -867,13 +855,8 @@ api_operations_post(clicon_handle h,
* <rpc username="foo"><myfn xmlns="uri"><x>42</x><y>99</y></myfn></rpc>
*/
#if 0
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xtop, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s 6. Validate and defaults:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xtop, "%s 6. Validate and defaults:", __FUNCTION__);
#endif
/* 7. Send to RPC handler, either local or backend
* Note (1) xtop is <rpc><method> xbot is <method>
@ -907,13 +890,8 @@ api_operations_post(clicon_handle h,
* <rpc-reply><x xmlns="uri">0</x></rpc-reply>
*/
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s 8. Receive reply:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
if (debug)
clicon_log_xml(LOG_DEBUG, xret, "%s Receive reply:", __FUNCTION__);
#endif
youtput = yang_find(yrpc, Y_OUTPUT, NULL);
if ((ret = api_operations_post_output(h, r, xret, yspec, youtput, namespace,

View file

@ -249,7 +249,8 @@ restconf_stream(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cb, "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>%s</stream>", name);
cprintf(cb, "<rpc><create-subscription xmlns=\"%s\"><stream>%s</stream>",
EVENT_RFC5277_NAMESPACE, name);
/* Print all fields */
for (i=0; i<cvec_len(qvec); i++){
cv = cvec_i(qvec, i);