From ee329ee382e8b1022f26e688025680c9117ae0f5 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 17 Aug 2019 10:54:13 +0200 Subject: [PATCH] * RESTCONF "depth" query parameter supported * C API change: Added `depth` parameter to function `clicon_xml2cbuf`, default is -1. --- CHANGELOG.md | 10 ++- README.md | 7 +- apps/backend/backend_client.c | 29 +++++++-- apps/backend/backend_commit.c | 6 +- apps/backend/clixon_backend_transaction.c | 8 +-- apps/cli/cli_common.c | 6 +- apps/cli/cli_show.c | 4 +- apps/netconf/netconf_main.c | 4 +- apps/netconf/netconf_rpc.c | 6 +- apps/restconf/restconf_lib.c | 2 +- apps/restconf/restconf_main.c | 4 +- apps/restconf/restconf_methods.c | 8 +-- apps/restconf/restconf_methods_get.c | 45 +++++++++---- apps/restconf/restconf_methods_post.c | 18 +++--- apps/restconf/restconf_stream.c | 2 +- example/main/example_backend.c | 2 +- example/main/example_netconf.c | 13 ++-- example/main/example_restconf.c | 2 +- lib/clixon/clixon_proto_client.h | 2 +- lib/clixon/clixon_xml.h | 2 +- lib/clixon/clixon_xpath.h | 3 +- lib/src/clixon_netconf_lib.c | 24 +++---- lib/src/clixon_proto.c | 2 +- lib/src/clixon_proto_client.c | 23 ++++--- lib/src/clixon_stream.c | 2 +- lib/src/clixon_xml.c | 23 ++++--- lib/src/clixon_xpath.c | 43 +++++++++--- test/test_restconf_jukebox.sh | 23 ++++++- util/clixon_util_json.c | 2 +- util/clixon_util_xml.c | 2 +- util/clixon_util_xpath.c | 79 +++++++++++++++++++++-- 31 files changed, 289 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8044c3b2..8622973c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index aa4d3106..026ad920 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index c6b46b74..bbdb0b54 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -176,6 +176,8 @@ client_get_capabilities(clicon_handle h, goto done; if (xml_parse_va(&xcap, yspec, "urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit") < 0) goto done; + if (xml_parse_va(&xcap, yspec, "urn:ietf:params:restconf:capability:depth:1.0") < 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, ""); @@ -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", + "depth", "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, ""); @@ -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; } diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 66d2a628..3c115532 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -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) && diff --git a/apps/backend/clixon_backend_transaction.c b/apps/backend/clixon_backend_transaction.c index 46d4f827..a5779633 100644 --- a/apps/backend/clixon_backend_transaction.c +++ b/apps/backend/clixon_backend_transaction.c @@ -240,7 +240,7 @@ transaction_log(clicon_handle h, } for (i=0; itd_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; itd_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; itd_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)); diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index f0840491..4d0bdb8b 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -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; diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 51285bba..43e22237 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -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; } diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index f1fadb92..fe46fa25 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -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; diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 7f09ea55..ad91b728 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -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; diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 82db21a9..2e14cf2d 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -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){ diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index eb93c6eb..a5c4a41d 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -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: diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 85da9f5b..bf4c37db 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -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, ""); cprintf(cbx, "none"); - if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0) + if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0) goto done; cprintf(cbx, ""); 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, ""); cprintf(cbx, "none"); - if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0) + if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0) goto done; cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 3cba9b47..36004540 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -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", "content", "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", + "depth", "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; diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index 55583eae..c30b9487 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -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, ""); cprintf(cbx, "none"); - if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0) + if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0) goto done; cprintf(cbx, ""); 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: 0 */ break; diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c index a53db543..a75a63ea 100644 --- a/apps/restconf/restconf_stream.c +++ b/apps/restconf/restconf_stream.c @@ -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"); diff --git a/example/main/example_backend.c b/example/main/example_backend.c index 234c8d92..25a38514 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -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, ""); diff --git a/example/main/example_netconf.c b/example/main/example_netconf.c index acab6ef0..9d2dd32a 100644 --- a/example/main/example_netconf.c +++ b/example/main/example_netconf.c @@ -64,11 +64,12 @@ plugin_exit(clicon_handle h) /*! Local example netconf rpc callback */ -int netconf_client_rpc(clicon_handle h, - cxobj *xe, - cbuf *cbret, - void *arg, - void *regarg) +int +netconf_client_rpc(clicon_handle h, + cxobj *xe, + cbuf *cbret, + void *arg, + void *regarg) { int retval = -1; cxobj *x = NULL; @@ -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, ""); diff --git a/example/main/example_restconf.c b/example/main/example_restconf.c index 71bdd391..24db9d82 100644 --- a/example/main/example_restconf.c +++ b/example/main/example_restconf.c @@ -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, ""); diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index d16615bf..9bdbe204 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -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); diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 0784869a..9c961581 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -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 diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index c8a18b0e..dea6f832 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -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); diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index bf24e203..95c7a414 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -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, "%s", 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: diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index c357e300..e365738b 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -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; diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index a7cfcf79..40a8ee89 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -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 or . * @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); @@ -563,11 +564,12 @@ clicon_rpc_unlock(clicon_handle h, * @see clicon_rpc_generate_error */ int -clicon_rpc_get(clicon_handle h, - char *xpath, - char *namespace, +clicon_rpc_get(clicon_handle h, + char *xpath, + char *namespace, netconf_content content, - cxobj **xt) + int32_t depth, + cxobj **xt) { int retval = -1; struct clicon_msg *msg = NULL; @@ -584,11 +586,12 @@ clicon_rpc_get(clicon_handle h, if (namespace) cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE); cprintf(cb, "> */ + if (depth != -1) + cprintf(cb, " depth=\"%d\"", depth); cprintf(cb, ">"); if (xpath && strlen(xpath)) { if (namespace) diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c index 7cc3b119..397b91db 100644 --- a/lib/src/clixon_stream.c +++ b/lib/src/clixon_stream.c @@ -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 */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 2a860d0a..c5b1715f 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -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); @@ -1665,10 +1666,11 @@ xml_print(FILE *f, * @see clicon_xml2file */ int -clicon_xml2cbuf(cbuf *cb, - cxobj *x, - int level, - int prettyprint) +clicon_xml2cbuf(cbuf *cb, + cxobj *x, + int level, + 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 */ diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 7d62b39e..b9eb88b1 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -148,17 +148,40 @@ 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 *xs) +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 @@ -203,7 +226,8 @@ 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"; xy.xy_linenum = 1; @@ -219,17 +243,20 @@ xpath_parse(cvec *nsc, goto done; } if (debug > 1){ - cbuf *cb = cbuf_new(); - xpath_tree_print(cb, xy.xy_top); + 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)); - cbuf_free(cb); } - /* done: */ xpath_parse_exit(&xy); xpath_scan_exit(&xy); *xptree = xy.xy_top; retval = 0; done: + if (cb) + cbuf_free(cb); return retval; } diff --git a/test/test_restconf_jukebox.sh b/test/test_restconf_jukebox.sh index d4b55b99..4b571781 100755 --- a/test/test_restconf_jukebox.sh +++ b/test/test_restconf_jukebox.sh @@ -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' 'urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit' +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' 'urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=expliciturn:ietf:params:restconf:capability:depth +' 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' diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index 5ad62f2a..c1083690 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -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; diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index 06504753..ea38cf23 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -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); } diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index c5d5ce2c..d799c4bb 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -51,6 +51,7 @@ See https://www.w3.org/TR/xpath/ #include #include #include +#include /* cligen */ #include @@ -68,6 +69,8 @@ usage(char *argv0) "\t-f \tXML file\n" "\t-p \tPrimary XPATH string\n" "\t-i \t(optional) Initial XPATH string\n" + "\t-y \tYang filename or dir (load all files)\n" + "\t-Y \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, is expected as the first line on stdin\n" @@ -88,7 +91,7 @@ ctx_print2(cbuf *cb, case XT_NODESET: for (i=0; ixc_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 */