* Fixed instance-id multiple results

* Fixed last NACM write paths
This commit is contained in:
Olof hagsand 2020-04-22 14:00:40 +02:00
parent 04017c97ba
commit dc36282874
15 changed files with 806 additions and 211 deletions

View file

@ -50,7 +50,7 @@ Expected: May 2020
### Corrected Bugs
* Fixed: Insertion of subtree leaf nodes were not made in the crrect place, always ended up last regardless of yang spec (if ordered-by system).
* Fixed: Insertion of subtree leaf nodes were not made in the correct place, always ended up last regardless of yang spec (if ordered-by system).
## 4.4.0
5 April 2020

View file

@ -1,6 +1,6 @@
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC (Netgate)
CLIXON is dual license.

View file

@ -52,7 +52,6 @@ typedef struct {
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
int csm_nsyntax; /* Num syntax specs registered by plugin */
parse_tree csm_pt; /* CLIgen parse tree */
} cli_syntaxmode_t;
/* Plugin group object. Just a single object, not list. part of cli_handle */

View file

@ -273,11 +273,11 @@ expand_dbvar(void *h,
/*! List files in a directory
*/
int
expand_dir(char *dir,
int *nr,
expand_dir(char *dir,
int *nr,
char ***commands,
mode_t flags,
int detail)
mode_t flags,
int detail)
{
DIR *dirp;
struct dirent *dp;

View file

@ -50,7 +50,7 @@ int xml_search_indexvar_binary_pos(cxobj *xp, char *indexvar, clixon_xvec *xvec,
#endif
int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp);
int clixon_xml_find_index(cxobj *xp, yang_stmt *yp, char *namespace, char *name,
cvec *cvk, clixon_xvec **xvec);
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, clixon_xvec **xvec);
cvec *cvk, clixon_xvec *xvec);
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, clixon_xvec *xvec);
#endif /* _CLIXON_XML_SORT_H */

View file

