* RESTCONF "depth" query parameter supported

* C API change: Added `depth` parameter to function `clicon_xml2cbuf`, default is -1.
This commit is contained in:
Olof hagsand 2019-08-17 10:54:13 +02:00
parent 10a2dbe8ec
commit ee329ee382
31 changed files with 289 additions and 117 deletions

View file

@ -7,6 +7,9 @@
* RESTCONF PATCH (plain patch) is being implemented according to RFC 8040 Section 4.6.1
* Note RESTCONF plain patch is different from RFC 8072 "YANG Patch Media Type" which is not implemented
* RESTCONF "content" query parameter supported
* Extended Netconf with content attribute for internal use
* RESTCONF "depth" query parameter supported
* Extended Netconf with depth attribute for internal use
* RESTCONF "insert" and "point" query parameters supported
* Applies to ordered-by-user leaf and leaf-lists
* RESTCONF PUT/POST erroneously returned 200 OK. Instead restconf now returns:
@ -45,10 +48,15 @@
* Empty yang container encoded as `{}`
* Empty leaf/leaf-list of type empty encoded as `[null]`
* Other empty values remain as `null`
* C API changes:
* Added `depth` parameter to function `clicon_xml2cbuf`, default is -1.
* Added two parameters to function `clicon_rpc_get`
* `content`: to select state or config. Allowed values: CONTENT_CONFIG,CONTENT_NOCONFIG, CONTENT_ALL (default)
* `depth`: Get levels of XML in get function: -1 is unbounded, 0 is nothing, 1 is top-level node only.
### Minor changes
* Added experimental binary search API function: `xml_binsearch`
* Added content parameter to `clicon_rpc_get` (-1 or CONTENT_ALL is default)
* Removed unnecessary configure dependencies
* libnsl, libcrypt, if_vlan,...
* pseudo-plugin added, to enable callbacks also for main programs. Useful for extensions

View file

