* 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

@ -55,12 +55,17 @@ Users may have to change how they access the system
Developers may need to change their code
* C-API
* `xml_diff`: removed 1st `yspec` parameter
* `xml2xpath()`: Added `int apostrophe` as 4th parameter, default 0
* This is for being able to choose single or double quote as xpath literal quotes
* `clicon_msg_rcv`: Added `intr` parameter for interrupting on `^C` (default 0)
* Renamed include file: `clixon_backend_handle.h`to `clixon_backend_client.h`
* `candidate_commit()`: validate_level (added in 6.1) marked obsolete
### Minor features
* RFC 8528 YANG schema mount
* Made cli/autocli mount-point-aware
* Internal NETCONF (client <-> backend)
* Ensure message-id increments
* Separated rpc from notification socket in same session

View file

@ -510,8 +510,7 @@ validate_common(clicon_handle h,
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(XML_FLAG_MARK|XML_FLAG_CHANGE));
/* 3. Compute differences */
if (xml_diff(yspec,
td->td_src,
if (xml_diff(td->td_src,
td->td_target,
&td->td_dvec, /* removed: only in running */
&td->td_dlen,
@ -982,8 +981,7 @@ from_client_restart_one(clicon_handle h,
goto done;
/* 3. Compute differences */
if (xml_diff(yspec,
td->td_src,
if (xml_diff(td->td_src,
td->td_target,
&td->td_dvec, /* removed: only in running */
&td->td_dlen,

View file

@ -57,7 +57,6 @@
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <netinet/in.h>
/* cligen */
@ -559,6 +558,8 @@ list_pagination_hdr(clicon_handle h,
* @param[in] xe Request: <rpc><xn></rpc>
* @param[in] content Get config/state/both
* @param[in] db Database name
* @param[in] depth Depth attribute
* @param[in] yspec (Top-level) yang spec
* @param[in] xpath XPath point to object to get
* @param[in] nsc Namespace context of xpath
* @param[in] username

View file

@ -61,7 +61,6 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <libgen.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -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)
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)
@ -700,18 +801,17 @@ cli_commit(clicon_handle h,
cg_var *timeout_var;
char *persist = NULL;
char *persist_id = NULL;
int confirmed;
int cancel;
int confirmed = (cvec_find_str(vars, "confirmed") != NULL);
int cancel = (cvec_find_str(vars, "cancel") != NULL);
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

@ -134,7 +134,11 @@ cli_expand_var_generate(clicon_handle h,
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;
}
@ -239,12 +241,28 @@ expand_dbvar(void *h,
goto done;
}
api_path_fmt = cv_string_get(cv);
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,9 +272,10 @@ 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;
}
@ -264,8 +283,9 @@ expand_dbvar(void *h,
if (y==NULL)
goto ok;
/* Transform api-path to xpath for netconf */
if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
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)

View file

@ -123,7 +123,6 @@
#include <syslog.h>
#include <pwd.h>
#include <ctype.h>
#include <assert.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/socket.h>

View file

@ -63,7 +63,6 @@
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <assert.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h>

View file

@ -201,3 +201,9 @@
* Introduced in 6.1, remove in 6.2
*/
#define AUTOCLI_DEPRECATED_HIDE
/*! Temporar fix for xpath_traverse_canonical for yang schema mount
* Must rewrite function to handle mountpoints, now just ignore errors
* See also
*/
#define XPATH_CANONICAL_SKIP_CHECK

View file

@ -53,10 +53,11 @@ int isxmlns(cxobj *x);
int xmlns_assign(cxobj *x);
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
int xml_diff(yang_stmt *yspec, cxobj *x0, cxobj *x1,
int xml_diff(cxobj *x0, cxobj *x1,
cxobj ***first, int *firstlen,
cxobj ***second, int *secondlen,
cxobj ***changed_x0, cxobj ***changed_x1, int *changedlen);
int xml_tree_equal(cxobj *x0, cxobj *x1);
int xml_tree_prune_flagged_sub(cxobj *xt, int flag, int test, int *upmark);
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
int xml_tree_prune_flags(cxobj *xt, int flags, int mask);

View file

@ -60,7 +60,6 @@ int xml_nsctx_node(cxobj *x, cvec **ncp);
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
int xml_nsctx_cbuf(cbuf *cb, cvec *nsc);
int xml2ns(cxobj *x, char *localname, char **ns);
int xml2ns_recurse(cxobj *x);
int xmlns_set(cxobj *x, char *prefix, char *ns);

View file

@ -150,6 +150,8 @@ int xpath_vec(cxobj *xcur, cvec *nsc, const char *xpformat, cxobj ***vec, siz
int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1, cbuf **cbreason);
int xpath_count(cxobj *xcur, cvec *nsc, const char *xpath, uint32_t *count);
int xml2xpath(cxobj *x, cvec *nsc, int spec, char **xpath);
int xml2xpath(cxobj *x, cvec *nsc, int spec, int apostrophe, char **xpath);
int xpath2xml(char *xpath, cvec *nsc, cxobj *xtop, yang_stmt *ytop,
cxobj **xbotp, yang_stmt **ybotp, cxobj **xerr);
#endif /* _CLIXON_XPATH_H */

View file

@ -54,7 +54,6 @@
* Prototypes
*/
int yang_schema_mount_point(yang_stmt *y);
int yang_mount_get(yang_stmt *yu, char *xpath, yang_stmt **yspec);
int yang_mount_set(yang_stmt *yu, char *xpath, yang_stmt *yspec);
int xml_yang_mount_get(clicon_handle h, cxobj *x, validate_level *vl, yang_stmt **yspec);

View file

@ -227,6 +227,7 @@ clixon_event_unreg_fd(int s,
}
/*! Call a callback function at an absolute time
*
* @param[in] t Absolute (not relative!) timestamp when callback is called
* @param[in] fn Function to call at time t
* @param[in] arg Argument to function fn
@ -241,11 +242,9 @@ clixon_event_unreg_fd(int s,
* }
* @endcode
*
* Note that the timestamp is an absolute timestamp, not relative.
* Note also that the callback is not periodic, you need to make a new
* registration for each period, see example above.
* Note also that the first argument to fn is a dummy, just to get the same
* signature as for file-descriptor callbacks.
* @note The timestamp is an absolute timestamp, not relative.
* @note The callback is not periodic, you need to make a new registration for each period, see example.
* @note The first argument to fn is a dummy, just to get the same signature as for file-descriptor callbacks.
* @see clixon_event_reg_fd
* @see clixon_event_unreg_timeout
*/

View file

@ -1045,7 +1045,7 @@ netconf_missing_choice_xml(cxobj **xret,
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
/* error-path: Path to the element with the missing choice. */
if (xml2xpath(x, NULL, 0, &path) < 0)
if (xml2xpath(x, NULL, 0, 0, &path) < 0)
goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done;
@ -1407,7 +1407,7 @@ netconf_data_not_unique_xml(cxobj **xret,
if (cvec_len(cvk)){
if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL)
goto done;
if (xml2xpath(x, NULL, 0, &path) < 0)
if (xml2xpath(x, NULL, 0, 0, &path) < 0)
goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done;
@ -1465,7 +1465,7 @@ netconf_minmax_elements_xml(cxobj **xret,
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
if (xml_parent(xp)){ /* Dont include root, eg <config> */
if (xml2xpath(xp, NULL, 0, &path) < 0)
if (xml2xpath(xp, NULL, 0, 0, &path) < 0)
goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done;

View file

@ -75,7 +75,6 @@
#include <string.h>
#include <syslog.h>
#include <fcntl.h>
#include <assert.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <netinet/in.h>
@ -97,9 +96,12 @@
#include "clixon_xml_nsctx.h"
#include "clixon_xml_vec.h"
#include "clixon_xml_sort.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_yang_module.h"
#include "clixon_yang_schema_mount.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
#include "clixon_instance_id_parse.h"
@ -664,6 +666,8 @@ api_path2xpath_cvv(cvec *api_path,
cvec *nsc = NULL;
char *val1;
char *decval;
int ret;
int root;
cprintf(xpath, "/");
/* Initialize namespace context */
@ -673,6 +677,7 @@ api_path2xpath_cvv(cvec *api_path,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
root = 1; /* root or mountpoint */
for (i=offset; i<cvec_len(api_path); i++){
cv = cvec_i(api_path, i);
nodeid = cv_name_get(cv);
@ -698,16 +703,16 @@ api_path2xpath_cvv(cvec *api_path,
}
namespace = yang_find_mynamespace(ymod); /* change namespace */
}
if (i == offset && ymod) /* root */
if (root && ymod) /* root */
y = yang_find_datanode(ymod, name);
else
y = yang_find_datanode(y, name);
root = 0;
if (y == NULL){
if (xerr && netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
goto done;
goto fail;
}
/* Get XML/xpath prefix given namespace.
* note different from api-path prefix
*/
@ -790,6 +795,23 @@ api_path2xpath_cvv(cvec *api_path,
cprintf(xpath, "%s:", xprefix);
cprintf(xpath, "%s", name);
}
/* If x/y is mountpoint, pass moint yspec to children */
if ((ret = yang_schema_mount_point(y)) < 0)
goto done;
if (ret == 1){
yang_stmt *y1 = NULL;
if (xml_nsctx_yangspec(yspec, &nsc) < 0)
goto done;
if (yang_mount_get(y, cbuf_get(xpath), &y1) < 0)
goto done;
if (y1 == NULL || yang_keyword_get(y1) != Y_SPEC){
clicon_err(OE_YANG, 0, "No such mountpoint %s", cbuf_get(xpath));
goto done;
}
yspec = y1;
root = 1;
}
if (prefix){
free(prefix);
prefix = NULL;
@ -826,7 +848,7 @@ api_path2xpath_cvv(cvec *api_path,
* @param[in] api_path URI-encoded path expression" (RFC8040 3.5.3)
* @param[in] yspec Yang spec
* @param[out] xpath xpath (use free() to deallocate)
* @param[out] nsc Namespace context of xpath (free w xml_nsctx_free)
* @param[out] nsc Namespace context of xpath (free w cvec_free)
* @param[out] xerr Netconf error message
* @retval 1 OK
* @retval 0 Invalid api_path or associated XML, netconf called
@ -897,9 +919,10 @@ api_path2xpath(char *api_path,
}
/*! Create xml tree from api-path as vector
*
* @param[in] vec APIpath as char* vector
* @param[in] nvec Length of vec
* @param[in] x0 Xpath tree so far
* @param[in] x0 XML tree so far
* @param[in] y0 Yang spec for x0
* @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
@ -945,6 +968,9 @@ api_path2xml_vec(char **vec,
char *namespace = NULL;
cbuf *cberr = NULL;
char *val = NULL;
int ret;
char *xpath = NULL;
cvec *nsc = NULL;
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
if (xbotp)
@ -1112,6 +1138,27 @@ api_path2xml_vec(char **vec,
if (xmlns_set(x, NULL, namespace) < 0)
goto done;
}
/* If x/y is mountpoint, pass moint yspec to children */
if ((ret = yang_schema_mount_point(y)) < 0)
goto done;
if (ret == 1){
yang_stmt *y1 = NULL;
if (xml_nsctx_yangspec(ys_spec(y), &nsc) < 0)
goto done;
if (xml2xpath(x, nsc, 0, 1, &xpath) < 0) // XXX should be canonical
goto done;
if (xpath == NULL){
clicon_err(OE_YANG, 0, "No xpath from xml");
goto done;
}
if (yang_mount_get(y, xpath, &y1) < 0)
goto done;
if (y1 == NULL){
clicon_err(OE_YANG, 0, "No such mountpoint %s", xpath);
goto done;
}
y = y1;
}
if ((retval = api_path2xml_vec(vec+1, nvec-1,
x, y,
nodeclass, strict,
@ -1121,6 +1168,10 @@ api_path2xml_vec(char **vec,
retval = 1; /* OK */
done:
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
if (xpath)
free(xpath);
if (nsc)
cvec_free(nsc);
if (cberr)
cbuf_free(cberr);
if (prefix)
@ -1218,7 +1269,6 @@ api_path2xml(char *api_path,
if (xmlns_assign(xroot) < 0)
goto done;
}
// ok:
retval = 1;
done:
if (cberr)
@ -1241,7 +1291,6 @@ api_path2xml(char *api_path,
*/
int
xml2api_path_1(cxobj *x,
cbuf *cb)
{
int retval = -1;

View file

@ -59,7 +59,6 @@
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -101,7 +101,6 @@ set_signal_flags(int signo,
*oldhandler = sold.sa_handler;
return 0;
#elif defined(HAVE_SIGVEC)
assert(0);
return 0;
#endif
}

View file

@ -49,7 +49,6 @@
#include <string.h>
#include <syslog.h>
#include <fcntl.h>
#include <assert.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <netinet/in.h>

View file

@ -48,7 +48,6 @@
#include <string.h>
#include <syslog.h>
#include <fcntl.h>
#include <assert.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <netinet/in.h>

View file

@ -52,7 +52,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -314,8 +314,8 @@ xml_diff1(cxobj *x0,
cxobj *x1c = NULL; /* x1 child */
yang_stmt *yc0;
yang_stmt *yc1;
char *b0;
char *b1;
char *b2;
int eq;
/* Traverse x0 and x1 in lock-step */
@ -366,12 +366,12 @@ xml_diff1(cxobj *x0,
else
if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
/* if x0c and x1c are leafs w bodies, then they may be changed */
b1 = xml_body(x0c);
b2 = xml_body(x1c);
if (b1 == NULL && b2 == NULL)
b0 = xml_body(x0c);
b1 = xml_body(x1c);
if (b0 == NULL && b1 == NULL)
;
else if (b1 == NULL || b2 == NULL
|| strcmp(b1, b2) != 0
else if (b0 == NULL || b1 == NULL
|| strcmp(b0, b1) != 0
){
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
goto done;
@ -396,7 +396,7 @@ xml_diff1(cxobj *x0,
}
/*! Compute differences between two xml trees
* @param[in] yspec Yang specification
*
* @param[in] x0 First XML tree
* @param[in] x1 Second XML tree
* @param[out] first Pointervector to XML nodes existing in only first tree
@ -406,11 +406,13 @@ xml_diff1(cxobj *x0,
* @param[out] changed_x0 Pointervector to XML nodes changed orig value
* @param[out] changed_x1 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
* @retval 0 OK
* @retval -1 Error
* All xml vectors should be freed after use.
* @see xml_tree_equal same algorithm but do not bother with what has changed
*/
int
xml_diff(yang_stmt *yspec,
cxobj *x0,
xml_diff(cxobj *x0,
cxobj *x1,
cxobj ***first,
int *firstlen,
@ -448,6 +450,85 @@ xml_diff(yang_stmt *yspec,
return retval;
}
/*! Compute if two XML trees are equal or not
*
* @param[in] x0 First XML tree
* @param[in] x1 Second XML tree
* @retval 1 Not equal
* @retval 0 Equal
* @see xml_diff
*/
int
xml_tree_equal(cxobj *x0,
cxobj *x1)
{
int retval = 1; /* Not equal */
int eq;
yang_stmt *yc0;
yang_stmt *yc1;
char *b0;
char *b1;
cxobj *x0c = NULL; /* x0 child */
cxobj *x1c = NULL; /* x1 child */
/* Traverse x0 and x1 in lock-step */
x0c = x1c = NULL;
x0c = xml_child_each(x0, x0c, CX_ELMNT);
x1c = xml_child_each(x1, x1c, CX_ELMNT);
for (;;){
if (x0c == NULL && x1c == NULL)
goto ok;
else if (x0c == NULL){
goto done;
}
else if (x1c == NULL){
goto done;
}
/* Both x0c and x1c exists, check if they are yang-equal. */
eq = xml_cmp(x0c, x1c, 0, 0, NULL);
if (eq < 0){
goto done;
}
else if (eq > 0){
goto done;
}
else{ /* equal */
/* xml-spec NULL could happen with anydata children for example,
* if so, continute compare children but without yang
*/
yc0 = xml_spec(x0c);
yc1 = xml_spec(x1c);
if (yc0 && yc1 && yc0 != yc1){ /* choice */
goto done;
}
else
if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
/* if x0c and x1c are leafs w bodies, then they may be changed */
b0 = xml_body(x0c);
b1 = xml_body(x1c);
if (b0 == NULL && b1 == NULL)
;
else if (b0 == NULL || b1 == NULL
|| strcmp(b0, b1) != 0
){
goto done;
}
}
else {
eq = xml_tree_equal(x0c, x1c);
if (eq)
goto done;
}
}
x0c = xml_child_each(x0, x0c, CX_ELMNT);
x1c = xml_child_each(x1, x1c, CX_ELMNT);
}
ok:
retval = 0;
done:
return retval;
}
/*! Prune everything that does not pass test or have at least a child* does not
*
* @param[in] xt XML tree with some node marked

View file

@ -95,6 +95,7 @@ xml_nsctx_namespace_netconf_default(clicon_handle h)
}
/*! Create and initialize XML namespace context
*
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] ns Set this namespace. If NULL create empty nsctx
* @retval nsc Return namespace context in form of a cvec
@ -126,6 +127,7 @@ xml_nsctx_init(char *prefix,
}
/*! Free XML namespace context
*
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] namespace Cached namespace to set (assume non-null?)
* @retval nsc Return namespace context in form of a cvec
@ -142,6 +144,7 @@ xml_nsctx_free(cvec *nsc)
}
/*! Get namespace given prefix (or NULL for default) from namespace context
*
* @param[in] cvv Namespace context
* @param[in] prefix Namespace prefix, or NULL for default
* @retval ns Cached namespace
@ -159,11 +162,12 @@ xml_nsctx_get(cvec *cvv,
}
/*! Reverse get prefix given namespace
*
* @param[in] cvv Namespace context
* @param[in] ns Namespace
* @param[out] prefix Prefix (direct pointer)
* @retval 0 No prefix found
* @retval 1 Prefix found
* @retval 0 No prefix found
* @note NULL is a valid prefix (default)
*/
int
@ -188,6 +192,7 @@ xml_nsctx_get_prefix(cvec *cvv,
}
/*! Set or replace namespace in namespace context
*
* @param[in] cvv Namespace context
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] ns Cached namespace to set (assume non-null?)
@ -261,6 +266,7 @@ xml_nsctx_node1(cxobj *xn,
}
/*! Create and initialize XML namespace from XML node context
*
* Fully explore all prefix:namespace pairs from context of one node
* @param[in] xn XML node
* @param[out] ncp XML namespace context
@ -297,6 +303,7 @@ xml_nsctx_node(cxobj *xn,
}
/*! Create and initialize XML namespace context from Yang node (non-spec)
*
* Primary use is Yang path statements, eg leafrefs and others
* Fully explore all prefix:namespace pairs from context of one node
* @param[in] yn Yang statement in module tree (or module itself)
@ -394,7 +401,7 @@ xml_nsctx_yang(yang_stmt *yn,
* Also add netconf base namespace: nc , urn:ietf:params:xml:ns:netconf:base:1.0
* Fully explore all prefix:namespace pairs of all yang modules
* @param[in] yspec Yang spec
* @param[out] ncp XML namespace context
* @param[out] ncp XML namespace context (create if does not exist)
* @retval 0 OK
* @retval -1 Error
* @code
@ -416,7 +423,9 @@ xml_nsctx_yangspec(yang_stmt *yspec,
yang_stmt *yprefix;
yang_stmt *ynamespace;
if ((nc = cvec_new(0)) == NULL){
if (ncp && *ncp)
nc = *ncp;
else if ((nc = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new");
goto done;
}
@ -443,6 +452,7 @@ xml_nsctx_yangspec(yang_stmt *yspec,
}
/*! Print a namespace context to a cbuf using xmlns notation
*
* @param[in] *cb CLIgen buf written to
* @param[in] *nsc Namespace context
* @retval 0 OK
@ -531,6 +541,7 @@ xml2ns(cxobj *x,
}
/*! Recursively check prefix / namespaces (and populate ns cache)
*
* @retval 1 OK
* @retval 0 (Some) prefix not found
* @retval -1 Error
@ -563,6 +574,7 @@ xml2ns_recurse(cxobj *xt)
}
/*! Add a namespace attribute to an XML node, either default or specific prefix
*
* @param[in] x XML tree
* @param[in] prefix prefix/ns localname. If NULL then set default xmlns
* @param[in] ns URI namespace (or NULL). Will be copied
@ -636,12 +648,13 @@ xmlns_set_all(cxobj *x,
}
/*! Get prefix of given namespace recursively
*
* @param[in] xn XML node
* @param[in] namespace Namespace
* @param[out] prefixp Pointer to prefix if found
* @retval -1 Error
* @retval 0 No namespace found
* @retval 1 Namespace found, prefix returned in prefixp
* @retval 0 No namespace found
* @retval -1 Error
* @note a namespace can have two or more prefixes, this just returns the first
* @see xml2prefixexists to check a specific pair
*/
@ -700,8 +713,8 @@ xml2prefix(cxobj *xn,
goto done;
}
/*! Add prefix:namespace pair to xml node, set cache, etc
*
* @param[in] x XML node whose namespace should change
* @param[in] xp XML node where namespace attribute should be declared (can be same)
* @param[in] prefix1 Use this prefix

View file

@ -48,7 +48,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -90,12 +90,21 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
#include "clixon_yang_schema_mount.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
#include "clixon_xpath_eval.h"
/* Use apostrophe(') in xpath literals, eg a/[x='foo'], not double-quotes(")
* If not set, use ": a/[x="foo"]
* Advantage with ' is it works well in clispecs, " must be escaped
* @see https://www.w3.org/TR/xpath-10/#NT-Literal
*/
#define XPATH_USE_APOSTROPHE
/*
* Variables
*/
@ -174,7 +183,10 @@ xpath_tree_int2str(int nodetype)
return (char*)clicon_int2str(xpath_tree_map, nodetype);
}
/*! Print XPATH parse tree */
/*! Print XPATH parse tree
*
* @note uses "" instead of '' in printing literals, rule [29] in https://www.w3.org/TR/xpath-10
*/
static int
xpath_tree_print0(cbuf *cb,
xpath_tree *xs,
@ -243,6 +255,8 @@ xpath_tree_print(FILE *f,
/*! Create an xpath string from an xpath tree, ie "unparsing"
* @param[in] xs XPATH tree
* @param[out] xpath XPath string as CLIgen buf
* @retval 0 OK
* @retval -1 Error
* @see xpath_tree_print
*/
int
@ -279,7 +293,11 @@ xpath_tree2cbuf(xpath_tree *xs,
cprintf(xcb, "%s", xs->xs_strnr?xs->xs_strnr:"0");
break;
case XP_PRIME_STR:
#ifdef XPATH_USE_APOSTROPHE
cprintf(xcb, "'%s'", xs->xs_s0?xs->xs_s0:"");
#else
cprintf(xcb, "\"%s\"", xs->xs_s0?xs->xs_s0:"");
#endif
break;
case XP_PRIME_FN:
if (xs->xs_s0)
@ -951,9 +969,10 @@ xpath_vec_bool(cxobj *xcur,
* @retval 1 OK with nsc1 containing the transformed nsc
* @retval 0 XPath failure with reason set to why
* @retval -1 Fatal Error
* XXX Detects mountpoint but is not mountpoint aware, just copies prefixes
*/
static int
traverse_canonical(xpath_tree *xs,
xpath_traverse_canonical(xpath_tree *xs,
yang_stmt *yspec,
cvec *nsc0,
cvec *nsc1,
@ -961,11 +980,12 @@ traverse_canonical(xpath_tree *xs,
{
int retval = -1;
char *prefix0;
char *prefix1;
char *prefix1 = NULL;
char *namespace;
yang_stmt *ymod;
cbuf *cb = NULL;
int ret;
// char *name;
switch (xs->xs_type){
case XP_NODE: /* s0 is namespace prefix, s1 is name */
@ -973,6 +993,7 @@ traverse_canonical(xpath_tree *xs,
if (xs->xs_s1 && strcmp(xs->xs_s1, "*") == 0)
break;
prefix0 = xs->xs_s0;
// name = xs->xs_s1;
if ((namespace = xml_nsctx_get(nsc0, prefix0)) == NULL){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
@ -981,19 +1002,23 @@ traverse_canonical(xpath_tree *xs,
cprintf(cb, "No namespace found for prefix: %s", prefix0);
if (reason)
*reason = cb;
goto failed;
goto fail;
}
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
#ifndef XPATH_CANONICAL_SKIP_CHECK
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "No modules found for namespace: %s", namespace);
cprintf(cb, "No yang found for namespace: %s", namespace);
if (reason)
*reason = cb;
goto failed;
goto fail;
#endif
}
if ((prefix1 = yang_find_myprefix(ymod)) == NULL){
if (ymod == NULL)
prefix1 = prefix0;
else if ((prefix1 = yang_find_myprefix(ymod)) == NULL){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
@ -1001,7 +1026,7 @@ traverse_canonical(xpath_tree *xs,
cprintf(cb, "No prefix found in module: %s", yang_argument_get(ymod));
if (reason)
*reason = cb;
goto failed;
goto fail;
}
if (xml_nsctx_get(nsc1, prefix1) == NULL)
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
@ -1019,21 +1044,21 @@ traverse_canonical(xpath_tree *xs,
break;
}
if (xs->xs_c0){
if ((ret = traverse_canonical(xs->xs_c0, yspec, nsc0, nsc1, reason)) < 0)
if ((ret = xpath_traverse_canonical(xs->xs_c0, yspec, nsc0, nsc1, reason)) < 0)
goto done;
if (ret == 0)
goto failed;
goto fail;
}
if (xs->xs_c1){
if ((ret = traverse_canonical(xs->xs_c1, yspec, nsc0, nsc1, reason)) < 0)
if ((ret = xpath_traverse_canonical(xs->xs_c1, yspec, nsc0, nsc1, reason)) < 0)
goto done;
if (ret == 0)
goto failed;
goto fail;
}
retval = 1;
done:
return retval;
failed:
fail:
retval = 0;
goto done;
}
@ -1092,10 +1117,10 @@ xpath2canonical(const char *xpath0,
/* Traverse tree to find prefixes, transform them to canonical form and
* create a canonical network namespace
*/
if ((ret = traverse_canonical(xpt, yspec, nsc0, nsc1, cbreason)) < 0)
if ((ret = xpath_traverse_canonical(xpt, yspec, nsc0, nsc1, cbreason)) < 0)
goto done;
if (ret == 0)
goto failed;
goto fail;
/* Print tree with new prefixes */
if ((xcb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
@ -1122,7 +1147,7 @@ xpath2canonical(const char *xpath0,
if (xpt)
xpath_tree_free(xpt);
return retval;
failed:
fail:
retval = 0;
goto done;
}
@ -1166,10 +1191,11 @@ xpath_count(cxobj *xcur,
}
/*! Given an XML node, build an xpath recursively to root, internal function
*
* @param[in] x XML object
* @param[in] nsc Namespace context
* @param[in] spec If set, recursively continue only to root without spec
* @param[out] cb XPath string as cbuf.
* @param[in] apostrophe If set, use apostrophe in xpath literals, eg a/[x='foo'], not double-quotes(") * @param[out] cb XPath string as cbuf.
* @retval 0 OK
* @retval -1 Error. eg XML malformed
*/
@ -1177,6 +1203,7 @@ static int
xml2xpath1(cxobj *x,
cvec *nsc,
int spec,
int apostrophe,
cbuf *cb)
{
int retval = -1;
@ -1196,7 +1223,7 @@ xml2xpath1(cxobj *x,
goto ok;
if (spec && xml_spec(x) == NULL)
goto ok;
if (xml2xpath1(xp, nsc, spec, cb) < 0)
if (xml2xpath1(xp, nsc, spec, apostrophe, cb) < 0)
goto done;
if (nsc){
if (xml2ns(x, xml_prefix(x), &namespace) < 0)
@ -1219,10 +1246,18 @@ xml2xpath1(cxobj *x,
keyword = yang_keyword_get(y);
switch (keyword){
case Y_LEAF_LIST:
if (apostrophe){
if ((b = xml_body(x)) != NULL)
cprintf(cb, "[.='%s']", b);
else
cprintf(cb, "[.='']");
}
else{
if ((b = xml_body(x)) != NULL)
cprintf(cb, "[.=\"%s\"]", b);
else
cprintf(cb, "[.=\"\"]");
}
break;
case Y_LIST:
cvk = yang_cvec_get(y);
@ -1234,9 +1269,16 @@ xml2xpath1(cxobj *x,
if ((xb = xml_find(x, keyname)) == NULL)
goto done;
b = xml_body(xb);
#if 1
if (b==NULL || strlen(b)==0)
continue;
#endif
cprintf(cb, "[");
if (prefix)
cprintf(cb, "%s:", prefix);
if (apostrophe)
cprintf(cb, "%s='%s']", keyname, b?b:"");
else
cprintf(cb, "%s=\"%s\"]", keyname, b?b:"");
}
break;
@ -1260,6 +1302,7 @@ xml2xpath1(cxobj *x,
* @param[in] x XML object
* @param[in] nsc Namespace context
* @param[in] spec If set, recursively continue only to root without spec (added in 6.1 for yang mount)
* @param[in] apostrophe If set, use apostrophe in xpath literals, eg a/[x='foo'], not double-quotes(")
* @param[out] xpath Malloced xpath string. Need to free() after use
* @retval 0 OK
* @retval -1 Error. (eg XML malformed)
@ -1267,16 +1310,18 @@ xml2xpath1(cxobj *x,
* char *xpath = NULL;
* cxobj *x;
* ... x is inside an xml tree ...
* if (xml2xpath(x, nsc, 0, &xpath) < 0)
* if (xml2xpath(x, nsc, 0, 0, &xpath) < 0)
* err;
* free(xpath);
* @endcode
* @note x needs to be bound to YANG, see eg xml_bind_yang()
* @note namespaces of xpath is not well-defined, follows xml, should be canonical?
*/
int
xml2xpath(cxobj *x,
cvec *nsc,
int spec,
int apostrophe,
char **xpathp)
{
int retval = -1;
@ -1287,7 +1332,7 @@ xml2xpath(cxobj *x,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xml2xpath1(x, nsc, spec, cb) < 0)
if (xml2xpath1(x, nsc, spec, apostrophe, cb) < 0)
goto done;
/* XXX: see xpath in test statement,.. */
xpath = cbuf_get(cb);
@ -1304,3 +1349,167 @@ xml2xpath(cxobj *x,
cbuf_free(cb);
return retval;
}
/*! Create xml tree from xpath as xpath-tree
*
* @param[in] xs Parsed xpath - xpath_tree
* @param[in] nsc Namespace context for xpath
* @param[in] x0 XML tree so far
* @param[out] xbotp Resulting xml tree (end of xpath) (optional)
* @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK
* @retval 0 Invalid xpath
* @retval -1 Fatal error, clicon_err called
* @see xpath_traverse_canonical
*/
static int
xpath2xml_traverse(xpath_tree *xs,
cvec *nsc,
cxobj *x0,
yang_stmt *y0,
cxobj **xbotp,
yang_stmt **ybotp,
cxobj **xerr)
{
int retval = -1;
int ret;
char *name;
char *prefix;
char *namespace;
char *ns = NULL;
cbuf *cberr = NULL;
cxobj *xc;
yang_stmt *ymod;
yang_stmt *yc;
*xbotp = x0;
*ybotp = y0;
switch (xs->xs_type){
case XP_NODE: /* s0 is namespace prefix, s1 is name */
prefix = xs->xs_s0;
name = xs->xs_s1;
if ((namespace = xml_nsctx_get(nsc, prefix)) == NULL){
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "No namespace found for prefix: %s", prefix);
if (xerr &&
netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
if (yang_keyword_get(y0) == Y_SPEC){ /* top-node */
if ((ymod = yang_find_module_by_namespace(y0, namespace)) == NULL){
cprintf(cberr, "No such yang module namespace");
if (xerr &&
netconf_unknown_element_xml(xerr, "application", namespace, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
y0 = ymod;
}
if ((yc = yang_find_datanode(y0, name)) == NULL){
if (xerr &&
netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
goto done;
goto fail;
}
if ((xc = xml_new(name, x0, CX_ELMNT)) == NULL)
goto done;
if (xml2ns(x0, prefix, &ns) < 0)
goto done;
if (ns == NULL)
if (xmlns_set(xc, NULL, namespace) < 0)
goto done;
*xbotp = xc;
*ybotp = yc;
break;
default:
break;
}
if (xs->xs_c0){
if ((ret = xpath2xml_traverse(xs->xs_c0, nsc, x0, y0, xbotp, ybotp, xerr)) < 0)
goto done;
if (ret == 0){
goto fail;
}
}
if (xs->xs_c1){
x0 = *xbotp;
y0 = *ybotp;
if ((ret = xpath2xml_traverse(xs->xs_c1, nsc, x0, y0, xbotp, ybotp, xerr)) < 0)
goto done;
if (ret == 0){
goto fail;
}
if (xs->xs_type == XP_STEP){
*xbotp = x0;
*ybotp = y0;
}
}
retval = 1;
done:
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
return retval;
fail:
retval = 0;
goto done;
}
/*! Create xml tree from restricted xpath
*
* Create an XML tree from "scratch" using xpath.
* @param[in] xpath (Absolute) XPath
* @param[in] nsc Namespace context for xpath
* @param[in,out] xtop Incoming XML tree
* @param[in] yspec Yang spec for xtop
* @param[out] xbotp Resulting xml tree (end of xpath) (optional)
* @param[out] ybotp Yang spec matching xpathp
* @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK
* @retval 0 Invalid xpath
* @retval -1 Fatal error, clicon_err called
* @see api_path2xml
* @see xml2xpath
* @note xpath is restricted to absolute paths, and simple expressions, eg as "node-identifier"
*/
int
xpath2xml(char *xpath,
cvec *nsc,
cxobj *xtop,
yang_stmt *ytop,
cxobj **xbotp,
yang_stmt **ybotp,
cxobj **xerr)
{
int retval = -1;
cbuf *cberr = NULL;
xpath_tree *xpt = NULL;
clicon_debug(CLIXON_DBG_DETAIL, "%s xpath:%s", __FUNCTION__, xpath);
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (*xpath != '/'){
cprintf(cberr, "Invalid absolute xpath: %s (must start with '/')", xpath);
if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
/* Parse input xpath into an xpath-tree */
if (xpath_parse(xpath, &xpt) < 0)
goto done;
if ((retval = xpath2xml_traverse(xpt, nsc, xtop, ytop, xbotp, ybotp, xerr)) < 1)
goto done;
done:
if (xpt)
xpath_tree_free(xpt);
if (cberr)
cbuf_free(cberr);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -65,7 +65,6 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h> /* NaN */

View file

@ -47,7 +47,6 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h> /* NaN */

View file

@ -47,7 +47,6 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h> /* NaN */

View file

@ -88,6 +88,7 @@ struct yang_stmt{
fraction-digits for fraction-digits
revision (uint32)
unknown-stmt (optional argument)
spec: mount-point xpath
*/
cvec *ys_cvec; /* List of stmt-specific variables
Y_RANGE: range_min, range_max

View file

@ -45,6 +45,10 @@
* 4. yang_schema_mount_statedata(): from get_common/get_statedata to retrieve system state
* 5. yang_schema_yanglib_parse_mount(): from xml_bind_yang to parse and mount
* 6. yang_schema_get_child(): from xmldb_put/text_modify when adding new XML nodes
*
* Note: the xpath used as key in yang unknown cvec is "canonical" in the sense:
* - it uses prefixes of the yang spec of relevance
* - it uses '' not "" in prefixes (eg a[x='foo']. The reason is '' is easier printed in clispecs
*/
#ifdef HAVE_CONFIG_H
@ -157,9 +161,9 @@ yang_mount_get(yang_stmt *yu,
/*! Set yangspec mount-point on yang unknwon node
*
* Stored in a separate structure (not in XML config tree)
* Mount-points are stored in unknown yang cvec
* @param[in] yu Yang unknown node to save the yspecs
* @param[in] xpath Key for yspec on yu
* @param[in] xpath Key for yspec on yu, in canonical form
* @param[in] yspec Yangspec for this mount-point (consumed)
* @retval 0 OK
* @retval -1 Error
@ -173,6 +177,7 @@ yang_mount_set(yang_stmt *yu,
yang_stmt *yspec0;
cvec *cvv;
cg_var *cv;
cg_var *cv2;
if ((cvv = yang_cvec_get(yu)) != NULL &&
(cv = cvec_find(cvv, xpath)) != NULL &&
@ -184,6 +189,16 @@ yang_mount_set(yang_stmt *yu,
}
else if ((cv = yang_cvec_add(yu, CGV_VOID, xpath)) == NULL)
goto done;
if ((cv2 = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if (cv_string_set(cv2, xpath) == NULL){
clicon_err(OE_UNIX, errno, "cv_string_set");
goto done;
}
/* tag yspec with key/xpath */
yang_cv_set(yspec, cv2);
cv_void_set(cv, yspec);
retval = 0;
done:
@ -224,7 +239,7 @@ xml_yang_mount_get(clicon_handle h,
// XXX hardcoded prefix: yangmnt
if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL)
goto ok;
if (xml2xpath(xt, NULL, 1, &xpath) < 0)
if (xml2xpath(xt, NULL, 1, 0, &xpath) < 0)
goto done;
if (yang_mount_get(yu, xpath, yspec) < 0)
goto done;
@ -261,7 +276,7 @@ xml_yang_mount_set(cxobj *x,
(yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){
goto done;
}
if (xml2xpath(x, NULL, 1, &xpath) < 0)
if (xml2xpath(x, NULL, 1, 0, &xpath) < 0)
goto done;
if (yang_mount_set(yu, xpath, yspec) < 0)
goto done;

View file

@ -76,7 +76,6 @@
#include <arpa/inet.h>
#include <regex.h>
#include <syslog.h>
#include <assert.h>
#include <regex.h>
#include <netinet/in.h>
#include <sys/param.h>

View file

@ -59,8 +59,9 @@ expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:exampl
new "xpath canonical form (no default should fail)"
expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b 2>&1)" 0 "/x/j:y: No namespace found for prefix"
new "xpath canonical form (wrong namespace should fail)"
expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b 2>&1)" 0 "/i:x/j:y: No modules found for namespace"
# comment as long as XPATH_CANONICAL_SKIP_CHECK
#new "xpath canonical form (wrong namespace should fail)"
#expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b 2>&1)" 0 "/i:x/j:y: No yang found for namespace"
rm -rf $dir

View file

@ -72,7 +72,6 @@ Example sshd-config (-c option):n
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <assert.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

View file

@ -383,7 +383,7 @@ main(int argc,
char *xpathi = NULL;
for (i=0; i<xc->xc_size; i++){
xi = xc->xc_nodeset[i];
if (xml2xpath(xi, nsc, 0, &xpathi) < 0)
if (xml2xpath(xi, nsc, 0, 0, &xpathi) < 0)
goto done;
fprintf(stdout, "Inverse: %s\n", xpathi);
if (xpathi){