NACM RFC341 datanode paths, read operation
This commit is contained in:
parent
58c36decef
commit
ba59e22fd7
26 changed files with 689 additions and 325 deletions
|
|
@ -24,6 +24,12 @@
|
|||
## 4.5.0
|
||||
Expected: May 2020
|
||||
|
||||
### Major New features
|
||||
|
||||
* NACM RFC341 datanode paths
|
||||
* Read operations now implemented
|
||||
* Create/Write/Update not yet
|
||||
|
||||
### API changes on existing protocol/config features (You may have have to change how you use Clixon)
|
||||
|
||||
* Stricter incoming RPC sanity checking, error messages may have changed.
|
||||
|
|
@ -36,6 +42,7 @@ Expected: May 2020
|
|||
|
||||
### Minor changes
|
||||
|
||||
* Sanity check of duplicates prefixes in Yang modules and submodules as defined in RFC 7950 Sec 7.1.4
|
||||
|
||||
## 4.4.0
|
||||
5 April 2020
|
||||
|
|
@ -49,7 +56,7 @@ features include optimized search functions and a repair callback.
|
|||
|
||||
* New "general-purpose" datastore upgrade/repair callback called once on startup, intended for low-level general upgrades and as a complement to module-specific upgrade.
|
||||
* Called on startup after initial XML parsing, but before module-specific upgrades
|
||||
* Enabled by definign the `.ca_datastore_upgrade`
|
||||
* Enabled by defining the `.ca_datastore_upgrade`
|
||||
* [General-purpose upgrade documentation](https://clixon-docs.readthedocs.io/en/latest/backend.html#general-purpose)
|
||||
* New and updated search functions using xpath, api-path and instance-id, and explicit indexes
|
||||
* New search functions using api-path and instance_id:
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ client_get_config_only(clicon_handle h,
|
|||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* NACM datanode/module read validation */
|
||||
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
|
||||
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbret, "<rpc-reply>");
|
||||
|
|
@ -1174,7 +1174,7 @@ from_client_get(clicon_handle h,
|
|||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* NACM datanode/module read validation */
|
||||
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
|
||||
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbret, "<rpc-reply>"); /* OK */
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ clixon_plugin_statedata(clicon_handle h,
|
|||
if (debug)
|
||||
clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__);
|
||||
#endif
|
||||
/* XXX: ret == 0 invalid yang binding should be handled as internal error */
|
||||
if (xml_bind_yang(x, YB_MODULE, yspec, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_apply(x, CX_ELMNT, xml_sort, h) < 0)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ leafstring(cxobj *x)
|
|||
return xml_value(c);
|
||||
}
|
||||
|
||||
/*! Internal recursive part where configuration xml tree is pruned frim filter
|
||||
/*! Internal recursive part where configuration xml tree is pruned from filter
|
||||
* assume parent has been selected and filter match (same name) as parent
|
||||
* parent is pruned according to selection.
|
||||
* @param[in] xfilter Filter xml
|
||||
|
|
|
|||
|
|
@ -738,9 +738,11 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
}
|
||||
/* Add prefix/namespaces used in attributes */
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(nsc, cv)) != NULL)
|
||||
if (xmlns_set(xdata, cv_name_get(cv), cv_string_get(cv)) < 0)
|
||||
while ((cv = cvec_each(nsc, cv)) != NULL){
|
||||
char *ns = cv_string_get(cv);
|
||||
if (xmlns_set(xdata, cv_name_get(cv), ns) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (nsc)
|
||||
xml_sort(xdata, NULL); /* Ensure attr is first */
|
||||
cprintf(cb, "/>");
|
||||
|
|
|
|||
|
|
@ -57,8 +57,9 @@ enum nacm_access{
|
|||
* Prototypes
|
||||
*/
|
||||
int nacm_rpc(char *rpc, char *module, char *username, cxobj *xnacm, cbuf *cbret);
|
||||
int nacm_datanode_read(cxobj *xt, cxobj **xvec, size_t xlen, char *username, cxobj *nacm_xtree);
|
||||
int nacm_datanode_write(cxobj *xt, cxobj *xr, enum nacm_access access,
|
||||
int nacm_datanode_read(clicon_handle h, cxobj *xt, cxobj **xvec, size_t xlen, char *username,
|
||||
cxobj *nacm_xtree);
|
||||
int nacm_datanode_write(clicon_handle h, cxobj *xt, cxobj *xr, enum nacm_access access,
|
||||
char *username, cxobj *xnacm, cbuf *cbret);
|
||||
int nacm_access_pre(clicon_handle h, char *username, cxobj **xnacmp);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ int xml_sanity(cxobj *x, void *arg);
|
|||
int xml_non_config_data(cxobj *xt, void *arg);
|
||||
|
||||
int xml2xpath(cxobj *x, char **xpath);
|
||||
int assign_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p);
|
||||
int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
|
||||
int assign_namespace_body(cxobj *x0, char *x0bstr, cxobj *x1);
|
||||
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
|
||||
int yang_enum_int_value(cxobj *node, int32_t *val);
|
||||
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ int xml_nsctx_node(cxobj *x, cvec **ncp);
|
|||
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
||||
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
|
||||
|
||||
int xml2ns(cxobj *x, char *localname, char **namespace);
|
||||
int xml2prefix(cxobj *xn, char *namespace, char **prefixp);
|
||||
int xml_localname_check(cxobj *xn, void *arg);
|
||||
int xml2ns(cxobj *x, char *localname, char **namespace);
|
||||
int xml2prefix(cxobj *xn, char *namespace, char **prefixp);
|
||||
int xml_localname_check(cxobj *xn, void *arg);
|
||||
|
||||
#endif /* _CLIXON_XML_NSCTX_H */
|
||||
|
|
|
|||
|
|
@ -131,15 +131,19 @@ attr_ns_value(cxobj *x,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! When new body is added, some needs type lookup is made and namespace checked
|
||||
* This includes identityrefs, paths
|
||||
* This code identifies x0 as an identityref, looks at the _body_ string and ensures the right
|
||||
* namespace is inserted in x1.
|
||||
*/
|
||||
static int
|
||||
check_identityref(cxobj *x0,
|
||||
cxobj *x1,
|
||||
cxobj *x1p,
|
||||
char *x1bstr,
|
||||
yang_stmt *y)
|
||||
check_body_namespace(cxobj *x0,
|
||||
cxobj *x1,
|
||||
cxobj *x1p,
|
||||
char *x1bstr,
|
||||
yang_stmt *y)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yrestype = NULL;
|
||||
char *prefix = NULL;
|
||||
char *ns0 = NULL;
|
||||
char *ns1 = NULL;
|
||||
|
|
@ -151,11 +155,6 @@ check_identityref(cxobj *x0,
|
|||
isroot = xml_parent(x1p)==NULL &&
|
||||
strcmp(xml_name(x1p), "config") == 0 &&
|
||||
xml_prefix(x1p)==NULL;
|
||||
if (yang_type_get(y, NULL, &yrestype,
|
||||
NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (strcmp(yang_argument_get(yrestype), "identityref") != 0)
|
||||
goto ok; /* skip */
|
||||
if (nodeid_split(x1bstr, &prefix, NULL) < 0)
|
||||
goto done;
|
||||
if (prefix == NULL)
|
||||
|
|
@ -240,12 +239,12 @@ text_modify(clicon_handle h,
|
|||
char *opstr = NULL;
|
||||
char *x1name;
|
||||
char *x1cname; /* child name */
|
||||
cxobj *x0c; /* base child */
|
||||
cxobj *x0b; /* base body */
|
||||
cxobj *x1c; /* mod child */
|
||||
char *x0bstr; /* mod body string */
|
||||
char *x1bstr; /* mod body string */
|
||||
yang_stmt *yc; /* yang child */
|
||||
cxobj *x0c; /* base child */
|
||||
cxobj *x0b; /* base body */
|
||||
cxobj *x1c; /* mod child */
|
||||
char *x0bstr; /* mod body string */
|
||||
char *x1bstr; /* mod body string */
|
||||
yang_stmt *yc; /* yang child */
|
||||
cxobj **x0vec = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
|
|
@ -318,7 +317,7 @@ text_modify(clicon_handle h,
|
|||
* of ordered-by user and (changed) insert attribute.
|
||||
*/
|
||||
if (!permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -335,7 +334,7 @@ text_modify(clicon_handle h,
|
|||
case OP_NONE: /* fall thru */
|
||||
if (x0==NULL){
|
||||
if ((op != OP_NONE) && !permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -350,8 +349,9 @@ text_modify(clicon_handle h,
|
|||
/* Get namespace from x1
|
||||
* Check if namespace exists in x0 parent
|
||||
* if not add new binding and replace in x0.
|
||||
* See also xmlns copying of attributes in the body section below
|
||||
*/
|
||||
if (assign_namespaces(x1, x0, x0p) < 0)
|
||||
if (assign_namespace_element(x1, x0, x0p) < 0)
|
||||
goto done;
|
||||
changed++;
|
||||
if (op==OP_NONE)
|
||||
|
|
@ -362,17 +362,46 @@ text_modify(clicon_handle h,
|
|||
}
|
||||
}
|
||||
if (x1bstr){
|
||||
/* XXX: Do the type lookup here inline instead, there are more
|
||||
* cases where we check for types, eg where we now call clixon_trim2
|
||||
* AND in nacm path where we need proper namespace contexts.
|
||||
/* Some bodies (eg identityref) requires proper namespace setup, so a type lookup is
|
||||
* necessary.
|
||||
*/
|
||||
if (check_identityref(x1, x0, x0p, x1bstr, y0) < 0)
|
||||
yang_stmt *yrestype = NULL;
|
||||
char *restype;
|
||||
|
||||
if (yang_type_get(y0, NULL, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (yrestype == NULL){
|
||||
clicon_err(OE_CFG, EFAULT, "No restype (internal error)");
|
||||
goto done;
|
||||
}
|
||||
restype = yang_argument_get(yrestype);
|
||||
if (strcmp(restype, "identityref") == 0){
|
||||
x1bstr = clixon_trim2(x1bstr, " \t\n");
|
||||
if (check_body_namespace(x1, x0, x0p, x1bstr, y0) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
/* Some bodies strip pretty-printed here, unsure where to do this,.. */
|
||||
if (strcmp(restype, "enumeration") == 0 ||
|
||||
strcmp(restype, "bits") == 0)
|
||||
x1bstr = clixon_trim2(x1bstr, " \t\n");
|
||||
|
||||
/* If origin body has namespace definitions, copy them. The reason is that
|
||||
* some bodies rely on namespace prefixes, such as NACM path, but there is
|
||||
* no way we can now this here.
|
||||
* However, this may lead to namespace collisions if these prefixes are not
|
||||
* canonical, and may collide with the assign_namespace_element() above (but that
|
||||
* is for element sysmbols)
|
||||
* Oh well.
|
||||
*/
|
||||
if (assign_namespace_body(x1, x1bstr, x0) < 0)
|
||||
goto done;
|
||||
}
|
||||
if ((x0b = xml_body_get(x0)) != NULL){
|
||||
x0bstr = xml_value(x0b);
|
||||
if (x0bstr==NULL || strcmp(x0bstr, x1bstr)){
|
||||
if ((op != OP_NONE) && !permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x1,
|
||||
if ((ret = nacm_datanode_write(h, NULL, x1,
|
||||
x0bstr==NULL?NACM_CREATE:NACM_UPDATE,
|
||||
username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
|
|
@ -398,7 +427,7 @@ text_modify(clicon_handle h,
|
|||
case OP_REMOVE: /* fall thru */
|
||||
if (x0){
|
||||
if ((op != OP_NONE) && !permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -460,7 +489,7 @@ text_modify(clicon_handle h,
|
|||
* of ordered-by user and (changed) insert attribute.
|
||||
*/
|
||||
if (!permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -486,7 +515,7 @@ text_modify(clicon_handle h,
|
|||
if (op == OP_NONE)
|
||||
break;
|
||||
if (op==OP_MERGE && !permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -503,7 +532,7 @@ text_modify(clicon_handle h,
|
|||
} /* anyxml, anydata */
|
||||
if (x0==NULL){
|
||||
if (op==OP_MERGE && !permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -522,7 +551,7 @@ text_modify(clicon_handle h,
|
|||
* Check if namespace exists in x0 parent
|
||||
* if not add new binding and replace in x0.
|
||||
*/
|
||||
if (assign_namespaces(x1, x0, x0p) < 0)
|
||||
if (assign_namespace_element(x1, x0, x0p) < 0)
|
||||
goto done;
|
||||
if (op==OP_NONE)
|
||||
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
|
||||
|
|
@ -595,7 +624,7 @@ text_modify(clicon_handle h,
|
|||
case OP_REMOVE: /* fall thru */
|
||||
if (x0){
|
||||
if (!permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -676,7 +705,7 @@ text_modify_top(clicon_handle h,
|
|||
case OP_REMOVE:
|
||||
case OP_REPLACE:
|
||||
if (!permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -710,7 +739,7 @@ text_modify_top(clicon_handle h,
|
|||
/* Special case top-level replace */
|
||||
else if (op == OP_REPLACE || op == OP_DELETE){
|
||||
if (!permit && xnacm){
|
||||
if ((ret = nacm_datanode_write(NULL, x1, NACM_UPDATE, username, xnacm, cbret)) < 0)
|
||||
if ((ret = nacm_datanode_write(h, NULL, x1, NACM_UPDATE, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@
|
|||
#include "clixon_yang_module.h"
|
||||
#include "clixon_datastore.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_path.h"
|
||||
#include "clixon_nacm.h"
|
||||
|
||||
/* NACM namespace for use with xml namespace contexts and xpath */
|
||||
|
|
@ -311,38 +313,131 @@ nacm_rpc(char *rpc,
|
|||
* Datanode/module read and write
|
||||
*/
|
||||
|
||||
/*! We have a rule matching user group. Now match proper write operation and module
|
||||
/*! There is a data-node rule with a path. Match it.
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] xt XML root tree with "config" label.
|
||||
* @param[in] xr XML requestor node (part of xt)
|
||||
* @param[in] pathobj XML nacm path node
|
||||
* @retval -1 Error
|
||||
* @retval 0 No Match
|
||||
* @retval 1 Match
|
||||
* @see RFC8341 3.4.5. Data Node Access Validation point (6)
|
||||
* @retval 1 SubMatch
|
||||
|
||||
o For HEAD and GET requests, any data nodes that are ancestor nodes
|
||||
of the target resource are considered to be part of the retrieval
|
||||
request for access control purposes.
|
||||
Data nodes to which the client does not have read access are silently
|
||||
omitted, along with any descendants,
|
||||
the selection criteria are applied to the subset of nodes that the user
|
||||
is authorized to read, not the entire datastore.
|
||||
* Assume tree:
|
||||
* tree
|
||||
* |
|
||||
* a
|
||||
* / \
|
||||
* b c
|
||||
* access: /
|
||||
* rule1: /a/b deny
|
||||
* rule2: / permit
|
||||
* returned tree:
|
||||
* tree
|
||||
* |
|
||||
* a
|
||||
* \
|
||||
* c
|
||||
* Algorithm:
|
||||
* Traverse all rules and access the tree:
|
||||
* rule1: purge b
|
||||
* rule2: accept rest.
|
||||
*/
|
||||
static int
|
||||
nacm_rule_datanode(cxobj *xt,
|
||||
cxobj *xr,
|
||||
nacm_rule_datanode_path(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xreq,
|
||||
cxobj *pathobj)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *nsc0 = NULL; /* Non-canonical namespace context */
|
||||
cxobj *xp; /* Node specified by path */
|
||||
cxobj *xa; /* Ancestor */
|
||||
char *path0; /* Non-canonical path */
|
||||
char *path; /* Canonical path */
|
||||
yang_stmt *yspec;
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen = 0;
|
||||
int ret;
|
||||
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
path0 = clixon_trim2(xml_body(pathobj), " \t\n");
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if (xml_nsctx_node(pathobj, &nsc0) < 0)
|
||||
goto done;
|
||||
/* instance-id requires canonical paths */
|
||||
if (xpath2canonical(path0, nsc0, yspec, &path, NULL) < 0)
|
||||
goto done;
|
||||
if ((ret = clixon_xml_find_instance_id(xt, yspec, &xvec, &xlen, "%s", path)) < 0)
|
||||
goto nomatch;
|
||||
if (ret == 0)
|
||||
goto nomatch;
|
||||
assert(xlen == 1); /* XXX: This may be rich */
|
||||
/* xp is the node specified by the path */
|
||||
xp = xvec[0];
|
||||
|
||||
/* The requested node (xreq) is the node specified by the path (xp) or is a
|
||||
* descendant node of the path xn.
|
||||
* By checking if xreq is equal to xp or if any of its ancestors is =
|
||||
* xmatch is one of xvec[] or an ancestor of the xvec[] nodes.
|
||||
*/
|
||||
xa = xreq;
|
||||
do {
|
||||
if (xp == xa)
|
||||
goto match;
|
||||
} while ((xa = xml_parent(xa)) != NULL);
|
||||
match:
|
||||
retval = 1;
|
||||
done:
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
return retval;
|
||||
nomatch:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! We have a rule matching user group. Now match proper write operation and module
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] xt XML root tree with "config" label.
|
||||
* @param[in] xreq XML requestor node (part of xt)
|
||||
* @param[in] xrule XML rule node
|
||||
* @param[in] access read/write/create/update/delete/exec
|
||||
* @retval -1 Error
|
||||
* @retval 0 No Match
|
||||
* @retval 1 Match
|
||||
* @see RFC8341 3.4.5. Data Node Access Validation point (6)
|
||||
* @note read access is not handled in this function, see nacm_data_read_xrule
|
||||
*/
|
||||
static int
|
||||
nacm_rule_datanode(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xreq,
|
||||
cxobj *xrule,
|
||||
enum nacm_access access)
|
||||
{
|
||||
int retval = -1;
|
||||
char *path;
|
||||
char *access_operations;
|
||||
char *module_rule; /* rule module name */
|
||||
yang_stmt *ys;
|
||||
yang_stmt *ymod;
|
||||
char *module;
|
||||
cxobj *xpath; /* xpath match */
|
||||
cxobj *xp; /* parent */
|
||||
cvec *nsc = NULL;
|
||||
cxobj *pathobj;
|
||||
int ret;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
|
||||
* the YANG module where the requested data node is defined. */
|
||||
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
|
||||
goto nomatch;
|
||||
if (strcmp(module_rule,"*")!=0){
|
||||
if (xr==NULL || (ys = xml_spec(xr)) == NULL)
|
||||
if (xreq==NULL || (ys = xml_spec(xreq)) == NULL)
|
||||
goto nomatch;
|
||||
ymod = ys_module(ys);
|
||||
module = yang_argument_get(ymod);
|
||||
|
|
@ -353,15 +448,13 @@ nacm_rule_datanode(cxobj *xt,
|
|||
/* 6b) Either (1) the rule does not have a "rule-type" defined or
|
||||
(2) the "rule-type" is "data-node" and the "path" matches the
|
||||
requested data node, action node, or notification node. */
|
||||
if ((path = xml_find_body(xrule, "path")) == NULL){
|
||||
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
|
||||
if (xml_find_body(xrule, "rpc-name") ||xml_find_body(xrule, "notification-name"))
|
||||
goto nomatch;
|
||||
}
|
||||
else
|
||||
path = clixon_trim2(path, " \t\n");
|
||||
access_operations = xml_find_body(xrule, "access-operations");
|
||||
switch (access){
|
||||
case NACM_READ:
|
||||
case NACM_READ: /* Not used, see nacm_data_read_xrule */
|
||||
/* 6c) For a "read" access operation, the rule's "access-operations"
|
||||
leaf has the "read" bit set or has the special value "*" */
|
||||
if (!match_access(access_operations, "read", NULL))
|
||||
|
|
@ -388,108 +481,166 @@ nacm_rule_datanode(cxobj *xt,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
/* Here module is matched, now check for path if any NYI
|
||||
* A path is considered to match if the requested node is the node
|
||||
/* Here module is matched, now check for path if any
|
||||
* A path is considered to match if the requested node (xreq) is the node
|
||||
* specified by the path or is a descendant node of the path */
|
||||
if (path){
|
||||
#if 0
|
||||
if ((ret = clixon_xml_find_instance_id(xt, yt, xvec, xlen, "%s", path)) == NULL)
|
||||
if (pathobj){
|
||||
if ((ret = nacm_rule_datanode_path(h, xt, xreq, pathobj)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto nomatch;
|
||||
#else
|
||||
if ((xpath = xpath_first(xt, nsc, "%s", path)) == NULL)
|
||||
goto nomatch;
|
||||
#endif
|
||||
/* The requested node xr is the node specified by the path or is a
|
||||
* descendant node of the path:
|
||||
* xmatch is one of xvec[] or an ancestor of the xvec[] nodes.
|
||||
*/
|
||||
xp = xr;
|
||||
do {
|
||||
if (xpath == xp)
|
||||
goto match;
|
||||
} while ((xp = xml_parent(xp)) != NULL);
|
||||
if (ret == 2)
|
||||
goto submatch;
|
||||
}
|
||||
match:
|
||||
// match:
|
||||
retval = 1;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
return retval;
|
||||
nomatch:
|
||||
retval = 0;
|
||||
goto done;
|
||||
submatch:
|
||||
retval = 2;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Go through all rules for a requested node
|
||||
* @param[in] xt XML root tree with "config" label
|
||||
* @param[in] xr Requested node (node in xt)
|
||||
* @param[in] gvec NACM groups where user is member
|
||||
* @param[in] glen Length of gvec
|
||||
* @param[in] rlistvec NACM rule-list entries
|
||||
* @param[in] rlistlen Length of rlistvec
|
||||
* @param[in] nsc NACM namespace context for xpaths
|
||||
* @param[out] xrulep If set, then points to matching rule
|
||||
*/
|
||||
static int
|
||||
nacm_data_read_xr(cxobj *xt,
|
||||
cxobj *xr,
|
||||
cxobj **gvec,
|
||||
size_t glen,
|
||||
cxobj **rlistvec,
|
||||
size_t rlistlen,
|
||||
cvec *nsc,
|
||||
cxobj **xrulep)
|
||||
nacm_data_read_action(cxobj *xrule,
|
||||
cxobj *xp,
|
||||
int default_permit)
|
||||
{
|
||||
int retval = -1;
|
||||
int i, j;
|
||||
cxobj *rlist;
|
||||
char *gname;
|
||||
cxobj **rvec = NULL; /* rules */
|
||||
size_t rlen;
|
||||
cxobj *xrule = NULL;
|
||||
int match = 0;
|
||||
int retval = -1;
|
||||
char *action;
|
||||
|
||||
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
|
||||
rlist = rlistvec[i];
|
||||
/* Loop through user's group to find match in this rule-list */
|
||||
for (j=0; j<glen; j++){
|
||||
gname = xml_find_body(gvec[j], "name");
|
||||
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
|
||||
break; /* found */
|
||||
}
|
||||
if (j==glen) /* not found */
|
||||
continue;
|
||||
/* 6. For each rule-list entry found, process all rules, in order,
|
||||
until a rule that matches the requested access operation is
|
||||
found. (see 6 sub rules in nacm_rule_datanode
|
||||
*/
|
||||
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
|
||||
goto done;
|
||||
for (j=0; j<rlen; j++){ /* Loop through rules */
|
||||
xrule = rvec[j];
|
||||
if ((match = nacm_rule_datanode(xt, xr, xrule, NACM_READ)) < 0)
|
||||
if ((action = xml_find_body(xrule, "action")) != NULL){
|
||||
if (strcmp(action, "deny")==0){
|
||||
if (xml_purge(xp) < 0)
|
||||
goto done;
|
||||
if (match) /* xrule match */
|
||||
break;
|
||||
}
|
||||
if (rvec){
|
||||
free(rvec);
|
||||
rvec=NULL;
|
||||
else if (strcmp(action, "permit")==0){
|
||||
if (default_permit)
|
||||
;
|
||||
else
|
||||
xml_flag_set(xp, XML_FLAG_MARK);
|
||||
}
|
||||
if (match) /* xrule match */
|
||||
break;
|
||||
}
|
||||
if (match)
|
||||
*xrulep = xrule;
|
||||
else
|
||||
*xrulep = NULL;
|
||||
retval = 0;
|
||||
done:
|
||||
if (rvec)
|
||||
free(rvec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK and rule does not match
|
||||
* @retval 1 OK and rule matches
|
||||
* Two distinct cases:
|
||||
* (1) read_default is permit
|
||||
* mark all deny rules and remove them
|
||||
* (2) read_default is deny:
|
||||
* mark all permit rules and ancestors, remove everything else
|
||||
*/
|
||||
// xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
|
||||
static int
|
||||
nacm_data_read_xrule(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xrule,
|
||||
int default_permit)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xp;
|
||||
int ret;
|
||||
cxobj *pathobj;
|
||||
char *access_operations;
|
||||
cvec *nsc0 = NULL; /* Non-canonical namespace context */
|
||||
char *path0; /* Non-canonical path */
|
||||
char *path; /* Canonical path */
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ymod;
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen = 0;
|
||||
char *module_rule; /* rule module name */
|
||||
int i;
|
||||
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
|
||||
goto nomatch;
|
||||
|
||||
/* 6b) Either (1) the rule does not have a "rule-type" defined or
|
||||
(2) the "rule-type" is "data-node" and the "path" matches the
|
||||
requested data node, action node, or notification node. */
|
||||
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
|
||||
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
|
||||
goto nomatch;
|
||||
}
|
||||
access_operations = xml_find_body(xrule, "access-operations");
|
||||
/* 6c) For a "read" access operation, the rule's "access-operations"
|
||||
leaf has the "read" bit set or has the special value "*" */
|
||||
if (!match_access(access_operations, "read", NULL))
|
||||
goto nomatch;
|
||||
if (pathobj == NULL){
|
||||
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
|
||||
* the YANG module where the requested data node is defined.
|
||||
* Go thru all children of xt,
|
||||
*/
|
||||
i = 0;
|
||||
xp = NULL;
|
||||
while ((xp = xml_child_each(xt, xp, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(module_rule, "*") != 0){
|
||||
if (ys_module_by_xml(yspec, xp, &ymod) < 0)
|
||||
goto done;
|
||||
if (strcmp(yang_argument_get(ymod), module_rule) != 0)
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (nacm_data_read_action(xrule, xp, default_permit) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (i==0)
|
||||
goto nomatch;
|
||||
goto match;
|
||||
}
|
||||
path0 = clixon_trim2(xml_body(pathobj), " \t\n");
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if (xml_nsctx_node(pathobj, &nsc0) < 0)
|
||||
goto done;
|
||||
/* instance-id requires canonical paths */
|
||||
if (xpath2canonical(path0, nsc0, yspec, &path, NULL) < 0)
|
||||
goto done;
|
||||
/* XXX path may not be yang-consistent gives an error,... but retval =0 back,... */
|
||||
if ((ret = clixon_xml_find_instance_id(xt, yspec, &xvec, &xlen, "%s", path)) < 0)
|
||||
goto done;
|
||||
switch (ret){
|
||||
case 0: /* Actually some kind of failure, eg yang mismatch */
|
||||
goto nomatch;
|
||||
break;
|
||||
case 1:
|
||||
if (xlen == 0)
|
||||
goto nomatch;
|
||||
assert(xlen == 1);
|
||||
xp = xvec[0]; /* XXX: loop? */
|
||||
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
|
||||
* the YANG module where the requested data node is defined. */
|
||||
if (strcmp(module_rule, "*") != 0){
|
||||
if (ys_module_by_xml(yspec, xp, &ymod) < 0)
|
||||
goto done;
|
||||
if (strcmp(yang_argument_get(ymod), module_rule) != 0)
|
||||
goto nomatch;
|
||||
}
|
||||
if (nacm_data_read_action(xrule, xp, default_permit) < 0)
|
||||
goto done;
|
||||
break;
|
||||
} /* switch */
|
||||
match:
|
||||
retval = 1; /* match */
|
||||
done:
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
return retval;
|
||||
nomatch:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Make nacm datanode and module rule read access validation
|
||||
* Just purge nodes that fail validation (dont send netconf error message)
|
||||
* @param[in] xt XML root tree with "config" label
|
||||
|
|
@ -566,23 +717,26 @@ nacm_data_read_xr(cxobj *xt,
|
|||
* @see nacm_rpc
|
||||
*/
|
||||
int
|
||||
nacm_datanode_read(cxobj *xt,
|
||||
cxobj **xrvec,
|
||||
size_t xrlen,
|
||||
char *username,
|
||||
cxobj *xnacm)
|
||||
nacm_datanode_read(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj **xrvec,
|
||||
size_t xrlen,
|
||||
char *username,
|
||||
cxobj *xnacm)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj **gvec = NULL; /* groups */
|
||||
size_t glen;
|
||||
cxobj *xr;
|
||||
char *gname;
|
||||
cxobj **rlistvec = NULL; /* rule-list */
|
||||
size_t rlistlen;
|
||||
int i;
|
||||
int j;
|
||||
char *read_default = NULL;
|
||||
cxobj *xrule;
|
||||
char *action;
|
||||
int matches = 0;
|
||||
cvec *nsc = NULL;
|
||||
cxobj *xrule;
|
||||
int ret;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
|
|
@ -610,37 +764,53 @@ nacm_datanode_read(cxobj *xt,
|
|||
clicon_err(OE_XML, EINVAL, "No nacm read-default rule");
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<xrlen; i++){ /* Loop through requested nodes */
|
||||
xr = xrvec[i]; /* requested node XR */
|
||||
/* Loop through rule-list (steps 5,6,7) to find match of requested node
|
||||
*/
|
||||
xrule = NULL;
|
||||
/* Skip if no groups */
|
||||
if (glen && nacm_data_read_xr(xt, xr, gvec, glen, rlistvec, rlistlen,
|
||||
nsc, &xrule) < 0)
|
||||
matches = 0;
|
||||
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
|
||||
cxobj *rlist;
|
||||
cxobj **rvec = NULL; /* rules */
|
||||
size_t rlen;
|
||||
|
||||
rlist = rlistvec[i];
|
||||
/* Loop through user's group to find match in this rule-list */
|
||||
for (j=0; j<glen; j++){
|
||||
gname = xml_find_body(gvec[j], "name");
|
||||
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
|
||||
break; /* found */
|
||||
}
|
||||
if (j==glen) /* not found */
|
||||
continue;
|
||||
/* 6. For each rule-list entry found, process all rules, in order,
|
||||
until a rule that matches the requested access operation is
|
||||
found. (see 6 sub rules in nacm_rule_datanode
|
||||
*/
|
||||
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
|
||||
goto done;
|
||||
if (xrule){ /* xrule match requested node xr */
|
||||
if ((action = xml_find_body(xrule, "action")) == NULL)
|
||||
continue;
|
||||
if (strcmp(action, "deny")==0){
|
||||
if (xml_purge(xr) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(action, "permit")==0)
|
||||
;/* XXX recursively find denies in xr and purge them
|
||||
* ie call nacm_data_read_xr recursively?
|
||||
*/
|
||||
for (j=0; j<rlen; j++){ /* Loop through rules */
|
||||
xrule = rvec[j];
|
||||
if ((ret = nacm_data_read_xrule(h, xt, xrule, strcmp(read_default,"permit")==0)) < 0)
|
||||
goto done;
|
||||
if (ret > 0)
|
||||
matches++;
|
||||
}
|
||||
else{ /* no rule matching xr, apply default */
|
||||
/*11. For a "read" access operation, if the "read-default" leaf is set
|
||||
to "permit", then include the requested data node in the reply;
|
||||
otherwise, do not include the requested data node or any of its
|
||||
descendants in the reply.*/
|
||||
if (strcmp(read_default, "deny")==0)
|
||||
if (xml_purge(xr) < 0)
|
||||
goto done;
|
||||
if (rvec){
|
||||
free(rvec);
|
||||
rvec=NULL;
|
||||
}
|
||||
} /* xr */
|
||||
}
|
||||
/* If there were accpet rules in some matches, remove everything else */
|
||||
if (matches && strcmp(read_default, "deny") == 0)
|
||||
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||
goto done;
|
||||
if (strcmp(read_default, "deny") == 0){
|
||||
if (matches == 0)
|
||||
goto step9; /* delete all */
|
||||
else /* Accept rules that override default delete: delete everything not marked) */
|
||||
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* reset flag */
|
||||
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
/* 8. At this point, no matching rule was found in any rule-list
|
||||
entry. */
|
||||
|
|
@ -670,9 +840,10 @@ nacm_datanode_read(cxobj *xt,
|
|||
/*! Make nacm datanode and module rule write access validation
|
||||
* The operations of NACM are: create, read, update, delete, exec
|
||||
* where write is short-hand for create+delete+update
|
||||
* @param[in] xt XML root tree with "config" label. XXX?
|
||||
* @param[in] xr XML requestor node (part of xt)
|
||||
* @param[in] op NACM access of xr
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] xt XML root tree with "config" label.
|
||||
* @param[in] xreq XML requestor node (part of xt)
|
||||
* @param[in] op NACM access of xreq
|
||||
* @param[in] username User making access
|
||||
* @param[in] xnacm NACM xml tree
|
||||
* @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
|
||||
|
|
@ -684,14 +855,15 @@ nacm_datanode_read(cxobj *xt,
|
|||
* @see nacm_rpc
|
||||
*/
|
||||
int
|
||||
nacm_datanode_write(cxobj *xt,
|
||||
cxobj *xr,
|
||||
nacm_datanode_write(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xreq,
|
||||
enum nacm_access access,
|
||||
char *username,
|
||||
cxobj *xnacm,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
int retval = -1;
|
||||
cxobj **gvec = NULL; /* groups */
|
||||
size_t glen;
|
||||
cxobj **rlistvec = NULL; /* rule-list */
|
||||
|
|
@ -749,7 +921,7 @@ nacm_datanode_write(cxobj *xt,
|
|||
*/
|
||||
for (j=0; j<rlen; j++){ /* Loop through rules */
|
||||
xrule = rvec[j];
|
||||
if ((match = nacm_rule_datanode(xt, xr, xrule, access)) < 0)
|
||||
if ((match = nacm_rule_datanode(h, xt, xreq, xrule, access)) < 0)
|
||||
goto done;
|
||||
if (match) /* match */
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -686,7 +686,10 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
if (xml_nsctx_get_prefix(nsc, namespace, &xprefix) == 0){
|
||||
xprefix = yang_find_myprefix(y);
|
||||
clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix);
|
||||
/* not found, add it to nsc */
|
||||
/* not found, add it to nsc
|
||||
* XXX: do we always have to add it? It could be default?
|
||||
*/
|
||||
// if (xml2prefix(x1, namespace, &pexisting));
|
||||
if (xml_nsctx_add(nsc, xprefix, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1358,7 +1361,8 @@ instance_id_resolve(clixon_path *cplist,
|
|||
}
|
||||
}
|
||||
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
|
||||
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id:%s not found",
|
||||
cp->cp_id);
|
||||
goto fail;
|
||||
}
|
||||
cp->cp_yang = yc;
|
||||
|
|
@ -1411,9 +1415,9 @@ instance_id_resolve(clixon_path *cplist,
|
|||
* @retval 1 OK with found xml nodes in xvec (if any)
|
||||
*/
|
||||
static int
|
||||
clixon_path_search(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
clixon_path *cplist,
|
||||
clixon_path_search(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
clixon_path *cplist,
|
||||
clixon_xvec **xvec0)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1480,8 +1484,8 @@ clixon_path_search(cxobj *xt,
|
|||
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
|
||||
* @param[in] format Format string for api-path syntax
|
||||
* @retval -1 Error
|
||||
* @retval 0 Nomatch
|
||||
* @retval 1 OK with found xml nodes in xvec (if any)
|
||||
* @retval 0 Non-fatal failure, yang bind failures, etc,
|
||||
* @retval 1 OK with found xml nodes in xvec (if any) (xvec contains matches)
|
||||
* Reasons for nomatch (retval = 0) are:
|
||||
* - Modulename in api-path does not correspond to existing module
|
||||
* - Modulename not defined for top-level id.
|
||||
|
|
@ -1572,7 +1576,7 @@ clixon_xml_find_api_path(cxobj *xt,
|
|||
* @param[out] xlen Returns length of vector in return value
|
||||
* @param[in] format Format string for api-path syntax
|
||||
* @retval -1 Error
|
||||
* @retval 0 Nomatch
|
||||
* @retval 0 Non-fatal failure, yang bind failures, etc,
|
||||
* @retval 1 OK with found xml nodes in xvec (if any)
|
||||
* Reasons for nomatch (retval = 0) are:
|
||||
* - Modulename in api-path does not correspond to existing module
|
||||
|
|
@ -1589,7 +1593,7 @@ clixon_xml_find_api_path(cxobj *xt,
|
|||
* }
|
||||
* clixon_xvec_free(xvec);
|
||||
* @endcode
|
||||
* @note canonical namespace contexts are used, seexpath2canonical
|
||||
* @note canonical namespace contexts are used, see xpath2canonical
|
||||
* @see clixon_xml_find_api_path for RESTCONF api-paths
|
||||
* @see RFC7950 Sec 9.13
|
||||
*/
|
||||
|
|
@ -1639,7 +1643,7 @@ clixon_xml_find_instance_id(cxobj *xt,
|
|||
if (ret == 0)
|
||||
goto fail;
|
||||
/* Convert to api xvec format */
|
||||
if (clixon_xvec_extract(xv, xvec, xlen) < 0)
|
||||
if (xv && clixon_xvec_extract(xv, xvec, xlen) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -270,9 +270,7 @@ populate_self_top(cxobj *xt,
|
|||
/*! Find yang spec association of tree of XML nodes
|
||||
*
|
||||
* Populate xt:s children as top-level symbols
|
||||
* This may be unnecessary if yspec is set on manual creation. Also note that for incoming or
|
||||
* outgoing RPC need specialized function. maybe it can be built into the same function, but
|
||||
* you dont know whether it is input or output rpc.
|
||||
* This may be unnecessary if yspec is set on manual creation: x=xml_new(); xml_spec_set(x,y)
|
||||
* @param[in] xt XML tree node
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for failure, or NULL
|
||||
|
|
|
|||
|
|
@ -763,6 +763,7 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Prune everything that passes test
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] flag Which flag to test for
|
||||
|
|
@ -799,7 +800,7 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Add prefix:namespace pair to xml node, set cache, prefix, etc
|
||||
/*! 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
|
||||
|
|
@ -835,9 +836,6 @@ add_namespace(cxobj *x,
|
|||
goto done;
|
||||
xml_sort(xp, NULL); /* Ensure attr is first / XXX xml_insert? */
|
||||
|
||||
/* 5. Add prefix to x, if any */
|
||||
if (prefix && xml_prefix_set(x, prefix) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -882,6 +880,10 @@ xml_namespace_change(cxobj *x,
|
|||
xp = xml_parent(x);
|
||||
if (add_namespace(x, xp, prefix, namespace) < 0)
|
||||
goto done;
|
||||
/* Add prefix to x, if any */
|
||||
if (prefix && xml_prefix_set(x, prefix) < 0)
|
||||
goto done;
|
||||
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -946,6 +948,9 @@ xml_default(cxobj *xt,
|
|||
}
|
||||
if (add_namespace(xc, xc, prefix, namespace) < 0)
|
||||
goto done;
|
||||
/* Add prefix to x, if any */
|
||||
if (prefix && xml_prefix_set(xc, prefix) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1114,7 +1119,6 @@ xml2xpath(cxobj *x,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Check if the module tree x is in is assigned right XML namespace, assign if not
|
||||
* @param[in] x XML node
|
||||
*(0. You should probably find the XML root and apply this function to that.)
|
||||
|
|
@ -1160,50 +1164,24 @@ xmlns_assign(cxobj *x)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace
|
||||
* @param[in] x0 Source XML tree
|
||||
* @param[in] x1 Target XML tree
|
||||
* 1. Find N=namespace(x0)
|
||||
* 2. Detect if N is declared in x1 parent
|
||||
* 3. If yes, assign prefix to x1
|
||||
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
|
||||
* 5. Add prefix to x1, if any
|
||||
* 6. Ensure x1 cache is updated
|
||||
* @note switch use of x0 and x1 compared to datastore text_modify
|
||||
* @see xml2ns
|
||||
* XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
|
||||
goto done;
|
||||
/*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
|
||||
* @see assign_namespace_element this is a subroutine
|
||||
*/
|
||||
int
|
||||
assign_namespaces(cxobj *x0, /* source */
|
||||
cxobj *x1, /* target */
|
||||
cxobj *x1p)
|
||||
static int
|
||||
assign_namespace(cxobj *x0, /* source */
|
||||
cxobj *x1, /* target */
|
||||
cxobj *x1p,
|
||||
int isroot,
|
||||
char *namespace,
|
||||
char *prefix0)
|
||||
{
|
||||
int retval = -1;
|
||||
char *namespace = NULL;
|
||||
char *prefix0 = NULL;;
|
||||
char *prefix1 = NULL;
|
||||
char *prefixb = NULL; /* identityref body prefix */
|
||||
char *pexist = NULL;
|
||||
cvec *nsc0 = NULL;
|
||||
cvec *nsc = NULL;
|
||||
int isroot;
|
||||
char *pexist = NULL;
|
||||
yang_stmt *y;
|
||||
|
||||
/* XXX: need to identify root better than hiereustics and strcmp,... */
|
||||
isroot = xml_parent(x1p)==NULL &&
|
||||
(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
|
||||
xml_prefix(x1p)==NULL;
|
||||
|
||||
/* 1. Find N=namespace(x0) */
|
||||
prefix0 = xml_prefix(x0);
|
||||
if (xml2ns(x0, prefix0, &namespace) < 0)
|
||||
goto done;
|
||||
if (namespace == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
|
||||
prefix0?prefix0:"NULL");
|
||||
goto done;
|
||||
}
|
||||
/* 2a. Detect if namespace is declared in x1 target parent */
|
||||
if (xml2prefix(x1p, namespace, &pexist) == 1){
|
||||
/* Yes, and it has prefix pexist */
|
||||
|
|
@ -1235,7 +1213,7 @@ assign_namespaces(cxobj *x0, /* source */
|
|||
if (xml2prefix(x1, namespace, &pexist) == 1){
|
||||
/* Yes it exists, but is it equal? */
|
||||
if (clicon_strcmp(pexist, prefix0) == 0)
|
||||
; /* Equal, reuse */
|
||||
; /* Equal, reuse */
|
||||
else{ /* namespace exist, but not equal, use existing */
|
||||
/* Add prefix to x1, if any */
|
||||
if (pexist && xml_prefix_set(x1, pexist) < 0)
|
||||
|
|
@ -1253,31 +1231,133 @@ assign_namespaces(cxobj *x0, /* source */
|
|||
}
|
||||
}
|
||||
else{
|
||||
char *ptmp;
|
||||
if ((y = xml_spec(x0)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec",
|
||||
xml_name(x0));
|
||||
goto done;
|
||||
}
|
||||
if ((prefix1 = strdup(yang_find_myprefix(y))) == NULL){
|
||||
/* Find local (imported) prefix for that module namespace */
|
||||
if (yang_find_prefix_by_namespace(y, namespace, &ptmp) < 0)
|
||||
goto done;
|
||||
if ((prefix1 = strdup(ptmp)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (add_namespace(x1, x1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefixb)
|
||||
free(prefixb);
|
||||
if (prefix1)
|
||||
free(prefix1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
|
||||
* @param[in] x0 Source XML tree
|
||||
* @param[in] x1 Target XML tree
|
||||
* @retval 0 OK
|
||||
* @retval -1 OK
|
||||
* 1. Find N=namespace(x0)
|
||||
* 2. Detect if N is declared in x1 parent
|
||||
* 3. If yes, assign prefix to x1
|
||||
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
|
||||
* 5. Add prefix to x1, if any
|
||||
* 6. Ensure x1 cache is updated
|
||||
* @note switch use of x0 and x1 compared to datastore text_modify
|
||||
* @see xml2ns
|
||||
*/
|
||||
int
|
||||
assign_namespace_element(cxobj *x0, /* source */
|
||||
cxobj *x1, /* target */
|
||||
cxobj *x1p)
|
||||
{
|
||||
int retval = -1;
|
||||
char *namespace = NULL;
|
||||
char *prefix0 = NULL;;
|
||||
int isroot;
|
||||
|
||||
/* XXX: need to identify root better than hiereustics and strcmp,... */
|
||||
isroot = xml_parent(x1p)==NULL &&
|
||||
(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
|
||||
xml_prefix(x1p)==NULL;
|
||||
|
||||
/* 1. Find N=namespace(x0) in x0 element */
|
||||
prefix0 = xml_prefix(x0);
|
||||
if (xml2ns(x0, prefix0, &namespace) < 0)
|
||||
goto done;
|
||||
if (namespace == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
|
||||
prefix0?prefix0:"NULL");
|
||||
goto done;
|
||||
}
|
||||
if (assign_namespace(x0, x1, x1p, isroot, namespace, prefix0) < 0)
|
||||
goto done;
|
||||
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* If origin body has namespace definitions, copy them. The reason is that
|
||||
* some bodies rely on namespace prefixes, such as NACM path, but there is
|
||||
* no way we can now this here.
|
||||
* However, this may lead to namespace collisions if these prefixes are not
|
||||
* canonical, and may collide with the assign_namespace_element() above (but that
|
||||
* is for element sysmbols)
|
||||
*/
|
||||
int
|
||||
assign_namespace_body(cxobj *x0, /* source */
|
||||
char *x0bstr,
|
||||
cxobj *x1) /* target */
|
||||
{
|
||||
int retval = -1;
|
||||
char *namespace = NULL;
|
||||
char *name;
|
||||
char *prefix;
|
||||
char *prefix0 = NULL;;
|
||||
char *pexisting = NULL;;
|
||||
cxobj *xa;
|
||||
|
||||
xa = NULL;
|
||||
while ((xa = xml_child_each(x0, xa, CX_ATTR)) != NULL) {
|
||||
prefix = xml_prefix(xa);
|
||||
name = xml_name(xa);
|
||||
namespace = xml_value(xa);
|
||||
if ((strcmp(name, "xmlns")==0 && prefix==NULL) ||
|
||||
(prefix != NULL && strcmp(prefix, "xmlns")==0)){
|
||||
if (prefix == NULL)
|
||||
prefix0 = NULL;
|
||||
else
|
||||
prefix0 = name;
|
||||
if (strcmp(namespace, NETCONF_BASE_NAMESPACE) ==0 ||
|
||||
strcmp(namespace, YANG_XML_NAMESPACE) ==0)
|
||||
continue;
|
||||
/* Detect if prefix:namespace is declared already? */
|
||||
if (xml2prefix(x1, namespace, &pexisting) == 1){
|
||||
/* Yes, and it has prefix pexist */
|
||||
if (clicon_strcmp(pexisting, prefix0) ==0)
|
||||
continue;
|
||||
}
|
||||
if (add_namespace(x1, x1, prefix0, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Merge a base tree x0 with x1 with yang spec y
|
||||
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||
|
|
@ -1334,7 +1414,7 @@ xml_merge1(cxobj *x0, /* the target */
|
|||
if (xml_value_set(x0b, x1bstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (assign_namespaces(x1, x0, x0p) < 0)
|
||||
if (assign_namespace_element(x1, x0, x0p) < 0)
|
||||
goto done;
|
||||
} /* if LEAF|LEAF_LIST */
|
||||
else { /* eg Y_CONTAINER, Y_LIST */
|
||||
|
|
@ -1343,7 +1423,7 @@ xml_merge1(cxobj *x0, /* the target */
|
|||
goto done;
|
||||
xml_spec_set(x0, y0);
|
||||
}
|
||||
if (assign_namespaces(x1, x0, x0p) < 0)
|
||||
if (assign_namespace_element(x1, x0, x0p) < 0)
|
||||
goto done;
|
||||
if ((second_wave = calloc(xml_child_nr(x1), sizeof(*second_wave))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
|
|
|
|||
|
|
@ -152,11 +152,13 @@ xml_nsctx_get_prefix(cvec *cvv,
|
|||
while ((cv = cvec_each(cvv, cv)) != NULL){
|
||||
if ((ns = cv_string_get(cv)) != NULL &&
|
||||
strcmp(ns, namespace) == 0){
|
||||
*prefix = cv_name_get(cv); /* can be NULL */
|
||||
if (prefix)
|
||||
*prefix = cv_name_get(cv); /* can be NULL */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
*prefix = NULL;
|
||||
if (prefix)
|
||||
*prefix = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -493,7 +495,6 @@ xmlns_set(cxobj *x,
|
|||
goto done;
|
||||
if (xml_prefix_set(xa, "xmlns") < 0)
|
||||
goto done;
|
||||
|
||||
}
|
||||
else{ /* xmlns="<uri>" */
|
||||
if ((xa = xml_new("xmlns", x, CX_ATTR)) == NULL)
|
||||
|
|
|
|||
|
|
@ -755,12 +755,18 @@ xml_search_yang(cxobj *xp,
|
|||
char *indexvar,
|
||||
clixon_xvec *xvec)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xa;
|
||||
int low = 0;
|
||||
int upper = xml_child_nr(xp);
|
||||
int sorted = 1;
|
||||
int yangi;
|
||||
|
||||
if (xp == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xp is NULL");
|
||||
goto done;
|
||||
}
|
||||
upper = xml_child_nr(xp);
|
||||
/* Assume if there are any attributes, they are first in the list, mask
|
||||
them by raising low to skip them */
|
||||
for (low=0; low<upper; low++)
|
||||
|
|
@ -775,7 +781,12 @@ xml_search_yang(cxobj *xp,
|
|||
if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
|
||||
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
|
||||
yangi = yang_order(yc);
|
||||
return xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec);
|
||||
|
||||
if (xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Insert xn in xp:s sorted child list (special case of ordered-by user)
|
||||
|
|
@ -1281,6 +1292,10 @@ xml_find_index_yang(cxobj *xp,
|
|||
int revert = 0;
|
||||
char *indexvar = NULL;
|
||||
|
||||
if (xp == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xp is NULL");
|
||||
goto done;
|
||||
}
|
||||
name = yang_argument_get(yc);
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
|
|
|
|||
|
|
@ -203,13 +203,20 @@ clixon_xvec_i(clixon_xvec *xv,
|
|||
* Used in glue code between clixon_xvec code and cxobj **, size_t code, may go AWAY?
|
||||
* @param[in] xv XML tree vector
|
||||
* @param[out] xvec XML object vector
|
||||
* @retval 0
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clixon_xvec_extract(clixon_xvec *xv,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (xv == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xv is NULL");
|
||||
goto done;
|
||||
}
|
||||
*xvec = xv->xv_vec;
|
||||
*xlen = xv->xv_len;
|
||||
if (xv->xv_vec != NULL){
|
||||
|
|
@ -217,7 +224,9 @@ clixon_xvec_extract(clixon_xvec *xv,
|
|||
xv->xv_max = 0;
|
||||
xv->xv_vec = NULL;
|
||||
}
|
||||
return 0;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Append a new xml tree to an existing xml vector last in the list
|
||||
|
|
|
|||
|
|
@ -873,7 +873,9 @@ yang_find_mynamespace(yang_stmt *ys)
|
|||
* @retval -1 found
|
||||
* @note prefix NULL is not returned, if own module, then return its prefix
|
||||
* @code
|
||||
* char *pfx = yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix);
|
||||
* char *prefix = NULL;
|
||||
* if (yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
|
|
@ -1103,7 +1105,7 @@ yang_key2str(int keyword)
|
|||
}
|
||||
|
||||
/*! Find top data node among all modules by namespace in xml tree
|
||||
* @param[in] ysp Yang specification
|
||||
* @param[in] yspec Yang specification
|
||||
* @param[in] xt XML node
|
||||
* @param[out] ymod Yang module (NULL if not found)
|
||||
* @retval 0 OK
|
||||
|
|
@ -1112,7 +1114,7 @@ yang_key2str(int keyword)
|
|||
* Note that xt xml symbol may belong to submodule of ymod
|
||||
*/
|
||||
int
|
||||
ys_module_by_xml(yang_stmt *ysp,
|
||||
ys_module_by_xml(yang_stmt *yspec,
|
||||
cxobj *xt,
|
||||
yang_stmt **ymodp)
|
||||
{
|
||||
|
|
@ -1130,7 +1132,7 @@ ys_module_by_xml(yang_stmt *ysp,
|
|||
if (namespace == NULL)
|
||||
goto ok;
|
||||
/* We got the namespace, now get the module */
|
||||
ym = yang_find_module_by_namespace(ysp, namespace);
|
||||
ym = yang_find_module_by_namespace(yspec, namespace);
|
||||
/* Set result param */
|
||||
if (ymodp && ym)
|
||||
*ymodp = ym;
|
||||
|
|
@ -1892,6 +1894,58 @@ ys_populate_unknown(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Populate modules / submodules
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] ys The yang statement (module/submodule) to populate.
|
||||
*
|
||||
* Check RFC 7950: 7.1.4: All prefixes, including the prefix for the module itself,
|
||||
* MUST be unique within the module or submodule.
|
||||
*/
|
||||
static int
|
||||
ys_populate_module_submodule(clicon_handle h,
|
||||
yang_stmt *ym)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yp;
|
||||
yang_stmt *yi;
|
||||
yang_stmt *yi2; /* remaining */
|
||||
char *p0 = NULL;
|
||||
char *pi;
|
||||
char *pi2; /* remaining */
|
||||
|
||||
/* Modules but not submodules have prefixes */
|
||||
if ((yp = yang_find(ym, Y_PREFIX, NULL)) != NULL)
|
||||
p0 = yang_argument_get(yp);
|
||||
yi = NULL;
|
||||
while ((yi = yn_each(ym, yi)) != NULL) {
|
||||
if (yang_keyword_get(yi) != Y_IMPORT)
|
||||
continue;
|
||||
yp = yang_find(yi, Y_PREFIX, NULL);
|
||||
pi = yang_argument_get(yp);
|
||||
if (p0 && strcmp(p0, pi) == 0){ /* Check top-level */
|
||||
clicon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
|
||||
pi, yang_argument_get(ym));
|
||||
goto done;
|
||||
}
|
||||
/* Check rest of imports */
|
||||
yi2 = yi;
|
||||
while ((yi2 = yn_each(ym, yi2)) != NULL) {
|
||||
if (yang_keyword_get(yi2) != Y_IMPORT)
|
||||
continue;
|
||||
yp = yang_find(yi2, Y_PREFIX, NULL);
|
||||
pi2 = yang_argument_get(yp);
|
||||
if (strcmp(pi2, pi) == 0){
|
||||
clicon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
|
||||
pi, yang_argument_get(ym));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
|
||||
*
|
||||
* @param[in] ys Yang statement
|
||||
|
|
@ -1913,24 +1967,29 @@ ys_populate(yang_stmt *ys,
|
|||
clicon_handle h = (clicon_handle)arg;
|
||||
|
||||
switch(ys->ys_keyword){
|
||||
case Y_LIST:
|
||||
if (ys_populate_list(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_RANGE:
|
||||
if (ys_populate_range(h, ys) < 0)
|
||||
case Y_IDENTITY:
|
||||
if (ys_populate_identity(h, ys, NULL) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LENGTH:
|
||||
if (ys_populate_length(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_TYPE:
|
||||
if (ys_populate_type(h, ys) < 0)
|
||||
case Y_LIST:
|
||||
if (ys_populate_list(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_IDENTITY:
|
||||
if (ys_populate_identity(h, ys, NULL) < 0)
|
||||
case Y_MODULE:
|
||||
case Y_SUBMODULE:
|
||||
if (ys_populate_module_submodule(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_RANGE:
|
||||
if (ys_populate_range(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_TYPE:
|
||||
if (ys_populate_type(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_UNIQUE:
|
||||
|
|
@ -1949,7 +2008,6 @@ ys_populate(yang_stmt *ys,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Run after grouping expand and augment
|
||||
* @see ys_populate run before grouping expand and augment
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -978,7 +978,7 @@ ys_list_check(clicon_handle h,
|
|||
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||
*
|
||||
* Perform secondary actions after yang parsing. These actions cannot be made at
|
||||
* compile-time for various reasons.
|
||||
* parse-time for various reasons.
|
||||
* These includes:
|
||||
* - Detect imported yang specs that are not loaded and load and parse them too
|
||||
* - Check cardinality of yang (that nr of children match)
|
||||
|
|
@ -1015,14 +1015,17 @@ yang_parse_post(clicon_handle h,
|
|||
goto done;
|
||||
|
||||
/* 3: Check features: check if enabled and remove disabled features */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++) /* XXX */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_features(h, yspec->ys_stmt[i]) < 0)
|
||||
goto done;
|
||||
|
||||
/* 4: Go through parse tree and populate it with cv types */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
for (i=modnr; i<yang_len_get(yspec); i++){
|
||||
if (ys_populate(yspec->ys_stmt[i], h) < 0) /* Alt: make a yang_apply0 */
|
||||
goto done;
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate, (void*)h) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* 5: Resolve all types: populate type caches. Requires eg length/range cvecs
|
||||
* from ys_populate step.
|
||||
|
|
|
|||
|
|
@ -526,9 +526,9 @@ outoflength(uint64_t u64,
|
|||
* @param[in] regexps Vector of compiled regexps
|
||||
* @param[out] reason If given, and return value is 0, contains malloced str
|
||||
|
||||
* @retval -1 Error (fatal), with errno set to indicate error
|
||||
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
|
||||
* @retval 1 Validation OK
|
||||
* @retval -1 Error (fatal), with errno set to indicate error
|
||||
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
|
||||
* @retval 1 Validation OK
|
||||
* @note reason if given must be freed by caller
|
||||
* @see cv_validate Corresponding type check in cligen
|
||||
*/
|
||||
|
|
@ -661,7 +661,7 @@ cv_validate1(clicon_handle h,
|
|||
found = 0;
|
||||
yi = NULL;
|
||||
if (str != NULL) {
|
||||
str = clixon_trim2(str, " \t\n"); /* May be misplaced, strip earlier? */
|
||||
// str = clixon_trim2(str, " \t\n"); /* May be misplaced, strip earlier? */
|
||||
while ((yi = yn_each(yrestype, yi)) != NULL){
|
||||
if (yang_keyword_get(yi) != Y_ENUM)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ cat <<EOF > $fyang
|
|||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
prefix nex;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ cat <<EOF > $fyang
|
|||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
prefix nex;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ cat <<EOF > $fyang
|
|||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
prefix nex;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ cat <<EOF > $fyang
|
|||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
prefix nex;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
|
|
@ -112,7 +112,7 @@ RULES=$(cat <<EOF
|
|||
<translate xmlns="urn:example:clixon"><translate><k>key43</k><value>val43</value></translate></translate>
|
||||
EOF
|
||||
)
|
||||
# NOTE use of translate^ has nothing to do with the CLI translate semantics
|
||||
# NOTE use of translate^ has nothing to do with the CLI translate semantics CONFUSING!
|
||||
|
||||
new "test params: -f $cfg -- -s"
|
||||
|
||||
|
|
@ -147,30 +147,26 @@ new "commit it"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "enable nacm"
|
||||
expecteq "$(curl -u andy:bar -sS -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 ""
|
||||
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
|
||||
|
||||
#----READ access
|
||||
#user:admin
|
||||
new "admin read ok"
|
||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
|
||||
'
|
||||
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 200 OK' '{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]}}'
|
||||
|
||||
new "admin read netconf ok"
|
||||
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon" /></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate><translate><k>key43</k><value>val43</value></translate></translate></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "admin read element ok"
|
||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"}
|
||||
'
|
||||
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 'HTTP/1.1 200 OK' '{"clixon-example:value":"val42"}'
|
||||
|
||||
new "admin read other module OK"
|
||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"nacm-example:x":42}
|
||||
'
|
||||
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":42}'
|
||||
|
||||
new "admin read state OK"
|
||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":["41","42","43"]}}
|
||||
'
|
||||
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}'
|
||||
|
||||
new "admin read top ok (all)"
|
||||
ret=$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)
|
||||
|
|
@ -183,65 +179,59 @@ fi
|
|||
#user:limit
|
||||
|
||||
new "limit read ok"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 200 OK' '{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]}}'
|
||||
|
||||
new "limit read netconf ok"
|
||||
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate><translate><k>key43</k><value>val43</value></translate></translate></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "limit read element ok"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 'HTTP/1.1 200 OK' '{"clixon-example:value":"val42"}'
|
||||
|
||||
new "limit read other module fail"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
|
||||
|
||||
new "limit read state OK"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":["41","42","43"]}}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}'
|
||||
|
||||
new "limit read top ok (part)"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]},"clixon-example:state":{"op":["41","42","43"]}}}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data)" 0 'HTTP/1.1 200 OK' '{"data":{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]},"clixon-example:state":{"op":\["41","42","43"\]}}}'
|
||||
|
||||
#user:guest
|
||||
|
||||
new "guest read fail"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}
'
|
||||
new "guest read forbidden"
|
||||
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
|
||||
|
||||
new "guest read netconf fail"
|
||||
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
|
||||
new "guest read element fail"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}
'
|
||||
new "guest read element forbidden"
|
||||
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
|
||||
|
||||
new "guest read other module fail"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}
'
|
||||
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
|
||||
|
||||
new "guest read state fail"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}
'
|
||||
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
|
||||
|
||||
new "guest read top ok (part)"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}
'
|
||||
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
|
||||
|
||||
#------- RPC operation
|
||||
|
||||
new "admin rpc ok"
|
||||
expecteq "$(curl -u andy:bar -s -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"78"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output":{"x":"78","y":"42"}}
|
||||
'
|
||||
expectpart "$(curl -u andy:bar -si -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"78"}}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 200 OK' '{"clixon-example:output":{"x":"78","y":"42"}}'
|
||||
|
||||
new "admin rpc netconf ok"
|
||||
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' 0 '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
|
||||
|
||||
new "limit rpc ok"
|
||||
expecteq "$(curl -u wilma:bar -s -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 '{"clixon-example:output":{"x":"42","y":"42"}}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -si -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 'HTTP/1.1 200 OK' '{"clixon-example:output":{"x":"42","y":"42"}}'
|
||||
|
||||
new "limit rpc netconf ok"
|
||||
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' 0 '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
|
||||
|
||||
new "guest rpc fail"
|
||||
expecteq "$(curl -u guest:bar -s -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}
'
|
||||
expectpart "$(curl -u guest:bar -si -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
|
||||
|
||||
new "guest rpc netconf fail"
|
||||
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' 0 '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
|
|
@ -249,19 +239,16 @@ expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml
|
|||
#------------------ Set read-default permit
|
||||
|
||||
new "admin set read-default permit"
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 ""
|
||||
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 'HTTP/1.1 204 No Content'
|
||||
|
||||
new "limit read ok"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 200 OK' '{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]}}'
|
||||
|
||||
new "limit read other module ok"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"nacm-example:x":42}
|
||||
'
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":42}'
|
||||
|
||||
new "guest read state fail"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}
'
|
||||
|
||||
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "Kill restconf daemon"
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ cat <<EOF > $fyang
|
|||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
prefix nex;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ RULES=$(cat <<EOF
|
|||
<rule>
|
||||
<name>deny-update</name>
|
||||
<module-name>nacm-example</module-name>
|
||||
<access-operations>read update</access-operations>
|
||||
<access-operations>update</access-operations>
|
||||
<action>deny</action>
|
||||
</rule>
|
||||
</rule-list>
|
||||
|
|
@ -121,7 +121,7 @@ RULES=$(cat <<EOF
|
|||
<rule>
|
||||
<name>deny-create-delete</name>
|
||||
<module-name>nacm-example</module-name>
|
||||
<access-operations>read create delete</access-operations>
|
||||
<access-operations>create delete</access-operations>
|
||||
<action>deny</action>
|
||||
</rule>
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ cat <<EOF > $fyang
|
|||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
prefix nex;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,9 +242,7 @@ new 'B.3.5. "point" Parameter leaf-list 4 before 3'
|
|||
expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' -d '{"example-jukebox:extra":"4"}' http://localhost/restconf/data?insert=before\&point=%2Fexample-jukebox%3Aextra%3D3 )" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=4'
|
||||
|
||||
new 'B.3.5. "insert/point" leaf-list check order (2,4,3,1)'
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'
|
||||
|
||||
if false; then # NYI
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'
|
||||
|
||||
new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed
|
||||
new 'B.3.3. "fields" Parameter'
|
||||
|
|
@ -253,8 +251,6 @@ new 'B.3.7. "start-time" Parameter'
|
|||
new 'B.3.8. "stop-time" Parameter'
|
||||
new 'B.3.9. "with-defaults" Parameter'
|
||||
|
||||
fi # NYI
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue