* 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

@ -1160,7 +1160,7 @@ text_modify_top(clicon_handle h,
* @param[out] cbret Initialized cligen buffer. On exit contains XML if retval == 0
* @retval 1 OK
* @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.
* @code
* cxobj *xt;

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_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,11 +266,12 @@ 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
* @retval 0 OK
* @retval -1 Error
* @retval -1 Error
* @code
* cxobj *x; // must initialize
* cvec *nsc = NULL;
@ -297,12 +303,13 @@ 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)
* @param[out] ncp XML namespace context
* @retval 0 OK
* @retval -1 Error
* @retval -1 Error
* @code
* yang_stmt *y; // must initialize
* 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
* 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,28 +969,31 @@ 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,
yang_stmt *yspec,
cvec *nsc0,
cvec *nsc1,
cbuf **reason)
xpath_traverse_canonical(xpath_tree *xs,
yang_stmt *yspec,
cvec *nsc0,
cvec *nsc1,
cbuf **reason)
{
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 */
/* Nodetest = * needs no prefix */
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 ((b = xml_body(x)) != NULL)
cprintf(cb, "[.=\"%s\"]", b);
else
cprintf(cb, "[.=\"\"]");
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,10 +1269,17 @@ 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);
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;
default:
@ -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
@ -133,8 +137,8 @@ yang_schema_mount_point(yang_stmt *y)
/*! Get yangspec mount-point
*
* @param[in] yu Yang unknown node to save the yspecs
* @param[in] xpath Key for yspec on yu
* @param[in] yu Yang unknown node to save the yspecs
* @param[in] xpath Key for yspec on yu
* @param[out] yspec YANG stmt spec
* @retval 0 OK
* @retval -1 Error
@ -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>