@ -236,15 +236,14 @@ run with NGINX.
The implementatation is based on [RFC 8040: RESTCONF Protocol](https://tools.ietf.org/html/rfc8040).
The following features of RFC8040 are supported:
- OPTIONS, HEAD, GET, POST, PUT, DELETE
- OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH
- stream notifications (Sec 6)
- query parameters: "insert", "point", "start-time" and "stop-time".
- query parameters: "insert", "point", "content", "depth", "start-time" and "stop-time".
- Monitoring (Sec 9)
The following features are not implemented:
- ETag/Last-Modified
- PATCH
- Query parameters: "content", "depth", "fields", "filter", "with-defaults"
- Query parameters: "fields", "filter", "with-defaults"
See [more detailed instructions](apps/restconf/README.md).

View file

@ -176,6 +176,8 @@ client_get_capabilities(clicon_handle h,
goto done;
if (xml_parse_va(&xcap, yspec, "<capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability>") < 0)
goto done;
if (xml_parse_va(&xcap, yspec, "<capability>urn:ietf:params:restconf:capability:depth:1.0</capability>") < 0)
goto done;
retval = 0;
done:
return retval;
@ -428,7 +430,7 @@ from_client_get_config(clicon_handle h,
else{
if (xml_name_set(xret, "data") < 0)
goto done;
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");
@ -541,7 +543,7 @@ from_client_edit_config(clicon_handle h,
if ((ret = xml_yang_validate_list_key_only(h, xc, &xret)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto ok;
}
@ -877,6 +879,7 @@ from_client_get(clicon_handle h,
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
char *attr;
netconf_content content = CONTENT_ALL;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
username = clicon_username_get(h);
if ((xfilter = xml_find(xe, "filter")) != NULL){
@ -890,9 +893,24 @@ from_client_get(clicon_handle h,
if (xml_nsctx_node(xfilter, &nsc) < 0)
goto done;
}
/* Clixon extensions: depth and content */
if ((attr = xml_find_value(xe, "content")) != NULL)
content = netconf_content_str2int(attr);
if ((attr = xml_find_value(xe, "depth")) != NULL){
char *reason = NULL;
if ((ret = parse_int32(attr, &depth, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_int32");
goto done;
}
if (ret == 0){
if (netconf_bad_attribute(cbret, "application",
"<bad-attribute>depth</bad-attribute>", "Unrecognized value of depth attribute") < 0)
goto done;
goto ok;
}
}
if (content != CONTENT_NONCONFIG){
/* Get config
* Note xret can be pruned by nacm below and change name and
@ -911,7 +929,7 @@ from_client_get(clicon_handle h,
if ((ret = client_statedata(h, xpath, nsc, &xret)) < 0)
goto done;
if (ret == 0){ /* Error from callback (error in xret) */
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto ok;
}
@ -932,7 +950,8 @@ from_client_get(clicon_handle h,
else{
if (xml_name_set(xret, "data") < 0)
goto done;
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
/* Top level is data, so add 1 to depth if significant */
if (clicon_xml2cbuf(cbret, xret, 0, 0, depth>0?depth+1:depth) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");
@ -1250,7 +1269,7 @@ from_client_msg(clicon_handle h,
if ((ret = xml_yang_validate_rpc(h, x, &xret)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto reply;
}

View file

@ -230,7 +230,7 @@ startup_common(clicon_handle h,
if ((ret = generic_validate(h, yspec, td, &xret)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto fail; /* STARTUP_INVALID */
}
@ -522,7 +522,7 @@ candidate_commit(clicon_handle h,
if ((ret = from_validate_common(h, candidate, td, &xret)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto fail;
}
@ -755,7 +755,7 @@ from_client_validate(clicon_handle h,
/* A little complex due to several sources of validation fails or errors.
* (1) xerr is set -> translate to cbret; (2) cbret set use that; otherwise
* use clicon_err. */
if (xret && clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (xret && clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
plugin_transaction_abort(h, td);
if (!cbuf_len(cbret) &&

View file

@ -240,7 +240,7 @@ transaction_log(clicon_handle h,
}
for (i=0; i<td->td_dlen; i++){
xn = td->td_dvec[i];
clicon_xml2cbuf(cb, xn, 0, 0);
clicon_xml2cbuf(cb, xn, 0, 0, -1);
}
if (i)
clicon_log(level, "%s %" PRIu64 " %s del: %s",
@ -248,7 +248,7 @@ transaction_log(clicon_handle h,
cbuf_reset(cb);
for (i=0; i<td->td_alen; i++){
xn = td->td_avec[i];
clicon_xml2cbuf(cb, xn, 0, 0);
clicon_xml2cbuf(cb, xn, 0, 0, -1);
}
if (i)
clicon_log(level, "%s %" PRIu64 " %s add: %s", __FUNCTION__, td->td_id, op, cbuf_get(cb));
@ -256,10 +256,10 @@ transaction_log(clicon_handle h,
for (i=0; i<td->td_clen; i++){
if (td->td_scvec){
xn = td->td_scvec[i];
clicon_xml2cbuf(cb, xn, 0, 0);
clicon_xml2cbuf(cb, xn, 0, 0, -1);
}
xn = td->td_tcvec[i];
clicon_xml2cbuf(cb, xn, 0, 0);
clicon_xml2cbuf(cb, xn, 0, 0, -1);
}
if (i)
clicon_log(level, "%s %" PRIu64 " %s change: %s", __FUNCTION__, td->td_id, op, cbuf_get(cb));

View file

@ -279,7 +279,7 @@ cli_dbxml(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xtop, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xtop, 0, 0, -1) < 0)
goto done;
if (clicon_rpc_edit_config(h, "candidate", OP_NONE, cbuf_get(cb)) < 0)
goto done;
@ -792,7 +792,7 @@ load_config_file(clicon_handle h,
while ((x = xml_child_each(xt, x, -1)) != NULL) {
/* Ensure top-level is "config", maybe this is too rough? */
xml_name_set(x, "config");
if (clicon_xml2cbuf(cbxml, x, 0, 0) < 0)
if (clicon_xml2cbuf(cbxml, x, 0, 0, -1) < 0)
goto done;
}
if (clicon_rpc_edit_config(h, "candidate",
@ -1235,7 +1235,7 @@ cli_copy_config(clicon_handle h,
/* resuse cb */
cbuf_reset(cb);
/* create xml copy tree and merge it with database configuration */
clicon_xml2cbuf(cb, x2, 0, 0);
clicon_xml2cbuf(cb, x2, 0, 0, -1);
if (clicon_rpc_edit_config(h, db, OP_MERGE, cbuf_get(cb)) < 0)
goto done;
retval = 0;

View file

@ -472,7 +472,7 @@ cli_show_config1(clicon_handle h,
clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db);
goto done;
}
if (clicon_rpc_get(h, cbuf_get(cbxpath), namespace, CONTENT_ALL, &xt) < 0)
if (clicon_rpc_get(h, cbuf_get(cbxpath), namespace, CONTENT_ALL, -1, &xt) < 0)
goto done;
}
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
@ -723,7 +723,7 @@ cli_show_auto1(clicon_handle h,
clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db);
goto done;
}
if (clicon_rpc_get(h, xpath, namespace, CONTENT_ALL, &xt) < 0)
if (clicon_rpc_get(h, xpath, namespace, CONTENT_ALL, -1, &xt) < 0)
goto done;
}

View file

@ -128,7 +128,7 @@ netconf_input_packet(clicon_handle h,
if ((ret = xml_yang_validate_rpc(h, xrpc, &xret)) < 0)
goto done;
if (ret == 0){
clicon_xml2cbuf(cbret, xret, 0, 0);
clicon_xml2cbuf(cbret, xret, 0, 0, -1);
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
@ -170,7 +170,7 @@ netconf_input_packet(clicon_handle h,
if (xml_addsub(xc, xa2) < 0)
goto done;
}
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0, -1);
if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;

View file

@ -455,7 +455,7 @@ netconf_notification_cb(int s,
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xn, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xn, 0, 0, -1) < 0)
goto done;
/* Send it to listening client on stdout */
if (netconf_output_encap(1, cb, "notification") < 0){
@ -599,7 +599,7 @@ netconf_application_rpc(clicon_handle h,
if (ret > 0 && (ret = xml_yang_validate_add(h, xn, &xerr)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xerr, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
@ -632,7 +632,7 @@ netconf_application_rpc(clicon_handle h,
if (ret > 0 && (ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xerr, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
goto ok;

View file

@ -496,7 +496,7 @@ api_return_err(clicon_handle h,
FCGX_FPrintF(r->out, "Content-Type: %s\r\n\r\n", restconf_media_int2str(media));
switch (media){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xerr, 2, pretty) < 0)
if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
goto done;
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
if (pretty){

View file

@ -242,7 +242,7 @@ api_root(clicon_handle h,
goto done;
switch (media_out){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xt, 0, pretty) < 0)
if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0)
goto done;
break;
case YANG_DATA_JSON:
@ -292,7 +292,7 @@ api_yang_library_version(clicon_handle h,
}
switch (media_out){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xt, 0, pretty) < 0)
if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0)
goto done;
break;
case YANG_DATA_JSON:

View file

@ -326,7 +326,7 @@ api_data_write(clicon_handle h,
#if 0
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xret, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XRET: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
@ -457,7 +457,7 @@ api_data_write(clicon_handle h,
#if 0
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xdata, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
@ -620,7 +620,7 @@ api_data_write(clicon_handle h,
NETCONF_BASE_NAMESPACE); /* bind nc to netconf namespace */
cprintf(cbx, "<edit-config><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>");
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0)
goto done;
cprintf(cbx, "</edit-config></rpc>");
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
@ -891,7 +891,7 @@ api_data_delete(clicon_handle h,
NETCONF_BASE_NAMESPACE); /* bind nc to netconf namespace */
cprintf(cbx, "<edit-config><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>");
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0)
goto done;
cprintf(cbx, "</edit-config></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)

View file

@ -114,8 +114,9 @@ api_data_get2(clicon_handle h,
int ret;
char *namespace = NULL;
cvec *nsc = NULL;
char *str;
char *attr; /* attribute value string */
netconf_content content = CONTENT_ALL;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -123,9 +124,9 @@ api_data_get2(clicon_handle h,
goto done;
}
/* Check for content attribute */
if ((str = cvec_find_str(qvec, "content")) != NULL){
clicon_debug(1, "%s content=%s", __FUNCTION__, str);
if ((content = netconf_content_str2int(str)) == -1){
if ((attr = cvec_find_str(qvec, "content")) != NULL){
clicon_debug(1, "%s content=%s", __FUNCTION__, attr);
if ((content = netconf_content_str2int(attr)) == -1){
if (netconf_bad_attribute_xml(&xerr, "application",
"<bad-attribute>content</bad-attribute>", "Unrecognized value of content attribute") < 0)
goto done;
@ -138,7 +139,29 @@ api_data_get2(clicon_handle h,
goto ok;
}
}
/* Check for depth attribute */
if ((attr = cvec_find_str(qvec, "depth")) != NULL){
clicon_debug(1, "%s depth=%s", __FUNCTION__, attr);
if (strcmp(attr, "unbounded") != 0){
char *reason = NULL;
if ((ret = parse_int32(attr, &depth, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_int32");
goto done;
}
if (ret==0){
if (netconf_bad_attribute_xml(&xerr, "application",
"<bad-attribute>depth</bad-attribute>", "Unrecognized value of depth attribute") < 0)
goto done;
if ((xe = xpath_first(xerr, "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;
}
}
}
if ((cbpath = cbuf_new()) == NULL)
goto done;
cprintf(cbpath, "/");
@ -165,13 +188,13 @@ api_data_get2(clicon_handle h,
goto done;
switch (content){
case CONTENT_CONFIG:
ret = clicon_rpc_get_config(h, "running", xpath, namespace, &xret);
ret = clicon_rpc_get(h, xpath, namespace, CONTENT_CONFIG, depth, &xret);
break;
case CONTENT_NONCONFIG:
ret = clicon_rpc_get(h, xpath, namespace, CONTENT_NONCONFIG, &xret);
ret = clicon_rpc_get(h, xpath, namespace, CONTENT_NONCONFIG, depth, &xret);
break;
case CONTENT_ALL:
ret = clicon_rpc_get(h, xpath, namespace, CONTENT_ALL, &xret);
ret = clicon_rpc_get(h, xpath, namespace, CONTENT_ALL, depth, &xret);
break;
default:
clicon_err(OE_XML, EINVAL, "Invalid content attribute %d", content);
@ -196,7 +219,7 @@ api_data_get2(clicon_handle h,
#if 0 /* DEBUG */
if (debug){
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, xret, 0, 0);
clicon_xml2cbuf(cb, xret, 0, 0, -1);
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
cbuf_free(cb);
}
@ -219,7 +242,7 @@ api_data_get2(clicon_handle h,
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
switch (media_out){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cbx, xret, 0, pretty) < 0) /* Dont print top object? */
if (clicon_xml2cbuf(cbx, xret, 0, pretty, -1) < 0) /* Dont print top object? */
goto done;
break;
case YANG_DATA_JSON:
@ -268,7 +291,7 @@ api_data_get2(clicon_handle h,
if (namespace2 && xmlns_set(x, prefix, namespace2) < 0)
goto done;
}
if (clicon_xml2cbuf(cbx, x, 0, pretty) < 0) /* Dont print top object? */
if (clicon_xml2cbuf(cbx, x, 0, pretty, -1) < 0) /* Dont print top object? */
goto done;
}
break;

View file

@ -164,7 +164,7 @@ api_data_post(clicon_handle h,
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xtop, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XURI:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
@ -309,7 +309,7 @@ api_data_post(clicon_handle h,
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xdata, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XDATA:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
@ -330,7 +330,7 @@ api_data_post(clicon_handle h,
NETCONF_BASE_NAMESPACE); /* bind nc to netconf namespace */
cprintf(cbx, "<edit-config><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>");
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0)
goto done;
cprintf(cbx, "</edit-config></rpc>");
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
@ -507,7 +507,7 @@ api_operations_post_input(clicon_handle h,
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xdata, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
@ -614,7 +614,7 @@ api_operations_post_output(clicon_handle h,
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xoutput, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xoutput, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s XOUTPUT:%s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
@ -855,7 +855,7 @@ api_operations_post(clicon_handle h,
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
if (clicon_xml2cbuf(ccc, xtop, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s 5. Translate input args: %s",
__FUNCTION__, cbuf_get(ccc));
@ -882,7 +882,7 @@ api_operations_post(clicon_handle h,
#if 0
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
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);
@ -922,7 +922,7 @@ api_operations_post(clicon_handle h,
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0) < 0)
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);
@ -942,7 +942,7 @@ api_operations_post(clicon_handle h,
cbuf_reset(cbret);
switch (media_out){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cbret, xoutput, 0, pretty) < 0)
if (clicon_xml2cbuf(cbret, xoutput, 0, pretty, -1) < 0)
goto done;
/* xoutput should now look: <output xmlns="uri"><x>0</x></output> */
break;

View file

@ -201,7 +201,7 @@ restconf_stream_cb(int s,
FCGX_FPrintF(r->out, "M#id: %02d:0\r\n", tv.tv_sec);
}
#endif
if (clicon_xml2cbuf(cb, xn, 0, pretty) < 0)
if (clicon_xml2cbuf(cb, xn, 0, pretty, -1) < 0)
goto done;
FCGX_FPrintF(r->out, "data: %s\r\n", cbuf_get(cb));
FCGX_FPrintF(r->out, "\r\n");

View file

@ -248,7 +248,7 @@ example_rpc(clicon_handle h, /* Clicon handle */
else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
if (xmlns_set(x, NULL, namespace) < 0)
goto done;
if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, x, 0, 0, -1) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");

View file

@ -64,7 +64,8 @@ plugin_exit(clicon_handle h)
/*! Local example netconf rpc callback
*/
int netconf_client_rpc(clicon_handle h,
int
netconf_client_rpc(clicon_handle h,
cxobj *xe,
cbuf *cbret,
void *arg,
@ -85,7 +86,7 @@ int netconf_client_rpc(clicon_handle h,
else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
if (xmlns_set(x, NULL, namespace) < 0)
goto done;
if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, x, 0, 0, -1) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");

View file

@ -287,7 +287,7 @@ restconf_client_rpc(clicon_handle h,
else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
if (xmlns_set(x, NULL, namespace) < 0)
goto done;
if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
if (clicon_xml2cbuf(cbret, x, 0, 0, -1) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");

View file

@ -52,7 +52,7 @@ int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2);
int clicon_rpc_delete_config(clicon_handle h, char *db);
int clicon_rpc_lock(clicon_handle h, char *db);
int clicon_rpc_unlock(clicon_handle h, char *db);
int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, netconf_content content, cxobj **xret);
int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, netconf_content content, int32_t depth, cxobj **xret);
int clicon_rpc_close_session(clicon_handle h);
int clicon_rpc_kill_session(clicon_handle h, int session_id);
int clicon_rpc_validate(clicon_handle h, char *db);

View file

@ -170,7 +170,7 @@ int xml_free(cxobj *xn);
int xml_print(FILE *f, cxobj *xn);
int clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint);
int clicon_xml2cbuf(cbuf *xf, cxobj *xn, int level, int prettyprint);
int clicon_xml2cbuf(cbuf *xf, cxobj *xn, int level, int prettyprint, int32_t depth);
int xml_parse_file(int fd, char *endtag, yang_stmt *yspec, cxobj **xt);
int xml_parse_string(const char *str, yang_stmt *yspec, cxobj **xml_top);
#if defined(__GNUC__) && __GNUC__ >= 3

View file

@ -113,7 +113,8 @@ typedef struct xpath_tree xpath_tree;
* Prototypes
*/
char* xpath_tree_int2str(int nodetype);
int xpath_tree_print(cbuf *cb, xpath_tree *xs);
int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs);
int xpath_tree_print(FILE *f, xpath_tree *xs);
int xpath_tree_free(xpath_tree *xs);
int xpath_parse(cvec *nsc, char *xpath, xpath_tree **xptree);
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp);

View file

@ -167,7 +167,7 @@ netconf_invalid_value(cbuf *cb,
if (netconf_invalid_value_xml(&xret, type, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -276,7 +276,7 @@ netconf_bad_attribute(cbuf *cb,
if (netconf_bad_attribute_xml(&xret, type, info, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -441,7 +441,7 @@ netconf_missing_element(cbuf *cb,
if (netconf_common_xml(&xret, type, "missing-element",
"bad-element", element, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -487,7 +487,7 @@ netconf_bad_element(cbuf *cb,
if (netconf_common_xml(&xret, type, "bad-element",
"bad-element",element, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -525,7 +525,7 @@ netconf_unknown_element(cbuf *cb,
if (netconf_common_xml(&xret, type, "unknown-element",
"bad-element", element, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -572,7 +572,7 @@ netconf_unknown_namespace(cbuf *cb,
if (netconf_common_xml(&xret, type, "unknown-namespace",
"bad-namespace", namespace, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -610,7 +610,7 @@ netconf_access_denied(cbuf *cb,
if (netconf_access_denied_xml(&xret, type, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -846,7 +846,7 @@ netconf_data_missing(cbuf *cb,
if (netconf_data_missing_xml(&xret, missing_choice, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -967,7 +967,7 @@ netconf_operation_failed(cbuf *cb,
if (netconf_operation_failed_xml(&xret, type, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -1045,7 +1045,7 @@ netconf_malformed_message(cbuf *cb,
if (netconf_malformed_message_xml(&xret, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
goto done;
retval = 0;
done:
@ -1148,7 +1148,7 @@ netconf_data_not_unique_xml(cxobj **xret,
while ((cvi = cvec_each(cvk, cvi)) != NULL){
if ((xi = xml_find(x, cv_string_get(cvi))) == NULL)
continue; /* ignore, shouldnt happen */
clicon_xml2cbuf(cb, xi, 0, 0);
clicon_xml2cbuf(cb, xi, 0, 0, -1);
if (xml_parse_va(&xinfo, NULL, "<non-unique>%s</non-unique>", cbuf_get(cb)) < 0)
goto done;
cbuf_reset(cb);
@ -1369,7 +1369,7 @@ netconf_err2cb(cxobj *xerr,
if ((x=xpath_first(xerr, "error-message"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-info"))!=NULL)
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0, -1);
*cberr = cb;
retval = 0;
done:

View file

@ -606,7 +606,7 @@ send_msg_notify_xml(int s,
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xev, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xev, 0, 0, -1) < 0)
goto done;
if (send_msg_notify(s, cbuf_get(cb)) < 0)
goto done;

View file

@ -213,7 +213,7 @@ clicon_rpc_netconf_xml(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xml, 0, 0) < 0)
if (clicon_xml2cbuf(cb, xml, 0, 0, -1) < 0)
goto done;
if (clicon_rpc_netconf(h, cbuf_get(cb), xret, sp) < 0)
goto done;
@ -541,7 +541,8 @@ clicon_rpc_unlock(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] xpath XPath in a filter stmt (or NULL/"" for no filter)
* @param[in] namespace Namespace associated w xpath
* @param[in] content CLixon extension: all, config, noconfig. -1 means all
* @param[in] content Clixon extension: all, config, noconfig. -1 means all
* @param[in] depth Nr of XML levels to get, -1 is all, 0 is none
* @param[out] xt XML tree. Free with xml_free.
* Either <config> or <rpc-error>.
* @retval 0 OK
@ -550,7 +551,7 @@ clicon_rpc_unlock(clicon_handle h,
* namespace will be used which is most probably wrong.
* @code
* cxobj *xt = NULL;
* if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", CONTENT_ALL, &xt) < 0)
* if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", CONTENT_ALL, -1, &xt) < 0)
* err;
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
* clicon_rpc_generate_error(xerr);
@ -567,6 +568,7 @@ clicon_rpc_get(clicon_handle h,
char *xpath,
char *namespace,
netconf_content content,
int32_t depth,
cxobj **xt)
{
int retval = -1;
@ -584,11 +586,12 @@ clicon_rpc_get(clicon_handle h,
if (namespace)
cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, "><get");
#if 1
/* Clixon extension, content all, config, nonconfig */
/* Clixon extension, content=all,config, or nonconfig */
if (content != -1)
cprintf(cb, " content=\"%s\"", netconf_content_int2str(content));
#endif
/* Clixon extension, depth=<level> */
if (depth != -1)
cprintf(cb, " depth=\"%d\"", depth);
cprintf(cb, ">");
if (xpath && strlen(xpath)) {
if (namespace)

View file

@ -984,7 +984,7 @@ stream_publish_cb(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(d, event, 0, 0) < 0)
if (clicon_xml2cbuf(d, event, 0, 0, -1) < 0)
goto done;
if (url_post(cbuf_get(u), /* url+stream */
cbuf_get(d), /* postfields */

View file

@ -1651,13 +1651,14 @@ xml_print(FILE *f,
*
* @param[in,out] cb Cligen buffer to write to
* @param[in] xn Clicon xml tree
* @param[in] level Indentation level
* @param[in] level Indentation level for prettyprint
* @param[in] prettyprint insert \n and spaces tomake the xml more readable.
* @param[in] depth Limit levels of child resources: -1 is all, 0 is none, 1 is node itself
*
* @code
* cbuf *cb;
* cb = cbuf_new();
* if (clicon_xml2cbuf(cb, xn, 0, 1) < 0)
* if (clicon_xml2cbuf(cb, xn, 0, 1, -1) < 0)
* goto err;
* fprintf(stderr, "%s", cbuf_get(cb));
* cbuf_free(cb);
@ -1668,7 +1669,8 @@ int
clicon_xml2cbuf(cbuf *cb,
cxobj *x,
int level,
int prettyprint)
int prettyprint,
int32_t depth)
{
int retval = -1;
cxobj *xc;
@ -1679,6 +1681,8 @@ clicon_xml2cbuf(cbuf *cb,
char *encstr = NULL; /* xml encoded string */
char *val;
if (depth == 0)
goto ok;
name = xml_name(x);
namespace = xml_prefix(x);
switch(xml_type(x)){
@ -1707,7 +1711,7 @@ clicon_xml2cbuf(cbuf *cb,
while ((xc = xml_child_each(x, xc, -1)) != NULL)
switch (xc->x_type){
case CX_ATTR:
if (clicon_xml2cbuf(cb, xc, level+1, prettyprint) < 0)
if (clicon_xml2cbuf(cb, xc, level+1, prettyprint, -1) < 0)
goto done;
break;
case CX_BODY:
@ -1729,7 +1733,7 @@ clicon_xml2cbuf(cbuf *cb,
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR)
if (clicon_xml2cbuf(cb, xc, level+1, prettyprint) < 0)
if (clicon_xml2cbuf(cb, xc, level+1, prettyprint, depth-1) < 0)
goto done;
if (prettyprint && hasbody == 0)
cprintf(cb, "%*s", level*XML_INDENT, "");
@ -1744,6 +1748,7 @@ clicon_xml2cbuf(cbuf *cb,
default:
break;
}/* switch */
ok:
retval = 0;
done:
if (encstr)
@ -2522,7 +2527,7 @@ clicon_log_xml(int level,
/* Print xml as cbuf */
if ((cb = cbuf_new()) == NULL)
goto done;
if (clicon_xml2cbuf(cb, x, 0, 0) < 0)
if (clicon_xml2cbuf(cb, x, 0, 0, -1) < 0)
goto done;
/* first round: compute length of debug message */

View file

@ -148,18 +148,41 @@ xpath_tree_print0(cbuf *cb,
return 0;
}
/*! Print a xpath_tree
/*! Print a xpath_tree to CLIgen buf
* @param[out] cb CLIgen buffer
* @param[in] xs XPATH tree
*/
int
xpath_tree_print(cbuf *cb,
xpath_tree_print_cb(cbuf *cb,
xpath_tree *xs)
{
xpath_tree_print0(cb, xs, 0);
return 0;
}
/*! Print a xpath_tree
* @param[in] f UNIX output stream
* @param[in] xs XPATH tree
*/
int
xpath_tree_print(FILE *f,
xpath_tree *xs)
{
int retval = -1;
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (xpath_tree_print0(cb, xs, 0) < 0)
goto done;
fprintf(f, "%s", cbuf_get(cb));
retval = 0;
done:
return retval;
}
/*! Free a xpath_tree
* @param[in] xs XPATH tree
* @see xpath_parse creates a xpath_tree
@ -203,6 +226,7 @@ xpath_parse(cvec *nsc,
{
int retval = -1;
struct clicon_xpath_yacc_arg xy = {0,};
cbuf *cb = NULL;
xy.xy_parse_string = xpath;
xy.xy_name = "xpath parser";
@ -219,17 +243,20 @@ xpath_parse(cvec *nsc,
goto done;
}
if (debug > 1){
cbuf *cb = cbuf_new();
xpath_tree_print(cb, xy.xy_top);
clicon_debug(2, "xpath parse tree:\n%s", cbuf_get(cb));
cbuf_free(cb);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
xpath_tree_print_cb(cb, xy.xy_top);
clicon_debug(2, "xpath parse tree:\n%s", cbuf_get(cb));
}
/* done: */
xpath_parse_exit(&xy);
xpath_scan_exit(&xy);
*xptree = xy.xy_top;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}

View file

@ -100,7 +100,8 @@ new "B.1.2. Retrieve the Server Module Information"
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":' '"module":\[{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}'
new "B.1.3. Retrieve the Server Capability Information"
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "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></capabilities>'
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "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</capability>
</capabilities>'
new "B.2.1. Create New Data Resources (artist+json)"
expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' http://localhost/restconf/data/example-jukebox:jukebox/library -d '{"example-jukebox:artist":[{"name":"Foo Fighters"}]}')" 0 "HTTP/1.1 201 Created" "Location: http://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters"
@ -165,9 +166,28 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/example-events:even
new 'B.3.1. "content" Parameter example 3: content=nonconfig'
expectpart "$(curl -si -X GET http://localhost/restconf/data/example-events:events?content=nonconfig -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","event-count":90},{"name":"interface-up","event-count":77}\]}}'
#new "restconf DELETE whole datastore"
#expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
new 'B.3.2. "depth" Parameter example 1 unbound'
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/example-jukebox:jukebox?depth=unbounded)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Foo Fighters","album":\[{"name":"One by One","year":2012}\]},{"name":"Nick Cave and the Bad Seeds","album":\[{"name":"Tender Prey","year":1988},{"name":"The Good Son","year":1990}\]}\]}}}'
new 'B.3.2. "depth" Parameter example 2 depth=1'
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/example-jukebox:jukebox?depth=1)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{}}'
new 'B.3.2. "depth" Parameter depth=2'
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/example-jukebox:jukebox?depth=2)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{}}}'
# Maybe this is not correct w [null,null]but I have no good examples
new 'B.3.2. "depth" Parameter depth=3'
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}}
'
new "restconf DELETE whole datastore"
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
#new 'B.3.3. "fields" Parameter'
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']\"}]}"
expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=1'
@ -224,7 +244,6 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/example-jukebox:ext
if false; then # NYI
new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed
new 'B.3.2. "depth" Parameter'
new 'B.3.3. "fields" Parameter'
new 'B.3.6. "filter" Parameter'
new 'B.3.7. "start-time" Parameter'

View file

@ -147,7 +147,7 @@ main(int argc,
if (json)
xml2json_cbuf(cb, xc, pretty); /* print xml */
else
clicon_xml2cbuf(cb, xc, 0, pretty); /* print xml */
clicon_xml2cbuf(cb, xc, 0, pretty, -1); /* print xml */
fprintf(stdout, "%s", cbuf_get(cb));
fflush(stdout);
retval = 0;

View file

@ -256,7 +256,7 @@ main(int argc,
if (jsonout)
xml2json_cbuf(cb, xc, pretty); /* print xml */
else
clicon_xml2cbuf(cb, xc, 0, pretty); /* print xml */
clicon_xml2cbuf(cb, xc, 0, pretty, -1); /* print xml */
fprintf(stdout, "%s", cbuf_get(cb));
fflush(stdout);
}

View file

@ -51,6 +51,7 @@ See https://www.w3.org/TR/xpath/
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/stat.h>
/* cligen */
#include <cligen/cligen.h>
@ -68,6 +69,8 @@ usage(char *argv0)
"\t-f <file> \tXML file\n"
"\t-p <xpath> \tPrimary XPATH string\n"
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
"\t-y <filename> \tYang filename or dir (load all files)\n"
"\t-Y <dir> \tYang dirs (can be several)\n"
"and the following extra rules:\n"
"\tif -f is not given, XML input is expected on stdin\n"
"\tif -p is not given, <xpath> is expected as the first line on stdin\n"
@ -88,7 +91,7 @@ ctx_print2(cbuf *cb,
case XT_NODESET:
for (i=0; i<xc->xc_size; i++){
cprintf(cb, "%d:", i);
clicon_xml2cbuf(cb, xc->xc_nodeset[i], 0, 0);
clicon_xml2cbuf(cb, xc->xc_nodeset[i], 0, 0, -1);
}
break;
case XT_BOOL:
@ -105,7 +108,8 @@ ctx_print2(cbuf *cb,
}
int
main(int argc, char **argv)
main(int argc,
char **argv)
{
int retval = -1;
char *argv0 = argv[0];
@ -118,16 +122,24 @@ main(int argc, char **argv)
char *buf = NULL;
int ret;
int fd = 0; /* unless overriden by argv[1] */
char *yang_file_dir = NULL;
yang_stmt *yspec = NULL;
char *xpath = NULL;
char *xpath0 = NULL;
char *filename;
xp_ctx *xc = NULL;
cbuf *cb = NULL;
clicon_handle h;
struct stat st;
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
if ((h = clicon_handle_init()) == NULL)
goto done;
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, "hD:f:p:i:")) != -1)
while ((c = getopt(argc, argv, "hD:f:p:i:y:Y:")) != -1)
switch (c) {
case 'h':
usage(argv0);
@ -149,10 +161,35 @@ main(int argc, char **argv)
case 'i': /* Optional initial XPATH string */
xpath0 = optarg;
break;
case 'y':
yang_file_dir = optarg;
break;
case 'Y':
if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
goto done;
break;
default:
usage(argv[0]);
break;
}
/* Parse yang */
if (yang_file_dir){
if ((yspec = yspec_new()) == NULL)
goto done;
if (stat(yang_file_dir, &st) < 0){
clicon_err(OE_YANG, errno, "%s not found", yang_file_dir);
goto done;
}
if (S_ISDIR(st.st_mode)){
if (yang_spec_load_dir(h, yang_file_dir, yspec) < 0)
goto done;
}
else{
if (yang_spec_parse_file(h, yang_file_dir, yspec) < 0)
goto done;
}
}
if (xpath==NULL){
/* First read xpath */
len = 1024; /* any number is fine */
@ -192,7 +229,37 @@ main(int argc, char **argv)
return -1;
}
/* If xpath0 given, position current x */
/* Validate XML as well */
if (yang_file_dir){
cbuf *cbret = NULL;
cxobj *x1;
cxobj *xerr = NULL; /* malloced must be freed */
x1 = xml_child_i(x0, 0);
/* Populate */
if (xml_apply0(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
/* Sort */
if (xml_apply0(x1, CX_ELMNT, xml_sort, h) < 0)
goto done;
/* Add default values */
if (xml_apply(x1, CX_ELMNT, xml_default, h) < 0)
goto done;
if (xml_apply0(x1, -1, xml_sort_verify, h) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed", __FUNCTION__);
if ((ret = xml_yang_validate_all_top(h, x1, &xerr)) < 0)
goto done;
if (ret > 0 && (ret = xml_yang_validate_add(h, x1, &xerr)) < 0)
goto done;
if (ret == 0){
if (netconf_err2cb(xerr, &cbret) < 0)
goto done;
fprintf(stderr, "xml validation error: %s\n", cbuf_get(cbret));
goto done;
}
}
/* If xpath0 given, position current x (ie somewhere else than root) */
if (xpath0){
if ((x = xpath_first(x0, "%s", xpath0)) == NULL){
fprintf(stderr, "Error: xpath0 returned NULL\n");
@ -202,7 +269,7 @@ main(int argc, char **argv)
else
x = x0;
/* Parse XML (use nsc == NULL to indicate dont use) */
/* Parse XPATH (use nsc == NULL to indicate dont use) */
if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
return -1;
/* Print results */