* Added new functions: xml_tree_equal and xpath2xml

* RFC 8528 yang schema mount-points:
  * Made expand_dbvar and cli_dbxml mountpoint-aware (RFC 8528)
  * autocli supportgenerate
  * Made api_path2xml and xml2api_path mount-point-aware
  * Temporar fix in clixon_custom.h: XPATH_CANONICAL_SKIP_CHECK
* `xml2xpath()`: Added `apostrophe` as 4th parameter, default 0
* removed extra assert.h includes
This commit is contained in:
Olof hagsand 2023-03-21 09:10:40 +01:00
parent 1e136bc9df
commit da2edceb7e
37 changed files with 658 additions and 145 deletions

View file

@ -245,8 +245,8 @@ identityref_add_ns(cxobj *x,
if (ns == NULL &&
(yns = yang_find_module_by_prefix_yspec(yspec, pf)) != NULL){
if ((ns = yang_find_mynamespace(yns)) != NULL)
if (xmlns_set(x, pf, ns) < 0)
goto done;
if (xmlns_set(x, pf, ns) < 0)
goto done;
}
}
}
@ -260,10 +260,90 @@ identityref_add_ns(cxobj *x,
return retval;
}
/*! Given a top-level yspec and montpoint xpath compute a set of
*
* Manipulate top-level and a mointpoint:
* YSPEC: yspec0 yspec1
* XML: top0-->bot0-->top1-->bot1
* API-PATH: api-path0 api-path1
* api-path01---------
* The result computed from the top-level yspec and montpoint xpath are:
* - api_pathfmt10 Combined api-path for both trees
*/
int
mtpoint_paths(yang_stmt *yspec0,
char *mtpoint,
char *api_path_fmt1,
char **api_path_fmt01)
{
int retval = -1;
yang_stmt *yu = NULL;
yang_stmt *ybot0 = NULL;
cvec *nsc0 = NULL;
int ret;
char *api_path_fmt0;
cbuf *cb = NULL;
cxobj *xbot0 = NULL;
cxobj *xtop0 = NULL;
yang_stmt *yspec1;
if (api_path_fmt01 == NULL){
clicon_err(OE_FATAL, EINVAL, "arg is NULL");
goto done;
}
if ((xtop0 = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (yang_path_arg(yspec0, mtpoint, &yu) < 0)
goto done;
if (yu == NULL){
clicon_err(OE_FATAL, 0, "yu not found");
goto done;
}
if (yang_mount_get(yu, mtpoint, &yspec1) < 0)
goto done;
if (yspec1 == NULL){
clicon_err(OE_FATAL, 0, "yspec1 not found");
goto done;
}
xbot0 = xtop0;
if (xml_nsctx_yangspec(yspec0, &nsc0) < 0)
goto done;
if ((ret = xpath2xml(mtpoint, nsc0, xtop0, yspec0, &xbot0, &ybot0, NULL)) < 0)
goto done;
if (xbot0 == NULL){
clicon_err(OE_YANG, 0, "No xbot");
goto done;
}
if (yang2api_path_fmt(ybot0, 0, &api_path_fmt0) < 0)
goto done;
if (api_path_fmt0 == NULL){
clicon_err(OE_YANG, 0, "No api_path_fmt0");
goto done;
}
cprintf(cb, "%s%s", api_path_fmt0, api_path_fmt1);
if ((*api_path_fmt01 = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (api_path_fmt0)
free(api_path_fmt0);
if (nsc0)
cvec_free(nsc0);
return retval;
}
/*! Modify xml datastore from a callback using xml key format strings
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
* @param[in] argv Vector: <apipathfmt> [<mointpt>], eg "/aaa/%s"
* @param[in] op Operation to perform on database
* @param[in] nsctx Namespace context for last value added
* cvv first contains the complete cli string, and then a set of optional
@ -289,43 +369,62 @@ cli_dbxml(clicon_handle h,
{
int retval = -1;
char *api_path_fmt; /* xml key format */
char *api_path = NULL; /* xml key */
char *api_path_fmt01 = NULL;
char *api_path = NULL;
cg_var *arg;
cbuf *cb = NULL;
yang_stmt *yspec;
cxobj *xbot = NULL; /* xpath, NULL if datastore */
yang_stmt *y = NULL; /* yang spec of xpath */
cxobj *xtop = NULL; /* xpath root */
cxobj *xerr = NULL;
int ret;
cg_var *cv;
int cvv_i = 0;
int cvvi = 0;
char *mtpoint = NULL;
yang_stmt *yspec0 = NULL;
if (cvec_len(argv) != 1){
if (cvec_len(argv) != 1 && cvec_len(argv) != 2){
clicon_err(OE_PLUGIN, EINVAL, "Requires one element to be xml key format string");
goto done;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
/* Top-level yspec */
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
arg = cvec_i(argv, 0);
api_path_fmt = cv_string_get(arg);
if (cvec_len(argv) > 1){
arg = cvec_i(argv, 1);
mtpoint = cv_string_get(arg);
}
/* Remove all keywords */
if (cvec_exclude_keys(cvv) < 0)
goto done;
/* Transform template format string + cvv to actual api-path
* cvv_i indicates if all cvv entries were used
*/
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvv_i) < 0)
goto done;
if (mtpoint){
/* Get and combined api-path01 */
if (mtpoint_paths(yspec0, mtpoint, api_path_fmt, &api_path_fmt01) < 0)
goto done;
/* Transform template format string + cvv to actual api-path
* cvvi indicates if all cvv entries were used
*/
if (api_path_fmt2api_path(api_path_fmt01, cvv, &api_path, &cvvi) < 0)
goto done;
}
else {
/* Only top-level tree */
/* Transform template format string + cvv to actual api-path
* cvvi indicates if all cvv entries were used
*/
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0)
goto done;
}
/* Create config top-of-tree */
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
goto done;
xbot = xtop;
if (api_path){
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
if ((ret = api_path2xml(api_path, yspec0, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
goto done;
if (ret == 0){
if ((cb = cbuf_new()) == NULL){
@ -352,7 +451,7 @@ cli_dbxml(clicon_handle h,
* Discussion: one can claim (1) is "bad" usage but one could see cases where
* you would want to delete a value if it has a specific value but not otherwise
*/
if (cvv_i != cvec_len(cvv))
if (cvvi != cvec_len(cvv))
if (dbxml_body(xbot, cvv) < 0)
goto done;
/* Loop over namespace context and add them to this leaf node */
@ -367,7 +466,7 @@ cli_dbxml(clicon_handle h,
/* Special handling of identityref:s whose body may be: <namespace prefix>:<id>
* Ensure the namespace is declared if it exists in YANG
*/
if ((ret = xml_apply0(xbot, CX_ELMNT, identityref_add_ns, yspec)) < 0)
if ((ret = xml_apply0(xbot, CX_ELMNT, identityref_add_ns, yspec0)) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
@ -379,6 +478,8 @@ cli_dbxml(clicon_handle h,
goto done;
retval = 0;
done:
if (api_path_fmt01)
free(api_path_fmt01);
if (xerr)
xml_free(xerr);
if (cb)
@ -695,23 +796,22 @@ cli_commit(clicon_handle h,
cvec *vars,
cvec *argv)
{
int retval = -1;
uint32_t timeout = 0; /* any non-zero value means "confirmed-commit" */
cg_var *timeout_var;
char *persist = NULL;
char *persist_id = NULL;
int confirmed = (cvec_find_str(vars, "confirmed") != NULL);
int cancel = (cvec_find_str(vars, "cancel") != NULL);
int retval = -1;
uint32_t timeout = 0; /* any non-zero value means "confirmed-commit" */
cg_var *timeout_var;
char *persist = NULL;
char *persist_id = NULL;
int confirmed;
int cancel;
confirmed = (cvec_find_str(vars, "confirmed") != NULL);
cancel = (cvec_find_str(vars, "cancel") != NULL);
if ((timeout_var = cvec_find(vars, "timeout")) != NULL) {
timeout = cv_uint32_get(timeout_var);
clicon_debug(1, "commit confirmed with timeout %ul", timeout);
}
persist = cvec_find_str(vars, "persist-val");
persist_id = cvec_find_str(vars, "persist-id-val");
if (clicon_rpc_commit(h, confirmed, cancel, timeout, persist, persist_id) < 1)
goto done;
retval = 0;

View file

@ -41,6 +41,7 @@
void cli_signal_block(clicon_handle h);
void cli_signal_unblock(clicon_handle h);
int mtpoint_paths(yang_stmt *yspec0, char *mtpoint, char *api_path_fmt1, char **api_path_fmt01);
/* If you do not find a function here it may be in clixon_cli_api.h which is
the external API */

View file

@ -131,10 +131,14 @@ cli_expand_var_generate(clicon_handle h,
int pre,
cbuf *cb)
{
int retval = -1;
char *api_path_fmt = NULL;
int extvalue = 0;
int retval = -1;
char *api_path_fmt = NULL;
int extvalue = 0;
yang_stmt *yspec;
cg_var *cv = NULL;
if ((yspec = ys_spec(ys)) != NULL)
cv = yang_cv_get(yspec);
if (yang_extension_value(ys, "hide", CLIXON_AUTOCLI_NS, &extvalue, NULL) < 0)
goto done;
if (extvalue
@ -152,9 +156,12 @@ cli_expand_var_generate(clicon_handle h,
cprintf(cb, "<%s:%s", yang_argument_get(ys), cvtypestr);
if (options & YANG_OPTIONS_FRACTION_DIGITS)
cprintf(cb, " fraction-digits:%u", fraction_digits);
cprintf(cb, " %s(\"candidate\",\"%s\")>",
cprintf(cb, " %s(\"candidate\",\"%s\"",
GENERATE_EXPAND_XMLDB,
api_path_fmt);
if (cv) /* Add optional mountpoint */
cprintf(cb, ",\"%s\"", cv_string_get(cv));
cprintf(cb, ")>");
retval = 0;
done:
if (api_path_fmt)
@ -176,11 +183,17 @@ cli_callback_generate(clicon_handle h,
{
int retval = -1;
char *api_path_fmt = NULL;
yang_stmt *yspec;
cg_var *cv = NULL;
if ((yspec = ys_spec(ys)) != NULL)
cv = yang_cv_get(yspec);
if (yang2api_path_fmt(ys, 0, &api_path_fmt) < 0)
goto done;
cprintf(cb, ",%s(\"%s\")", GENERATE_CALLBACK,
api_path_fmt);
cprintf(cb, ",%s(\"%s\"", GENERATE_CALLBACK, api_path_fmt);
if (cv) /* Add optional mountpoint */
cprintf(cb, ",\"%s\"", cv_string_get(cv));
cprintf(cb, ")");
retval = 0;
done:
if (api_path_fmt)
@ -886,6 +899,7 @@ yang2cli_container(clicon_handle h,
int compress = 0;
yang_stmt *ymod = NULL;
int extvalue = 0;
int ret;
if (ys_real_module(ys, &ymod) < 0)
goto done;
@ -930,6 +944,15 @@ yang2cli_container(clicon_handle h,
}
#endif
cprintf(cb, ", act-container;{\n");
}
/* Is schema mount-point? */
if (clicon_option_bool(h, "CLICON_YANG_SCHEMA_MOUNT")){
if ((ret = yang_schema_mount_point(ys)) < 0)
goto done;
if (ret){
cprintf(cb, "%*s%s", (level+1)*3, "", "@mountpoint;\n");
}
}
yc = NULL;
while ((yc = yn_each(ys, yc)) != NULL)
@ -1335,6 +1358,8 @@ yang2cli_post(clicon_handle h,
* @param[in] treename Name of tree
* @param[in] xautocli Autocli config tree (instance of clixon-autocli.yang)
* @param[in] printgen Log the generated CLIgen syntax
* @retval 0 OK
* @retval -1 Error
* @note Tie-break of same top-level symbol: prefix is NYI
*/
int

View file

@ -175,7 +175,7 @@ xpath_append(cbuf *cb0,
* @param[in] h clicon handle
* @param[in] name Name of this function (eg "expand_dbvar")
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
* @param[in] argv Arguments given at the callback ("<db>" "<xmlkeyfmt>")
* @param[in] argv Arguments given at the callback: <db> <apipathfmt> [<mointpt>]
* @param[out] commands vector of function pointers to callback functions
* @param[out] helptxt vector of pointers to helptexts
* @see cli_expand_var_generate This is where arg is generated
@ -191,6 +191,7 @@ expand_dbvar(void *h,
int retval = -1;
char *api_path_fmt;
char *api_path = NULL;
char *api_path_fmt01 = NULL;
char *dbstr;
cxobj *xt = NULL;
char *xpath = NULL;
@ -203,7 +204,6 @@ expand_dbvar(void *h,
int i;
char *bodystr0 = NULL; /* previous */
cg_var *cv;
yang_stmt *yspec;
cxobj *xtop = NULL; /* xpath root */
cxobj *xbot = NULL; /* xpath, NULL if datastore */
yang_stmt *y = NULL; /* yang spec of xpath */
@ -214,12 +214,14 @@ expand_dbvar(void *h,
cbuf *cbxpath = NULL;
yang_stmt *ypath;
yang_stmt *ytype;
char *mtpoint = NULL;
yang_stmt *yspec0 = NULL;
if (argv == NULL || cvec_len(argv) != 2){
clicon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <xmlkeyfmt>");
if (argv == NULL || (cvec_len(argv) != 2 && cvec_len(argv) != 3)){
clicon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <apipathfmt> [<mountpt>]");
goto done;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
@ -227,7 +229,7 @@ expand_dbvar(void *h,
clicon_err(OE_PLUGIN, 0, "Error when accessing argument <db>");
goto done;
}
dbstr = cv_string_get(cv);
dbstr = cv_string_get(cv);
if (strcmp(dbstr, "running") != 0 &&
strcmp(dbstr, "candidate") != 0 &&
strcmp(dbstr, "startup") != 0){
@ -239,12 +241,28 @@ expand_dbvar(void *h,
goto done;
}
api_path_fmt = cv_string_get(cv);
/* api_path_fmt = /interface/%s/address/%s
* api_path: --> /interface/eth0/address/.*
* xpath: --> /interface/[name="eth0"]/address
*/
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0)
goto done;
if (cvec_len(argv) > 2){
cv = cvec_i(argv, 2);
mtpoint = cv_string_get(cv);
}
if (mtpoint){
/* Get combined api-path01 */
if (mtpoint_paths(yspec0, mtpoint, api_path_fmt, &api_path_fmt01) < 0)
goto done;
/* Transform template format string + cvv to actual api-path
* cvv_i indicates if all cvv entries were used
*/
if (api_path_fmt2api_path(api_path_fmt01, cvv, &api_path, &cvvi) < 0)
goto done;
}
else {
/* api_path_fmt = /interface/%s/address/%s
* api_path: --> /interface/eth0/address/.*
* xpath: --> /interface/[name="eth0"]/address
*/
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0)
goto done;
}
/* Create config top-of-tree */
if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
goto done;
@ -254,18 +272,20 @@ expand_dbvar(void *h,
* XXX: but y is just the first in this list, there could be other y:s?
*/
if (api_path){
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0)
if ((ret = api_path2xml(api_path, yspec0, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0)
goto done;
if (ret == 0){
// XXX cf cli_dbxml
clixon_netconf_error(xerr, "Expand datastore symbol", NULL);
goto done;
}
}
if (y==NULL)
goto ok;
/* Transform api-path to xpath for netconf */
if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
/* Transform api-path to xpath for netconf */
if (api_path2xpath(api_path, yspec0, &xpath, &nsc, NULL) < 0)
goto done;
if ((cbxpath = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
@ -356,6 +376,8 @@ expand_dbvar(void *h,
ok:
retval = 0;
done:
if (api_path_fmt01)
free(api_path_fmt01);
if (cbxpath)
cbuf_free(cbxpath);
if (xerr)