@ -310,6 +310,48 @@ nacm_rpc(char *rpc,
goto done;
}
/* Local struct for keeping preparation/compiled data in NACM data path code */
struct prepvec{
qelem_t pv_q;
cxobj *pv_xrule;
clixon_xvec *pv_xpathvec;
};
typedef struct prepvec prepvec;
/*! Delete all Upgrade callbacks
*/
int
prepvec_free(prepvec *pv_list)
{
prepvec *pv;
while((pv = pv_list) != NULL) {
DELQ(pv, pv_list, prepvec *);
if (pv->pv_xpathvec)
clixon_xvec_free(pv->pv_xpathvec);
free(pv);
}
return 0;
}
prepvec *
prepvec_add(prepvec **pv_listp,
cxobj *xrule)
{
prepvec *pv;
if ((pv = malloc(sizeof(*pv))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
return NULL;
}
memset(pv, 0, sizeof(*pv));
ADDQ(pv, *pv_listp);
pv->pv_xrule = xrule;
if ((pv->pv_xpathvec = clixon_xvec_new()) == NULL)
return NULL;
return pv;
}
/*! Prepare datastructures before running through XML tree
* Save rules in a "cache"
* These rules match:
@ -318,21 +360,21 @@ nacm_rpc(char *rpc,
* Also make instance-id lookups on top object for each rule. Assume at most one result
*/
static int
nacm_datanode_prepare(clicon_handle h,
cxobj *xt,
enum nacm_access access,
cxobj **gvec,
size_t glen,
cxobj **rlistvec,
size_t rlistlen,
cvec *nsc,
clixon_xvec *rulevec,
clixon_xvec *xpathvec)
nacm_datanode_prepare(clicon_handle h,
cxobj *xt,
enum nacm_access access,
cxobj **gvec,
size_t glen,
cxobj **rlistvec,
size_t rlistlen,
cvec *nsc,
prepvec **pv_listp)
{
int retval = -1;
cxobj *rlist;
int i;
int j;
int k;
char *gname;
cxobj **rvec = NULL; /* rules */
size_t rlen;
@ -346,6 +388,7 @@ nacm_datanode_prepare(clicon_handle h,
cxobj **xvec = NULL;
int xlen = 0;
int ret;
prepvec *pv;
yspec = clicon_dbspec_yang(h);
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
@ -368,7 +411,6 @@ nacm_datanode_prepare(clicon_handle h,
xrule = rvec[j];
/* 6c) For a "read" access operation, the rule's "access-operations"
leaf has the "read" bit set or has the special value "*" */
access_operations = xml_find_body(xrule, "access-operations");
switch (access){
case NACM_READ:
@ -406,7 +448,8 @@ nacm_datanode_prepare(clicon_handle h,
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
continue;
if (clixon_xvec_append(xpathvec, NULL) < 0) /* XXX: vector of vectors? */
/* Here a new xrule is found, add it */
if (prepvec_add(pv_listp, xrule) == NULL)
goto done;
}
else{
@ -421,10 +464,13 @@ nacm_datanode_prepare(clicon_handle h,
goto done;
if (ret == 0)
continue;
if (xlen > 1)
clicon_log(LOG_WARNING, "%s path:%s Clixon only supports single returns, this had: %d", __FUNCTION__, path, xlen);
if (clixon_xvec_append(xpathvec, xvec?xvec[0]:NULL) < 0) /* XXX: vector of vectors? */
/* Here a new xrule is found, add it */
if ((pv = prepvec_add(pv_listp, xrule)) == NULL)
goto done;
for (k=0; k<xlen; k++){
if (clixon_xvec_append(pv->pv_xpathvec, xvec[k]) < 0)
goto done;
}
if (xvec){
free(xvec);
xvec = NULL;
@ -438,8 +484,6 @@ nacm_datanode_prepare(clicon_handle h,
path = NULL;
}
}
if (clixon_xvec_append(rulevec, xrule) < 0) /* save it */
goto done;
}
if (rvec){
free(rvec);
@ -472,15 +516,17 @@ nacm_datanode_prepare(clicon_handle h,
* @retval 2 OK and rule matches permit
*/
static int
nacm_data_write_xrule_xml(cxobj *xn,
cxobj *xrule,
cxobj *xp,
yang_stmt *yspec)
nacm_data_write_xrule_xml(cxobj *xn,
cxobj *xrule,
clixon_xvec *xpathvec,
yang_stmt *yspec)
{
int retval = -1;
yang_stmt *ymod;
char *module_pattern; /* rule module name */
char *action;
cxobj *xp;
int i;
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
goto nomatch;
@ -502,11 +548,16 @@ nacm_data_write_xrule_xml(cxobj *xn,
goto deny;
goto permit;
}
/* Check if ancestor is xp */
if (xn != xp && !xml_isancestor(xn, xp))
goto nomatch;
if (strcmp(action, "deny")==0)
goto deny;
for (i=0; i<clixon_xvec_len(xpathvec); i++){
xp = clixon_xvec_i(xpathvec, i);
/* Check if ancestor is xp (for every xpathvec?) */
if (xn == xp || xml_isancestor(xn, xp)){
if (strcmp(action, "deny")==0)
goto deny;
goto permit;
}
}
goto nomatch;
permit:
retval = 2; /* rule match and permit */
done:
@ -536,35 +587,40 @@ nacm_data_write_xrule_xml(cxobj *xn,
* deny: Send error message
*/
static int
nacm_datanode_write_recurse(clicon_handle h,
cxobj *xn,
clixon_xvec *rulevec,
clixon_xvec *xpathvec,
int defpermit,
yang_stmt *yspec,
cbuf *cbret)
nacm_datanode_write_recurse(clicon_handle h,
cxobj *xn,
prepvec *pv_list,
int defpermit,
yang_stmt *yspec,
cbuf *cbret)
{
int retval=-1;
cxobj *x;
int i;
int ret = 0;
int retval = -1;
cxobj *x;
int ret = 0;
prepvec *pv;
for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
if ((ret = nacm_data_write_xrule_xml(xn,
clixon_xvec_i(rulevec, i),
clixon_xvec_i(xpathvec, i),
yspec)) < 0)
goto done;
if (ret == 0) /* No match, continue with next rule */
;
else if (ret == 1){ /* Match and deny: break all traversal and send error back to client */
if (netconf_access_denied(cbret, "application", "access denied") < 0)
pv = pv_list;
if (pv){
do {
/* return values: -1:Error /0:no match /1: deny /2: permit
*/
if ((ret = nacm_data_write_xrule_xml(xn, pv->pv_xrule, pv->pv_xpathvec, yspec)) < 0)
goto done;
goto deny;
break;
}
else if (ret == 2) /* Match and accpt: break rule processing but continue recursion */
break;
switch(ret){
case 0: /* No match, continue with next rule */
break;
case 1: /* Match and deny: break all traversal and send error back to client */
if (netconf_access_denied(cbret, "application", "access denied") < 0)
goto done;
goto deny;
break;
case 2: /* Match and permit: break rule processing but continue recursion */
break;
}
if (ret == 2)
break;
pv = NEXTQ(prepvec *, pv);
} while (pv && pv != pv_list);
}
/* If no rule match, check default rule: if deny then break traversal and send error */
if (ret == 0 && !defpermit){
@ -575,8 +631,7 @@ nacm_datanode_write_recurse(clicon_handle h,
/* If node should be purged, dont recurse and defer removal to caller */
x = NULL; /* Recursively check XML */
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
if ((ret = nacm_datanode_write_recurse(h, x,
rulevec, xpathvec,
if ((ret = nacm_datanode_write_recurse(h, x, pv_list,
defpermit, yspec, cbret)) < 0)
goto done;
if (ret == 0)
@ -616,17 +671,16 @@ nacm_datanode_write(clicon_handle h,
cxobj *xnacm,
cbuf *cbret)
{
int retval = -1;
cxobj **gvec = NULL; /* groups */
size_t glen;
cxobj **rlistvec = NULL; /* rule-list */
size_t rlistlen;
cxobj **rvec = NULL; /* rules */
char *write_default = NULL;
cvec *nsc = NULL;
clixon_xvec *rulevec = NULL;
clixon_xvec *xpathvec = NULL;
int ret;
int retval = -1;
cxobj **gvec = NULL; /* groups */
size_t glen;
cxobj **rlistvec = NULL; /* rule-list */
size_t rlistlen;
cxobj **rvec = NULL; /* rules */
char *write_default = NULL;
cvec *nsc = NULL;
int ret;
prepvec *pv_list = NULL;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
@ -638,7 +692,6 @@ nacm_datanode_write(clicon_handle h,
clicon_err(OE_XML, EINVAL, "No nacm write-default rule");
goto done;
}
/* 3. Check all the "group" entries to see if any of them contain a
"user-name" entry that equals the username for the session
making the request. (If the "enable-external-groups" leaf is
@ -660,16 +713,10 @@ nacm_datanode_write(clicon_handle h,
goto done;
/* First run through rules and cache rules as well as lookup objects in xt.
*/
if ((rulevec = clixon_xvec_new()) == NULL)
goto done;
if ((xpathvec = clixon_xvec_new()) == NULL)
goto done;
if (nacm_datanode_prepare(h, xt, access, gvec, glen, rlistvec, rlistlen, nsc,
rulevec, xpathvec) < 0)
if (nacm_datanode_prepare(h, xt, access, gvec, glen, rlistvec, rlistlen, nsc, &pv_list) < 0)
goto done;
/* Then recursivelyy traverse all requested nodes */
if ((ret = nacm_datanode_write_recurse(h, xreq,
rulevec, xpathvec,
if ((ret = nacm_datanode_write_recurse(h, xreq, pv_list,
strcmp(write_default, "deny"),
clicon_dbspec_yang(h),
cbret)) < 0)
@ -701,10 +748,8 @@ nacm_datanode_write(clicon_handle h,
retval = 1;
done:
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
if (xpathvec)
clixon_xvec_free(xpathvec);
if (rulevec)
clixon_xvec_free(rulevec);
if (pv_list)
prepvec_free(pv_list);
if (nsc)
xml_nsctx_free(nsc);
if (gvec)
@ -764,12 +809,14 @@ nacm_data_read_action(cxobj *xrule,
static int
nacm_data_read_xrule_xml(cxobj *xn,
cxobj *xrule,
cxobj *xp,
clixon_xvec *xpathvec,
yang_stmt *yspec)
{
int retval = -1;
yang_stmt *ymod;
char *module_pattern; /* rule module name */
cxobj *xp;
int i;
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
goto nomatch;
@ -790,17 +837,21 @@ nacm_data_read_xrule_xml(cxobj *xn,
goto done;
goto match;
}
/* Check if ancestor is xp */
if (xn != xp && !xml_isancestor(xn, xp))
goto nomatch;
if (nacm_data_read_action(xrule, xn) < 0)
goto done;
match:
retval = 1; /* match */
done:
return retval;
for (i=0; i<clixon_xvec_len(xpathvec); i++){
xp = clixon_xvec_i(xpathvec, i);
/* Check if ancestor is xp (for every xpathvec?) */
if (xn == xp || xml_isancestor(xn, xp)){
if (nacm_data_read_action(xrule, xn) < 0)
goto done;
goto match;
}
}
nomatch:
retval = 0;
done:
return retval;
match:
retval = 1; /* match */
goto done;
}
@ -816,26 +867,30 @@ nacm_data_read_xrule_xml(cxobj *xn,
static int
nacm_datanode_read_recurse(clicon_handle h,
cxobj *xn,
clixon_xvec *rulevec,
clixon_xvec *xpathvec,
prepvec *pv_list,
yang_stmt *yspec)
{
int retval=-1;
cxobj *x;
int i;
cxobj *xprev;
int ret;
int retval = -1;
cxobj *x;
cxobj *xprev;
int ret;
prepvec *pv;
if (xml_spec(xn)){ /* Check this node */
for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
if ((ret = nacm_data_read_xrule_xml(xn,
clixon_xvec_i(rulevec, i),
clixon_xvec_i(xpathvec, i),
yspec)) < 0)
goto done;
if (ret == 1)
break; /* stop at first match */
pv = pv_list;
if (pv){
do {
if ((ret = nacm_data_read_xrule_xml(xn,
pv->pv_xrule,
pv->pv_xpathvec,
yspec)) < 0)
goto done;
if (ret == 1)
break; /* stop at first match */
pv = NEXTQ(prepvec *, pv);
} while (pv && pv != pv_list);
}
#if 0 /* 6(A) in algorithm
* If N did not match any rule R, and default rule is deny, remove that subtree */
if (strcmp(read_default, "deny") == 0)
@ -849,7 +904,7 @@ nacm_datanode_read_recurse(clicon_handle h,
x = NULL; /* Recursively check XML */
xprev = NULL;
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
if (nacm_datanode_read_recurse(h, x, rulevec, xpathvec, yspec) < 0)
if (nacm_datanode_read_recurse(h, x, pv_list, yspec) < 0)
goto done;
/* check for delayed remove */
if (xml_flag(x, XML_FLAG_DEL)){
@ -920,16 +975,15 @@ nacm_datanode_read(clicon_handle h,
char *username,
cxobj *xnacm)
{
int retval = -1;
cxobj **gvec = NULL; /* groups */
size_t glen;
cxobj **rlistvec = NULL; /* rule-list */
size_t rlistlen;
int i;
char *read_default = NULL;
cvec *nsc = NULL;
clixon_xvec *rulevec = NULL;
clixon_xvec *xpathvec = NULL;
int retval = -1;
cxobj **gvec = NULL; /* groups */
size_t glen;
cxobj **rlistvec = NULL; /* rule-list */
size_t rlistlen;
int i;
char *read_default = NULL;
cvec *nsc = NULL;
prepvec *pv_list = NULL;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
@ -960,15 +1014,10 @@ nacm_datanode_read(clicon_handle h,
/* First run through rules and cache rules as well as lookup objects in xt.
* DANGER: objects could be stale if they are removed?
*/
if ((rulevec = clixon_xvec_new()) == NULL)
goto done;
if ((xpathvec = clixon_xvec_new()) == NULL)
goto done;
if (nacm_datanode_prepare(h, xt, NACM_READ, gvec, glen, rlistvec, rlistlen, nsc,
rulevec, xpathvec) < 0)
if (nacm_datanode_prepare(h, xt, NACM_READ, gvec, glen, rlistvec, rlistlen, nsc, &pv_list) < 0)
goto done;
/* Then recursivelyy traverse all nodes */
if (nacm_datanode_read_recurse(h, xt, rulevec, xpathvec, clicon_dbspec_yang(h)) < 0)
if (nacm_datanode_read_recurse(h, xt, pv_list, clicon_dbspec_yang(h)) < 0)
goto done;
#if 1
/* Step 8(B) above:
@ -999,10 +1048,8 @@ nacm_datanode_read(clicon_handle h,
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xpathvec)
clixon_xvec_free(xpathvec);
if (rulevec)
clixon_xvec_free(rulevec);
if (pv_list)
prepvec_free(pv_list);
if (nsc)
xml_nsctx_free(nsc);
if (gvec)

View file

@ -37,10 +37,16 @@
* - instance-identifier as defined by YANG
* - clixon-path is an internal format which both ^ use as internal representation
*
* 1. Instance-identifier
* "Instance-identifier" is a subset of XML Xpaths and defined in Yang, used in NACM for example.
* and defined in RF7950 Sections 9.13 and 14.
* To note: prefixes depend on the XML context in which the value occurs,
* In RFC8341 node-instance-identifiers are defined as:
* All the same rules as an instance-identifier apply, except that predicates for keys are optional.
* If a key predicate is missing, then the node-instance-identifier represents all possible server
* instances for that key.
*
* 2. Api-path
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
@ -1365,31 +1371,40 @@ instance_id_resolve(clixon_path *cplist,
goto fail;
}
cp->cp_yang = yc;
if (cp->cp_cvk){
/* Iterate over yang list keys and assign as names in cvk */
if (yang_keyword_get(yc) == Y_LEAF_LIST)
;
else if (yang_keyword_get(yc) == Y_LIST){
#ifdef notused
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
goto fail;
}
switch (yang_keyword_get(yc)){
case Y_LIST:
if (cp->cp_cvk == NULL){
#if 0 /* XXX why is this not enforced? */
clicon_err(OE_YANG, ENOENT, "key-values mandatory for lists");
goto fail;
#endif
i = 0;
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL){
cvy = cvec_i(yang_cvec_get(yc), i);
cv_name_set(cva, cv_string_get(cvy));
}
i++;
}
break;
}
else{
#if 0 /* XXX why is this not enforced? */
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
goto fail;
}
#endif
i = 0;
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL){
cvy = cvec_i(yang_cvec_get(yc), i);
cv_name_set(cva, cv_string_get(cvy));
}
i++;
}
break;
case Y_LEAF_LIST:
break;
default:
if (cp->cp_cvk != NULL){
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
goto fail;
}
break;
}
yt = yc;
cp = NEXTQ(clixon_path *, cp);
@ -1439,22 +1454,26 @@ clixon_path_search(cxobj *xt,
yc = cp->cp_yang;
if ((modns = yang_find_mynamespace(yc)) == NULL)
goto fail;
if (xvecp)
for (i=0; i<clixon_xvec_len(xvecp); i++){
xp = clixon_xvec_i(xvecp, i); /* Iterate over parent set */
xvecc = NULL;
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
(cv_type_get(cv) == CGV_UINT32)){
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), &xvecc) < 0)
goto done;
}
else if (clixon_xml_find_index(xp, yang_parent_get(yc),
modns, yang_argument_get(yc),
cp->cp_cvk, &xvecc) < 0)
if (xvecp){
if ((xvecc = clixon_xvec_new()) == NULL)
goto done;
} /* for */
for (i=0; i<clixon_xvec_len(xvecp); i++){
xp = clixon_xvec_i(xvecp, i); /* Iterate over parent set */
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
(cv_type_get(cv) == CGV_UINT32)){
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), xvecc) < 0)
goto done;
}
else if (clixon_xml_find_index(xp, yang_parent_get(yc),
modns, yang_argument_get(yc),
cp->cp_cvk, xvecc) < 0)
goto done;
/* XXX: xvecc append! either here or in the functions */
} /* for */
}
if (xvecp)
clixon_xvec_free(xvecp);
xvecp = xvecc;

View file

@ -179,6 +179,8 @@ populate_self_parent(cxobj *xt,
#endif
retval = 1;
done:
if (cb)
cbuf_free(cb);
return retval;
fail:
retval = 0;

View file

@ -735,7 +735,8 @@ xml_search_binary(cxobj *xp,
/*! Search XML child under xp matching x1 using yang-based binary search for list/leaf-list keys
*
* Match is tried xp with x1 with either name only (container/leaf) or using keys (list/leaf-lists)
* Any non-key leafs or other structure of x1 is not matched.
* Any non-key leafs or other structure of x1 is not matched (unless indexvar is set).
* If x1 is list or leaf-list, the function assumes key values / indexvar exists in x1.
*
* @param[in] xp Parent xml node.
* @param[in] x1 Find this object among xp:s children
@ -748,11 +749,11 @@ xml_search_binary(cxobj *xp,
* @see xml_find_index for a generic search function
*/
static int
xml_search_yang(cxobj *xp,
cxobj *x1,
yang_stmt *yc,
int skip1,
char *indexvar,
xml_search_yang(cxobj *xp,
cxobj *x1,
yang_stmt *yc,
int skip1,
char *indexvar,
clixon_xvec *xvec)
{
int retval = -1;
@ -779,7 +780,7 @@ xml_search_yang(cxobj *xp,
else
#endif
if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
yangi = yang_order(yc);
if (xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec) < 0)
@ -1258,7 +1259,7 @@ xml_find_noyang_name(cxobj *xp,
/*! Try to find an XML child from parent with yang available using list keys and leaf-lists
*
* Must be populated with Yang specs, parent must be list or leaf-list, and (for list) search
* index must be keys in the order they are declared.
* index MUST be keys in the order they are declared.
* First identify that this search qualifies for yang-based list/leaf-list optimized search,
* - if no, revert (return 0) so that the overlying algorithm can try next or fallback to
* linear seacrh
@ -1306,6 +1307,10 @@ xml_find_index_yang(cxobj *xp,
cprintf(cb, "<%s>", name);
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
cvi = NULL;
if (cvk == NULL){ /* If list and no keys, all should match */
revert++;
break;
}
i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
if ((kname = cv_name_get(cvi)) == NULL){
@ -1313,7 +1318,7 @@ xml_find_index_yang(cxobj *xp,
goto done;
}
/* Parameter in cvk is not key or not in right key order, then we cannot call
* xml_find_keys and we need to revert to noynag
* xml_find_keys and we need to revert to noyang
*/
if ((ycv = cvec_i(ycvk, i)) == NULL || strcmp(kname, cv_string_get(ycv))){
revert++;
@ -1411,12 +1416,11 @@ xml_find_index_yang(cxobj *xp,
* - Otherwise search is made using linear search
*
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child (preferred) See rule (2) above
* @param[in] yp Yang spec of parent node or yang-spec/yang (Alternative if yc not given).
* @param[in] namespace Namespace (needed only if name is derived from top-symbol)
* @param[in] name Name of child (not required if yc given)
* @param[in] name Name of child
* @param[in] cvk List of keys and values as CLIgen vector on the form k1=foo, k2=bar
* @param[out] xvec Array of found nodes
* @param[out] xvec Array of result nodes. Must be initialized on entry
* @retval 0 OK, see xret
* @retval -1 Error
* @code
@ -1424,10 +1428,12 @@ xml_find_index_yang(cxobj *xp,
* cvec *cvk = NULL; vector of index keys
* cxobj *x;
* ... Populate cvk with key/values eg a:5 b:6
* if (clixon_xml_find_index(xp, yp, NULL, "a", ns, cvk, &xv) < 0)
* if ((xv = clixon_xvec_new()) == NULL)
* err;
* for (i=0; i<clixon_xvec_len(xvec); i++){
* x = clixon_xpath_i(xvec, i);
* if (clixon_xml_find_index(xp, yp, NULL, "a", ns, cvk, xv) < 0)
* err;
* for (i=0; i<clixon_xvec_len(xv); i++){
* x = clixon_xpath_i(xv, i);
* ...
* }
* clixon_xvec_free(xvec);
@ -1443,14 +1449,18 @@ clixon_xml_find_index(cxobj *xp,
char *namespace,
char *name,
cvec *cvk,
clixon_xvec **xvec)
clixon_xvec *xvec)
{
int retval = -1;
int ret;
yang_stmt *yc = NULL;
if (xvec == NULL){
clicon_err(OE_YANG, EINVAL, "xvec");
goto done;
}
if (name == NULL){
clicon_err(OE_YANG, ENOENT, "name");
clicon_err(OE_YANG, EINVAL, "name");
goto done;
}
if (yp == NULL)
@ -1461,27 +1471,21 @@ clixon_xml_find_index(cxobj *xp,
if (yp)
yc = yang_find_datanode(yp, name);
}
if ((*xvec = clixon_xvec_new()) == NULL)
goto done;
if (yc){
if ((ret = xml_find_index_yang(xp, yc, cvk, *xvec)) < 0)
if ((ret = xml_find_index_yang(xp, yc, cvk, xvec)) < 0)
goto done;
if (ret == 0){ /* This means yang method did not work for some reason
* such as not being list key indexes in cvk, for example
*/
if (xml_find_noyang_name(xp, namespace, name, cvk, *xvec) < 0)
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec) < 0)
goto done;
}
}
else
if (xml_find_noyang_name(xp, namespace, name, cvk, *xvec) < 0)
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec) < 0)
goto done;
retval = 0;
done:
if (retval < 0){
clixon_xvec_free(*xvec);
*xvec = NULL;
}
return retval;
}
@ -1499,7 +1503,7 @@ int
clixon_xml_find_pos(cxobj *xp,
yang_stmt *yc,
uint32_t pos,
clixon_xvec **xvec)
clixon_xvec *xvec)
{
int retval = -1;
cxobj *xc = NULL;
@ -1513,22 +1517,16 @@ clixon_xml_find_pos(cxobj *xp,
name = yang_argument_get(yc);
u = 0;
xc = NULL;
if ((*xvec = clixon_xvec_new()) == NULL)
goto done;
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(xc)))
continue;
if (pos == u++){ /* Found */
if (clixon_xvec_append(*xvec, xc) < 0)
if (clixon_xvec_append(xvec, xc) < 0)
goto done;
break;
}
}
retval = 0;
done:
if (retval < 0){
clixon_xvec_free(*xvec);
*xvec = NULL;
}
return retval;
}

View file

@ -227,9 +227,9 @@ loop_preds(xpath_tree *xt,
* y[k=3] # corresponds to: <name>[<keyname>=<keyval>]
*/
static int
xpath_list_optimize_fn(xpath_tree *xt,
cxobj *xv,
clixon_xvec **xvec)
xpath_list_optimize_fn(xpath_tree *xt,
cxobj *xv,
clixon_xvec *xvec)
{
int retval = -1;
xpath_tree *xm = NULL;
@ -329,8 +329,10 @@ xpath_optimize_check(xpath_tree *xs,
if (!_optimize_enable)
return 0; /* use regular code */
if ((xvec = clixon_xvec_new()) == NULL)
return -1;
/* Glue code since xpath code uses (old) cxobj ** and search code uses (new) clixon_xvec */
if ((ret = xpath_list_optimize_fn(xs, xv, &xvec)) < 0)
if ((ret = xpath_list_optimize_fn(xs, xv, xvec)) < 0)
return -1;
if (ret == 1){
if (clixon_xvec_extract(xvec, xvec0, xlen0) < 0)

View file

@ -156,8 +156,10 @@ trigger_rpc(clicon_handle h, /* Clicon handle */
goto done;
cv_name_set(cv, "k");
cv_string_set(cv, "5");
if ((xv = clixon_xvec_new()) == NULL)
goto done;
/* Use form 2c use spec of xc + name */
if (clixon_xml_find_index(xc, NULL, NULL, "y3", cvk, &xv) < 0)
if (clixon_xml_find_index(xc, NULL, NULL, "y3", cvk, xv) < 0)
goto done;
if (clixon_xvec_len(xv))
val = xml_find_body(clixon_xvec_i(xv,0), "val");

102
test/test_instance_id_multi.sh Executable file
View file

@ -0,0 +1,102 @@
#!/usr/bin/env bash
# Instance-id tests with multiple results
# RFC 7950 Sections 9.13 and 14 says that:
# Predicates are used only for specifying the values for the key nodes for list entries
# but does not explicitly say that list nodes can skip them
# And in RFC8341 node-instance-identifiers are defined as:
# All the same rules as an instance-identifier apply, except that predicates for keys are optional.
# If a key predicate is missing, then the node-instance-identifier represents all possible server
# instances for that key.
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
: ${clixon_util_path:=clixon_util_path -D $DBG}
xml1=$dir/xml1.xml
ydir=$dir/yang
if [ ! -d $ydir ]; then
mkdir $ydir
fi
# Nested lists
cat <<EOF > $ydir/example.yang
module example{
yang-version 1.1;
namespace "urn:example:id";
prefix ex;
container table{
list parameter{
key name;
leaf name{
type string;
}
container next{
list parameter{
key name;
leaf name{
type string;
}
leaf value{
type string;
}
}
}
}
}
}
EOF
cat <<EOF > $xml1
<table xmlns="urn:example:id">
<parameter>
<name>a</name>
<next>
<parameter>
<name>a</name>
<value>11</value>
</parameter>
<parameter>
<name>b</name>
<value>22</value>
</parameter>
</next>
</parameter>
<parameter>
<name>b</name>
<next>
<parameter>
<name>a</name>
<value>33</value>
</parameter>
</next>
</parameter>
</table>
EOF
new "instance-id top-level param"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter)" 0 "0: <parameter><name>a</name><next><parameter><name>a</name><value>11</value></parameter><parameter><name>b</name><value>22</value></parameter></next></parameter>" "1: <parameter><name>b</name><next><parameter><name>a</name><value>33</value></parameter></next></parameter>"
new "instance-id a/next"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next)" 0 "0: <next><parameter><name>a</name><value>11</value></parameter><parameter><name>b</name><value>22</value></parameter></next>"
new "instance-id a/next/param"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next/ex:parameter)" 0 "0: <parameter><name>a</name><value>11</value></parameter>" "1: <parameter><name>b</name><value>22</value></parameter>"
new "instance-id a/next/param/a"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next/ex:parameter[ex:name=\"a\"])" 0 "0: <parameter><name>a</name><value>11</value></parameter>" "0: <parameter><name>a</name><value>11</value></parameter>"
new "instance-id a/next/param/a/value"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next/ex:parameter[ex:name=\"a\"]/ex:value)" 0 "0: <value>11</value>"
new "instance-id top-level parameter/next"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter/ex:next)" 0 "0: <next><parameter><name>a</name><value>11</value></parameter><parameter><name>b</name><value>22</value></parameter></next>" "1: <next><parameter><name>a</name><value>33</value></parameter></next>"
new "instance-id top-level parameter/next/parameter"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter/ex:next/ex:parameter)" 0 "0: <parameter><name>a</name><value>11</value></parameter>" "1: <parameter><name>b</name><value>22</value></parameter>" "2: <parameter><name>a</name><value>33</value></parameter>"
rm -rf $dir
unset clixon_util_path # for other script reusing it

296
test/test_nacm_datanode_write.sh Executable file
View file

@ -0,0 +1,296 @@
#!/usr/bin/env bash
# Authentication and authorization and IETF NACM
# NACM data node rules
# The RFC 8341 examples in the appendix are very limited.
# Here focus on datanode paths from a write perspective.
# Especially a list in a list to test vector rules
# The test uses a nested list and makes CRUD operations on one object "b"
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
# Common NACM scripts
. ./nacm.sh
cfg=$dir/conf_yang.xml
fyang=$dir/nacm-example.yang
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
</clixon-config>
EOF
cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix ex;
import ietf-netconf-acm {
prefix nacm;
}
container table{
list parameter{
key name;
leaf name{
type string;
}
container next{
list parameter{
key name;
leaf name{
type string;
}
leaf value{
type string;
}
}
}
}
}
}
EOF
RULES=$(cat <<EOF
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<enable-nacm>false</enable-nacm>
<read-default>deny</read-default>
<write-default>deny</write-default>
<exec-default>permit</exec-default>
$NGROUPS
<rule-list>
<name>limited-acl</name>
<group>limited</group>
<rule>
<name>value</name>
<module-name>nacm-example</module-name>
<access-operations>create delete update</access-operations>
<path xmlns:ex="urn:example:nacm">/ex:table/ex:parameter/ex:next/ex:parameter/ex:value</path>
<action>permit</action>
</rule>
<rule>
<name>parameter</name>
<module-name>nacm-example</module-name>
<access-operations>read update</access-operations>
<path xmlns:ex="urn:example:nacm">/ex:table/ex:parameter/ex:next/ex:parameter</path>
<action>permit</action>
</rule>
</rule-list>
$NADMIN
</nacm>
EOF
)
CONFIG=$(cat <<EOF
<table xmlns="urn:example:nacm">
<parameter>
<name>a</name>
<next>
<parameter>
<name>a</name>
<value>72</value>
</parameter>
</next>
</parameter>
<parameter>
<name>b</name>
<next>
<parameter>
<name>a</name>
<value>99</value>
</parameter>
</next>
</parameter>
</table>
EOF
)
#
# Arguments, permit/deny on different levels:
# Configs (permit/deny):
# - write-default
# - param access
# - param action
# - value access
# - value action
# Tests, epxect true/false:
# - create
# - read
# - update
# - delete
testrun(){
writedefault=$1
paramaccess=$2
paramaction=$3
valueaccess=$4
valueaction=$5
testc=$6
testr=$7
testu=$8
testd=$9
new "set write-default $writedefault"
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/write-default -d "{\"ietf-netconf-acm:write-default\":\"$writedefault\"}" )" 0 "HTTP/1.1 204 No Content"
new "set param rule access: $paramaccess"
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$paramaccess\"}" )" 0 "HTTP/1.1 204 No Content"
new "set param rule access: $paramaction"
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/action -d "{\"ietf-netconf-acm:action\":\"$paramaction\"}" )" 0 "HTTP/1.1 204 No Content"
new "set value rule access: $valueaccess"
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$valueaccess\"}" )" 0 "HTTP/1.1 204 No Content"
new "set value rule access: $valueaction"
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/action -d "{\"ietf-netconf-acm:action\":\"$valueaction\"}" )" 0 "HTTP/1.1 204 No Content"
#--------------- Here tests: create/update/read/delete
new "create object b"
if $testc; then
expectpart "$(curl -u wilma:bar -siS -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 'HTTP/1.1 201 Created'
else
expectpart "$(curl -u wilma:bar -siS -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
fi
new "read object b"
if $testr; then
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"b","value":"17"}\]}'
else
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 404 Not Found'
fi
new "update object b"
if $testu; then
expectpart "$(curl -u wilma:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 'HTTP/1.1 204 No Content'
else
expectpart "$(curl -u wilma:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 'HTTP/1.1 403 Forbidden'
# '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
fi
new "delete object b"
if $testd; then
expectpart "$(curl -u wilma:bar -siS -X DELETE http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 204 No Content'
else # XXX can vara olika
ret=$(curl -u wilma:bar -siS -X DELETE http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)
r=$?
if [ $r != 0 ]; then
err "retval: $r" "0"
fi
match1=$(echo "$ret" | grep --null -o 'HTTP/1.1 403 Forbidden')
r1=$?
match2=$(echo "$ret" | grep --null -o 'HTTP/1.1 409 Conflict')
r2=$?
if [ $r1 != 0 -a $r2 != 0 ]; then
err "'HTTP/1.1 403 Forbidden' or 'HTTP/1.1 409 Conflict'" "$ret"
fi
# Ensure delete
new "ensure delete object b"
expectpart "$(curl -u andy:bar -siS -X DELETE http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1' # ignore error
fi
} # testrun
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
new "waiting"
wait_backend
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
wait_restconf
fi
new "auth set authentication config"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "set app config"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$CONFIG</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "commit it"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "enable nacm"
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content"
#--------------- nacm enabled
# config: def param:access/action value:access/action
# test: create read update delete
# default deny
testrun permit "*" permit "*" permit true true true true
testrun permit "*" deny "*" deny false false false false
testrun permit "*" permit "*" deny false false false false
testrun permit "*" permit "update delete" deny true true false false
testrun permit "*" permit "delete" deny true true true false
testrun permit "delete" deny "*" permit true true true false
testrun permit "update" deny "*" permit true true false true
testrun permit "read" deny "*" permit true false true true
testrun deny "*" permit "*" permit true true true true
testrun deny "*" permit "*" deny false false false false
testrun deny "create" permit "*" permit true true false false
# strange: a read permit on a sub-object while default read deny opens up all
testrun deny "create read" permit "*" permit true true false false
testrun deny "create" permit "create" permit true false false false
testrun deny "create update" permit "create update" permit true false true false
testrun deny "create update delete" permit "create update delete" permit true false true true
testrun deny "create update delete" permit "update" deny true false false true
testrun deny "create update delete" permit "delete" deny true false true false
# OK but only gives sub-.object (not value) too complex to test
# testrun deny "create update read" permit "read" deny true true true false
testrun deny "*" deny "*" deny false false false false
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -ne 0 ]; then # Bring your own backend
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
fi
rm -rf $dir

128
test/test_xml_trees.sh Executable file
View file

@ -0,0 +1,128 @@
#!/usr/bin/env bash
# XML inserty and merge two trees
# This is mainly a development API check
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
: ${clixon_util_xml_mod:=clixon_util_xml_mod}
OPTS="-D $DBG"
APPNAME=example
cfg=$dir/conf_yang.xml
fyang=$dir/example.yang
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
</clixon-config>
EOF
cat <<EOF > $fyang
module example {
yang-version 1.1;
namespace "urn:example:example";
prefix ex;
revision 2019-01-13;
container c{
leaf d{
type int32;
}
list a{
key x;
leaf x{
type int32;
}
}
}
}
EOF
# insert/merge a tree into a base tree
# Args:
# 1: operation
# 2: base tree
# 3: insert tree
# 4: xpath
# 5: retval
# 6: result
testrun(){
op=$1
x0=$2
x1=$3
xp=$4
ret=$5
res=$6
echo "$clixon_util_xml_mod -o $op -y $fyang -b "$x0" -x "$x1" -p $xp $OPTS"
expectpart "$($clixon_util_xml_mod -o $op -y $fyang -b "$x0" -x "$x1" -p $xp $OPTS)" $ret "$res"
}
new "test params: -y $fyang $OPTS"
# -------- insert
# Empty element base list
x0a='<c xmlns="urn:example:example">'
x0b='</c>'
p=c
new "insert 1st element"
testrun insert "$x0a$x0b" "$x0a<a><x>1</x></a>$x0b" $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
new "insert 2nd element"
testrun insert "$x0a<a><x>2</x></a>$x0b" "$x0a<a><x>1</x></a>$x0b" $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a><a><x>2</x></a></c>'
new "insert container"
testrun insert "$x0a<a><x>1</x></a>$x0b" "$x0a<d>42</d>$x0b" c 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a></c>'
# -------- parse parent
new "parent 1st element"
testrun parent "$x0a$x0b" "<a><x>1</x></a>" $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
new "patrse parent element"
testrun parent "$x0a<a><x>2</x></a>$x0b" '<a><x>1</x></a>' $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a><a><x>2</x></a></c>'
new "parse parent container"
testrun parent "$x0a<a><x>1</x></a>$x0b" '<d>42</d>' c 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a></c>'
# -------- merge
new "merge empty"
testrun merge "$x0a$x0b" "$x0a$x0b" $p 0 '<c xmlns="urn:example:example"/>'
new "merge single w empty"
testrun merge "$x0a<a><x>1</x></a>$x0b" "$x0a$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
new "merge empty w single"
testrun merge "$x0a$x0b" "$x0a<a><x>1</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
new "merge equal single"
testrun merge "$x0a<a><x>1</x></a>$x0b" "$x0a<a><x>1</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
new "merge overlap"
testrun merge "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" "$x0a<a><x>2</x></a><a><x>3</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a><a><x>2</x></a><a><x>3</x></a></c>'
new "merge list and leaf"
testrun merge "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" "$x0a<d>42</d>$x0b" . 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a><a><x>2</x></a></c>'
new "merge leaf and list"
testrun merge "$x0a<d>42</d>$x0b" "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a><a><x>2</x></a></c>'
new "merge overlap with path fail, merge does not work w subtrees"
testrun merge "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" "$x0a<a><x>2</x></a><a><x>3</x></a>$x0b" c 255 ''
rm -rf $dir
# unset conditional parameters
unset clixon_util_xml_mod

View file

@ -70,10 +70,10 @@ struct curlbuf{
* realloc. Therefore, we append new data to the userdata buffer.
*/
static size_t
curl_get_cb(void *ptr,
curl_get_cb(void *ptr,
size_t size,
size_t nmemb,
void *userdata)
void *userdata)
{
struct curlbuf *buf = (struct curlbuf *)userdata;
int len;