* 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:
parent
1e136bc9df
commit
da2edceb7e
37 changed files with 658 additions and 145 deletions
|
|
@ -55,12 +55,17 @@ Users may have to change how they access the system
|
||||||
Developers may need to change their code
|
Developers may need to change their code
|
||||||
|
|
||||||
* C-API
|
* 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)
|
* `clicon_msg_rcv`: Added `intr` parameter for interrupting on `^C` (default 0)
|
||||||
* Renamed include file: `clixon_backend_handle.h`to `clixon_backend_client.h`
|
* Renamed include file: `clixon_backend_handle.h`to `clixon_backend_client.h`
|
||||||
* `candidate_commit()`: validate_level (added in 6.1) marked obsolete
|
* `candidate_commit()`: validate_level (added in 6.1) marked obsolete
|
||||||
|
|
||||||
### Minor features
|
### Minor features
|
||||||
|
|
||||||
|
* RFC 8528 YANG schema mount
|
||||||
|
* Made cli/autocli mount-point-aware
|
||||||
* Internal NETCONF (client <-> backend)
|
* Internal NETCONF (client <-> backend)
|
||||||
* Ensure message-id increments
|
* Ensure message-id increments
|
||||||
* Separated rpc from notification socket in same session
|
* Separated rpc from notification socket in same session
|
||||||
|
|
|
||||||
|
|
@ -510,8 +510,7 @@ validate_common(clicon_handle h,
|
||||||
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||||
(void*)(XML_FLAG_MARK|XML_FLAG_CHANGE));
|
(void*)(XML_FLAG_MARK|XML_FLAG_CHANGE));
|
||||||
/* 3. Compute differences */
|
/* 3. Compute differences */
|
||||||
if (xml_diff(yspec,
|
if (xml_diff(td->td_src,
|
||||||
td->td_src,
|
|
||||||
td->td_target,
|
td->td_target,
|
||||||
&td->td_dvec, /* removed: only in running */
|
&td->td_dvec, /* removed: only in running */
|
||||||
&td->td_dlen,
|
&td->td_dlen,
|
||||||
|
|
@ -982,8 +981,7 @@ from_client_restart_one(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* 3. Compute differences */
|
/* 3. Compute differences */
|
||||||
if (xml_diff(yspec,
|
if (xml_diff(td->td_src,
|
||||||
td->td_src,
|
|
||||||
td->td_target,
|
td->td_target,
|
||||||
&td->td_dvec, /* removed: only in running */
|
&td->td_dvec, /* removed: only in running */
|
||||||
&td->td_dlen,
|
&td->td_dlen,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
|
|
@ -559,6 +558,8 @@ list_pagination_hdr(clicon_handle h,
|
||||||
* @param[in] xe Request: <rpc><xn></rpc>
|
* @param[in] xe Request: <rpc><xn></rpc>
|
||||||
* @param[in] content Get config/state/both
|
* @param[in] content Get config/state/both
|
||||||
* @param[in] db Database name
|
* @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] xpath XPath point to object to get
|
||||||
* @param[in] nsc Namespace context of xpath
|
* @param[in] nsc Namespace context of xpath
|
||||||
* @param[in] username
|
* @param[in] username
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
|
||||||
|
|
@ -245,8 +245,8 @@ identityref_add_ns(cxobj *x,
|
||||||
if (ns == NULL &&
|
if (ns == NULL &&
|
||||||
(yns = yang_find_module_by_prefix_yspec(yspec, pf)) != NULL){
|
(yns = yang_find_module_by_prefix_yspec(yspec, pf)) != NULL){
|
||||||
if ((ns = yang_find_mynamespace(yns)) != NULL)
|
if ((ns = yang_find_mynamespace(yns)) != NULL)
|
||||||
if (xmlns_set(x, pf, ns) < 0)
|
if (xmlns_set(x, pf, ns) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -260,10 +260,90 @@ identityref_add_ns(cxobj *x,
|
||||||
return retval;
|
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
|
/*! Modify xml datastore from a callback using xml key format strings
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cvv Vector of cli string and instantiated variables
|
* @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] op Operation to perform on database
|
||||||
* @param[in] nsctx Namespace context for last value added
|
* @param[in] nsctx Namespace context for last value added
|
||||||
* cvv first contains the complete cli string, and then a set of optional
|
* 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;
|
int retval = -1;
|
||||||
char *api_path_fmt; /* xml key format */
|
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;
|
cg_var *arg;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
yang_stmt *yspec;
|
|
||||||
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
||||||
yang_stmt *y = NULL; /* yang spec of xpath */
|
yang_stmt *y = NULL; /* yang spec of xpath */
|
||||||
cxobj *xtop = NULL; /* xpath root */
|
cxobj *xtop = NULL; /* xpath root */
|
||||||
cxobj *xerr = NULL;
|
cxobj *xerr = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
cg_var *cv;
|
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");
|
clicon_err(OE_PLUGIN, EINVAL, "Requires one element to be xml key format string");
|
||||||
goto done;
|
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");
|
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
arg = cvec_i(argv, 0);
|
arg = cvec_i(argv, 0);
|
||||||
api_path_fmt = cv_string_get(arg);
|
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 */
|
/* Remove all keywords */
|
||||||
if (cvec_exclude_keys(cvv) < 0)
|
if (cvec_exclude_keys(cvv) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Transform template format string + cvv to actual api-path
|
if (mtpoint){
|
||||||
* cvv_i indicates if all cvv entries were used
|
/* Get and combined api-path01 */
|
||||||
*/
|
if (mtpoint_paths(yspec0, mtpoint, api_path_fmt, &api_path_fmt01) < 0)
|
||||||
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvv_i) < 0)
|
goto done;
|
||||||
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 */
|
/* Create config top-of-tree */
|
||||||
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
|
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
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;
|
goto done;
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((cb = cbuf_new()) == NULL){
|
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
|
* 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
|
* 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)
|
if (dbxml_body(xbot, cvv) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Loop over namespace context and add them to this leaf node */
|
/* 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>
|
/* Special handling of identityref:s whose body may be: <namespace prefix>:<id>
|
||||||
* Ensure the namespace is declared if it exists in YANG
|
* 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;
|
goto done;
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
|
@ -379,6 +478,8 @@ cli_dbxml(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (api_path_fmt01)
|
||||||
|
free(api_path_fmt01);
|
||||||
if (xerr)
|
if (xerr)
|
||||||
xml_free(xerr);
|
xml_free(xerr);
|
||||||
if (cb)
|
if (cb)
|
||||||
|
|
@ -695,23 +796,22 @@ cli_commit(clicon_handle h,
|
||||||
cvec *vars,
|
cvec *vars,
|
||||||
cvec *argv)
|
cvec *argv)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
uint32_t timeout = 0; /* any non-zero value means "confirmed-commit" */
|
uint32_t timeout = 0; /* any non-zero value means "confirmed-commit" */
|
||||||
cg_var *timeout_var;
|
cg_var *timeout_var;
|
||||||
char *persist = NULL;
|
char *persist = NULL;
|
||||||
char *persist_id = NULL;
|
char *persist_id = NULL;
|
||||||
|
int confirmed;
|
||||||
int confirmed = (cvec_find_str(vars, "confirmed") != NULL);
|
int cancel;
|
||||||
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) {
|
if ((timeout_var = cvec_find(vars, "timeout")) != NULL) {
|
||||||
timeout = cv_uint32_get(timeout_var);
|
timeout = cv_uint32_get(timeout_var);
|
||||||
clicon_debug(1, "commit confirmed with timeout %ul", timeout);
|
clicon_debug(1, "commit confirmed with timeout %ul", timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
persist = cvec_find_str(vars, "persist-val");
|
persist = cvec_find_str(vars, "persist-val");
|
||||||
persist_id = cvec_find_str(vars, "persist-id-val");
|
persist_id = cvec_find_str(vars, "persist-id-val");
|
||||||
|
|
||||||
if (clicon_rpc_commit(h, confirmed, cancel, timeout, persist, persist_id) < 1)
|
if (clicon_rpc_commit(h, confirmed, cancel, timeout, persist, persist_id) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
|
|
||||||
void cli_signal_block(clicon_handle h);
|
void cli_signal_block(clicon_handle h);
|
||||||
void cli_signal_unblock(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
|
/* If you do not find a function here it may be in clixon_cli_api.h which is
|
||||||
the external API */
|
the external API */
|
||||||
|
|
|
||||||
|
|
@ -131,10 +131,14 @@ cli_expand_var_generate(clicon_handle h,
|
||||||
int pre,
|
int pre,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *api_path_fmt = NULL;
|
char *api_path_fmt = NULL;
|
||||||
int extvalue = 0;
|
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)
|
if (yang_extension_value(ys, "hide", CLIXON_AUTOCLI_NS, &extvalue, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (extvalue
|
if (extvalue
|
||||||
|
|
@ -152,9 +156,12 @@ cli_expand_var_generate(clicon_handle h,
|
||||||
cprintf(cb, "<%s:%s", yang_argument_get(ys), cvtypestr);
|
cprintf(cb, "<%s:%s", yang_argument_get(ys), cvtypestr);
|
||||||
if (options & YANG_OPTIONS_FRACTION_DIGITS)
|
if (options & YANG_OPTIONS_FRACTION_DIGITS)
|
||||||
cprintf(cb, " fraction-digits:%u", fraction_digits);
|
cprintf(cb, " fraction-digits:%u", fraction_digits);
|
||||||
cprintf(cb, " %s(\"candidate\",\"%s\")>",
|
cprintf(cb, " %s(\"candidate\",\"%s\"",
|
||||||
GENERATE_EXPAND_XMLDB,
|
GENERATE_EXPAND_XMLDB,
|
||||||
api_path_fmt);
|
api_path_fmt);
|
||||||
|
if (cv) /* Add optional mountpoint */
|
||||||
|
cprintf(cb, ",\"%s\"", cv_string_get(cv));
|
||||||
|
cprintf(cb, ")>");
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (api_path_fmt)
|
if (api_path_fmt)
|
||||||
|
|
@ -176,11 +183,17 @@ cli_callback_generate(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *api_path_fmt = NULL;
|
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)
|
if (yang2api_path_fmt(ys, 0, &api_path_fmt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, ",%s(\"%s\")", GENERATE_CALLBACK,
|
cprintf(cb, ",%s(\"%s\"", GENERATE_CALLBACK, api_path_fmt);
|
||||||
api_path_fmt);
|
if (cv) /* Add optional mountpoint */
|
||||||
|
cprintf(cb, ",\"%s\"", cv_string_get(cv));
|
||||||
|
cprintf(cb, ")");
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (api_path_fmt)
|
if (api_path_fmt)
|
||||||
|
|
@ -886,6 +899,7 @@ yang2cli_container(clicon_handle h,
|
||||||
int compress = 0;
|
int compress = 0;
|
||||||
yang_stmt *ymod = NULL;
|
yang_stmt *ymod = NULL;
|
||||||
int extvalue = 0;
|
int extvalue = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (ys_real_module(ys, &ymod) < 0)
|
if (ys_real_module(ys, &ymod) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -930,6 +944,15 @@ yang2cli_container(clicon_handle h,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
cprintf(cb, ", act-container;{\n");
|
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;
|
yc = NULL;
|
||||||
while ((yc = yn_each(ys, 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] treename Name of tree
|
||||||
* @param[in] xautocli Autocli config tree (instance of clixon-autocli.yang)
|
* @param[in] xautocli Autocli config tree (instance of clixon-autocli.yang)
|
||||||
* @param[in] printgen Log the generated CLIgen syntax
|
* @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
|
* @note Tie-break of same top-level symbol: prefix is NYI
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ xpath_append(cbuf *cb0,
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] name Name of this function (eg "expand_dbvar")
|
* @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] 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] commands vector of function pointers to callback functions
|
||||||
* @param[out] helptxt vector of pointers to helptexts
|
* @param[out] helptxt vector of pointers to helptexts
|
||||||
* @see cli_expand_var_generate This is where arg is generated
|
* @see cli_expand_var_generate This is where arg is generated
|
||||||
|
|
@ -191,6 +191,7 @@ expand_dbvar(void *h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *api_path_fmt;
|
char *api_path_fmt;
|
||||||
char *api_path = NULL;
|
char *api_path = NULL;
|
||||||
|
char *api_path_fmt01 = NULL;
|
||||||
char *dbstr;
|
char *dbstr;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
char *xpath = NULL;
|
char *xpath = NULL;
|
||||||
|
|
@ -203,7 +204,6 @@ expand_dbvar(void *h,
|
||||||
int i;
|
int i;
|
||||||
char *bodystr0 = NULL; /* previous */
|
char *bodystr0 = NULL; /* previous */
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
yang_stmt *yspec;
|
|
||||||
cxobj *xtop = NULL; /* xpath root */
|
cxobj *xtop = NULL; /* xpath root */
|
||||||
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
||||||
yang_stmt *y = NULL; /* yang spec of xpath */
|
yang_stmt *y = NULL; /* yang spec of xpath */
|
||||||
|
|
@ -214,12 +214,14 @@ expand_dbvar(void *h,
|
||||||
cbuf *cbxpath = NULL;
|
cbuf *cbxpath = NULL;
|
||||||
yang_stmt *ypath;
|
yang_stmt *ypath;
|
||||||
yang_stmt *ytype;
|
yang_stmt *ytype;
|
||||||
|
char *mtpoint = NULL;
|
||||||
|
yang_stmt *yspec0 = NULL;
|
||||||
|
|
||||||
if (argv == NULL || cvec_len(argv) != 2){
|
if (argv == NULL || (cvec_len(argv) != 2 && cvec_len(argv) != 3)){
|
||||||
clicon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <xmlkeyfmt>");
|
clicon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <apipathfmt> [<mountpt>]");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +229,7 @@ expand_dbvar(void *h,
|
||||||
clicon_err(OE_PLUGIN, 0, "Error when accessing argument <db>");
|
clicon_err(OE_PLUGIN, 0, "Error when accessing argument <db>");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
dbstr = cv_string_get(cv);
|
dbstr = cv_string_get(cv);
|
||||||
if (strcmp(dbstr, "running") != 0 &&
|
if (strcmp(dbstr, "running") != 0 &&
|
||||||
strcmp(dbstr, "candidate") != 0 &&
|
strcmp(dbstr, "candidate") != 0 &&
|
||||||
strcmp(dbstr, "startup") != 0){
|
strcmp(dbstr, "startup") != 0){
|
||||||
|
|
@ -239,12 +241,28 @@ expand_dbvar(void *h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
api_path_fmt = cv_string_get(cv);
|
api_path_fmt = cv_string_get(cv);
|
||||||
/* api_path_fmt = /interface/%s/address/%s
|
if (cvec_len(argv) > 2){
|
||||||
* api_path: --> /interface/eth0/address/.*
|
cv = cvec_i(argv, 2);
|
||||||
* xpath: --> /interface/[name="eth0"]/address
|
mtpoint = cv_string_get(cv);
|
||||||
*/
|
}
|
||||||
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0)
|
if (mtpoint){
|
||||||
goto done;
|
/* 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 */
|
/* Create config top-of-tree */
|
||||||
if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
|
if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
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?
|
* XXX: but y is just the first in this list, there could be other y:s?
|
||||||
*/
|
*/
|
||||||
if (api_path){
|
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;
|
goto done;
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
|
// XXX cf cli_dbxml
|
||||||
clixon_netconf_error(xerr, "Expand datastore symbol", NULL);
|
clixon_netconf_error(xerr, "Expand datastore symbol", NULL);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (y==NULL)
|
if (y==NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
/* Transform api-path to xpath for netconf */
|
/* 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;
|
goto done;
|
||||||
|
|
||||||
if ((cbxpath = cbuf_new()) == NULL){
|
if ((cbxpath = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -356,6 +376,8 @@ expand_dbvar(void *h,
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (api_path_fmt01)
|
||||||
|
free(api_path_fmt01);
|
||||||
if (cbxpath)
|
if (cbxpath)
|
||||||
cbuf_free(cbxpath);
|
cbuf_free(cbxpath);
|
||||||
if (xerr)
|
if (xerr)
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/* net-snmp */
|
/* net-snmp */
|
||||||
#include <net-snmp/net-snmp-config.h>
|
#include <net-snmp/net-snmp-config.h>
|
||||||
|
|
|
||||||
|
|
@ -201,3 +201,9 @@
|
||||||
* Introduced in 6.1, remove in 6.2
|
* Introduced in 6.1, remove in 6.2
|
||||||
*/
|
*/
|
||||||
#define AUTOCLI_DEPRECATED_HIDE
|
#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
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,11 @@ int isxmlns(cxobj *x);
|
||||||
int xmlns_assign(cxobj *x);
|
int xmlns_assign(cxobj *x);
|
||||||
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
||||||
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
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 ***first, int *firstlen,
|
||||||
cxobj ***second, int *secondlen,
|
cxobj ***second, int *secondlen,
|
||||||
cxobj ***changed_x0, cxobj ***changed_x1, int *changedlen);
|
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_sub(cxobj *xt, int flag, int test, int *upmark);
|
||||||
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
|
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
|
||||||
int xml_tree_prune_flags(cxobj *xt, int flags, int mask);
|
int xml_tree_prune_flags(cxobj *xt, int flags, int mask);
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ int xml_nsctx_node(cxobj *x, cvec **ncp);
|
||||||
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
||||||
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
|
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
|
||||||
int xml_nsctx_cbuf(cbuf *cb, cvec *nsc);
|
int xml_nsctx_cbuf(cbuf *cb, cvec *nsc);
|
||||||
|
|
||||||
int xml2ns(cxobj *x, char *localname, char **ns);
|
int xml2ns(cxobj *x, char *localname, char **ns);
|
||||||
int xml2ns_recurse(cxobj *x);
|
int xml2ns_recurse(cxobj *x);
|
||||||
int xmlns_set(cxobj *x, char *prefix, char *ns);
|
int xmlns_set(cxobj *x, char *prefix, char *ns);
|
||||||
|
|
|
||||||
|
|
@ -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 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 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 */
|
#endif /* _CLIXON_XPATH_H */
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int yang_schema_mount_point(yang_stmt *y);
|
int yang_schema_mount_point(yang_stmt *y);
|
||||||
|
|
||||||
int yang_mount_get(yang_stmt *yu, char *xpath, yang_stmt **yspec);
|
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 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);
|
int xml_yang_mount_get(clicon_handle h, cxobj *x, validate_level *vl, yang_stmt **yspec);
|
||||||
|
|
|
||||||
|
|
@ -1160,7 +1160,7 @@ text_modify_top(clicon_handle h,
|
||||||
* @param[out] cbret Initialized cligen buffer. On exit contains XML if retval == 0
|
* @param[out] cbret Initialized cligen buffer. On exit contains XML if retval == 0
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* @retval 0 Failed, cbret contains error xml message
|
* @retval 0 Failed, cbret contains error xml message
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* The xml may contain the "operation" attribute which defines the operation.
|
* The xml may contain the "operation" attribute which defines the operation.
|
||||||
* @code
|
* @code
|
||||||
* cxobj *xt;
|
* cxobj *xt;
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@ clixon_event_unreg_fd(int s,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Call a callback function at an absolute time
|
/*! Call a callback function at an absolute time
|
||||||
|
*
|
||||||
* @param[in] t Absolute (not relative!) timestamp when callback is called
|
* @param[in] t Absolute (not relative!) timestamp when callback is called
|
||||||
* @param[in] fn Function to call at time t
|
* @param[in] fn Function to call at time t
|
||||||
* @param[in] arg Argument to function fn
|
* @param[in] arg Argument to function fn
|
||||||
|
|
@ -241,11 +242,9 @@ clixon_event_unreg_fd(int s,
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* Note that the timestamp is an absolute timestamp, not relative.
|
* @note The timestamp is an absolute timestamp, not relative.
|
||||||
* Note also that the callback is not periodic, you need to make a new
|
* @note The callback is not periodic, you need to make a new registration for each period, see example.
|
||||||
* registration for each period, see example above.
|
* @note The first argument to fn is a dummy, just to get the same signature as for file-descriptor callbacks.
|
||||||
* Note also that 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_reg_fd
|
||||||
* @see clixon_event_unreg_timeout
|
* @see clixon_event_unreg_timeout
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1045,7 +1045,7 @@ netconf_missing_choice_xml(cxobj **xret,
|
||||||
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
/* error-path: Path to the element with the missing choice. */
|
/* 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;
|
goto done;
|
||||||
if (xml_chardata_encode(&encpath, "%s", path) < 0)
|
if (xml_chardata_encode(&encpath, "%s", path) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1407,7 +1407,7 @@ netconf_data_not_unique_xml(cxobj **xret,
|
||||||
if (cvec_len(cvk)){
|
if (cvec_len(cvk)){
|
||||||
if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL)
|
if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml2xpath(x, NULL, 0, &path) < 0)
|
if (xml2xpath(x, NULL, 0, 0, &path) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_chardata_encode(&encpath, "%s", path) < 0)
|
if (xml_chardata_encode(&encpath, "%s", path) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1465,7 +1465,7 @@ netconf_minmax_elements_xml(cxobj **xret,
|
||||||
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_parent(xp)){ /* Dont include root, eg <config> */
|
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;
|
goto done;
|
||||||
if (xml_chardata_encode(&encpath, "%s", path) < 0)
|
if (xml_chardata_encode(&encpath, "%s", path) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
@ -97,9 +96,12 @@
|
||||||
#include "clixon_xml_nsctx.h"
|
#include "clixon_xml_nsctx.h"
|
||||||
#include "clixon_xml_vec.h"
|
#include "clixon_xml_vec.h"
|
||||||
#include "clixon_xml_sort.h"
|
#include "clixon_xml_sort.h"
|
||||||
|
#include "clixon_xpath_ctx.h"
|
||||||
|
#include "clixon_xpath.h"
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
|
#include "clixon_yang_schema_mount.h"
|
||||||
#include "clixon_path.h"
|
#include "clixon_path.h"
|
||||||
#include "clixon_api_path_parse.h"
|
#include "clixon_api_path_parse.h"
|
||||||
#include "clixon_instance_id_parse.h"
|
#include "clixon_instance_id_parse.h"
|
||||||
|
|
@ -664,6 +666,8 @@ api_path2xpath_cvv(cvec *api_path,
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
char *val1;
|
char *val1;
|
||||||
char *decval;
|
char *decval;
|
||||||
|
int ret;
|
||||||
|
int root;
|
||||||
|
|
||||||
cprintf(xpath, "/");
|
cprintf(xpath, "/");
|
||||||
/* Initialize namespace context */
|
/* Initialize namespace context */
|
||||||
|
|
@ -673,6 +677,7 @@ api_path2xpath_cvv(cvec *api_path,
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
root = 1; /* root or mountpoint */
|
||||||
for (i=offset; i<cvec_len(api_path); i++){
|
for (i=offset; i<cvec_len(api_path); i++){
|
||||||
cv = cvec_i(api_path, i);
|
cv = cvec_i(api_path, i);
|
||||||
nodeid = cv_name_get(cv);
|
nodeid = cv_name_get(cv);
|
||||||
|
|
@ -698,16 +703,16 @@ api_path2xpath_cvv(cvec *api_path,
|
||||||
}
|
}
|
||||||
namespace = yang_find_mynamespace(ymod); /* change namespace */
|
namespace = yang_find_mynamespace(ymod); /* change namespace */
|
||||||
}
|
}
|
||||||
if (i == offset && ymod) /* root */
|
if (root && ymod) /* root */
|
||||||
y = yang_find_datanode(ymod, name);
|
y = yang_find_datanode(ymod, name);
|
||||||
else
|
else
|
||||||
y = yang_find_datanode(y, name);
|
y = yang_find_datanode(y, name);
|
||||||
|
root = 0;
|
||||||
if (y == NULL){
|
if (y == NULL){
|
||||||
if (xerr && netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
|
if (xerr && netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get XML/xpath prefix given namespace.
|
/* Get XML/xpath prefix given namespace.
|
||||||
* note different from api-path prefix
|
* note different from api-path prefix
|
||||||
*/
|
*/
|
||||||
|
|
@ -790,6 +795,23 @@ api_path2xpath_cvv(cvec *api_path,
|
||||||
cprintf(xpath, "%s:", xprefix);
|
cprintf(xpath, "%s:", xprefix);
|
||||||
cprintf(xpath, "%s", name);
|
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){
|
if (prefix){
|
||||||
free(prefix);
|
free(prefix);
|
||||||
prefix = NULL;
|
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] api_path URI-encoded path expression" (RFC8040 3.5.3)
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[out] xpath xpath (use free() to deallocate)
|
* @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
|
* @param[out] xerr Netconf error message
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* @retval 0 Invalid api_path or associated XML, netconf called
|
* @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
|
/*! Create xml tree from api-path as vector
|
||||||
|
*
|
||||||
* @param[in] vec APIpath as char* vector
|
* @param[in] vec APIpath as char* vector
|
||||||
* @param[in] nvec Length of vec
|
* @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] y0 Yang spec for x0
|
||||||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
|
* @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;
|
char *namespace = NULL;
|
||||||
cbuf *cberr = NULL;
|
cbuf *cberr = NULL;
|
||||||
char *val = NULL;
|
char *val = NULL;
|
||||||
|
int ret;
|
||||||
|
char *xpath = NULL;
|
||||||
|
cvec *nsc = NULL;
|
||||||
|
|
||||||
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
||||||
if (xbotp)
|
if (xbotp)
|
||||||
|
|
@ -1112,6 +1138,27 @@ api_path2xml_vec(char **vec,
|
||||||
if (xmlns_set(x, NULL, namespace) < 0)
|
if (xmlns_set(x, NULL, namespace) < 0)
|
||||||
goto done;
|
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,
|
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
||||||
x, y,
|
x, y,
|
||||||
nodeclass, strict,
|
nodeclass, strict,
|
||||||
|
|
@ -1121,6 +1168,10 @@ api_path2xml_vec(char **vec,
|
||||||
retval = 1; /* OK */
|
retval = 1; /* OK */
|
||||||
done:
|
done:
|
||||||
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (xpath)
|
||||||
|
free(xpath);
|
||||||
|
if (nsc)
|
||||||
|
cvec_free(nsc);
|
||||||
if (cberr)
|
if (cberr)
|
||||||
cbuf_free(cberr);
|
cbuf_free(cberr);
|
||||||
if (prefix)
|
if (prefix)
|
||||||
|
|
@ -1218,7 +1269,6 @@ api_path2xml(char *api_path,
|
||||||
if (xmlns_assign(xroot) < 0)
|
if (xmlns_assign(xroot) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
// ok:
|
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
if (cberr)
|
if (cberr)
|
||||||
|
|
@ -1241,7 +1291,6 @@ api_path2xml(char *api_path,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml2api_path_1(cxobj *x,
|
xml2api_path_1(cxobj *x,
|
||||||
|
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ set_signal_flags(int signo,
|
||||||
*oldhandler = sold.sa_handler;
|
*oldhandler = sold.sa_handler;
|
||||||
return 0;
|
return 0;
|
||||||
#elif defined(HAVE_SIGVEC)
|
#elif defined(HAVE_SIGVEC)
|
||||||
assert(0);
|
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
|
||||||
|
|
@ -314,8 +314,8 @@ xml_diff1(cxobj *x0,
|
||||||
cxobj *x1c = NULL; /* x1 child */
|
cxobj *x1c = NULL; /* x1 child */
|
||||||
yang_stmt *yc0;
|
yang_stmt *yc0;
|
||||||
yang_stmt *yc1;
|
yang_stmt *yc1;
|
||||||
|
char *b0;
|
||||||
char *b1;
|
char *b1;
|
||||||
char *b2;
|
|
||||||
int eq;
|
int eq;
|
||||||
|
|
||||||
/* Traverse x0 and x1 in lock-step */
|
/* Traverse x0 and x1 in lock-step */
|
||||||
|
|
@ -366,12 +366,12 @@ xml_diff1(cxobj *x0,
|
||||||
else
|
else
|
||||||
if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
|
if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
|
||||||
/* if x0c and x1c are leafs w bodies, then they may be changed */
|
/* if x0c and x1c are leafs w bodies, then they may be changed */
|
||||||
b1 = xml_body(x0c);
|
b0 = xml_body(x0c);
|
||||||
b2 = xml_body(x1c);
|
b1 = xml_body(x1c);
|
||||||
if (b1 == NULL && b2 == NULL)
|
if (b0 == NULL && b1 == NULL)
|
||||||
;
|
;
|
||||||
else if (b1 == NULL || b2 == NULL
|
else if (b0 == NULL || b1 == NULL
|
||||||
|| strcmp(b1, b2) != 0
|
|| strcmp(b0, b1) != 0
|
||||||
){
|
){
|
||||||
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
|
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -396,7 +396,7 @@ xml_diff1(cxobj *x0,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Compute differences between two xml trees
|
/*! Compute differences between two xml trees
|
||||||
* @param[in] yspec Yang specification
|
*
|
||||||
* @param[in] x0 First XML tree
|
* @param[in] x0 First XML tree
|
||||||
* @param[in] x1 Second XML tree
|
* @param[in] x1 Second XML tree
|
||||||
* @param[out] first Pointervector to XML nodes existing in only first 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_x0 Pointervector to XML nodes changed orig value
|
||||||
* @param[out] changed_x1 Pointervector to XML nodes changed wanted value
|
* @param[out] changed_x1 Pointervector to XML nodes changed wanted value
|
||||||
* @param[out] changedlen Length of changed vector
|
* @param[out] changedlen Length of changed vector
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
* All xml vectors should be freed after use.
|
* All xml vectors should be freed after use.
|
||||||
|
* @see xml_tree_equal same algorithm but do not bother with what has changed
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_diff(yang_stmt *yspec,
|
xml_diff(cxobj *x0,
|
||||||
cxobj *x0,
|
|
||||||
cxobj *x1,
|
cxobj *x1,
|
||||||
cxobj ***first,
|
cxobj ***first,
|
||||||
int *firstlen,
|
int *firstlen,
|
||||||
|
|
@ -448,6 +450,85 @@ xml_diff(yang_stmt *yspec,
|
||||||
return retval;
|
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
|
/*! Prune everything that does not pass test or have at least a child* does not
|
||||||
*
|
*
|
||||||
* @param[in] xt XML tree with some node marked
|
* @param[in] xt XML tree with some node marked
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ xml_nsctx_namespace_netconf_default(clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create and initialize XML namespace context
|
/*! Create and initialize XML namespace context
|
||||||
|
*
|
||||||
* @param[in] prefix Namespace prefix, or NULL for default
|
* @param[in] prefix Namespace prefix, or NULL for default
|
||||||
* @param[in] ns Set this namespace. If NULL create empty nsctx
|
* @param[in] ns Set this namespace. If NULL create empty nsctx
|
||||||
* @retval nsc Return namespace context in form of a cvec
|
* @retval nsc Return namespace context in form of a cvec
|
||||||
|
|
@ -126,6 +127,7 @@ xml_nsctx_init(char *prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Free XML namespace context
|
/*! Free XML namespace context
|
||||||
|
*
|
||||||
* @param[in] prefix Namespace prefix, or NULL for default
|
* @param[in] prefix Namespace prefix, or NULL for default
|
||||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||||
* @retval nsc Return namespace context in form of a cvec
|
* @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
|
/*! Get namespace given prefix (or NULL for default) from namespace context
|
||||||
|
*
|
||||||
* @param[in] cvv Namespace context
|
* @param[in] cvv Namespace context
|
||||||
* @param[in] prefix Namespace prefix, or NULL for default
|
* @param[in] prefix Namespace prefix, or NULL for default
|
||||||
* @retval ns Cached namespace
|
* @retval ns Cached namespace
|
||||||
|
|
@ -159,11 +162,12 @@ xml_nsctx_get(cvec *cvv,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Reverse get prefix given namespace
|
/*! Reverse get prefix given namespace
|
||||||
|
*
|
||||||
* @param[in] cvv Namespace context
|
* @param[in] cvv Namespace context
|
||||||
* @param[in] ns Namespace
|
* @param[in] ns Namespace
|
||||||
* @param[out] prefix Prefix (direct pointer)
|
* @param[out] prefix Prefix (direct pointer)
|
||||||
* @retval 0 No prefix found
|
|
||||||
* @retval 1 Prefix found
|
* @retval 1 Prefix found
|
||||||
|
* @retval 0 No prefix found
|
||||||
* @note NULL is a valid prefix (default)
|
* @note NULL is a valid prefix (default)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -188,6 +192,7 @@ xml_nsctx_get_prefix(cvec *cvv,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Set or replace namespace in namespace context
|
/*! Set or replace namespace in namespace context
|
||||||
|
*
|
||||||
* @param[in] cvv Namespace context
|
* @param[in] cvv Namespace context
|
||||||
* @param[in] prefix Namespace prefix, or NULL for default
|
* @param[in] prefix Namespace prefix, or NULL for default
|
||||||
* @param[in] ns Cached namespace to set (assume non-null?)
|
* @param[in] ns Cached namespace to set (assume non-null?)
|
||||||
|
|
@ -261,11 +266,12 @@ xml_nsctx_node1(cxobj *xn,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create and initialize XML namespace from XML node context
|
/*! Create and initialize XML namespace from XML node context
|
||||||
|
*
|
||||||
* Fully explore all prefix:namespace pairs from context of one node
|
* Fully explore all prefix:namespace pairs from context of one node
|
||||||
* @param[in] xn XML node
|
* @param[in] xn XML node
|
||||||
* @param[out] ncp XML namespace context
|
* @param[out] ncp XML namespace context
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* cxobj *x; // must initialize
|
* cxobj *x; // must initialize
|
||||||
* cvec *nsc = NULL;
|
* cvec *nsc = NULL;
|
||||||
|
|
@ -297,12 +303,13 @@ xml_nsctx_node(cxobj *xn,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create and initialize XML namespace context from Yang node (non-spec)
|
/*! Create and initialize XML namespace context from Yang node (non-spec)
|
||||||
|
*
|
||||||
* Primary use is Yang path statements, eg leafrefs and others
|
* Primary use is Yang path statements, eg leafrefs and others
|
||||||
* Fully explore all prefix:namespace pairs from context of one node
|
* Fully explore all prefix:namespace pairs from context of one node
|
||||||
* @param[in] yn Yang statement in module tree (or module itself)
|
* @param[in] yn Yang statement in module tree (or module itself)
|
||||||
* @param[out] ncp XML namespace context
|
* @param[out] ncp XML namespace context
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* yang_stmt *y; // must initialize
|
* yang_stmt *y; // must initialize
|
||||||
* cvec *nsc = NULL;
|
* cvec *nsc = NULL;
|
||||||
|
|
@ -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
|
* 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
|
* Fully explore all prefix:namespace pairs of all yang modules
|
||||||
* @param[in] yspec Yang spec
|
* @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 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
|
|
@ -416,7 +423,9 @@ xml_nsctx_yangspec(yang_stmt *yspec,
|
||||||
yang_stmt *yprefix;
|
yang_stmt *yprefix;
|
||||||
yang_stmt *ynamespace;
|
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");
|
clicon_err(OE_XML, errno, "cvec_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -443,6 +452,7 @@ xml_nsctx_yangspec(yang_stmt *yspec,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Print a namespace context to a cbuf using xmlns notation
|
/*! Print a namespace context to a cbuf using xmlns notation
|
||||||
|
*
|
||||||
* @param[in] *cb CLIgen buf written to
|
* @param[in] *cb CLIgen buf written to
|
||||||
* @param[in] *nsc Namespace context
|
* @param[in] *nsc Namespace context
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
|
@ -531,6 +541,7 @@ xml2ns(cxobj *x,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Recursively check prefix / namespaces (and populate ns cache)
|
/*! Recursively check prefix / namespaces (and populate ns cache)
|
||||||
|
*
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* @retval 0 (Some) prefix not found
|
* @retval 0 (Some) prefix not found
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
@ -563,6 +574,7 @@ xml2ns_recurse(cxobj *xt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Add a namespace attribute to an XML node, either default or specific prefix
|
/*! Add a namespace attribute to an XML node, either default or specific prefix
|
||||||
|
*
|
||||||
* @param[in] x XML tree
|
* @param[in] x XML tree
|
||||||
* @param[in] prefix prefix/ns localname. If NULL then set default xmlns
|
* @param[in] prefix prefix/ns localname. If NULL then set default xmlns
|
||||||
* @param[in] ns URI namespace (or NULL). Will be copied
|
* @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
|
/*! Get prefix of given namespace recursively
|
||||||
|
*
|
||||||
* @param[in] xn XML node
|
* @param[in] xn XML node
|
||||||
* @param[in] namespace Namespace
|
* @param[in] namespace Namespace
|
||||||
* @param[out] prefixp Pointer to prefix if found
|
* @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 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
|
* @note a namespace can have two or more prefixes, this just returns the first
|
||||||
* @see xml2prefixexists to check a specific pair
|
* @see xml2prefixexists to check a specific pair
|
||||||
*/
|
*/
|
||||||
|
|
@ -700,8 +713,8 @@ xml2prefix(cxobj *xn,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Add prefix:namespace pair to xml node, set cache, etc
|
/*! Add prefix:namespace pair to xml node, set cache, etc
|
||||||
|
*
|
||||||
* @param[in] x XML node whose namespace should change
|
* @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] xp XML node where namespace attribute should be declared (can be same)
|
||||||
* @param[in] prefix1 Use this prefix
|
* @param[in] prefix1 Use this prefix
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,21 @@
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xml_nsctx.h"
|
#include "clixon_xml_nsctx.h"
|
||||||
|
#include "clixon_netconf_lib.h"
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
|
#include "clixon_yang_schema_mount.h"
|
||||||
#include "clixon_xpath_ctx.h"
|
#include "clixon_xpath_ctx.h"
|
||||||
#include "clixon_xpath.h"
|
#include "clixon_xpath.h"
|
||||||
#include "clixon_xpath_parse.h"
|
#include "clixon_xpath_parse.h"
|
||||||
#include "clixon_xpath_eval.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
|
* Variables
|
||||||
*/
|
*/
|
||||||
|
|
@ -174,7 +183,10 @@ xpath_tree_int2str(int nodetype)
|
||||||
return (char*)clicon_int2str(xpath_tree_map, 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
|
static int
|
||||||
xpath_tree_print0(cbuf *cb,
|
xpath_tree_print0(cbuf *cb,
|
||||||
xpath_tree *xs,
|
xpath_tree *xs,
|
||||||
|
|
@ -243,6 +255,8 @@ xpath_tree_print(FILE *f,
|
||||||
/*! Create an xpath string from an xpath tree, ie "unparsing"
|
/*! Create an xpath string from an xpath tree, ie "unparsing"
|
||||||
* @param[in] xs XPATH tree
|
* @param[in] xs XPATH tree
|
||||||
* @param[out] xpath XPath string as CLIgen buf
|
* @param[out] xpath XPath string as CLIgen buf
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
* @see xpath_tree_print
|
* @see xpath_tree_print
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -279,7 +293,11 @@ xpath_tree2cbuf(xpath_tree *xs,
|
||||||
cprintf(xcb, "%s", xs->xs_strnr?xs->xs_strnr:"0");
|
cprintf(xcb, "%s", xs->xs_strnr?xs->xs_strnr:"0");
|
||||||
break;
|
break;
|
||||||
case XP_PRIME_STR:
|
case XP_PRIME_STR:
|
||||||
|
#ifdef XPATH_USE_APOSTROPHE
|
||||||
cprintf(xcb, "'%s'", xs->xs_s0?xs->xs_s0:"");
|
cprintf(xcb, "'%s'", xs->xs_s0?xs->xs_s0:"");
|
||||||
|
#else
|
||||||
|
cprintf(xcb, "\"%s\"", xs->xs_s0?xs->xs_s0:"");
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case XP_PRIME_FN:
|
case XP_PRIME_FN:
|
||||||
if (xs->xs_s0)
|
if (xs->xs_s0)
|
||||||
|
|
@ -951,28 +969,31 @@ xpath_vec_bool(cxobj *xcur,
|
||||||
* @retval 1 OK with nsc1 containing the transformed nsc
|
* @retval 1 OK with nsc1 containing the transformed nsc
|
||||||
* @retval 0 XPath failure with reason set to why
|
* @retval 0 XPath failure with reason set to why
|
||||||
* @retval -1 Fatal Error
|
* @retval -1 Fatal Error
|
||||||
|
* XXX Detects mountpoint but is not mountpoint aware, just copies prefixes
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
traverse_canonical(xpath_tree *xs,
|
xpath_traverse_canonical(xpath_tree *xs,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
cvec *nsc0,
|
cvec *nsc0,
|
||||||
cvec *nsc1,
|
cvec *nsc1,
|
||||||
cbuf **reason)
|
cbuf **reason)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *prefix0;
|
char *prefix0;
|
||||||
char *prefix1;
|
char *prefix1 = NULL;
|
||||||
char *namespace;
|
char *namespace;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
// char *name;
|
||||||
|
|
||||||
switch (xs->xs_type){
|
switch (xs->xs_type){
|
||||||
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
||||||
/* Nodetest = * needs no prefix */
|
/* Nodetest = * needs no prefix */
|
||||||
if (xs->xs_s1 && strcmp(xs->xs_s1, "*") == 0)
|
if (xs->xs_s1 && strcmp(xs->xs_s1, "*") == 0)
|
||||||
break;
|
break;
|
||||||
prefix0 = xs->xs_s0;
|
prefix0 = xs->xs_s0;
|
||||||
|
// name = xs->xs_s1;
|
||||||
if ((namespace = xml_nsctx_get(nsc0, prefix0)) == NULL){
|
if ((namespace = xml_nsctx_get(nsc0, prefix0)) == NULL){
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
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);
|
cprintf(cb, "No namespace found for prefix: %s", prefix0);
|
||||||
if (reason)
|
if (reason)
|
||||||
*reason = cb;
|
*reason = cb;
|
||||||
goto failed;
|
goto fail;
|
||||||
}
|
}
|
||||||
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
|
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
|
||||||
|
#ifndef XPATH_CANONICAL_SKIP_CHECK
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cb, "No modules found for namespace: %s", namespace);
|
cprintf(cb, "No yang found for namespace: %s", namespace);
|
||||||
if (reason)
|
if (reason)
|
||||||
*reason = cb;
|
*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){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1001,7 +1026,7 @@ traverse_canonical(xpath_tree *xs,
|
||||||
cprintf(cb, "No prefix found in module: %s", yang_argument_get(ymod));
|
cprintf(cb, "No prefix found in module: %s", yang_argument_get(ymod));
|
||||||
if (reason)
|
if (reason)
|
||||||
*reason = cb;
|
*reason = cb;
|
||||||
goto failed;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (xml_nsctx_get(nsc1, prefix1) == NULL)
|
if (xml_nsctx_get(nsc1, prefix1) == NULL)
|
||||||
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
|
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
|
||||||
|
|
@ -1019,21 +1044,21 @@ traverse_canonical(xpath_tree *xs,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (xs->xs_c0){
|
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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto failed;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (xs->xs_c1){
|
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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto failed;
|
goto fail;
|
||||||
}
|
}
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
failed:
|
fail:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -1092,10 +1117,10 @@ xpath2canonical(const char *xpath0,
|
||||||
/* Traverse tree to find prefixes, transform them to canonical form and
|
/* Traverse tree to find prefixes, transform them to canonical form and
|
||||||
* create a canonical network namespace
|
* 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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto failed;
|
goto fail;
|
||||||
/* Print tree with new prefixes */
|
/* Print tree with new prefixes */
|
||||||
if ((xcb = cbuf_new()) == NULL){
|
if ((xcb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
|
@ -1122,7 +1147,7 @@ xpath2canonical(const char *xpath0,
|
||||||
if (xpt)
|
if (xpt)
|
||||||
xpath_tree_free(xpt);
|
xpath_tree_free(xpt);
|
||||||
return retval;
|
return retval;
|
||||||
failed:
|
fail:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -1166,10 +1191,11 @@ xpath_count(cxobj *xcur,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given an XML node, build an xpath recursively to root, internal function
|
/*! Given an XML node, build an xpath recursively to root, internal function
|
||||||
|
*
|
||||||
* @param[in] x XML object
|
* @param[in] x XML object
|
||||||
* @param[in] nsc Namespace context
|
* @param[in] nsc Namespace context
|
||||||
* @param[in] spec If set, recursively continue only to root without spec
|
* @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 0 OK
|
||||||
* @retval -1 Error. eg XML malformed
|
* @retval -1 Error. eg XML malformed
|
||||||
*/
|
*/
|
||||||
|
|
@ -1177,6 +1203,7 @@ static int
|
||||||
xml2xpath1(cxobj *x,
|
xml2xpath1(cxobj *x,
|
||||||
cvec *nsc,
|
cvec *nsc,
|
||||||
int spec,
|
int spec,
|
||||||
|
int apostrophe,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -1196,7 +1223,7 @@ xml2xpath1(cxobj *x,
|
||||||
goto ok;
|
goto ok;
|
||||||
if (spec && xml_spec(x) == NULL)
|
if (spec && xml_spec(x) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
if (xml2xpath1(xp, nsc, spec, cb) < 0)
|
if (xml2xpath1(xp, nsc, spec, apostrophe, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (nsc){
|
if (nsc){
|
||||||
if (xml2ns(x, xml_prefix(x), &namespace) < 0)
|
if (xml2ns(x, xml_prefix(x), &namespace) < 0)
|
||||||
|
|
@ -1219,10 +1246,18 @@ xml2xpath1(cxobj *x,
|
||||||
keyword = yang_keyword_get(y);
|
keyword = yang_keyword_get(y);
|
||||||
switch (keyword){
|
switch (keyword){
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
if ((b = xml_body(x)) != NULL)
|
if (apostrophe){
|
||||||
cprintf(cb, "[.=\"%s\"]", b);
|
if ((b = xml_body(x)) != NULL)
|
||||||
else
|
cprintf(cb, "[.='%s']", b);
|
||||||
cprintf(cb, "[.=\"\"]");
|
else
|
||||||
|
cprintf(cb, "[.='']");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if ((b = xml_body(x)) != NULL)
|
||||||
|
cprintf(cb, "[.=\"%s\"]", b);
|
||||||
|
else
|
||||||
|
cprintf(cb, "[.=\"\"]");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
cvk = yang_cvec_get(y);
|
cvk = yang_cvec_get(y);
|
||||||
|
|
@ -1234,10 +1269,17 @@ xml2xpath1(cxobj *x,
|
||||||
if ((xb = xml_find(x, keyname)) == NULL)
|
if ((xb = xml_find(x, keyname)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
b = xml_body(xb);
|
b = xml_body(xb);
|
||||||
|
#if 1
|
||||||
|
if (b==NULL || strlen(b)==0)
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
cprintf(cb, "[");
|
cprintf(cb, "[");
|
||||||
if (prefix)
|
if (prefix)
|
||||||
cprintf(cb, "%s:", prefix);
|
cprintf(cb, "%s:", prefix);
|
||||||
cprintf(cb, "%s=\"%s\"]", keyname, b?b:"");
|
if (apostrophe)
|
||||||
|
cprintf(cb, "%s='%s']", keyname, b?b:"");
|
||||||
|
else
|
||||||
|
cprintf(cb, "%s=\"%s\"]", keyname, b?b:"");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -1260,6 +1302,7 @@ xml2xpath1(cxobj *x,
|
||||||
* @param[in] x XML object
|
* @param[in] x XML object
|
||||||
* @param[in] nsc Namespace context
|
* @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] 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
|
* @param[out] xpath Malloced xpath string. Need to free() after use
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error. (eg XML malformed)
|
* @retval -1 Error. (eg XML malformed)
|
||||||
|
|
@ -1267,16 +1310,18 @@ xml2xpath1(cxobj *x,
|
||||||
* char *xpath = NULL;
|
* char *xpath = NULL;
|
||||||
* cxobj *x;
|
* cxobj *x;
|
||||||
* ... x is inside an xml tree ...
|
* ... x is inside an xml tree ...
|
||||||
* if (xml2xpath(x, nsc, 0, &xpath) < 0)
|
* if (xml2xpath(x, nsc, 0, 0, &xpath) < 0)
|
||||||
* err;
|
* err;
|
||||||
* free(xpath);
|
* free(xpath);
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note x needs to be bound to YANG, see eg xml_bind_yang()
|
* @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
|
int
|
||||||
xml2xpath(cxobj *x,
|
xml2xpath(cxobj *x,
|
||||||
cvec *nsc,
|
cvec *nsc,
|
||||||
int spec,
|
int spec,
|
||||||
|
int apostrophe,
|
||||||
char **xpathp)
|
char **xpathp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -1287,7 +1332,7 @@ xml2xpath(cxobj *x,
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml2xpath1(x, nsc, spec, cb) < 0)
|
if (xml2xpath1(x, nsc, spec, apostrophe, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* XXX: see xpath in test statement,.. */
|
/* XXX: see xpath in test statement,.. */
|
||||||
xpath = cbuf_get(cb);
|
xpath = cbuf_get(cb);
|
||||||
|
|
@ -1304,3 +1349,167 @@ xml2xpath(cxobj *x,
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
return retval;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <math.h> /* NaN */
|
#include <math.h> /* NaN */
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <math.h> /* NaN */
|
#include <math.h> /* NaN */
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <math.h> /* NaN */
|
#include <math.h> /* NaN */
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ struct yang_stmt{
|
||||||
fraction-digits for fraction-digits
|
fraction-digits for fraction-digits
|
||||||
revision (uint32)
|
revision (uint32)
|
||||||
unknown-stmt (optional argument)
|
unknown-stmt (optional argument)
|
||||||
|
spec: mount-point xpath
|
||||||
*/
|
*/
|
||||||
cvec *ys_cvec; /* List of stmt-specific variables
|
cvec *ys_cvec; /* List of stmt-specific variables
|
||||||
Y_RANGE: range_min, range_max
|
Y_RANGE: range_min, range_max
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@
|
||||||
* 4. yang_schema_mount_statedata(): from get_common/get_statedata to retrieve system state
|
* 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
|
* 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
|
* 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
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
@ -133,8 +137,8 @@ yang_schema_mount_point(yang_stmt *y)
|
||||||
|
|
||||||
/*! Get yangspec mount-point
|
/*! Get yangspec mount-point
|
||||||
*
|
*
|
||||||
* @param[in] yu Yang unknown node to save the yspecs
|
* @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
|
||||||
* @param[out] yspec YANG stmt spec
|
* @param[out] yspec YANG stmt spec
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
@ -157,9 +161,9 @@ yang_mount_get(yang_stmt *yu,
|
||||||
|
|
||||||
/*! Set yangspec mount-point on yang unknwon node
|
/*! 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] 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)
|
* @param[in] yspec Yangspec for this mount-point (consumed)
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
@ -173,6 +177,7 @@ yang_mount_set(yang_stmt *yu,
|
||||||
yang_stmt *yspec0;
|
yang_stmt *yspec0;
|
||||||
cvec *cvv;
|
cvec *cvv;
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
|
cg_var *cv2;
|
||||||
|
|
||||||
if ((cvv = yang_cvec_get(yu)) != NULL &&
|
if ((cvv = yang_cvec_get(yu)) != NULL &&
|
||||||
(cv = cvec_find(cvv, xpath)) != 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)
|
else if ((cv = yang_cvec_add(yu, CGV_VOID, xpath)) == NULL)
|
||||||
goto done;
|
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);
|
cv_void_set(cv, yspec);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -224,7 +239,7 @@ xml_yang_mount_get(clicon_handle h,
|
||||||
// XXX hardcoded prefix: yangmnt
|
// XXX hardcoded prefix: yangmnt
|
||||||
if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL)
|
if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
if (xml2xpath(xt, NULL, 1, &xpath) < 0)
|
if (xml2xpath(xt, NULL, 1, 0, &xpath) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (yang_mount_get(yu, xpath, yspec) < 0)
|
if (yang_mount_get(yu, xpath, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -261,7 +276,7 @@ xml_yang_mount_set(cxobj *x,
|
||||||
(yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){
|
(yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml2xpath(x, NULL, 1, &xpath) < 0)
|
if (xml2xpath(x, NULL, 1, 0, &xpath) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (yang_mount_set(yu, xpath, yspec) < 0)
|
if (yang_mount_set(yu, xpath, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
|
|
||||||
|
|
@ -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)"
|
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"
|
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)"
|
# comment as long as XPATH_CANONICAL_SKIP_CHECK
|
||||||
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"
|
#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
|
rm -rf $dir
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,6 @@ Example sshd-config (-c option):n
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
|
||||||
|
|
@ -383,7 +383,7 @@ main(int argc,
|
||||||
char *xpathi = NULL;
|
char *xpathi = NULL;
|
||||||
for (i=0; i<xc->xc_size; i++){
|
for (i=0; i<xc->xc_size; i++){
|
||||||
xi = xc->xc_nodeset[i];
|
xi = xc->xc_nodeset[i];
|
||||||
if (xml2xpath(xi, nsc, 0, &xpathi) < 0)
|
if (xml2xpath(xi, nsc, 0, 0, &xpathi) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
fprintf(stdout, "Inverse: %s\n", xpathi);
|
fprintf(stdout, "Inverse: %s\n", xpathi);
|
||||||
if (xpathi){
|
if (xpathi){
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue