System-only-config

* Fix startup diff
* Add system-only to running on startup
* JSON support:
This commit is contained in:
Olof hagsand 2024-10-16 13:46:47 +02:00
parent 3a656fac07
commit 69b65ad13d
19 changed files with 276 additions and 105 deletions

View file

@ -24,6 +24,9 @@ Expected: January 2025
* New `CLICON_XMLDB_SYSTEM_ONLY_CONFIG` configuration option * New `CLICON_XMLDB_SYSTEM_ONLY_CONFIG` configuration option
* New `system-only-config` extension * New `system-only-config` extension
* New `ca_system_only` backend callback for reading system-only data * 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 ### Corrected Bugs

View file

@ -172,13 +172,22 @@ startup_common(clixon_handle h,
cxobj *xret = NULL; cxobj *xret = NULL;
cxobj *xerr = 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 /* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with
* potentially non-matching module-state in msdiff * potentially non-matching module-state in msdiff
*/ */
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
if ((msdiff = modstate_diff_new()) == NULL) if ((msdiff = modstate_diff_new()) == NULL)
goto done; 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 (clicon_option_bool(h, "CLICON_XMLDB_UPGRADE_CHECKOLD")){
if ((ret = xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, 0, &xt, msdiff, &xerr)) < 0) if ((ret = xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, 0, &xt, msdiff, &xerr)) < 0)
goto done; goto done;
@ -659,6 +668,7 @@ candidate_validate(clixon_handle h,
* @retval 1 Validation OK * @retval 1 Validation OK
* @retval 0 Validation failed (with cbret set) * @retval 0 Validation failed (with cbret set)
* @retval -1 Error - or validation failed * @retval -1 Error - or validation failed
* @see startup_commit for commit on startup
*/ */
int int
candidate_commit(clixon_handle h, candidate_commit(clixon_handle h,

View file

@ -1461,7 +1461,7 @@ save_config_file(clixon_handle h,
goto done; goto done;
break; break;
case FORMAT_JSON: 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; goto done;
break; break;
case FORMAT_TEXT: case FORMAT_TEXT:
@ -1587,7 +1587,7 @@ cli_notification_cb(int s,
goto done; goto done;
switch (format){ switch (format){
case FORMAT_JSON: 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; goto done;
case FORMAT_TEXT: case FORMAT_TEXT:
if (clixon_text2file(stdout, xt, 0, cligen_output, 1, 1) < 0) if (clixon_text2file(stdout, xt, 0, cligen_output, 1, 1) < 0)

View file

@ -318,7 +318,7 @@ pipe_showas_fn(clixon_handle h,
goto done; goto done;
break; break;
case FORMAT_JSON: 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; goto done;
break; break;
case FORMAT_TEXT: case FORMAT_TEXT:

View file

@ -1434,7 +1434,7 @@ cli_pagination(clixon_handle h,
goto done; goto done;
break; break;
case FORMAT_JSON: 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; goto done;
break; break;
case FORMAT_TEXT: case FORMAT_TEXT:

View file

@ -308,14 +308,14 @@ api_return_err(clixon_handle h,
clixon_debug(CLIXON_DBG_RESTCONF, "code:%d", code); clixon_debug(CLIXON_DBG_RESTCONF, "code:%d", code);
if (pretty){ if (pretty){
cprintf(cb, "{\n\"ietf-restconf:errors\" : "); 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; goto done;
cprintf(cb, "\n}\r\n"); cprintf(cb, "\n}\r\n");
} }
else{ else{
cprintf(cb, "{"); cprintf(cb, "{");
cprintf(cb, "\"ietf-restconf:errors\":"); 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; goto done;
cprintf(cb, "}\r\n"); cprintf(cb, "}\r\n");
} }

View file

@ -237,7 +237,7 @@ api_data_get2(clixon_handle h,
goto done; goto done;
break; break;
case YANG_DATA_JSON: 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; goto done;
break; break;
default: default:

View file

@ -120,7 +120,7 @@ yang_patch_xml2json_modified_cbuf(cxobj *x_simple_patch)
if (json_simple_patch == NULL) if (json_simple_patch == NULL)
return NULL; return NULL;
cb = cbuf_new(); 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; goto done;
// Insert a '[' after the first '{' to get the JSON to match what api_data_post/write() expect // 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 // 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; goto done;
// Send the POST request // Send the POST request
@ -336,7 +336,7 @@ yang_patch_do_create(clixon_handle h,
xml_addsub(x_simple_patch, value_vec_tmp); 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; goto done;
if (api_data_post(h, req, cbuf_get(simple_patch_request_uri), if (api_data_post(h, req, cbuf_get(simple_patch_request_uri),
pi, qvec, pi, qvec,
@ -492,7 +492,7 @@ yang_patch_do_merge(clixon_handle h,
xml_addsub(x_simple_patch, value_vec_tmp); xml_addsub(x_simple_patch, value_vec_tmp);
} }
cbuf_reset(cb); /* reuse cb */ 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; goto done;
if ((json_simple_patch = yang_patch_xml2json_modified_cbuf(x_simple_patch)) == NULL) if ((json_simple_patch = yang_patch_xml2json_modified_cbuf(x_simple_patch)) == NULL)

View file

@ -884,7 +884,7 @@ api_operations_post(clixon_handle h,
/* xoutput should now look: <output xmlns="uri"><x>0</x></output> */ /* xoutput should now look: <output xmlns="uri"><x>0</x></output> */
break; break;
case YANG_DATA_JSON: 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; goto done;
/* xoutput should now look: {"example:output": {"x":0,"y":42}} */ /* xoutput should now look: {"example:output": {"x":0,"y":42}} */
break; break;

View file

@ -213,7 +213,7 @@ api_root_restconf_exact(clixon_handle h,
break; break;
case YANG_DATA_JSON: case YANG_DATA_JSON:
case YANG_PATCH_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; goto done;
break; break;
default: default:
@ -299,7 +299,7 @@ api_yang_library_version(clixon_handle h,
break; break;
case YANG_DATA_JSON: case YANG_DATA_JSON:
case YANG_PATCH_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; goto done;
break; break;
default: default:

View file

@ -44,9 +44,9 @@
* Prototypes * Prototypes
*/ */
int json2xml_decode(cxobj *x, cxobj **xerr); 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 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 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 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); int clixon_json_parse_string(char *str, int rfc7951, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret);

View file

@ -887,7 +887,7 @@ xmldb_get_cache(clixon_handle h,
goto done; goto done;
} }
if (clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG") && 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) if (xmldb_system_only_config(h, xpath?xpath:"/", nsc, &x1t) < 0)
goto done; goto done;
} }

View file

@ -1628,8 +1628,7 @@ xmldb_dump(clixon_handle h,
switch (format){ switch (format){
case FORMAT_XML: case FORMAT_XML:
if (clixon_xml2file1(f, xt, 0, pretty, NULL, fprintf, 0, 0, wdef, multi, if (clixon_xml2file1(f, xt, 0, pretty, NULL, fprintf, 0, 0, wdef, multi,
clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG") clicon_option_bool(h, "CLICON_XMLDB_SYSTEM_ONLY_CONFIG")) < 0)
) < 0)
goto done; goto done;
if (multi){ if (multi){
mw.mw_h = h; mw.mw_h = h;
@ -1646,7 +1645,8 @@ xmldb_dump(clixon_handle h,
clixon_err(OE_CFG, errno, "JSON+multi not supported"); clixon_err(OE_CFG, errno, "JSON+multi not supported");
goto done; 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; goto done;
break; break;
default: default:

View file

@ -789,6 +789,7 @@ xml2json_encode_attr(cxobj *xa,
* @param[in] level Indentation level * @param[in] level Indentation level
* @param[in] pretty Pretty-print output (2 means debug) * @param[in] pretty Pretty-print output (2 means debug)
* @param[in] flat Dont print NO_ARRAY object name (for _vec call) * @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[in] modname0
* @param[out] metacbp Meta encoding of attribute * @param[out] metacbp Meta encoding of attribute
* @retval 0 OK * @retval 0 OK
@ -829,6 +830,7 @@ xml2json1_cbuf(cbuf *cb,
int level, int level,
int pretty, int pretty,
int flat, int flat,
int system_only,
char *modname0, char *modname0,
cbuf *metacbp) cbuf *metacbp)
{ {
@ -838,11 +840,13 @@ xml2json1_cbuf(cbuf *cb,
cxobj *xp; cxobj *xp;
enum childtype childt; enum childtype childt;
enum array_element_type xc_arraytype; enum array_element_type xc_arraytype;
yang_stmt *yc;
yang_stmt *ys; yang_stmt *ys;
yang_stmt *ymod = NULL; /* yang module */ yang_stmt *ymod = NULL; /* yang module */
int commas; int commas;
char *modname = NULL; char *modname = NULL;
cbuf *metacbc = NULL; cbuf *metacbc = NULL;
int exist;
if ((ys = xml_spec(x)) != NULL){ if ((ys = xml_spec(x)) != NULL){
if (ys_real_module(ys, &ymod) < 0) if (ys_real_module(ys, &ymod) < 0)
@ -957,10 +961,18 @@ xml2json1_cbuf(cbuf *cb,
xc_arraytype = array_eval(i?xml_child_i(x,i-1):NULL, xc_arraytype = array_eval(i?xml_child_i(x,i-1):NULL,
xc, xc,
xml_child_i(x, i+1)); xml_child_i(x, i+1));
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, if (xml2json1_cbuf(cb,
xc, xc,
xc_arraytype, xc_arraytype,
level+1, pretty, 0, modname0, level+1, pretty, 0, system_only, modname0,
metacbc) < 0) metacbc) < 0)
goto done; goto done;
if (commas > 0) { if (commas > 0) {
@ -968,10 +980,10 @@ xml2json1_cbuf(cbuf *cb,
--commas; --commas;
} }
} }
}
if (cbuf_len(metacbc)){ if (cbuf_len(metacbc)){
cprintf(cb, "%s", cbuf_get(metacbc)); cprintf(cb, "%s", cbuf_get(metacbc));
} }
switch (arraytype){ switch (arraytype){
case BODY_ARRAY: case BODY_ARRAY:
break; break;
@ -1045,6 +1057,7 @@ xml2json1_cbuf(cbuf *cb,
* @param[in] x XML tree to translate from * @param[in] x XML tree to translate from
* @param[in] pretty Set if output is pretty-printed * @param[in] pretty Set if output is pretty-printed
* @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow * @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 0 OK
* @retval -1 Error * @retval -1 Error
* *
@ -1055,7 +1068,8 @@ static int
xml2json_cbuf1(cbuf *cb, xml2json_cbuf1(cbuf *cb,
cxobj *x, cxobj *x,
int pretty, int pretty,
int autocliext) int autocliext,
int system_only)
{ {
int retval = 1; int retval = 1;
int level = 0; int level = 0;
@ -1070,6 +1084,7 @@ xml2json_cbuf1(cbuf *cb,
if (exist) if (exist)
goto ok; goto ok;
} }
cprintf(cb, "%*s{%s", cprintf(cb, "%*s{%s",
pretty?level*PRETTYPRINT_INDENT:0,"", pretty?level*PRETTYPRINT_INDENT:0,"",
pretty?"\n":""); pretty?"\n":"");
@ -1090,6 +1105,7 @@ xml2json_cbuf1(cbuf *cb,
level+1, level+1,
pretty, pretty,
0, 0,
system_only,
NULL, /* ancestor modname / namespace */ NULL, /* ancestor modname / namespace */
NULL) < 0) NULL) < 0)
goto done; goto done;
@ -1113,6 +1129,7 @@ xml2json_cbuf1(cbuf *cb,
* @param[in] pretty Set if output is pretty-printed * @param[in] pretty Set if output is pretty-printed
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children, * @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] 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 0 OK
* @retval -1 Error * @retval -1 Error
* @code * @code
@ -1128,7 +1145,8 @@ clixon_json2cbuf(cbuf *cb,
cxobj *xt, cxobj *xt,
int pretty, int pretty,
int skiptop, int skiptop,
int autocliext) int autocliext,
int system_only)
{ {
int retval = -1; int retval = -1;
cxobj *xc; cxobj *xc;
@ -1139,12 +1157,12 @@ clixon_json2cbuf(cbuf *cb,
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){ while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){
if (i++) if (i++)
cprintf(cb, ","); cprintf(cb, ",");
if (xml2json_cbuf1(cb, xc, pretty, autocliext) < 0) if (xml2json_cbuf1(cb, xc, pretty, autocliext, system_only) < 0)
goto done; goto done;
} }
} }
else { else {
if (xml2json_cbuf1(cb, xt, pretty, autocliext) < 0) if (xml2json_cbuf1(cb, xt, pretty, autocliext, system_only) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;
@ -1217,7 +1235,7 @@ xml2json_cbuf_vec(cbuf *cb,
NO_ARRAY, NO_ARRAY,
level, level,
pretty, pretty,
1, NULL, NULL) < 0) 1, 0, NULL, NULL) < 0)
goto done; goto done;
if (0){ if (0){
@ -1243,13 +1261,14 @@ xml2json_cbuf_vec(cbuf *cb,
* @param[in] fn File print function * @param[in] fn File print function
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children, * @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] 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 0 OK
* @retval -1 Error * @retval -1 Error
* *
* @note yang is necessary to translate to one-member lists, * @note yang is necessary to translate to one-member lists,
* eg if a is a yang LIST <a>0</a> -> {"a":["0"]} and not {"a":"0"} * eg if a is a yang LIST <a>0</a> -> {"a":["0"]} and not {"a":"0"}
* @code * @code
* if (clixon_json2file(stderr, xn, 0, fprintf, 0, 0) < 0) * if (clixon_json2file(stderr, xn, 0, fprintf, 0, 0, 0) < 0)
* goto err; * goto err;
* @endcode * @endcode
*/ */
@ -1259,7 +1278,8 @@ clixon_json2file(FILE *f,
int pretty, int pretty,
clicon_output_cb *fn, clicon_output_cb *fn,
int skiptop, int skiptop,
int autocliext) int autocliext,
int system_only)
{ {
int retval = 1; int retval = 1;
cbuf *cb = NULL; cbuf *cb = NULL;
@ -1270,7 +1290,7 @@ clixon_json2file(FILE *f,
clixon_err(OE_XML, errno, "cbuf_new"); clixon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
} }
if (clixon_json2cbuf(cb, xn, pretty, skiptop, autocliext) < 0) if (clixon_json2cbuf(cb, xn, pretty, skiptop, autocliext, system_only) < 0)
goto done; goto done;
(*fn)(f, "%s", cbuf_get(cb)); (*fn)(f, "%s", cbuf_get(cb));
retval = 0; retval = 0;
@ -1289,7 +1309,7 @@ int
json_print(FILE *f, json_print(FILE *f,
cxobj *x) 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. /*! Translate a vector of xml objects to JSON File.

View file

@ -253,7 +253,7 @@ clicon_option_dump1(clixon_handle h,
goto done; goto done;
break; break;
case FORMAT_JSON: 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; goto done;
break; break;
case FORMAT_TEXT: case FORMAT_TEXT:

View file

@ -73,7 +73,7 @@ DATASTORE_TOP="config"
# clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in) # clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in)
CLIXON_AUTOCLI_REV="2024-08-01" 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_CONFIG_REV="2024-08-01"
CLIXON_RESTCONF_REV="2022-08-01" CLIXON_RESTCONF_REV="2022-08-01"
CLIXON_EXAMPLE_REV="2022-11-01" CLIXON_EXAMPLE_REV="2022-11-01"

View file

@ -21,8 +21,13 @@ test -d $CFD || mkdir -p $CFD
AUTOCLI=$(autocli_config clixon-\* kw-nokey false) AUTOCLI=$(autocli_config clixon-\* kw-nokey false)
# Define default restconfig config: RESTCONFIG
RESTCONFIG=$(restconf_config none false)
cat <<EOF > $cfg cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config"> <clixon-config xmlns="http://clicon.org/config">
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_CONFIGDIR>$CFD</CLICON_CONFIGDIR> <CLICON_CONFIGDIR>$CFD</CLICON_CONFIGDIR>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR> <CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
@ -41,6 +46,7 @@ cat <<EOF > $cfg
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML> <CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
<CLICON_STREAM_DISCOVERY_RFC5277>true</CLICON_STREAM_DISCOVERY_RFC5277> <CLICON_STREAM_DISCOVERY_RFC5277>true</CLICON_STREAM_DISCOVERY_RFC5277>
<CLICON_YANG_SCHEMA_MOUNT>true</CLICON_YANG_SCHEMA_MOUNT> <CLICON_YANG_SCHEMA_MOUNT>true</CLICON_YANG_SCHEMA_MOUNT>
$RESTCONFIG
</clixon-config> </clixon-config>
EOF EOF
@ -74,6 +80,11 @@ module clixon-standard{
"System-only config data"; "System-only config data";
type string; type string;
} }
leaf normal-data {
description
"Normal config data";
type string;
}
} }
grouping store-grouping { grouping store-grouping {
container keys { container keys {
@ -95,7 +106,7 @@ EOF
# A "local" YANG # A "local" YANG
cat <<EOF > $flocal cat <<EOF > $flocal
module clixon-mount1{ module clixon-local{
yang-version 1.1; yang-version 1.1;
namespace "urn:example:local"; namespace "urn:example:local";
prefix local; prefix local;
@ -140,19 +151,45 @@ show("Show a particular state of the system"){
} }
EOF EOF
# Two reference files: What is expected in the datastore # Reference files: What is expected in the datastore
cat <<EOF > $dir/x_db cat <<EOF > $dir/x_db_xml
<config> <config>
<store xmlns="urn:example:std"> <store xmlns="urn:example:std">
<keys> <keys>
<key> <key>
<name>a</name> <name>a</name>
<normal-data>otherdata</normal-data>
</key> </key>
</keys> </keys>
</store> </store>
</config> </config>
EOF EOF
# Same in JSON (but broken)
cat <<EOF > $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) # What is expected in the system-only-config file (simulated system)
cat <<EOF > $dir/y_db cat <<EOF > $dir/y_db
<store xmlns="urn:example:std"> <store xmlns="urn:example:std">
@ -165,22 +202,23 @@ cat <<EOF > $dir/y_db
</store> </store>
EOF EOF
# Check content of db # Check content of db
# Args: # Args:
# 0: dbname # 1: dbname
# 1: system true/false check in system or not (only after commit) # 2: system true/false check in system or not (only after commit)
# 3: format xml/json
function check_db() function check_db()
{ {
dbname=$1 dbname=$1
system=$2 system=$2
format=$3
sudo chmod 755 $dir/${dbname}_db sudo chmod 755 $dir/${dbname}_db
new "Check not in ${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 if [ $? -ne 0 ]; then
err "$(cat $dir/x_db)" "$(cat $dir/${dbname}_db)" err "$(cat $dir/x_db_$format)" "$(cat $dir/${dbname}_db)"
fi fi
if $system; then if $system; then
@ -205,6 +243,11 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi 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" 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 start_backend -s init -f $cfg -- -o store/keys/key/system-only-data -O $dir/system-only.xml
fi fi
@ -212,40 +255,41 @@ fi
new "wait backend 1" new "wait backend 1"
wait_backend wait_backend
sudo rm -f $dir/system-only.xml
new "Add mydata" new "Add mydata"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add normal data"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><normal-data>otherdata</normal-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check mydata present, but not in candidate datastore" new "Check mydata present, but not in candidate datastore"
check_db candidate false check_db candidate false xml
new "Get mydata from candidate" new "Get mydata from candidate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
new "Commit 1" new "Commit 1"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check mydata present, but not in running datastore" new "Check mydata present, but not in running datastore"
check_db running true check_db running true xml
new "Get mydata from running" new "Get mydata from running"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
new "Remove mydata" new "Remove mydata"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data nc:operation=\"delete\" xmlns:nc=\"${BASENS}\">mydata</system-only-data></key></keys></store></config><default-operation>none</default-operation></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data nc:operation=\"delete\" xmlns:nc=\"${BASENS}\">mydata</system-only-data></key></keys></store></config><default-operation>none</default-operation></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check mydata present, but not in candidate datastore" new "Check mydata present, but not in candidate datastore"
check_db candidate true check_db candidate true xml
new "Commit 2" new "Commit 2"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Get mydata from running, expected not" new "Get mydata from running, expecte no system-nly"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name></key></keys></store></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
new "Check mydata not present, but not in running datastore" new "Check mydata not present, but not in running datastore"
check_db running false check_db running false xml
new "Add mydata again" new "Add mydata again"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
@ -271,10 +315,10 @@ new "wait backend 2"
wait_backend wait_backend
new "Check mydata present, but not in running datastore" new "Check mydata present, but not in running datastore"
check_db running true check_db running true xml
new "Get mydata from running" new "Get mydata from running"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
new "Remove mydata" new "Remove mydata"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data nc:operation=\"delete\" xmlns:nc=\"${BASENS}\">mydata</system-only-data></key></keys></store></config><default-operation>none</default-operation></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data nc:operation=\"delete\" xmlns:nc=\"${BASENS}\">mydata</system-only-data></key></keys></store></config><default-operation>none</default-operation></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
@ -282,11 +326,118 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
new "Commit 4" new "Commit 4"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Get mydata from running, expected not" new "Get mydata from running, expected no system-only"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name></key></keys></store></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
new "Check mydata not present, but not in running datastore" 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" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
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" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add normal data"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><store xmlns=\"urn:example:std\"><keys><key><name>a</name><normal-data>otherdata</normal-data></key></keys></store></config></edit-config></rpc>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
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" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><store xmlns=\"urn:example:std\"><keys><key><name>a</name><system-only-data>mydata</system-only-data><normal-data>otherdata</normal-data></key></keys></store></data></rpc-reply>"
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 if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"

View file

@ -43,7 +43,7 @@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
# Note: mirror these to test/config.sh.in # Note: mirror these to test/config.sh.in
YANGSPECS = clixon-config@2024-08-01.yang # 7.2 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-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang
YANGSPECS += clixon-restconf@2022-08-01.yang # 5.9 YANGSPECS += clixon-restconf@2022-08-01.yang # 5.9

View file

@ -71,8 +71,7 @@ module clixon-lib {
revision 2024-08-01 { revision 2024-08-01 {
description description
"Added: list-pagination-partial-state "Added: system-only-config extension (tentative)
Added: system-only-config extension (tentative)
Released in Clixon 7.2"; Released in Clixon 7.2";
} }
revision 2024-04-01 { revision 2024-04-01 {
@ -323,18 +322,6 @@ module clixon-lib {
"A CLI session"; "A CLI session";
base ncm:transport; 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 { extension ignore-compare {
description description
"The object should be ignored when comparing device configs for equality. "The object should be ignored when comparing device configs for equality.