* NACM extension (RFC8341)

* NACM module support (RFC8341 A1+A2)
   * Recovery user "_nacm_recovery" added.
     * Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
   * Example user changed adm1 to andy to comply with RFC8341 example

 * Yang code upgrade (RFC7950)
   * RPC method input parameters validated
     * see https://github.com/clicon/clixon/issues/4
* Correct XML namespace handling
   * XML multiple modules was based on "loose" semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208), and causes problems with overlapping names and false positives. Below see XML accepted (but wrong), and correct namespace declaration:
```
      <rpc><my-own-method></rpc> # Wrong but accepted
      <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> # Correct
        <my-own-method xmlns="http://example.net/me/my-own/1.0">
      </rpc>
```
   * To keep old loose semantics set config option CLICON_XML_NS_ITERATE (true by default)
   * XML to JSON translator support for mapping xmlns attribute to module name prefix.
   * Default namespace is still "urn:ietf:params:xml:ns:netconf:base:1.0"
   * See https://github.com/clicon/clixon/issues/49
* Changed all make tags --> make TAGS
* Keyvalue datastore removed (it has been disabled since 3.3.3)
* debug rpc added in example application (should be in clixon-config).
This commit is contained in:
Olof hagsand 2018-12-16 19:46:26 +01:00
parent e5c0b06cf9
commit ae1af8da9e
63 changed files with 1852 additions and 3492 deletions

View file

@ -80,5 +80,5 @@ distclean: clean
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
tags:
TAGS:
find $(srcdir) -name '*.[chyl]' -print | etags -

View file

