From 69b65ad13d831a62e6e67c596c55fdb93ace642a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 16 Oct 2024 13:46:47 +0200 Subject: [PATCH] System-only-config * Fix startup diff * Add system-only to running on startup * JSON support: --- CHANGELOG.md | 5 +- apps/backend/backend_commit.c | 12 +- apps/cli/cli_common.c | 4 +- apps/cli/cli_pipe.c | 2 +- apps/cli/cli_show.c | 2 +- apps/restconf/restconf_err.c | 4 +- apps/restconf/restconf_methods_get.c | 2 +- apps/restconf/restconf_methods_patch.c | 8 +- apps/restconf/restconf_methods_post.c | 2 +- apps/restconf/restconf_root.c | 4 +- lib/clixon/clixon_json.h | 4 +- lib/src/clixon_datastore_read.c | 2 +- lib/src/clixon_datastore_write.c | 6 +- lib/src/clixon_json.c | 106 +++++++------ lib/src/clixon_options.c | 2 +- test/config.sh.in | 2 +- test/test_datastore_system_only.sh | 197 ++++++++++++++++++++++--- yang/clixon/Makefile.in | 2 +- yang/clixon/clixon-lib@2024-08-01.yang | 15 +- 19 files changed, 276 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 740a3c60..350bc6aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,9 +24,12 @@ Expected: January 2025 * New `CLICON_XMLDB_SYSTEM_ONLY_CONFIG` configuration option * New `system-only-config` extension * New `ca_system_only` backend callback for reading system-only data + * Changed C-API: add `system-only` parameter with default value `0` last: + * `clixon_json2file()` -> `clixon_json2file(,0)` + * `clixon_json2cbuf()` -> `clixon_json2cbuf(,0)` ### Corrected Bugs - + * Fixed: [string length validation doesn't work for the entry "" in case it has default value specified](https://github.com/clicon/clixon/issues/563) * Fixed: [SNMP: snmpwalk is slow and can timeout](https://github.com/clicon/clixon/issues/404) diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 913c76cc..87704db8 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -172,13 +172,22 @@ startup_common(clixon_handle h, cxobj *xret = NULL; cxobj *xerr = NULL; + clixon_debug(CLIXON_DBG_BACKEND, "Reading initial config from %s", db); /* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with * potentially non-matching module-state in msdiff */ if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) if ((msdiff = modstate_diff_new()) == NULL) goto done; - clixon_debug(CLIXON_DBG_BACKEND, "Reading initial config from %s", db); + /* Add system-only config to running */ + if (clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG")){ + if ((ret = xmldb_get0(h, "running", YB_MODULE, NULL, "/", 0, 0, &xt, NULL, NULL)) < 0) + goto done; + if (xmldb_system_only_config(h, "/", NULL, &xt) < 0) + goto done; + td->td_src = xt; + xt = NULL; + } if (clicon_option_bool(h, "CLICON_XMLDB_UPGRADE_CHECKOLD")){ if ((ret = xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, 0, &xt, msdiff, &xerr)) < 0) goto done; @@ -659,6 +668,7 @@ candidate_validate(clixon_handle h, * @retval 1 Validation OK * @retval 0 Validation failed (with cbret set) * @retval -1 Error - or validation failed + * @see startup_commit for commit on startup */ int candidate_commit(clixon_handle h, diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index f2293b26..5f0ddcca 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -1461,7 +1461,7 @@ save_config_file(clixon_handle h, goto done; break; case FORMAT_JSON: - if (clixon_json2file(f, xt, pretty, fprintf, 0, 1) < 0) + if (clixon_json2file(f, xt, pretty, fprintf, 0, 1, 0) < 0) goto done; break; case FORMAT_TEXT: @@ -1587,7 +1587,7 @@ cli_notification_cb(int s, goto done; switch (format){ case FORMAT_JSON: - if (clixon_json2file(stdout, xt, 1, cligen_output, 1, 1) < 0) + if (clixon_json2file(stdout, xt, 1, cligen_output, 1, 1, 0) < 0) goto done; case FORMAT_TEXT: if (clixon_text2file(stdout, xt, 0, cligen_output, 1, 1) < 0) diff --git a/apps/cli/cli_pipe.c b/apps/cli/cli_pipe.c index f2759027..7e35b812 100644 --- a/apps/cli/cli_pipe.c +++ b/apps/cli/cli_pipe.c @@ -318,7 +318,7 @@ pipe_showas_fn(clixon_handle h, goto done; break; case FORMAT_JSON: - if (clixon_json2file(stdout, xt, pretty, cligen_output, 1, 0) < 0) + if (clixon_json2file(stdout, xt, pretty, cligen_output, 1, 0, 0) < 0) goto done; break; case FORMAT_TEXT: diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 10a2df81..2dd5ba97 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -1434,7 +1434,7 @@ cli_pagination(clixon_handle h, goto done; break; case FORMAT_JSON: - if (clixon_json2file(stdout, xc, 1, cligen_output, 0, 1) < 0) + if (clixon_json2file(stdout, xc, 1, cligen_output, 0, 1, 0) < 0) goto done; break; case FORMAT_TEXT: diff --git a/apps/restconf/restconf_err.c b/apps/restconf/restconf_err.c index 28f55196..e7b9371f 100644 --- a/apps/restconf/restconf_err.c +++ b/apps/restconf/restconf_err.c @@ -308,14 +308,14 @@ api_return_err(clixon_handle h, clixon_debug(CLIXON_DBG_RESTCONF, "code:%d", code); if (pretty){ cprintf(cb, "{\n\"ietf-restconf:errors\" : "); - if (clixon_json2cbuf(cb, xerr, pretty, 0, 0) < 0) + if (clixon_json2cbuf(cb, xerr, pretty, 0, 0, 0) < 0) goto done; cprintf(cb, "\n}\r\n"); } else{ cprintf(cb, "{"); cprintf(cb, "\"ietf-restconf:errors\":"); - if (clixon_json2cbuf(cb, xerr, pretty, 0, 0) < 0) + if (clixon_json2cbuf(cb, xerr, pretty, 0, 0, 0) < 0) goto done; cprintf(cb, "}\r\n"); } diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 1027bb4c..6e00d0ec 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -237,7 +237,7 @@ api_data_get2(clixon_handle h, goto done; break; case YANG_DATA_JSON: - if (clixon_json2cbuf(cbx, xret, pretty, 0, 0) < 0) + if (clixon_json2cbuf(cbx, xret, pretty, 0, 0, 0) < 0) goto done; break; default: diff --git a/apps/restconf/restconf_methods_patch.c b/apps/restconf/restconf_methods_patch.c index 8bbf552d..90ef027d 100644 --- a/apps/restconf/restconf_methods_patch.c +++ b/apps/restconf/restconf_methods_patch.c @@ -120,7 +120,7 @@ yang_patch_xml2json_modified_cbuf(cxobj *x_simple_patch) if (json_simple_patch == NULL) return NULL; cb = cbuf_new(); - if (clixon_json2cbuf(cb, x_simple_patch, 0, 0) < 0) + if (clixon_json2cbuf(cb, x_simple_patch, 0, 0, 0) < 0) goto done; // Insert a '[' after the first '{' to get the JSON to match what api_data_post/write() expect @@ -271,7 +271,7 @@ yang_patch_do_replace(clixon_handle h, } } // Convert the data to json - if (clixon_json2cbuf(json_simple_patch, x_simple_patch, 0, 0) < 0) + if (clixon_json2cbuf(json_simple_patch, x_simple_patch, 0, 0, 0) < 0) goto done; // Send the POST request @@ -336,7 +336,7 @@ yang_patch_do_create(clixon_handle h, xml_addsub(x_simple_patch, value_vec_tmp); } } - if (clixon_json2cbuf(cb, x_simple_patch, 0, 0) < 0) + if (clixon_json2cbuf(cb, x_simple_patch, 0, 0, 0) < 0) goto done; if (api_data_post(h, req, cbuf_get(simple_patch_request_uri), pi, qvec, @@ -492,7 +492,7 @@ yang_patch_do_merge(clixon_handle h, xml_addsub(x_simple_patch, value_vec_tmp); } cbuf_reset(cb); /* reuse cb */ - if (clixon_json2cbuf(cb, x_simple_patch, 0, 0) < 0) + if (clixon_json2cbuf(cb, x_simple_patch, 0, 0, 0) < 0) goto done; if ((json_simple_patch = yang_patch_xml2json_modified_cbuf(x_simple_patch)) == NULL) diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index 6a5ee2de..90737864 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -884,7 +884,7 @@ api_operations_post(clixon_handle h, /* xoutput should now look: 0 */ break; case YANG_DATA_JSON: - if (clixon_json2cbuf(cbret, xoutput, pretty, 0, 0) < 0) + if (clixon_json2cbuf(cbret, xoutput, pretty, 0, 0, 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 83dcc036..353bed99 100644 --- a/apps/restconf/restconf_root.c +++ b/apps/restconf/restconf_root.c @@ -213,7 +213,7 @@ api_root_restconf_exact(clixon_handle h, break; case YANG_DATA_JSON: case YANG_PATCH_JSON: - if (clixon_json2cbuf(cb, xt, pretty, 0, 0) < 0) + if (clixon_json2cbuf(cb, xt, pretty, 0, 0, 0) < 0) goto done; break; default: @@ -299,7 +299,7 @@ api_yang_library_version(clixon_handle h, break; case YANG_DATA_JSON: case YANG_PATCH_JSON: - if (clixon_json2cbuf(cb, xt, pretty, 0, 0) < 0) + if (clixon_json2cbuf(cb, xt, pretty, 0, 0, 0) < 0) goto done; break; default: diff --git a/lib/clixon/clixon_json.h b/lib/clixon/clixon_json.h index 3477df81..939c3b7d 100644 --- a/lib/clixon/clixon_json.h +++ b/lib/clixon/clixon_json.h @@ -44,9 +44,9 @@ * Prototypes */ int json2xml_decode(cxobj *x, cxobj **xerr); -int clixon_json2cbuf(cbuf *cb, cxobj *x, int pretty, int skiptop, int autocliext); +int clixon_json2cbuf(cbuf *cb, cxobj *x, int pretty, int skiptop, int autocliext, int system_only); int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty, int skiptop); -int clixon_json2file(FILE *f, cxobj *x, int pretty, clicon_output_cb *fn, int skiptop, int autocliext); +int clixon_json2file(FILE *f, cxobj *x, int pretty, clicon_output_cb *fn, int skiptop, int autocliext, int system_only); int json_print(FILE *f, cxobj *x); int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, int pretty, clicon_output_cb *fn, int skiptop); int clixon_json_parse_string(char *str, int rfc7951, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c index 51239373..2ff81357 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -887,7 +887,7 @@ xmldb_get_cache(clixon_handle h, goto done; } if (clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG") && - strcmp(db, "running") == 0){ + (strcmp(db, "candidate") != 0)) { if (xmldb_system_only_config(h, xpath?xpath:"/", nsc, &x1t) < 0) goto done; } diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 42a221e3..7286441b 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -1628,8 +1628,7 @@ xmldb_dump(clixon_handle h, switch (format){ case FORMAT_XML: if (clixon_xml2file1(f, xt, 0, pretty, NULL, fprintf, 0, 0, wdef, multi, - clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG") - ) < 0) + clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG")) < 0) goto done; if (multi){ mw.mw_h = h; @@ -1646,7 +1645,8 @@ xmldb_dump(clixon_handle h, clixon_err(OE_CFG, errno, "JSON+multi not supported"); goto done; } - if (clixon_json2file(f, xt, pretty, fprintf, 0, 0) < 0) + if (clixon_json2file(f, xt, pretty, fprintf, 0, 0, + clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG")) < 0) goto done; break; default: diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 06bbf825..7f5778e0 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -789,6 +789,7 @@ xml2json_encode_attr(cxobj *xa, * @param[in] level Indentation level * @param[in] pretty Pretty-print output (2 means debug) * @param[in] flat Dont print NO_ARRAY object name (for _vec call) + * @param[in] system_only Enable checks for system-only-config extension * @param[in] modname0 * @param[out] metacbp Meta encoding of attribute * @retval 0 OK @@ -829,6 +830,7 @@ xml2json1_cbuf(cbuf *cb, int level, int pretty, int flat, + int system_only, char *modname0, cbuf *metacbp) { @@ -838,11 +840,13 @@ xml2json1_cbuf(cbuf *cb, cxobj *xp; enum childtype childt; enum array_element_type xc_arraytype; + yang_stmt *yc; yang_stmt *ys; yang_stmt *ymod = NULL; /* yang module */ int commas; char *modname = NULL; cbuf *metacbc = NULL; + int exist; if ((ys = xml_spec(x)) != NULL){ if (ys_real_module(ys, &ymod) < 0) @@ -957,21 +961,29 @@ xml2json1_cbuf(cbuf *cb, xc_arraytype = array_eval(i?xml_child_i(x,i-1):NULL, xc, xml_child_i(x, i+1)); - if (xml2json1_cbuf(cb, - xc, - xc_arraytype, - level+1, pretty, 0, modname0, - metacbc) < 0) - goto done; - if (commas > 0) { - cprintf(cb, ",%s", pretty?"\n":""); - --commas; + exist = 0; + if ((yc = xml_spec(xc)) != NULL && system_only){ + if (yang_extension_value(yc, "system-only-config", CLIXON_LIB_NS, &exist, NULL) < 0) + goto done; + if (exist && commas) + commas--; + } + if (!exist) { + if (xml2json1_cbuf(cb, + xc, + xc_arraytype, + level+1, pretty, 0, system_only, modname0, + metacbc) < 0) + goto done; + if (commas > 0) { + cprintf(cb, ",%s", pretty?"\n":""); + --commas; + } } } if (cbuf_len(metacbc)){ cprintf(cb, "%s", cbuf_get(metacbc)); } - switch (arraytype){ case BODY_ARRAY: break; @@ -1041,21 +1053,23 @@ xml2json1_cbuf(cbuf *cb, * XML-style namespace notation in tree, but RFC7951 in output assume yang * populated * - * @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] autocliext How to handle autocli extensions: 0: ignore 1: follow - * @retval 0 OK - * @retval -1 Error + * @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] autocliext How to handle autocli extensions: 0: ignore 1: follow + * @param[in] system_only Enable checks for system-only-config extension + * @retval 0 OK + * @retval -1 Error * * @see clixon_xml2cbuf XML corresponding function * @see xml2json_cbuf_vec Top symbol is list */ static int -xml2json_cbuf1(cbuf *cb, - cxobj *x, - int pretty, - int autocliext) +xml2json_cbuf1(cbuf *cb, + cxobj *x, + int pretty, + int autocliext, + int system_only) { int retval = 1; int level = 0; @@ -1070,6 +1084,7 @@ xml2json_cbuf1(cbuf *cb, if (exist) goto ok; } + cprintf(cb, "%*s{%s", pretty?level*PRETTYPRINT_INDENT:0,"", pretty?"\n":""); @@ -1090,6 +1105,7 @@ xml2json_cbuf1(cbuf *cb, level+1, pretty, 0, + system_only, NULL, /* ancestor modname / namespace */ NULL) < 0) goto done; @@ -1108,13 +1124,14 @@ xml2json_cbuf1(cbuf *cb, * 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, - * @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow - * @retval 0 OK - * @retval -1 Error + * @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, + * @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow + * @param[in] system_only Enable checks for system-only-config extension + * @retval 0 OK + * @retval -1 Error * @code * cbuf *cb = cbuf_new(); * if (xml2json_cbuf(cb, xn, 0, 0, 0) < 0) @@ -1128,7 +1145,8 @@ clixon_json2cbuf(cbuf *cb, cxobj *xt, int pretty, int skiptop, - int autocliext) + int autocliext, + int system_only) { int retval = -1; cxobj *xc; @@ -1139,12 +1157,12 @@ clixon_json2cbuf(cbuf *cb, while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){ if (i++) cprintf(cb, ","); - if (xml2json_cbuf1(cb, xc, pretty, autocliext) < 0) + if (xml2json_cbuf1(cb, xc, pretty, autocliext, system_only) < 0) goto done; } } else { - if (xml2json_cbuf1(cb, xt, pretty, autocliext) < 0) + if (xml2json_cbuf1(cb, xt, pretty, autocliext, system_only) < 0) goto done; } retval = 0; @@ -1217,7 +1235,7 @@ xml2json_cbuf_vec(cbuf *cb, NO_ARRAY, level, pretty, - 1, NULL, NULL) < 0) + 1, 0, NULL, NULL) < 0) goto done; if (0){ @@ -1237,19 +1255,20 @@ 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] xn XML tree to translate from - * @param[in] pretty Set if output is pretty-printed - * @param[in] fn File print function - * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, - * @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow - * @retval 0 OK - * @retval -1 Error + * @param[in] f File to print to + * @param[in] xn XML tree to translate from + * @param[in] pretty Set if output is pretty-printed + * @param[in] fn File print function + * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, + * @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow + * @param[in] system_only Enable checks for system-only-config extension + * @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 (clixon_json2file(stderr, xn, 0, fprintf, 0, 0) < 0) + * if (clixon_json2file(stderr, xn, 0, fprintf, 0, 0, 0) < 0) * goto err; * @endcode */ @@ -1259,7 +1278,8 @@ clixon_json2file(FILE *f, int pretty, clicon_output_cb *fn, int skiptop, - int autocliext) + int autocliext, + int system_only) { int retval = 1; cbuf *cb = NULL; @@ -1270,7 +1290,7 @@ clixon_json2file(FILE *f, clixon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (clixon_json2cbuf(cb, xn, pretty, skiptop, autocliext) < 0) + if (clixon_json2cbuf(cb, xn, pretty, skiptop, autocliext, system_only) < 0) goto done; (*fn)(f, "%s", cbuf_get(cb)); retval = 0; @@ -1289,7 +1309,7 @@ int json_print(FILE *f, cxobj *x) { - return clixon_json2file(f, x, 1, fprintf, 0, 0); + return clixon_json2file(f, x, 1, fprintf, 0, 0, 0); } /*! Translate a vector of xml objects to JSON File. diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 93fa2ce4..51f905fd 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -253,7 +253,7 @@ clicon_option_dump1(clixon_handle h, goto done; break; case FORMAT_JSON: - if (clixon_json2file(f, xc, pretty, cligen_output, 0, 0) < 0) + if (clixon_json2file(f, xc, pretty, cligen_output, 0, 0, 0) < 0) goto done; break; case FORMAT_TEXT: diff --git a/test/config.sh.in b/test/config.sh.in index f4816ae3..b9fc13e7 100755 --- a/test/config.sh.in +++ b/test/config.sh.in @@ -73,7 +73,7 @@ DATASTORE_TOP="config" # clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in) CLIXON_AUTOCLI_REV="2024-08-01" -CLIXON_LIB_REV="2024-04-01" +CLIXON_LIB_REV="2024-08-01" CLIXON_CONFIG_REV="2024-08-01" CLIXON_RESTCONF_REV="2022-08-01" CLIXON_EXAMPLE_REV="2022-11-01" diff --git a/test/test_datastore_system_only.sh b/test/test_datastore_system_only.sh index b789d0a5..43fef970 100755 --- a/test/test_datastore_system_only.sh +++ b/test/test_datastore_system_only.sh @@ -21,8 +21,13 @@ test -d $CFD || mkdir -p $CFD AUTOCLI=$(autocli_config clixon-\* kw-nokey false) +# Define default restconfig config: RESTCONFIG +RESTCONFIG=$(restconf_config none false) + cat < $cfg + ietf-netconf:startup + clixon-restconf:allow-auth-none $cfg $CFD ${YANG_INSTALLDIR} @@ -41,6 +46,7 @@ cat < $cfg true true true + $RESTCONFIG EOF @@ -74,6 +80,11 @@ module clixon-standard{ "System-only config data"; type string; } + leaf normal-data { + description + "Normal config data"; + type string; + } } grouping store-grouping { container keys { @@ -95,7 +106,7 @@ EOF # A "local" YANG cat < $flocal -module clixon-mount1{ +module clixon-local{ yang-version 1.1; namespace "urn:example:local"; prefix local; @@ -140,19 +151,45 @@ show("Show a particular state of the system"){ } EOF -# Two reference files: What is expected in the datastore -cat < $dir/x_db +# Reference files: What is expected in the datastore +cat < $dir/x_db_xml a + otherdata EOF +# Same in JSON (but broken) +cat < $dir/x_db_json +{ + "config": { + "clixon-standard:store": { + "keys": { + "key": [ + { + "name": "a", + "normal-data": "otherdata" + } + ] + } + }, + "ietf-netconf-acm:nacm": { + "enable-nacm": true, + "read-default": "permit", + "write-default": "deny", + "exec-default": "permit", + "enable-external-groups": true + } + } +} +EOF + # What is expected in the system-only-config file (simulated system) cat < $dir/y_db @@ -165,22 +202,23 @@ cat < $dir/y_db EOF - # Check content of db # Args: -# 0: dbname -# 1: system true/false check in system or not (only after commit) +# 1: dbname +# 2: system true/false check in system or not (only after commit) +# 3: format xml/json function check_db() { dbname=$1 system=$2 + format=$3 sudo chmod 755 $dir/${dbname}_db new "Check not in ${dbname}_db" - ret=$(diff $dir/x_db $dir/${dbname}_db) + ret=$(diff $dir/x_db_$format $dir/${dbname}_db) if [ $? -ne 0 ]; then - err "$(cat $dir/x_db)" "$(cat $dir/${dbname}_db)" + err "$(cat $dir/x_db_$format)" "$(cat $dir/${dbname}_db)" fi if $system; then @@ -205,6 +243,11 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi +fi + +sudo rm -f $dir/system-only.xml + +if [ $BE -ne 0 ]; then new "start backend -s init -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml" start_backend -s init -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml fi @@ -212,40 +255,41 @@ fi new "wait backend 1" wait_backend -sudo rm -f $dir/system-only.xml - new "Add mydata" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "amydata" "" +new "Add normal data" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "aotherdata" "" + new "Check mydata present, but not in candidate datastore" -check_db candidate false +check_db candidate false xml new "Get mydata from candidate" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydata" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydataotherdata" new "Commit 1" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" new "Check mydata present, but not in running datastore" -check_db running true +check_db running true xml new "Get mydata from running" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydata" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydataotherdata" new "Remove mydata" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "amydatanone" "" new "Check mydata present, but not in candidate datastore" -check_db candidate true +check_db candidate true xml new "Commit 2" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" -new "Get mydata from running, expected not" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "a" +new "Get mydata from running, expecte no system-nly" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "aotherdata" new "Check mydata not present, but not in running datastore" -check_db running false +check_db running false xml new "Add mydata again" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "amydata" "" @@ -271,10 +315,10 @@ new "wait backend 2" wait_backend new "Check mydata present, but not in running datastore" -check_db running true +check_db running true xml new "Get mydata from running" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydata" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydataotherdata" new "Remove mydata" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "amydatanone" "" @@ -282,11 +326,118 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" " new "Commit 4" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" -new "Get mydata from running, expected not" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "a" +new "Get mydata from running, expected no system-only" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "aotherdata" new "Check mydata not present, but not in running datastore" -check_db running false +check_db running false xml + +new "Restart" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi +fi + +# Setup startup and saved system-only +sudo cp $dir/x_db_xml $dir/startup_db +sudo cp $dir/y_db $dir/system-only.xml + +if [ $BE -ne 0 ]; then + new "start backend -s startup -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml" + start_backend -s startup -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml +fi + +new "wait backend 3" +wait_backend + +new "Get mydata from running after startup" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydataotherdata" + +new "Restart" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi +fi + +sudo rm -f $dir/system-only.xml + +if [ $BE -ne 0 ]; then + new "start backend -s init -f $cfg -o CLICON_XMLDB_FORMAT=json -- -o store/keys/key/system-only-data -O $dir/system-only.xml" + start_backend -s init -f $cfg -o CLICON_XMLDB_FORMAT=json -- -o store/keys/key/system-only-data -O $dir/system-only.xml +fi + +new "wait backend 4" +wait_backend + +new "Add mydata" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "amydata" "" + +new "Add normal data" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "aotherdata" "" + +new "Check mydata present, but not in candidate datastore" +check_db candidate false json + +new "Get mydata from candidate" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "amydataotherdata" + +new "Restart" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi +fi + +# restconf + +sudo rm -f $dir/system-only.xml + +if [ $BE -ne 0 ]; then + new "start backend -s init -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml" + start_backend -s init -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml +fi + +new "wait backend 5" +wait_backend + +if [ $RC -ne 0 ]; then + new "kill old restconf daemon" + stop_restconf_pre + + new "start restconf daemon" + start_restconf -f $cfg +fi + +new "wait restconf" +wait_restconf + +new "Add mydata" +expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-standard:store":{"keys":{"key":[{"name":"a","system-only-data":"mydata"}]}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201" + +new "Add normal data" +expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-standard:normal-data":"otherdata"}' $RCPROTO://localhost/restconf/data/clixon-standard:store/keys/key=a)" 0 "HTTP/$HVER 201" + +new "Check mydata present, but not in running datastore" +check_db running true xml + +new "Check mydata present, but not in candidate datastore" +check_db candidate true xml + +new "get" +expectpart "$(curl $CURLOPTS -X GET -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-standard:store)" 0 "HTTP/$HVER 200" '{"clixon-standard:store":{"keys":{"key":\[{"name":"a","system-only-data":"mydata","normal-data":"otherdata"}\]}}}' + +if [ $RC -ne 0 ]; then + new "Kill restconf daemon" + stop_restconf +fi if [ $BE -ne 0 ]; then new "Kill backend" diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index 79fd2c47..1e2d9e79 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -43,7 +43,7 @@ YANG_INSTALLDIR = @YANG_INSTALLDIR@ # Note: mirror these to test/config.sh.in YANGSPECS = clixon-config@2024-08-01.yang # 7.2 -YANGSPECS += clixon-lib@2024-04-01.yang # 7.1 +YANGSPECS += clixon-lib@2024-08-01.yang # 7.2 YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-restconf@2022-08-01.yang # 5.9 diff --git a/yang/clixon/clixon-lib@2024-08-01.yang b/yang/clixon/clixon-lib@2024-08-01.yang index 8c3cc4d7..faa8304f 100644 --- a/yang/clixon/clixon-lib@2024-08-01.yang +++ b/yang/clixon/clixon-lib@2024-08-01.yang @@ -71,8 +71,7 @@ module clixon-lib { revision 2024-08-01 { description - "Added: list-pagination-partial-state - Added: system-only-config extension (tentative) + "Added: system-only-config extension (tentative) Released in Clixon 7.2"; } revision 2024-04-01 { @@ -323,18 +322,6 @@ module clixon-lib { "A CLI session"; base ncm:transport; } - extension list-pagination-partial-state { - description - "List should be partially read according to the clixon_pagination_cb_register API. - This is a performance enhancement of pagination state data. - This means that a special callback is used for retreiving list state which is aware of - offset/limit attributes. - In this way the non-config data can be partially read by the server, instead of reading - the whole state on every pagination request. - It affects only the server/backend-side - It only handles the offset and limit attributes, all other attributes, - such as where, sort-by, direction, etc, are ignored"; - } extension ignore-compare { description "The object should be ignored when comparing device configs for equality.