From 43a57dad79b02d5ca55deb737f5ee9216b8dab6b Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 1 Jun 2022 10:48:39 +0200 Subject: [PATCH] Changed C-API for xml translation/print to other formats. * Added `skiptop` parameter, if set only apply to children of a node, skip top node * default is 0 * Functions are merged, ie removed and replaced with more generic functions * `xml2json_cbuf()`: Added `skiptop` parameter: `xml2json_cbuf(..., int skiptop)` * `xml2json()` and `xml2json_cb()` merged into `xml2json_file()` with `skiptop` * Replace `xml2json(...)` with `xml2json_file(..., stdout, 0)` * Replace `xml2json_cb(...)` with `xml2json_file(..., 0)` * `clicon_xml2cbuf()`: Added `skiptop` parameter: `clicon_xml2cbuf(..., int skiptop)` * `xml2cli()`: Added `skiptop` parameter: `xml2cli(..., int skiptop)` --- CHANGELOG.md | 15 ++ apps/backend/backend_client.c | 10 +- apps/backend/backend_commit.c | 16 +- apps/backend/backend_get.c | 10 +- apps/backend/backend_plugin_restconf.c | 2 +- apps/backend/clixon_backend_transaction.c | 8 +- apps/cli/cli_auto.c | 15 +- apps/cli/cli_common.c | 29 ++- apps/cli/cli_show.c | 75 ++++-- apps/cli/clixon_cli_api.h | 2 +- apps/netconf/netconf_main.c | 12 +- apps/netconf/netconf_rpc.c | 4 +- apps/restconf/restconf_err.c | 8 +- apps/restconf/restconf_methods.c | 4 +- apps/restconf/restconf_methods_get.c | 8 +- apps/restconf/restconf_methods_patch.c | 8 +- apps/restconf/restconf_methods_post.c | 6 +- apps/restconf/restconf_root.c | 8 +- apps/restconf/restconf_stream_fcgi.c | 2 +- example/main/example_backend.c | 8 +- example/main/example_netconf.c | 8 +- example/main/example_restconf.c | 8 +- lib/clixon/clixon_json.h | 5 +- lib/clixon/clixon_xml_io.h | 2 +- lib/src/clixon_datastore_write.c | 4 +- lib/src/clixon_json.c | 98 ++++---- lib/src/clixon_netconf_lib.c | 26 +-- lib/src/clixon_proto.c | 2 +- lib/src/clixon_proto_client.c | 2 +- lib/src/clixon_stream.c | 2 +- lib/src/clixon_validate.c | 4 +- lib/src/clixon_xml.c | 2 +- lib/src/clixon_xml_io.c | 77 +++++-- test/test_c++.sh | 10 +- util/clixon_util_json.c | 15 +- util/clixon_util_socket.c | 2 +- util/clixon_util_xml.c | 16 +- util/clixon_util_xpath.c | 2 +- yang/clixon/clixon-autocli@2022-05-30.yang | 252 --------------------- 39 files changed, 306 insertions(+), 481 deletions(-) delete mode 100644 yang/clixon/clixon-autocli@2022-05-30.yang diff --git a/CHANGELOG.md b/CHANGELOG.md index bec84b54..86c8b794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,21 @@ ## 5.8.0 Planned: July 2022 +### C/CLI-API changes on existing features + +Developers may need to change their code + +* Changed C-API for xml translation/print to other formats. + * Added `skiptop` parameter, if set only apply to children of a node, skip top node + * default is 0 + * Functions are merged, ie removed and replaced with more generic functions + * `xml2json_cbuf()`: Added `skiptop` parameter: `xml2json_cbuf(..., int skiptop)` + * `xml2json()` and `xml2json_cb()` merged into `xml2json_file()` with `skiptop` + * Replace `xml2json(...)` with `xml2json_file(..., stdout, 0)` + * Replace `xml2json_cb(...)` with `xml2json_file(..., 0)` + * `clicon_xml2cbuf()`: Added `skiptop` parameter: `clicon_xml2cbuf(..., int skiptop)` + * `xml2cli()`: Added `skiptop` parameter: `xml2cli(..., int skiptop)` + ## 5.7.0 17 May 2022 diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 279f0309..3daede97 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -366,7 +366,7 @@ from_client_edit_config(clicon_handle h, if ((ret = xml_bind_yang(xc, YB_MODULE, yspec, &xret)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto ok; } @@ -374,7 +374,7 @@ from_client_edit_config(clicon_handle h, if ((ret = xml_non_config_data(xc, &xret)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto ok; } @@ -387,7 +387,7 @@ from_client_edit_config(clicon_handle h, if ((ret = xml_yang_validate_list_key_only(xc, &xret)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto ok; } @@ -1241,7 +1241,7 @@ from_client_msg(clicon_handle h, goto reply; } if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto reply; } @@ -1303,7 +1303,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, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto reply; } diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 44a72165..b7491f5f 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -216,7 +216,7 @@ startup_common(clicon_handle h, * See similar clause below */ } - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; goto fail; } @@ -276,7 +276,7 @@ startup_common(clicon_handle h, if ((ret = xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto fail; } @@ -284,7 +284,7 @@ startup_common(clicon_handle h, if ((ret = xml_non_config_data(xt, &xret)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto fail; } @@ -319,7 +319,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, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto fail; /* STARTUP_INVALID */ } @@ -625,7 +625,7 @@ candidate_validate(clicon_handle h, clicon_err(OE_CFG, EINVAL, "xret is NULL"); goto done; } - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; if (!cbuf_len(cbret) && netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) @@ -685,7 +685,7 @@ candidate_commit(clicon_handle h, if ((ret = validate_common(h, db, td, &xret)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto fail; } @@ -957,7 +957,7 @@ from_client_restart_one(clicon_handle h, if ((ret = xml_yang_validate_all_top(h, td->td_target, &xerr)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; goto fail; } @@ -1007,7 +1007,7 @@ from_client_restart_one(clicon_handle h, if ((ret = generic_validate(h, yspec, td, &xerr)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; goto fail; } diff --git a/apps/backend/backend_get.c b/apps/backend/backend_get.c index 962d0973..5e1a9a46 100644 --- a/apps/backend/backend_get.c +++ b/apps/backend/backend_get.c @@ -365,7 +365,7 @@ get_nacm_and_reply(clicon_handle h, if (xml_name_set(xret, NETCONF_OUTPUT_DATA) < 0) goto done; /* Top level is data, so add 1 to depth if significant */ - if (clicon_xml2cbuf(cbret, xret, 0, 0, depth>0?depth+1:depth) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, depth>0?depth+1:depth, 0) < 0) goto done; } cprintf(cbret, ""); @@ -605,7 +605,7 @@ get_list_pagination(clicon_handle h, clicon_err_reason); if (netconf_operation_failed_xml(&xerr, "application", cbuf_get(cberr)) < 0) goto done; - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; goto ok; } @@ -620,7 +620,7 @@ get_list_pagination(clicon_handle h, ". Internal error, state callback returned invalid XML", NULL) < 0) goto done; - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; goto ok; } @@ -832,7 +832,7 @@ get_common(clicon_handle h, if ((ret = get_client_statedata(h, xpath?xpath:"/", nsc, &xret)) < 0) goto done; if (ret == 0){ /* Error from callback (error in xret) */ - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto ok; } @@ -857,7 +857,7 @@ get_common(clicon_handle h, ". Internal error, state callback returned invalid XML", NULL) < 0) goto done; - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; goto ok; } diff --git a/apps/backend/backend_plugin_restconf.c b/apps/backend/backend_plugin_restconf.c index 35c7f09b..4e0bfdcc 100644 --- a/apps/backend/backend_plugin_restconf.c +++ b/apps/backend/backend_plugin_restconf.c @@ -163,7 +163,7 @@ restconf_pseudo_set_inline(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (clicon_xml2cbuf(cb, xrestconf, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xrestconf, 0, 0, -1, 0) < 0) goto done; if ((str = strdup(cbuf_get(cb))) == NULL){ clicon_err(OE_XML, errno, "stdup"); diff --git a/apps/backend/clixon_backend_transaction.c b/apps/backend/clixon_backend_transaction.c index 793a277f..62eb57a6 100644 --- a/apps/backend/clixon_backend_transaction.c +++ b/apps/backend/clixon_backend_transaction.c @@ -256,7 +256,7 @@ transaction_log(clicon_handle h, } for (i=0; itd_dlen; i++){ xn = td->td_dvec[i]; - clicon_xml2cbuf(cb, xn, 0, 0, -1); + clicon_xml2cbuf(cb, xn, 0, 0, -1, 0); } if (i) clicon_log(level, "%s %" PRIu64 " %s del: %s", @@ -264,7 +264,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, -1); + clicon_xml2cbuf(cb, xn, 0, 0, -1, 0); } if (i) clicon_log(level, "%s %" PRIu64 " %s add: %s", __FUNCTION__, td->td_id, op, cbuf_get(cb)); @@ -272,10 +272,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, -1); + clicon_xml2cbuf(cb, xn, 0, 0, -1, 0); } xn = td->td_tcvec[i]; - clicon_xml2cbuf(cb, xn, 0, 0, -1); + clicon_xml2cbuf(cb, xn, 0, 0, -1, 0); } if (i) clicon_log(level, "%s %" PRIu64 " %s change: %s", __FUNCTION__, td->td_id, op, cbuf_get(cb)); diff --git a/apps/cli/cli_auto.c b/apps/cli/cli_auto.c index e2f4ff6a..c6227b2f 100644 --- a/apps/cli/cli_auto.c +++ b/apps/cli/cli_auto.c @@ -651,12 +651,8 @@ cli_auto_show(clicon_handle h, fprintf(stdout, "\n"); break; case FORMAT_JSON: - if (isroot) - xml2json_cb(stdout, xp, pretty, cligen_output); - else{ - while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) - xml2json_cb(stdout, xc, pretty, cligen_output); - } + if (xml2json_file(stdout, xp, pretty, cligen_output, !isroot) < 0) + goto done; fprintf(stdout, "\n"); break; case FORMAT_TEXT: @@ -667,11 +663,8 @@ cli_auto_show(clicon_handle h, cli_xml2txt(xc, cligen_output, 0); /* tree-formed text */ break; case FORMAT_CLI: - if (isroot) - xml2cli(h, stdout, xp, prefix, cligen_output); - else - while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) - xml2cli(h, stdout, xc, prefix, cligen_output); + if (xml2cli(h, stdout, xp, prefix, cligen_output, !isroot) < 0) + goto done; break; case FORMAT_NETCONF: fprintf(stdout, "", diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 56e6a06b..0b2ba8dc 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -325,7 +325,7 @@ cli_dbxml(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (clicon_xml2cbuf(cb, xtop, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xtop, 0, 0, -1, 0) < 0) goto done; if (clicon_rpc_edit_config(h, "candidate", OP_NONE, cbuf_get(cb)) < 0) goto done; @@ -925,9 +925,9 @@ load_config_file(clicon_handle h, while ((x = xml_child_each(xt, x, -1)) != NULL) { /* Read as datastore-top but transformed into an edit-config "config" */ xml_name_set(x, NETCONF_INPUT_CONFIG); - if (clicon_xml2cbuf(cbxml, x, 0, 0, -1) < 0) - goto done; } + if (clicon_xml2cbuf(cbxml, xt, 0, 0, -1, 1) < 0) + goto done; if (clicon_rpc_edit_config(h, "candidate", replace?OP_REPLACE:OP_MERGE, cbuf_get(cbxml)) < 0) @@ -973,7 +973,6 @@ save_config_file(clicon_handle h, char *dbstr; char *varstr; cxobj *xt = NULL; - cxobj *x; cxobj *xerr; FILE *f = NULL; char *formatstr; @@ -1031,7 +1030,7 @@ save_config_file(clicon_handle h, goto done; break; case FORMAT_JSON: - if (xml2json(f, xt, pretty) < 0) + if (xml2json_file(f, xt, pretty, fprintf, 0) < 0) goto done; break; case FORMAT_TEXT: @@ -1039,11 +1038,8 @@ save_config_file(clicon_handle h, goto done; break; case FORMAT_CLI: - x = NULL; - while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { - if (xml2cli(h, f, x, prefix, fprintf) < 0) - goto done; - } + if (xml2cli(h, f, xt, prefix, fprintf, 1) < 0) + goto done; break; case FORMAT_NETCONF: fprintf(f, "", @@ -1153,6 +1149,13 @@ cli_notification_cb(int s, clicon_err(OE_NETCONF, EFAULT, "Notification malformed"); goto done; } + switch (format){ + case FORMAT_JSON: + if (xml2json_file(stdout, xt, 1, cligen_output, 1) < 0) + goto done; + default: + break; + } if ((xe = xpath_first(xt, NULL, "//event")) != NULL){ x = NULL; while ((x = xml_child_each(xe, x, -1)) != NULL) { @@ -1165,10 +1168,6 @@ cli_notification_cb(int s, if (xml2txt_cb(stdout, x, cligen_output) < 0) goto done; break; - case FORMAT_JSON: - if (xml2json_cb(stdout, x, 1, cligen_output) < 0) - goto done; - break; default: break; } @@ -1417,7 +1416,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, -1); + clicon_xml2cbuf(cb, x2, 0, 0, -1, 0); 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 186f1222..2dc0acb2 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -500,7 +500,7 @@ cli_show_config1(clicon_handle h, cli_xml2file(xc, 0, 1, cligen_output); break; case FORMAT_JSON: - xml2json_cb(stdout, xt, 1, cligen_output); + xml2json_file(stdout, xt, 1, cligen_output, 0); break; case FORMAT_TEXT: xc = NULL; /* Dont print xt itself */ @@ -508,10 +508,8 @@ cli_show_config1(clicon_handle h, cli_xml2txt(xc, cligen_output, 0); /* tree-formed text */ break; case FORMAT_CLI: - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) - if (xml2cli(h, stdout, xc, prefix, cligen_output) < 0) - goto done; + if (xml2cli(h, stdout, xt, prefix, cligen_output, 1) < 0) + goto done; break; case FORMAT_NETCONF: cligen_output(stdout, "\n", @@ -773,7 +771,7 @@ cli_show_generated(clicon_handle h, switch (format){ case FORMAT_CLI: - if (xml2cli(h, stdout, xp, prefix, cligen_output) < 0) /* cli syntax */ + if (xml2cli(h, stdout, xp, prefix, cligen_output, 0) < 0) /* cli syntax */ goto done; break; case FORMAT_NETCONF: @@ -781,15 +779,15 @@ cli_show_generated(clicon_handle h, cli_xml2file(xp, 2, 1, fprintf); fprintf(stdout, "]]>]]>\n"); break; + case FORMAT_JSON: + xml2json_file(stdout, xp_helper, 1, cligen_output, 1); + break; default: for (; i < xml_child_nr(xml_parent(xp)) ; ++i, xp_helper = xml_child_i(xml_parent(xp), i)) { switch (format){ case FORMAT_XML: cli_xml2file(xp_helper, 0, 1, fprintf); break; - case FORMAT_JSON: - xml2json_cb(stdout, xp_helper, 1, cligen_output); - break; case FORMAT_TEXT: cli_xml2txt(xp_helper, cligen_output, 0); /* tree-formed text */ break; @@ -985,14 +983,14 @@ cli_pagination(clicon_handle h, clicon_xml2file_cb(stdout, xc, 0, 1, cligen_output); break; case FORMAT_JSON: - xml2json_cb(stdout, xc, 1, cligen_output); + xml2json_file(stdout, xc, 1, cligen_output, 0); break; case FORMAT_TEXT: xml2txt_cb(stdout, xc, cligen_output); /* tree-formed text */ break; case FORMAT_CLI: /* hardcoded to compress and list-keyword = nokey */ - xml2cli(h, stdout, xc, NULL, cligen_output); + xml2cli(h, stdout, xc, NULL, cligen_output, 0); break; default: break; @@ -1028,7 +1026,7 @@ cli_pagination(clicon_handle h, return retval; } -/*! Translate from XML to CLI commands +/*! Translate from XML to CLI commands, internal * * Howto: join strings and pass them down. * Identify unique/index keywords for correct set syntax. @@ -1037,14 +1035,13 @@ cli_pagination(clicon_handle h, * @param[in] xn XML Parse-tree (to translate) * @param[in] prepend Print this text in front of all commands. * @param[in] fn Callback to make print function - * @see xml2cli XXX should probably use the generic function */ -int -xml2cli(clicon_handle h, - FILE *f, - cxobj *xn, - char *prepend, - clicon_output_cb *fn) +static int +xml2cli1(clicon_handle h, + FILE *f, + cxobj *xn, + char *prepend, + clicon_output_cb *fn) { int retval = -1; cxobj *xe = NULL; @@ -1139,7 +1136,7 @@ xml2cli(clicon_handle h, if (match) continue; /* Not key itself */ } - if (xml2cli(h, f, xe, cbuf_get(cbpre), fn) < 0) + if (xml2cli1(h, f, xe, cbuf_get(cbpre), fn) < 0) goto done; } ok: @@ -1149,3 +1146,41 @@ xml2cli(clicon_handle h, cbuf_free(cbpre); return retval; } + +/*! Translate from XML to CLI commands + * + * Howto: join strings and pass them down. + * Identify unique/index keywords for correct set syntax. + * @param[in] h Clicon handle + * @param[in] f Output FILE (eg stdout) + * @param[in] xn XML Parse-tree (to translate) + * @param[in] prepend Print this text in front of all commands. + * @param[in] fn Callback to make print function + * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, + + */ +int +xml2cli(clicon_handle h, + FILE *f, + cxobj *xn, + char *prepend, + clicon_output_cb *fn, + int skiptop) +{ + int retval = 1; + cxobj *xc; + + if (skiptop){ + xc = NULL; + while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL) + if (xml2cli1(h, f, xc, prepend, fn) < 0) + goto done; + } + else { + if (xml2cli1(h, f, xn, prepend, fn) < 0) + goto done; + } + retval = 0; + done: + return retval; +} diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 87727f4e..58befa85 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -137,7 +137,7 @@ int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv, cvec *commands, cvec *helptexts); int cli_xml2file (cxobj *xn, int level, int prettyprint, clicon_output_cb *fn); int cli_xml2txt(cxobj *xn, clicon_output_cb *fn, int level); -int xml2cli(clicon_handle h, FILE *f, cxobj *xn, char *prepend, clicon_output_cb *fn); +int xml2cli(clicon_handle h, FILE *f, cxobj *xn, char *prepend, clicon_output_cb *fn, int skiptop); /* cli_show.c: CLIgen new vector arg callbacks */ int show_yang(clicon_handle h, cvec *vars, cvec *argv); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 6bc46d60..435a8f82 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -211,7 +211,7 @@ netconf_rpc_message(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - clicon_xml2cbuf(cbret, xret, 0, 0, -1); + clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0); if (netconf_output_encap(framing, cbret) < 0) goto done; if (netconf_output(1, cbret, "rpc-error") < 0) @@ -231,7 +231,7 @@ netconf_rpc_message(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - clicon_xml2cbuf(cbret, xret, 0, 0, -1); + clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0); if (netconf_output_encap(framing, cbret) < 0) goto done; if (netconf_output(1, cbret, "rpc-error") < 0) @@ -251,7 +251,7 @@ netconf_rpc_message(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - clicon_xml2cbuf(cbret, xret, 0, 0, -1); + clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0); if (netconf_output_encap(framing, cbret) < 0) goto done; if (netconf_output(1, cbret, "rpc-error") < 0) @@ -266,7 +266,7 @@ netconf_rpc_message(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0, -1); + clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0, -1, 0); if (netconf_output_encap(framing, cbret) < 0) goto done; if (netconf_output(1, cbret, "rpc-reply") < 0) @@ -322,7 +322,7 @@ netconf_input_packet(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - clicon_xml2cbuf(cbret, xret, 0, 0, -1); + clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0); if (netconf_output_encap(framing, cbret) < 0) goto done; if (netconf_output(1, cbret, "rpc-error") < 0) @@ -435,7 +435,7 @@ netconf_input_frame(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - clicon_xml2cbuf(cbret, xret, 0, 0, -1); + clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0); if (netconf_output_encap(framing, cbret) < 0) goto done; if (netconf_output(1, cbret, "rpc-error") < 0) diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index f789f2e5..95e6dcd7 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -481,7 +481,7 @@ netconf_notification_cb(int s, clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } - if (clicon_xml2cbuf(cb, xn, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xn, 0, 0, -1, 0) < 0) goto done; /* Send it to listening client on stdout */ if (netconf_output_encap(clicon_option_int(h, "netconf-framing"), cb) < 0){ @@ -659,7 +659,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, -1) < 0) + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1, 0) < 0) goto done; clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret)); goto ok; diff --git a/apps/restconf/restconf_err.c b/apps/restconf/restconf_err.c index 48878aa0..7cfe18c7 100644 --- a/apps/restconf/restconf_err.c +++ b/apps/restconf/restconf_err.c @@ -279,13 +279,13 @@ api_return_err(clicon_handle h, clicon_debug(1, "%s code:%d", __FUNCTION__, code); if (pretty){ cprintf(cb, " \n"); - if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0) + if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1, 0) < 0) goto done; cprintf(cb, " \r\n"); } else { cprintf(cb, ""); - if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0) + if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1, 0) < 0) goto done; cprintf(cb, "\r\n"); } @@ -295,14 +295,14 @@ api_return_err(clicon_handle h, clicon_debug(1, "%s code:%d", __FUNCTION__, code); if (pretty){ cprintf(cb, "{\n\"ietf-restconf:errors\" : "); - if (xml2json_cbuf(cb, xerr, pretty) < 0) + if (xml2json_cbuf(cb, xerr, pretty, 0) < 0) goto done; cprintf(cb, "\n}\r\n"); } else{ cprintf(cb, "{"); cprintf(cb, "\"ietf-restconf:errors\":"); - if (xml2json_cbuf(cb, xerr, pretty) < 0) + if (xml2json_cbuf(cb, xerr, pretty, 0) < 0) goto done; cprintf(cb, "}\r\n"); } diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index da5e3661..26d99d5d 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -537,7 +537,7 @@ api_data_write(clicon_handle h, cprintf(cbx, " autocommit=\"true\""); cprintf(cbx, ">"); cprintf(cbx, "none"); - if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0) goto done; cprintf(cbx, ""); clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); @@ -782,7 +782,7 @@ api_data_delete(clicon_handle h, cprintf(cbx, " autocommit=\"true\""); cprintf(cbx, ">"); cprintf(cbx, "none"); - if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 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 8761b8a0..e2c72112 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -225,11 +225,11 @@ 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, -1) < 0) /* Dont print top object? */ + if (clicon_xml2cbuf(cbx, xret, 0, pretty, -1, 0) < 0) /* Dont print top object? */ goto done; break; case YANG_DATA_JSON: - if (xml2json_cbuf(cbx, xret, pretty) < 0) + if (xml2json_cbuf(cbx, xret, pretty, 0) < 0) goto done; break; default: @@ -270,7 +270,7 @@ api_data_get2(clicon_handle h, if (namespace && xmlns_set(x, prefix, namespace) < 0) goto done; } - if (clicon_xml2cbuf(cbx, x, 0, pretty, -1) < 0) /* Dont print top object? */ + if (clicon_xml2cbuf(cbx, x, 0, pretty, -1, 0) < 0) /* Dont print top object? */ goto done; } break; @@ -535,7 +535,7 @@ api_data_pagination(clicon_handle h, if (xml_insert(xpr, xp, INS_LAST, NULL, NULL) < 0) goto done; } - if (clicon_xml2cbuf(cbx, xpr, 0, pretty, -1) < 0) /* Dont print top object? */ + if (clicon_xml2cbuf(cbx, xpr, 0, pretty, -1, 0) < 0) /* Dont print top object? */ goto done; break; case YANG_DATA_JSON: diff --git a/apps/restconf/restconf_methods_patch.c b/apps/restconf/restconf_methods_patch.c index f63b5fe1..f9519e34 100644 --- a/apps/restconf/restconf_methods_patch.c +++ b/apps/restconf/restconf_methods_patch.c @@ -119,7 +119,7 @@ yang_patch_xml2json_modified_cbuf(cxobj *x_simple_patch) if (json_simple_patch == NULL) return NULL; cb = cbuf_new(); - xml2json_cbuf(cb, x_simple_patch, 1); + xml2json_cbuf(cb, x_simple_patch, 0); // Insert a '[' after the first '{' to get the JSON to match what api_data_post/write() expect json_simple_patch_tmp = cbuf_get(cb); @@ -266,7 +266,7 @@ yang_patch_do_replace(clicon_handle h, } } // Convert the data to json - xml2json_cbuf(json_simple_patch, x_simple_patch, 1); + xml2json_cbuf(json_simple_patch, x_simple_patch, 0); // Send the POST request if (api_data_post(h, req, cbuf_get(simple_patch_request_uri), pi, qvec, cbuf_get(json_simple_patch), pretty, YANG_DATA_JSON, media_out, ds ) < 0) @@ -327,7 +327,7 @@ yang_patch_do_create(clicon_handle h, xml_addsub(x_simple_patch, value_vec_tmp); } } - if (xml2json_cbuf(cb, x_simple_patch, 1) < 0) + if (xml2json_cbuf(cb, x_simple_patch, 0) < 0) goto done; if (api_data_post(h, req, cbuf_get(simple_patch_request_uri), pi, qvec, @@ -477,7 +477,7 @@ yang_patch_do_merge(clicon_handle h, xml_addsub(x_simple_patch, value_vec_tmp); } cbuf_reset(cb); /* reuse cb */ - xml2json_cbuf(cb, x_simple_patch, 1); + xml2json_cbuf(cb, x_simple_patch, 0); if ((json_simple_patch = yang_patch_xml2json_modified_cbuf(x_simple_patch)) == NULL) goto done; diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index f3c55023..3992f5c5 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -360,7 +360,7 @@ api_data_post(clicon_handle h, cprintf(cbx, " autocommit=\"true\""); cprintf(cbx, ">"); cprintf(cbx, "none"); - if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0) goto done; cprintf(cbx, ""); clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); @@ -874,12 +874,12 @@ api_operations_post(clicon_handle h, cbuf_reset(cbret); switch (media_out){ case YANG_DATA_XML: - if (clicon_xml2cbuf(cbret, xoutput, 0, pretty, -1) < 0) + if (clicon_xml2cbuf(cbret, xoutput, 0, pretty, -1, 0) < 0) goto done; /* xoutput should now look: 0 */ break; case YANG_DATA_JSON: - if (xml2json_cbuf(cbret, xoutput, pretty) < 0) + if (xml2json_cbuf(cbret, xoutput, pretty, 0) < 0) goto done; /* xoutput should now look: {"example:output": {"x":0,"y":42}} */ break; diff --git a/apps/restconf/restconf_root.c b/apps/restconf/restconf_root.c index 7b25577b..27221f68 100644 --- a/apps/restconf/restconf_root.c +++ b/apps/restconf/restconf_root.c @@ -204,12 +204,12 @@ api_root_restconf_exact(clicon_handle h, switch (media_out){ case YANG_DATA_XML: case YANG_PATCH_XML: - if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0) + if (clicon_xml2cbuf(cb, xt, 0, pretty, -1, 0) < 0) goto done; break; case YANG_DATA_JSON: case YANG_PATCH_JSON: - if (xml2json_cbuf(cb, xt, pretty) < 0) + if (xml2json_cbuf(cb, xt, pretty, 0) < 0) goto done; break; default: @@ -288,12 +288,12 @@ api_yang_library_version(clicon_handle h, switch (media_out){ case YANG_DATA_XML: case YANG_PATCH_XML: - if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0) + if (clicon_xml2cbuf(cb, xt, 0, pretty, -1, 0) < 0) goto done; break; case YANG_DATA_JSON: case YANG_PATCH_JSON: - if (xml2json_cbuf(cb, xt, pretty) < 0) + if (xml2json_cbuf(cb, xt, pretty, 0) < 0) goto done; break; default: diff --git a/apps/restconf/restconf_stream_fcgi.c b/apps/restconf/restconf_stream_fcgi.c index 1b14367a..218edb71 100644 --- a/apps/restconf/restconf_stream_fcgi.c +++ b/apps/restconf/restconf_stream_fcgi.c @@ -254,7 +254,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, -1) < 0) + if (clicon_xml2cbuf(cb, xn, 0, pretty, -1, 0) < 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 d4c234f4..12f11e2f 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -326,12 +326,14 @@ example_rpc(clicon_handle h, /* Clicon handle */ cprintf(cbret, ">"); if (!xml_child_nr_type(xe, CX_ELMNT)) cprintf(cbret, ""); - else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) { + 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, -1) < 0) - goto done; } + if (clicon_xml2cbuf(cbret, xe, 0, 0, -1, 1) < 0) + goto done; + } cprintf(cbret, ""); retval = 0; done: diff --git a/example/main/example_netconf.c b/example/main/example_netconf.c index 74c2c115..1daf10c5 100644 --- a/example/main/example_netconf.c +++ b/example/main/example_netconf.c @@ -85,12 +85,14 @@ netconf_client_rpc(clicon_handle h, cprintf(cbret, "", NETCONF_BASE_NAMESPACE); if (!xml_child_nr_type(xe, CX_ELMNT)) cprintf(cbret, ""); - else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) { + 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, -1) < 0) - goto done; } + if (clicon_xml2cbuf(cbret, xe, 0, 0, -1, 1) < 0) + goto done; + } cprintf(cbret, ""); retval = 0; done: diff --git a/example/main/example_restconf.c b/example/main/example_restconf.c index 77ecc3f0..83039c6c 100644 --- a/example/main/example_restconf.c +++ b/example/main/example_restconf.c @@ -325,12 +325,14 @@ restconf_client_rpc(clicon_handle h, cprintf(cbret, "", NETCONF_BASE_NAMESPACE); if (!xml_child_nr_type(xe, CX_ELMNT)) cprintf(cbret, ""); - else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) { + 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, -1) < 0) - goto done; } + if (clicon_xml2cbuf(cbret, xe, 0, 0, -1, 1) < 0) + goto done; + } cprintf(cbret, ""); retval = 0; done: diff --git a/lib/clixon/clixon_json.h b/lib/clixon/clixon_json.h index 5ff09077..2f84fa15 100644 --- a/lib/clixon/clixon_json.h +++ b/lib/clixon/clixon_json.h @@ -44,10 +44,9 @@ * Prototypes */ int json2xml_decode(cxobj *x, cxobj **xerr); -int xml2json_cbuf(cbuf *cb, cxobj *x, int pretty); +int xml2json_cbuf(cbuf *cb, cxobj *x, int pretty, int skiptop); int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty); -int xml2json(FILE *f, cxobj *x, int pretty); -int xml2json_cb(FILE *f, cxobj *x, int pretty, clicon_output_cb *fn); +int xml2json_file(FILE *f, cxobj *x, int pretty, clicon_output_cb *fn, int skiptop); int json_print(FILE *f, cxobj *x); int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, int pretty); int clixon_json_parse_string(char *str, int rfc7951, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); diff --git a/lib/clixon/clixon_xml_io.h b/lib/clixon/clixon_xml_io.h index af1d0a43..af863bab 100644 --- a/lib/clixon/clixon_xml_io.h +++ b/lib/clixon/clixon_xml_io.h @@ -47,7 +47,7 @@ int clicon_xml2file_cb(FILE *f, cxobj *x, int level, int prettyprint, clicon_out int clicon_xml2file(FILE *f, cxobj *x, int level, int prettyprint); int xml_print(FILE *f, cxobj *xn); int xml_dump(FILE *f, cxobj *x); -int clicon_xml2cbuf(cbuf *cb, cxobj *x, int level, int prettyprint, int32_t depth); +int clicon_xml2cbuf(cbuf *cb, cxobj *x, int level, int prettyprint, int32_t depth, int skiptop); char *clicon_xml2str(cxobj *x); int xmltree2cbuf(cbuf *cb, cxobj *x, int level); diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 56f0de7f..03e08c59 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -1206,7 +1206,7 @@ xmldb_put(clicon_handle h, } pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY"); if (strcmp(format,"json")==0){ - if (xml2json(f, x0, pretty) < 0) + if (xml2json_file(f, x0, pretty, fprintf, 0) < 0) goto done; } else if (clicon_xml2file(f, x0, 0, pretty) < 0) @@ -1264,7 +1264,7 @@ xmldb_dump(clicon_handle h, } pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY"); if (strcmp(format,"json")==0){ - if (xml2json(f, xt, pretty) < 0) + if (xml2json_file(f, xt, pretty, fprintf, 0) < 0) goto done; } else if (clicon_xml2file(f, xt, 0, pretty) < 0) diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index d394a9ab..e4a4f4f9 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -1043,24 +1043,23 @@ xml2json1_cbuf(cbuf *cb, * @param[in,out] cb Cligen buffer to write to * @param[in] x XML tree to translate from * @param[in] pretty Set if output is pretty-printed - * @param[in] top By default only children are printed, set if include top * @retval 0 OK * @retval -1 Error * * @code * cbuf *cb; * cb = cbuf_new(); - * if (xml2json_cbuf(cb, xn, 0, 1) < 0) + * if (xml2json_cbuf(cb, xn, 0, 0) < 0) * goto err; * cbuf_free(cb); * @endcode * @see clicon_xml2cbuf XML corresponding function * @see xml2json_cbuf_vec Top symbol is list */ -int -xml2json_cbuf(cbuf *cb, - cxobj *x, - int pretty) +static int +xml2json_cbuf1(cbuf *cb, + cxobj *x, + int pretty) { int retval = 1; int level = 0; @@ -1100,6 +1099,44 @@ xml2json_cbuf(cbuf *cb, return retval; } +/*! Translate an XML tree to JSON in a CLIgen buffer skip top-level object + * + * XML-style namespace notation in tree, but RFC7951 in output assume yang + * populated + * + * @param[in,out] cb Cligen buffer to write to + * @param[in] xt Top-level xml object + * @param[in] pretty Set if output is pretty-printed + * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, + * @retval 0 OK + * @retval -1 Error + * @see xml2json_cbuf where the top level object is included + */ +int +xml2json_cbuf(cbuf *cb, + cxobj *xt, + int pretty, + int skiptop) +{ + int retval = -1; + cxobj *xc; + + if (skiptop){ + xc = NULL; + while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) + if (xml2json_cbuf1(cb, xc, pretty) < 0) + goto done; + + } + else { + if (xml2json_cbuf1(cb, xt, pretty) < 0) + goto done; + } + retval = 0; + done: + return retval; +} + /*! Translate a vector of xml objects to JSON Cligen buffer. * This is done by adding a top pseudo-object, and add the vector as subs, * and then not printing the top pseudo-object using the 'flat' option. @@ -1166,24 +1203,26 @@ xml2json_cbuf_vec(cbuf *cb, } /*! Translate from xml tree to JSON and print to file using a callback - * @param[in] f File to print to - * @param[in] x XML tree to translate from - * @param[in] pretty Set if output is pretty-printed - * @retval 0 OK - * @retval -1 Error + * @param[in] f File to print to + * @param[in] x XML tree to translate from + * @param[in] pretty Set if output is pretty-printed + * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, + * @retval 0 OK + * @retval -1 Error * * @note yang is necessary to translate to one-member lists, * eg if a is a yang LIST 0 -> {"a":["0"]} and not {"a":"0"} * @code - * if (xml2json(stderr, xn, 0) < 0) + * if (xml2json_file(stderr, xn, 0, fprintf, 0) < 0) * goto err; * @endcode */ int -xml2json_cb(FILE *f, - cxobj *x, - int pretty, - clicon_output_cb *fn) +xml2json_file(FILE *f, + cxobj *x, + int pretty, + clicon_output_cb *fn, + int skiptop) { int retval = 1; cbuf *cb = NULL; @@ -1192,7 +1231,7 @@ xml2json_cb(FILE *f, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (xml2json_cbuf(cb, x, pretty) < 0) + if (xml2json_cbuf(cb, x, pretty, skiptop) < 0) goto done; (*fn)(f, "%s", cbuf_get(cb)); retval = 0; @@ -1202,29 +1241,6 @@ xml2json_cb(FILE *f, return retval; } -/*! Translate from xml tree to JSON and print to file - * @param[in] f File to print to - * @param[in] x XML tree to translate from - * @param[in] pretty Set if output is pretty-printed - * @retval 0 OK - * @retval -1 Error - * - * @note yang is necessary to translate to one-member lists, - * eg if a is a yang LIST 0 -> {"a":["0"]} and not {"a":"0"} - * @code - * if (xml2json(stderr, xn, 0) < 0) - * goto err; - * @endcode - */ -int -xml2json(FILE *f, - cxobj *x, - int pretty) -{ - return xml2json_cb(f, x, pretty, fprintf); -} - - /*! Print an XML tree structure to an output stream as JSON * * @param[in] f UNIX output stream @@ -1234,7 +1250,7 @@ int json_print(FILE *f, cxobj *x) { - return xml2json_cb(f, x, 1, fprintf); + return xml2json_file(f, x, 1, fprintf, 0); } /*! Translate a vector of xml objects to JSON File. diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 0fef5874..4924cacb 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -186,7 +186,7 @@ netconf_invalid_value(cbuf *cb, if (netconf_invalid_value_xml(&xret, type, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -309,7 +309,7 @@ netconf_missing_attribute(cbuf *cb, if (netconf_missing_attribute_xml(&xret, type, attr, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -338,7 +338,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, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -521,7 +521,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, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -567,7 +567,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, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -605,7 +605,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, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -652,7 +652,7 @@ netconf_unknown_namespace(cbuf *cb, if (netconf_common_xml(&xret, type, "unknown-namespace", "bad-namespace", ns, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -690,7 +690,7 @@ netconf_access_denied(cbuf *cb, if (netconf_access_denied_xml(&xret, type, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -936,7 +936,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, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -1085,7 +1085,7 @@ netconf_operation_not_supported(cbuf *cb, if (netconf_operation_not_supported_xml(&xret, type, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -1113,7 +1113,7 @@ netconf_operation_failed(cbuf *cb, if (netconf_operation_failed_xml(&xret, type, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -1201,7 +1201,7 @@ netconf_malformed_message(cbuf *cb, if (netconf_malformed_message_xml(&xret, message) < 0) goto done; - if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0) goto done; retval = 0; done: @@ -1606,7 +1606,7 @@ netconf_err2cb(cxobj *xerr, cprintf(cberr, "%s ", xml_body(x)); if ((x=xpath_first(xerr, NULL, "//error-info")) != NULL && xml_child_nr(x) > 0){ - clicon_xml2cbuf(cberr, xml_child_i(x, 0), 0, 0, -1); + clicon_xml2cbuf(cberr, xml_child_i(x, 0), 0, 0, -1, 0); } if ((x=xpath_first(xerr, NULL, "//error-app-tag"))!=NULL) cprintf(cberr, ": %s ", xml_body(x)); diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index f1412569..4408b360 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -763,7 +763,7 @@ send_msg_notify_xml(clicon_handle h, clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } - if (clicon_xml2cbuf(cb, xev, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xev, 0, 0, -1, 0) < 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 5a37d44f..f04eff6d 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -420,7 +420,7 @@ clicon_rpc_netconf_xml(clicon_handle h, goto done; } rpcname = xml_name(xname); /* Store rpc name and use in yang binding after reply */ - if (clicon_xml2cbuf(cb, xml, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xml, 0, 0, -1, 0) < 0) goto done; if (clicon_rpc_netconf(h, cbuf_get(cb), xret, sp) < 0) goto done; diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c index 272bdb1f..70826e00 100644 --- a/lib/src/clixon_stream.c +++ b/lib/src/clixon_stream.c @@ -990,7 +990,7 @@ stream_publish_cb(clicon_handle h, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (clicon_xml2cbuf(d, event, 0, 0, -1) < 0) + if (clicon_xml2cbuf(d, event, 0, 0, -1, 0) < 0) goto done; if (url_post(cbuf_get(u), /* url+stream */ cbuf_get(d), /* postfields */ diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index b0a1ecc0..43dddd57 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -1680,7 +1680,7 @@ rpc_reply_check(clicon_handle h, if (ret == 0){ clicon_debug(1, "%s failure when validating:%s", __FUNCTION__, cbuf_get(cbret)); cbuf_reset(cbret); - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto fail; } @@ -1689,7 +1689,7 @@ rpc_reply_check(clicon_handle h, if (ret == 0){ clicon_debug(1, "%s failure when validating:%s", __FUNCTION__, cbuf_get(cbret)); cbuf_reset(cbret); - if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) goto done; goto fail; } diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 61b4f255..d27c9d65 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -2350,7 +2350,7 @@ clicon_log_xml(int level, clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (clicon_xml2cbuf(cb, x, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, x, 0, 0, -1, 0) < 0) goto done; /* first round: compute length of debug message */ diff --git a/lib/src/clixon_xml_io.c b/lib/src/clixon_xml_io.c index 70934b02..7944a099 100644 --- a/lib/src/clixon_xml_io.c +++ b/lib/src/clixon_xml_io.c @@ -288,30 +288,20 @@ xml_dump(FILE *f, return xml_dump1(f, x, 0); } -/*! Print an XML tree structure to a cligen buffer and encode chars "<>&" +/*! Internal: print XML tree structure to a cligen buffer and encode chars "<>&" * * @param[in,out] cb Cligen buffer to write to * @param[in] xn Clicon xml tree * @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, -1) < 0) - * goto err; - * fprintf(stderr, "%s", cbuf_get(cb)); - * cbuf_free(cb); - * @endcode - * @see clicon_xml2file */ -int -clicon_xml2cbuf(cbuf *cb, - cxobj *x, - int level, - int prettyprint, - int32_t depth) +static int +clicon_xml2cbuf1(cbuf *cb, + cxobj *x, + int level, + int prettyprint, + int32_t depth) { int retval = -1; cxobj *xc; @@ -357,7 +347,7 @@ clicon_xml2cbuf(cbuf *cb, while ((xc = xml_child_each(x, xc, -1)) != NULL) switch (xml_type(xc)){ case CX_ATTR: - if (clicon_xml2cbuf(cb, xc, level+1, prettyprint, -1) < 0) + if (clicon_xml2cbuf1(cb, xc, level+1, prettyprint, -1) < 0) goto done; break; case CX_BODY: @@ -379,7 +369,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, depth-1) < 0) + if (clicon_xml2cbuf1(cb, xc, level+1, prettyprint, depth-1) < 0) goto done; if (prettyprint && hasbody == 0) cprintf(cb, "%*s", level*XML_INDENT, ""); @@ -403,6 +393,53 @@ clicon_xml2cbuf(cbuf *cb, return retval; } +/*! Print an XML tree structure to a cligen buffer and encode chars "<>&" + * + * @param[in,out] cb Cligen buffer to write to + * @param[in] xt Top-level xml object + * @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: all, 0: none, 1: node itself + * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, + * @retval 0 OK + * @retval -1 Error + * + * @code + * cbuf *cb; + * cb = cbuf_new(); + * if (clicon_xml2cbuf(cb, xn, 0, 1, -1, 0) < 0) + * goto err; + * fprintf(stderr, "%s", cbuf_get(cb)); + * cbuf_free(cb); + * @endcode + * @see clicon_xml2file + */ +int +clicon_xml2cbuf(cbuf *cb, + cxobj *xt, + int level, + int pretty, + int32_t depth, + int skiptop) +{ + int retval = -1; + cxobj *xc; + + if (skiptop){ + xc = NULL; + while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) + if (clicon_xml2cbuf1(cb, xc, level, pretty, depth) < 0) + goto done; + } + else { + if (clicon_xml2cbuf1(cb, xt, level, pretty, depth) < 0) + goto done; + } + retval = 0; + done: + return retval; +} + /*! Return an xml tree as a pretty-printed malloced string. * @param[in] x XML tree * @retval str Malloced pretty-printed string (should be free:d after use) @@ -418,7 +455,7 @@ clicon_xml2str(cxobj *x) clicon_err(OE_XML, errno, "cbuf_new"); return NULL; } - if (clicon_xml2cbuf(cb, x, 0, 1, -1) < 0) + if (clicon_xml2cbuf(cb, x, 0, 1, -1, 0) < 0) return NULL; if ((str = strdup(cbuf_get(cb))) == NULL){ clicon_err(OE_XML, errno, "strdup"); diff --git a/test/test_c++.sh b/test/test_c++.sh index 59698821..fd84e3d1 100755 --- a/test/test_c++.sh +++ b/test/test_c++.sh @@ -140,14 +140,14 @@ int example_rpc(clicon_handle h, cprintf(cbret, "", NETCONF_BASE_NAMESPACE); if (!xml_child_nr_type(xe, CX_ELMNT)) cprintf(cbret, ""); - else - while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) - { + else{ + while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) { if (xmlns_set(x, NULL, ns) < 0) goto done; - if (clicon_xml2cbuf(cbret, x, 0, 0, -1) < 0) - goto done; } + if (clicon_xml2cbuf(cbret, xe, 0, 0, -1, 1) < 0) + goto done; + } cprintf(cbret, ""); retval = 0; done: diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index aae656bc..3fdff966 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -88,7 +88,6 @@ main(int argc, { int retval = -1; cxobj *xt = NULL; - cxobj *xc; cbuf *cb = cbuf_new(); int c; int logdst = CLICON_LOG_STDERR; @@ -145,20 +144,10 @@ main(int argc, xml_print(stderr, xerr); goto done; } -#if 0 if (json) - xml2json_cbuf(cb, xt, pretty); /* print json */ + xml2json_cbuf(cb, xt, pretty, 1); /* print json */ else - clicon_xml2cbuf(cb, xt, 0, pretty, -1); /* print xml */ -#else - xc = NULL; - /* XXX This is troublesome for JSON top-level lists */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - if (json) - xml2json_cbuf(cb, xc, pretty); /* print json */ - else - clicon_xml2cbuf(cb, xc, 0, pretty, -1); /* print xml */ -#endif + clicon_xml2cbuf(cb, xt, 0, pretty, -1, 1); /* print xml */ fprintf(stdout, "%s", cbuf_get(cb)); fflush(stdout); retval = 0; diff --git a/util/clixon_util_socket.c b/util/clixon_util_socket.c index c6d027a7..27ec2e01 100644 --- a/util/clixon_util_socket.c +++ b/util/clixon_util_socket.c @@ -167,7 +167,7 @@ main(int argc, fprintf(stderr, "No xml\n"); goto done; } - if (clicon_xml2cbuf(cb, xc, 0, 0, -1) < 0) + if (clicon_xml2cbuf(cb, xc, 0, 0, -1, 0) < 0) goto done; if ((msg = clicon_msg_encode(getpid(), "%s", cbuf_get(cb))) < 0) goto done; diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index 8845f86d..cd57a4e0 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -139,7 +139,6 @@ main(int argc, int retval = -1; int ret; cxobj *xt = NULL; /* Base cxobj tree parsed from xml or json */ - cxobj *xc; cbuf *cb = cbuf_new(); int c; int logdst = CLICON_LOG_STDERR; @@ -337,21 +336,10 @@ main(int argc, } /* 4. Output data (xml/json) */ if (output){ -#if 0 if (jsonout) - xml2json_cbuf(cb, xt, pretty); /* print json */ + xml2json_cbuf(cb, xt, pretty, 1); /* print json */ else - clicon_xml2cbuf(cb, xt, 0, pretty, -1); /* print xml */ -#else - xc = NULL; - /* XXX This is troublesome for JSON top-level lists */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL){ - if (jsonout) - xml2json_cbuf(cb, xc, pretty); /* print json */ - else - clicon_xml2cbuf(cb, xc, 0, pretty, -1); /* print xml */ - } -#endif + clicon_xml2cbuf(cb, xt, 0, pretty, -1, 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 04a0fb63..2580d431 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -98,7 +98,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, -1); + clicon_xml2cbuf(cb, xc->xc_nodeset[i], 0, 0, -1, 0); } break; case XT_BOOL: diff --git a/yang/clixon/clixon-autocli@2022-05-30.yang b/yang/clixon/clixon-autocli@2022-05-30.yang deleted file mode 100644 index 4bfb9f34..00000000 --- a/yang/clixon/clixon-autocli@2022-05-30.yang +++ /dev/null @@ -1,252 +0,0 @@ -module clixon-autocli{ - yang-version 1.1; - namespace "http://clicon.org/autocli"; - prefix autocli; - - organization - "Clicon / Clixon"; - - contact - "Olof Hagsand "; - - description - "Clixon CLIgen specification declarations, including autocli. - Design inspired by ietf-netconf-acm.yang - - ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate) - - This file is part of CLIXON - - Licensed under the Apache License, Version 2.0 (the \"License\"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Alternatively, the contents of this file may be used under the terms of - the GNU General Public License Version 3 or later (the \"GPL\"), - in which case the provisions of the GPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of the GPL, and not to allow others to - use your version of this file under the terms of Apache License version 2, - indicate your decision by deleting the provisions above and replace them with - the notice and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the Apache License version 2 or the GPL. - - ***** END LICENSE BLOCK *****"; - - revision 2022-05-30 { - description - " - Released in Clixon 5.8"; - } - revision 2022-02-11 { - description - "Initial version - Released in Clixon 5.6"; - } - revision 2021-12-05 { - description - "Initial version - Released in Clixon 5.5"; - } - extension hide { - description - "Modify the autocli by hiding the command associated with a YANG node and its - sub-commands. - The command is active but not shown by ? or TAB. In other words, it hides the - auto-completion of commands"; - } - extension hide-show { - description - "Modify the autocli by hiding the command associated with a YANG node and its - sub-commands in CLI show commands."; - } - extension strict-expand { - description - "Modify the autocli by only showing exactly the expanded values of a variable. - It should not be possible to add a new value that is not in the expanded list."; - } - typedef autocli-op { - description - "Autocli rule-type operation, each rule use different fields as - described in the individual enums below."; - type enumeration { - enum enable { - description - "Include a complete subtree to rendering of autocli. - Example: - false - - wifi - enable - openconfig-wifi - - Only on module-level and if module-default is false, - Rule fields used: module-name"; - } - enum compress { - description - "Skip a keyword from a command. - Keep the command, only make it shorter by omitting a part. - Example: compress containers if single list child - - container compress - compress - container - list - - Rule fields used: - module-name, yang-keyword, schema-nodeid, yang-keyword-child, extension"; - } - enum edit-mode { - description - "Autocli CLI edit modes for YANG symbols. - For example, - edit interface eth0 - enters a new mode with local context."; - } - } - } - typedef list-keyword-type { - description - "Autocli CLI keyword behaviour in YANG lists. - With 'keyword' is meant CLIgen 'constants' rather than 'variables'. - Assume a YANG LIST: list a{ key x; leaf x; leaf y;} and how to generate - the autocli"; - type enumeration { - enum kw-none{ - description "No extra keywords, only variables: a "; - } - enum kw-nokey{ - description "Keywords on non-key variables: a y "; - } - enum kw-all{ - description "Keywords on all variables: a x y "; - } - } - } - typedef yang-keywords { - type bits { - bit list; - bit listall{ /* NYI */ - description - "Variant of list encompassing all list entries, not just an instance"; - } - bit container; - bit leaf; /* Also leaf-list (NYI) */ - } - } - grouping clixon-autocli{ - /* options */ - leaf module-default { - description - "Include YANG modules for generation of autocli. - If true, all modules with a top-level datanode are generated, ie - they get a top-level entry in the @basemodel tree. - If false, you need to explicitly enable modules for autocli generation - using 'enable' rules"; - type boolean; - default true; - } - leaf list-keyword-default { - description - "Autocli CLI keyword behaviour in YANG lists."; - type list-keyword-type; - default kw-nokey; - } - leaf treeref-state-default { - description - "If 'true', generate CLI from YANG state/non-config statements as well, not only config data. - Many specs have very large state parts, for example openconfig has ca 10 times - larger state than config parts, see for example openconfig-isis.yang."; - type boolean; - default false; - } - leaf edit-mode-default { - description - "Open automatic edit-modes for some YANG keywords and do not allow others. - A CLI edit mode opens a carriage-return option and changes the context to be - in that local context. - For example: - cli> interfaces interface e0 - eth0> - Default is to generate edit-modes for all containers and lists."; - type yang-keywords; - default "list container"; - } - leaf completion-default { - description - "Generate code for CLI completion of existing db symbols. - That is, check existing configure database for completion options. - This is normally always enabled."; - type boolean; - default true; - } - /* rules */ - list rule { - description - "Represents a modification rule of a clixon clispec."; - key name; - leaf name { - description - "Arbitrary name assigned for the rule, must be unique"; - type string; - } - leaf description { - description - "Rule description"; - type string; - } - leaf operation { - description "Rule operation"; - type autocli-op; - } - leaf module-name { - description - "Name of the module associated with this rule. - Wildchars '*' and '?' can be used (glob pattern). - Revision and yang suffix are omitted - Example: 'openconfig-*'"; - type string; - } - leaf yang-keyword { - description - "If present identifes a YANG keyword which the rule applies to - Example: 'container' - "; - type string; - } - leaf schema-nodeid { - description - "path in the form of // or just a single identifying a YANG - schema-node identifier as defined in RFC 7950 Sec 6.5 - Example: 'config', '/interfaces/interface'"; - type string; - } - leaf yang-keyword-child { - description - "The YANG statement has a single child, and the yang type of the child is the - value of this option - A (maybe too) specific property to cover openconfig compressions - as defined here: - https://github.com/openconfig/ygot/blob/master/docs/design.md#openconfig-path-compression"; - type string; - } - leaf extension { - /* Consider making this a container with name/module/value instead */ - description - "The extension is set either in the node itself, or in this module - Extension prefix must be set - Example: oc-ext:openconfig-version"; - type string; - } - } - } -}