@ -467,12 +467,18 @@ from_client_edit_config(clicon_handle h,
goto ok;
}
}
if ((xc = xpath_first(xn, "config")) == NULL){
if ((xc = xpath_first(xn, "config")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>config</bad-element>", NULL) < 0)
goto done;
goto ok;
}
else{
/* <config> yang spec may be set to anyxml by ingress yang check,...*/
if (xml_spec(xc) != NULL)
xml_spec_set(xc, NULL);
/* Populate XML with Yang spec (why not do this in parser?)
* Maybe validate xml here as in text_modify_top?
*/
if (xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xc, CX_ELMNT, xml_non_config_data, &non_config) < 0)
@ -952,13 +958,16 @@ from_client_msg(clicon_handle h,
cxobj *xt = NULL;
cxobj *x;
cxobj *xe;
char *name = NULL;
char *rpc = NULL;
char *module = NULL;
char *db;
cbuf *cbret = NULL; /* return message */
int pid;
int ret;
char *username;
char *nacm_mode;
yang_spec *yspec;
yang_stmt *ye;
yang_stmt *ymod;
clicon_debug(1, "%s", __FUNCTION__);
pid = ce->ce_pid;
@ -974,62 +983,81 @@ from_client_msg(clicon_handle h,
goto done;
goto reply;
}
/* Get yang spec */
yspec = clicon_dbspec_yang(h); /* XXX maybe move to clicon_msg_decode? */
if ((x = xpath_first(xt, "/rpc")) == NULL){
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0)
goto done;
goto reply;
}
/* Populate incoming XML tree with yang */
if (xml_spec_populate_rpc(h, x, yspec) < 0)
goto done;
if ((ret = xml_yang_validate_rpc(x)) < 0)
goto done;
if (ret == 0){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
goto done;
goto reply;
}
xe = NULL;
username = xml_find_value(x, "username");
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
name = xml_name(xe);
clicon_debug(1, "%s name:%s", __FUNCTION__, name);
/* Make NACM access control if enabled as "internal"*/
nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
if (nacm_mode && strcmp(nacm_mode, "disabled") != 0){
if ((ret = nacm_access(h, nacm_mode, name, username, cbret)) < 0)
rpc = xml_name(xe);
if ((ye = xml_spec(xe)) == NULL){
if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0)
goto done;
if (!ret)
goto reply;
goto reply;
}
if (strcmp(name, "get-config") == 0){
if ((ymod = ys_module(ye)) == NULL){
clicon_err(OE_XML, ENOENT, "rpc yang does not have module");
goto done;
}
module = ymod->ys_argument;
clicon_debug(1, "%s module:%s rpc:%s", __FUNCTION__, module, rpc);
/* Make NACM access control if enabled as "internal"*/
if ((ret = nacm_access(h, rpc, module, username, cbret)) < 0)
goto done;
if (ret == 0)
goto reply;
if (strcmp(rpc, "get-config") == 0){
if (from_client_get_config(h, xe, cbret) <0)
goto done;
}
else if (strcmp(name, "edit-config") == 0){
else if (strcmp(rpc, "edit-config") == 0){
if (from_client_edit_config(h, xe, pid, cbret) <0)
goto done;
}
else if (strcmp(name, "copy-config") == 0){
else if (strcmp(rpc, "copy-config") == 0){
if (from_client_copy_config(h, xe, pid, cbret) <0)
goto done;
}
else if (strcmp(name, "delete-config") == 0){
else if (strcmp(rpc, "delete-config") == 0){
if (from_client_delete_config(h, xe, pid, cbret) <0)
goto done;
}
else if (strcmp(name, "lock") == 0){
else if (strcmp(rpc, "lock") == 0){
if (from_client_lock(h, xe, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "unlock") == 0){
else if (strcmp(rpc, "unlock") == 0){
if (from_client_unlock(h, xe, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "get") == 0){
else if (strcmp(rpc, "get") == 0){
if (from_client_get(h, xe, cbret) < 0)
goto done;
}
else if (strcmp(name, "close-session") == 0){
else if (strcmp(rpc, "close-session") == 0){
xmldb_unlock_all(h, pid);
stream_ss_delete_all(h, ce_event_cb, (void*)ce);
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
}
else if (strcmp(name, "kill-session") == 0){
else if (strcmp(rpc, "kill-session") == 0){
if (from_client_kill_session(h, xe, cbret) < 0)
goto done;
}
else if (strcmp(name, "validate") == 0){
else if (strcmp(rpc, "validate") == 0){
if ((db = netconf_db_find(xe, "source")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0)
goto done;
@ -1038,19 +1066,19 @@ from_client_msg(clicon_handle h,
if (from_client_validate(h, db, cbret) < 0)
goto done;
}
else if (strcmp(name, "commit") == 0){
else if (strcmp(rpc, "commit") == 0){
if (from_client_commit(h, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "discard-changes") == 0){
else if (strcmp(rpc, "discard-changes") == 0){
if (from_client_discard_changes(h, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "create-subscription") == 0){
else if (strcmp(rpc, "create-subscription") == 0){
if (from_client_create_subscription(h, xe, ce, cbret) < 0)
goto done;
}
else if (strcmp(name, "debug") == 0){
else if (strcmp(rpc, "debug") == 0){
if (from_client_debug(h, xe, cbret) < 0)
goto done;
}
@ -1104,7 +1132,7 @@ from_client_msg(clicon_handle h,
/* Sanity: log if clicon_err() is not called ! */
if (retval < 0 && clicon_errno < 0)
clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on error (message: %s)",
__FUNCTION__, name?name:"");
__FUNCTION__, rpc?rpc:"");
// clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;// -1 here terminates backend
}

View file

@ -95,6 +95,10 @@ process_incoming_packet(clicon_handle h,
clicon_debug(1, "RECV");
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
yspec = clicon_dbspec_yang(h);
if ((str0 = strdup(cbuf_get(cb))) == NULL){
clicon_log(LOG_ERR, "%s: strdup: %s", __FUNCTION__, strerror(errno));
@ -103,19 +107,25 @@ process_incoming_packet(clicon_handle h,
str = str0;
/* Parse incoming XML message */
if (xml_parse_string(str, yspec, &xreq) < 0){
if ((cbret = cbuf_new()) == NULL){
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
goto done;
netconf_output(1, cbret, "rpc-error");
}
else
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
free(str0);
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
goto done;
netconf_output(1, cbret, "rpc-error");
goto done;
}
free(str0);
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL)
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
int ret;
isrpc++;
if ((ret = xml_yang_validate_rpc(xrpc)) < 0)
goto done;
if (ret == 0){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
goto done;
netconf_output(1, cbret, "rpc-error");
goto done;
}
}
else
if (xpath_first(xreq, "//hello") != NULL)
;
@ -134,30 +144,27 @@ process_incoming_packet(clicon_handle h,
else{ /* there is a return message in xret */
cxobj *xa, *xa2;
assert(xret);
if ((cbret = cbuf_new()) != NULL){
if ((xc = xml_child_i(xret,0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
* RFC 6241:
* If additional attributes are present in an <rpc> element, a NETCONF
* peer MUST return them unmodified in the <rpc-reply> element. This
* includes any "xmlns" attributes.
*/
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
if (xml_addsub(xc, xa2) < 0)
goto done;
}
add_preamble(cbret);
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
add_postamble(cbret);
if (netconf_output(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
if ((xc = xml_child_i(xret,0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
* RFC 6241:
* If additional attributes are present in an <rpc> element, a NETCONF
* peer MUST return them unmodified in the <rpc-reply> element. This
* includes any "xmlns" attributes.
*/
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
}
if (xml_addsub(xc, xa2) < 0)
goto done;
}
add_preamble(cbret);
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
add_postamble(cbret);
if (netconf_output(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;
}
}
}

View file

@ -869,6 +869,7 @@ netconf_application_rpc(clicon_handle h,
int retval = -1;
yang_spec *yspec = NULL; /* application yspec */
yang_stmt *yrpc = NULL;
yang_stmt *ymod = NULL;
yang_stmt *yinput;
yang_stmt *youtput;
cxobj *xoutput;
@ -888,26 +889,33 @@ netconf_application_rpc(clicon_handle h,
goto done;
}
cbuf_reset(cb);
if (xml_namespace(xn) == NULL){
if (ys_module_by_xml(yspec, xn, &ymod) < 0)
goto done;
if (ymod == NULL){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"<error-info>Not recognized</error-info>"
"<error-info>Not recognized module</error-info>"
"</rpc-error></rpc-reply>", xml_name(xn));
goto ok;
}
cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn));
/* Find yang rpc statement, return yang rpc statement if found */
if (yang_abs_schema_nodeid(yspec, xml_spec(xn), cbuf_get(cb), Y_RPC, &yrpc) < 0)
goto done;
yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
if ((yrpc==NULL) && _CLICON_XML_NS_ITERATE){
int i;
for (i=0; i<yspec->yp_len; i++){
ymod = yspec->yp_stmt[i];
if ((yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn))) != NULL)
break;
}
}
/* Check if found */
if (yrpc != NULL){
/* 1. Check xn arguments with input statement. */
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xn, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
@ -933,7 +941,7 @@ netconf_application_rpc(clicon_handle h,
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
xoutput=xpath_first(*xret, "/");
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xoutput, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
@ -954,7 +962,6 @@ netconf_application_rpc(clicon_handle h,
return retval;
}
/*! The central netconf rpc dispatcher. Look at first tag and dispach to sub-functions.
* Call plugin handler if tag not found. If not handled by any handler, return
* error.
@ -985,7 +992,6 @@ netconf_rpc_dispatch(clicon_handle h,
if (xml_value_set(xa, username) < 0)
goto done;
}
xe = NULL;
while ((xe = xml_child_each(xn, xe, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xe), "get-config") == 0){

View file

@ -257,7 +257,6 @@ api_data_get2(clicon_handle h,
else{
if (xpath_vec(xret, "%s", &xvec, &xlen, path) < 0)
goto done;
clicon_debug(1, "%s: xpath:%s xlen:%d", __FUNCTION__, path, (int)xlen);
if (use_xml){
for (i=0; i<xlen; i++){
x = xvec[i];
@ -269,7 +268,7 @@ api_data_get2(clicon_handle h,
if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty) < 0)
goto done;
}
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
// clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
ok:
@ -408,9 +407,10 @@ api_data_post(clicon_handle h,
yang_spec *yspec;
cxobj *xa;
cxobj *xret = NULL;
cxobj *xretcom = NULL;
cxobj *xretcom = NULL; /* return from commit */
cxobj *xretdis = NULL; /* return from discard-changes */
cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xe;
cxobj *xe; /* dont free */
char *username;
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
@ -488,13 +488,22 @@ api_data_post(clicon_handle h,
}
/* Assume this is validation failed since commit includes validate */
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
/* commit/discard should be done automaticaly by the system, therefore
* recovery user is used here (edit-config but not commit may be permitted
by NACM */
cprintf(cbx, "<rpc username=\"%s\">", NACM_RECOVERY_USER);
cprintf(cbx, "<commit/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if (clicon_rpc_discard_changes(h) < 0)
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
cprintf(cbx, "<discard-changes/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done;
goto ok;
@ -512,6 +521,8 @@ api_data_post(clicon_handle h,
xml_free(xerr);
if (xretcom)
xml_free(xretcom);
if (xretdis)
xml_free(xretdis);
if (xtop)
xml_free(xtop);
if (xdata)
@ -623,7 +634,8 @@ api_data_put(clicon_handle h,
cxobj *xa;
char *api_path;
cxobj *xret = NULL;
cxobj *xretcom = NULL;
cxobj *xretcom = NULL; /* return from commit */
cxobj *xretdis = NULL; /* return from discard-changes */
cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xe;
char *username;
@ -734,13 +746,23 @@ api_data_put(clicon_handle h,
goto ok;
}
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
/* commit/discard should be done automaticaly by the system, therefore
* recovery user is used here (edit-config but not commit may be permitted
by NACM */
cprintf(cbx, "<rpc username=\"%s\">", NACM_RECOVERY_USER);
cprintf(cbx, "<commit/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if (clicon_rpc_discard_changes(h) < 0)
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
cprintf(cbx, "<discard-changes/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done;
goto ok;
@ -758,6 +780,8 @@ api_data_put(clicon_handle h,
xml_free(xerr);
if (xretcom)
xml_free(xretcom);
if (xretdis)
xml_free(xretdis);
if (xtop)
xml_free(xtop);
if (xdata)
@ -791,7 +815,7 @@ api_data_patch(clicon_handle h,
return 0;
}
/*! Generic REST DELETE method
/*! Generic REST DELETE method translated to edit-config
* @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
@ -821,7 +845,8 @@ api_data_delete(clicon_handle h,
yang_spec *yspec;
enum operation_type op = OP_DELETE;
cxobj *xret = NULL;
cxobj *xretcom = NULL;
cxobj *xretcom = NULL; /* return from commmit */
cxobj *xretdis = NULL; /* return from discard */
cxobj *xerr = NULL;
char *username;
@ -836,7 +861,6 @@ api_data_delete(clicon_handle h,
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
goto done;
xbot = xtop;
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
goto done;
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
@ -864,13 +888,22 @@ api_data_delete(clicon_handle h,
}
/* Assume this is validation failed since commit includes validate */
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
/* commit/discard should be done automaticaly by the system, therefore
* recovery user is used here (edit-config but not commit may be permitted
by NACM */
cprintf(cbx, "<rpc username=\"%s\">", NACM_RECOVERY_USER);
cprintf(cbx, "<commit/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
if (clicon_rpc_discard_changes(h) < 0)
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", NACM_RECOVERY_USER);
cprintf(cbx, "<discard-changes/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
@ -887,6 +920,8 @@ api_data_delete(clicon_handle h,
xml_free(xret);
if (xretcom)
xml_free(xretcom);
if (xretdis)
xml_free(xretdis);
if (xtop)
xml_free(xtop);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
@ -912,6 +947,11 @@ api_data_delete(clicon_handle h,
* data-model-specific RPC operations supported by the server. The
* server MAY omit this resource if no data-model-specific RPC
* operations are advertised.
* From ietf-restconf.yang:
* In XML, the YANG module namespace identifies the module:
* <system-restart xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/>
* In JSON, the YANG module name identifies the module:
* { 'ietf-system:system-restart' : [null] }
*/
int
api_operations_get(clicon_handle h,
@ -926,9 +966,9 @@ api_operations_get(clicon_handle h,
{
int retval = -1;
yang_spec *yspec;
yang_stmt *ym;
yang_stmt *ymod; /* yang module */
yang_stmt *yc;
char *modname;
char *namespace;
cbuf *cbx = NULL;
cxobj *xt = NULL;
@ -937,18 +977,17 @@ api_operations_get(clicon_handle h,
if ((cbx = cbuf_new()) == NULL)
goto done;
cprintf(cbx, "<operations>");
ym = NULL;
while ((ym = yn_each((yang_node*)yspec, ym)) != NULL) {
modname = ym->ys_argument;
ymod = NULL;
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
namespace = yang_find_mynamespace(ymod);
yc = NULL;
while ((yc = yn_each((yang_node*)ym, yc)) != NULL) {
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
if (yc->ys_keyword != Y_RPC)
continue;
cprintf(cbx, "<%s:%s />", modname, yc->ys_argument);
cprintf(cbx, "<%s xmlns=\"%s\"/>", yc->ys_argument, namespace);
}
}
cprintf(cbx, "</operations>");
clicon_debug(1, "%s xml:%s", __FUNCTION__, cbuf_get(cbx));
if (xml_parse_string(cbuf_get(cbx), yspec, &xt) < 0)
goto done;
if (xml_rootchild(xt, 0, &xt) < 0)
@ -962,7 +1001,6 @@ api_operations_get(clicon_handle h,
if (xml2json_cbuf(cbx, xt, pretty) < 0)
goto done;
}
clicon_debug(1, "%s ret:%s", __FUNCTION__, cbuf_get(cbx));
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
FCGX_FPrintF(r->out, "\r\n");
@ -1095,18 +1133,9 @@ api_operations_post(clicon_handle h,
if (xml_value_set(xa, username) < 0)
goto done;
}
/* XXX: something strange for rpc user */
if (api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y) < 0)
goto done;
#if 0
{
cbuf *c = cbuf_new();
clicon_xml2cbuf(c, xtop, 0, 0);
clicon_debug(1, "%s xtop:%s", __FUNCTION__, cbuf_get(c));
cbuf_free(c);
}
#endif
if (data && strlen(data)){
/* Parse input data as json or xml into xml */
if (parse_xml){
@ -1150,7 +1179,8 @@ api_operations_post(clicon_handle h,
}
if (yinput){
xml_spec_set(xbot, yinput); /* needed for xml_spec_populate */
if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yinput) < 0)
/* XXX yinput <-> h ?*/
if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xbot, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
@ -1215,7 +1245,7 @@ api_operations_post(clicon_handle h,
#endif
cbuf_reset(cbx);
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xoutput, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
@ -1235,9 +1265,6 @@ api_operations_post(clicon_handle h,
else
if (xml2json_cbuf(cbx, xoutput, pretty) < 0)
goto done;
#if 1
clicon_debug(1, "%s cbx:%s", __FUNCTION__, cbuf_get(cbx));
#endif
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
}