Replaced strchr with nodeid_split

Experimental code for replacing identityref lists with module:id instead of prefix:id
This commit is contained in:
Olof hagsand 2019-07-10 17:18:34 +02:00
parent bb04f778ab
commit e44685a100
13 changed files with 348 additions and 182 deletions

View file

@ -99,11 +99,14 @@ However, the following YANG syntax modules are not implemented (reference to RFC
- deviation (7.20.3) - deviation (7.20.3)
- action (7.15) - action (7.15)
- augment in a uses sub-clause (7.17) (module-level augment is implemented) - augment in a uses sub-clause (7.17) (module-level augment is implemented)
- require-instance
- instance-identifier type
- status (7.21.2) - status (7.21.2)
- extension (7.19) - extension (7.19) supported syntactically, but no hooks/plugins for extenstions
- YIN (13) - YIN (13)
- Yang extended Xpath functions: re-match(), deref)(), derived-from(), derived-from-or-self(), enum-value(), bit-is-set() (10.2-10.6) - Yang extended Xpath functions: re-match(), deref)(), derived-from(), derived-from-or-self(), enum-value(), bit-is-set() (10.2-10.6)
- Default values on leaf-lists are not supported (7.7.2) - Default values on leaf-lists are not supported (7.7.2)
- instance-identifier type
### Yang patterns ### Yang patterns
Yang type patterns use regexps defined in [W3C XML XSD](http://www.w3.org/TR/2004/REC-xmlschema-2-20041028). XSD regexp:s are Yang type patterns use regexps defined in [W3C XML XSD](http://www.w3.org/TR/2004/REC-xmlschema-2-20041028). XSD regexp:s are

View file

@ -184,6 +184,40 @@ cli_signal_flush(clicon_handle h)
cli_signal_block (h); cli_signal_block (h);
} }
/*! Transform data
* Add next-last cvv (resolved variable) as body to xml bottom for leaf and
* leaf-list.
* There may be some translation necessary.
*/
static int
dbxml_body(cxobj *xbot,
yang_stmt *ybot,
cvec *cvv)
{
int retval = -1;
char *str = NULL;
cxobj *xb;
cg_var *cval;
int len;
len = cvec_len(cvv);
cval = cvec_i(cvv, len-1);
if ((str = cv2str_dup(cval)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
if ((xb = xml_new("body", xbot, NULL)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if (xml_value_set(xb, str) < 0)
goto done;
retval = 0;
done:
if (str)
free(str);
return retval;
}
/*! Modify xml datastore from a callback using xml key format strings /*! Modify xml datastore from a callback using xml key format strings
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables * @param[in] cvv Vector of cli string and instantiated variables
@ -206,11 +240,11 @@ cli_dbxml(clicon_handle h,
enum operation_type op) enum operation_type op)
{ {
int retval = -1; int retval = -1;
char *str = NULL; // char *str = NULL;
char *api_path_fmt; /* xml key format */ char *api_path_fmt; /* xml key format */
char *api_path = NULL; /* xml key */ char *api_path = NULL; /* xml key */
cg_var *cval; // cg_var *cval;
int len; // int len;
cg_var *arg; cg_var *arg;
cbuf *cb = NULL; cbuf *cb = NULL;
yang_stmt *yspec; yang_stmt *yspec;
@ -218,7 +252,7 @@ cli_dbxml(clicon_handle h,
yang_stmt *y = NULL; /* yang spec of xpath */ yang_stmt *y = NULL; /* yang spec of xpath */
cxobj *xtop = NULL; /* xpath root */ cxobj *xtop = NULL; /* xpath root */
cxobj *xa; /* attribute */ cxobj *xa; /* attribute */
cxobj *xb; /* body */ // cxobj *xb; /* body */
if (cvec_len(argv) != 1){ if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string"); clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string");
@ -241,22 +275,12 @@ cli_dbxml(clicon_handle h,
if ((xa = xml_new("operation", xbot, NULL)) == NULL) if ((xa = xml_new("operation", xbot, NULL)) == NULL)
goto done; goto done;
xml_type_set(xa, CX_ATTR); xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, xml_operation2str(op)) < 0) if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done; goto done;
if (yang_keyword_get(y) != Y_LIST && yang_keyword_get(y) != Y_LEAF_LIST){ if (yang_keyword_get(y) != Y_LIST && yang_keyword_get(y) != Y_LEAF_LIST){
len = cvec_len(cvv); if (cvec_len(cvv) > 1 &&
if (len > 1){ dbxml_body(xbot, y, cvv) < 0)
cval = cvec_i(cvv, len-1); goto done;
if ((str = cv2str_dup(cval)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
if ((xb = xml_new("body", xbot, NULL)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if (xml_value_set(xb, str) < 0)
goto done;
}
} }
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new"); clicon_err(OE_XML, errno, "cbuf_new");
@ -274,8 +298,6 @@ cli_dbxml(clicon_handle h,
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (str)
free(str);
if (api_path) if (api_path)
free(api_path); free(api_path);
if (xtop) if (xtop)

View file

@ -530,7 +530,7 @@ yang2cli_var(clicon_handle h,
cbuf *cb) cbuf *cb)
{ {
int retval = -1; int retval = -1;
char *origtype; char *origtype = NULL;
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */ char *restype; /* resolved type */
cvec *cvv = NULL; cvec *cvv = NULL;
@ -596,6 +596,8 @@ yang2cli_var(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (origtype)
free(origtype);
if (patterns) if (patterns)
cvec_free(patterns); cvec_free(patterns);
return retval; return retval;

View file

@ -48,3 +48,9 @@
*/ */
#define USE_NETCONF_NS_AS_DEFAULT #define USE_NETCONF_NS_AS_DEFAULT
/* Use modulename:id instead of prefix:id in derived identityref list
* Modulenames are global/canonical but prefixes are not.
* Experimental since there is some mapping between prefixes and module names
* that needs to be done
*/
#undef USE_IDREF_LIST_MODULE

View file

@ -44,6 +44,7 @@ typedef enum yang_class yang_class;
/* /*
* Prototypes * Prototypes
*/ */
int isxmlns(cxobj *x);
int xml2txt(FILE *f, cxobj *x, int level); int xml2txt(FILE *f, cxobj *x, int level);
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt); int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
int xml_yang_root(cxobj *x, cxobj **xr); int xml_yang_root(cxobj *x, cxobj **xr);

View file

@ -164,8 +164,6 @@ yang_stmt *ys_dup(yang_stmt *old);
int yn_insert(yang_stmt *ys_parent, yang_stmt *ys_child); int yn_insert(yang_stmt *ys_parent, yang_stmt *ys_child);
yang_stmt *yn_each(yang_stmt *yn, yang_stmt *ys); yang_stmt *yn_each(yang_stmt *yn, yang_stmt *ys);
char *yang_key2str(int keyword); char *yang_key2str(int keyword);
char *yarg_prefix(yang_stmt *ys);
char *yarg_id(yang_stmt *ys);
int ys_module_by_xml(yang_stmt *ysp, struct xml *xt, yang_stmt **ymodp); int ys_module_by_xml(yang_stmt *ysp, struct xml *xt, yang_stmt **ymodp);
yang_stmt *ys_module(yang_stmt *ys); yang_stmt *ys_module(yang_stmt *ys);
yang_stmt *ys_real_module(yang_stmt *ys); yang_stmt *ys_real_module(yang_stmt *ys);

View file

@ -83,6 +83,45 @@
#include "clixon_datastore_read.h" #include "clixon_datastore_read.h"
#include "clixon_datastore_tree.h" #include "clixon_datastore_tree.h"
/*! Replace all xmlns attributes in x0 with xmlns attributes in x1
* This is an embryo of code to actually check if namespace binding is canonical
* and if it is not, either return error or transform to canonical.
* "Canonical" meaning comply to the yang module prefixes.
* The current code does not really do anything useful
*/
static int
replace_xmlns(cxobj *x0,
cxobj *x1)
{
int retval = -1;
cxobj *x = NULL;
cxobj *xcopy;
int i;
for (i=0; i<xml_child_nr(x0); ){
x = xml_child_i(x0, i);
if (!isxmlns(x)){
i++;
continue;
}
xml_rm(x);
xml_free(x);
}
x = NULL;
while ((x = xml_child_each(x1, x, CX_ATTR)) != NULL) {
/* split and only add xmlns= and xmlns:x= attributes! */
if (!isxmlns(x))
continue;
if ((xcopy = xml_new(xml_name(x), x0, xml_spec(x))) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0) /* recursion */
goto done;
}
retval = 0;
done:
return retval;
}
/*! Modify a base tree x0 with x1 with yang spec y according to operation op /*! Modify a base tree x0 with x1 with yang spec y according to operation op
* @param[in] th Datastore text handle * @param[in] th Datastore text handle
* @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x0 Base xml tree (can be NULL in add scenarios)
@ -187,6 +226,16 @@ text_modify(clicon_handle h,
xml_type_set(x0b, CX_BODY); xml_type_set(x0b, CX_BODY);
} }
} }
else { /* if change existing node, replace xmlns attributes
* This is only done for leaf/leaf-list now, eg terminals
* and is only an embryo of checking canonical namespace
* bindings.
* But it does catch some cornercases where a new
* namespace binding is replacing an old for eg identityref
*/
if (replace_xmlns(x0, x1) < 0)
goto done;
}
if (x1bstr){ if (x1bstr){
if ((x0b = xml_body_get(x0)) != NULL){ if ((x0b = xml_body_get(x0)) != NULL){
x0bstr = xml_value(x0b); x0bstr = xml_value(x0b);

View file

@ -623,8 +623,8 @@ clicon_str2int_search(const map_str2int *mstab,
/*! Split colon-separated node identifier into prefix and name /*! Split colon-separated node identifier into prefix and name
* @param[in] node-id * @param[in] node-id
* @param[out] prefix Malloced string. May be NULL. * @param[out] prefix If non-NULL, return malloced string, or NULL.
* @param[out] id Malloced identifier. * @param[out] id If non-NULL, return malloced identifier.
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @code * @code
@ -647,19 +647,21 @@ nodeid_split(char *nodeid,
char *str; char *str;
if ((str = strchr(nodeid, ':')) == NULL){ if ((str = strchr(nodeid, ':')) == NULL){
if ((*id = strdup(nodeid)) == NULL){ if (id && (*id = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup"); clicon_err(OE_YANG, errno, "strdup");
goto done; goto done;
} }
} }
else{ else {
if ((*prefix = strdup(nodeid)) == NULL){ if (prefix){
clicon_err(OE_YANG, errno, "strdup"); if ((*prefix = strdup(nodeid)) == NULL){
goto done; clicon_err(OE_YANG, errno, "strdup");
goto done;
}
(*prefix)[str-nodeid] = '\0';
} }
(*prefix)[str-nodeid] = '\0';
str++; str++;
if ((*id = strdup(str)) == NULL){ if (id && (*id = strdup(str)) == NULL){
clicon_err(OE_YANG, errno, "strdup"); clicon_err(OE_YANG, errno, "strdup");
goto done; goto done;
} }

View file

@ -94,6 +94,22 @@
#include "clixon_yang_type.h" #include "clixon_yang_type.h"
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
/*! Is attribute and is either of form xmlns="", or xmlns:x="" */
int
isxmlns(cxobj *x)
{
char *prefix = NULL;
if (xml_type(x) != CX_ATTR)
return 0;
if (strcmp(xml_name(x), "xmlns")==0)
return 1;
if ((prefix = xml_prefix(x)) != NULL
&& strcmp(xml_prefix(x), "xmlns")==0)
return 1;
return 0;
}
/*! x is element and has eactly one child which in turn has none */ /*! x is element and has eactly one child which in turn has none */
static int static int
tleaf(cxobj *x) tleaf(cxobj *x)
@ -344,27 +360,28 @@ validate_identityref(cxobj *xt,
{ {
int retval = -1; int retval = -1;
char *node = NULL; char *node = NULL;
char *idref = NULL;
yang_stmt *ybaseref; /* This is the type's base reference */ yang_stmt *ybaseref; /* This is the type's base reference */
yang_stmt *ybaseid; yang_stmt *ybaseid;
char *prefix = NULL; char *prefix = NULL;
char *id = NULL;
cbuf *cberr = NULL;
cbuf *cb = NULL; cbuf *cb = NULL;
cbuf *cb2 = NULL;
/* Get idref value. Then see if this value is derived from ytype.
* Always add default prefix because derived identifiers are stored with
* prefixes in the base identifiers derived-list.
*/
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
} }
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* Get idref value. Then see if this value is derived from ytype.
*/
if ((node = xml_body(xt)) == NULL) if ((node = xml_body(xt)) == NULL)
return 0; return 0;
if (strchr(node, ':') == NULL){ if (nodeid_split(node, &prefix, &id) < 0)
prefix = yang_find_myprefix(ys); goto done;
cprintf(cb, "%s:%s", prefix, node);
node = cbuf_get(cb);
}
/* This is the type's base reference */ /* This is the type's base reference */
if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){ if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0) if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
@ -377,26 +394,73 @@ validate_identityref(cxobj *xt,
goto done; goto done;
goto fail; goto fail;
} }
#if 0 /* Assume proper namespace, otherwise we assume module prefixes */
{
char *namespace;
yang_stmt *ymod;
yang_stmt *yspec;
/* Create an idref as <module>:<id> which is the format of the derived
* identityref list associated with the base identities.
*/
/* Get namespace (of idref) from xml */
if (xml2ns(xt, prefix, &namespace) < 0)
goto done;
yspec = ys_spec(ys);
/* Get module of that namespace */
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
clicon_err(OE_YANG, ENOENT, "No module found");
goto done;
}
cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
}
#else
#ifdef USE_IDREF_LIST_MODULE
{
yang_stmt *ymod;
/* idref from prefix:id to module:id */
if (prefix == NULL)
ymod = ys_module(ys);
else /* from prefix to name */
ymod = yang_find_module_by_prefix(ys, prefix);
if (ymod == NULL){
cprintf(cberr, "Identityref validation failed, %s not derived from %s",
node, yang_argument_get(ybaseid));
if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
}
#else
if (prefix == NULL)
cprintf(cb, "%s:%s", yang_find_myprefix(ys), id);
else
cprintf(cb, "%s:%s", prefix, id);
#endif
#endif
idref = cbuf_get(cb);
/* Here check if node is in the derived node list of the base identity /* Here check if node is in the derived node list of the base identity
* The derived node list is a cvec computed XXX * The derived node list is a cvec computed XXX
*/ */
if (cvec_find(yang_cvec_get(ybaseid), node) == NULL){ if (cvec_find(yang_cvec_get(ybaseid), idref) == NULL){
if ((cb2 = cbuf_new()) == NULL){ cprintf(cberr, "Identityref validation failed, %s not derived from %s",
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb2, "Identityref validation failed, %s not derived from %s",
node, yang_argument_get(ybaseid)); node, yang_argument_get(ybaseid));
if (netconf_operation_failed_xml(xret, "application", cbuf_get(cb2)) < 0) if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
goto done; goto done;
goto fail; goto fail;
} }
retval = 1; retval = 1;
done: done:
if (cberr)
cbuf_free(cberr);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (cb2) if (id)
cbuf_free(cb2); free(id);
if (prefix)
free(prefix);
return retval; return retval;
fail: fail:
retval = 0; retval = 0;

View file

@ -1102,44 +1102,6 @@ ys_spec(yang_stmt *ys)
return (yang_stmt*)ys; return (yang_stmt*)ys;
} }
/* Assume argument is id on the type: <[prefix:]id>, return 'id'
* Just return string from id
* @param[in] ys A yang statement
* @retval NULL No id (argument is NULL)
* @retval id Pointer to identifier
* @see yarg_prefix
*/
char*
yarg_id(yang_stmt *ys)
{
char *id;
if ((id = strchr(ys->ys_argument, ':')) == NULL)
id = ys->ys_argument;
else
id++;
return id;
}
/*! Assume argument is id on the type: <[prefix:]id>, return 'prefix'
* @param[in] ys A yang statement
* @retval NULL No prefix
* @retval prefix Malloced string that needs to be freed by caller.
* @see yarg_id
*/
char*
yarg_prefix(yang_stmt *ys)
{
char *id;
char *prefix = NULL;
if ((id = strchr(ys->ys_argument, ':')) != NULL){
prefix = strdup(ys->ys_argument);
prefix[id-ys->ys_argument] = '\0';
}
return prefix;
}
/*! Given a yang statement and a prefix, return yang module to that relative prefix /*! Given a yang statement and a prefix, return yang module to that relative prefix
* Note, not the other module but the proxy import statement only * Note, not the other module but the proxy import statement only
* @param[in] ys A yang statement * @param[in] ys A yang statement
@ -1333,8 +1295,8 @@ yang_print_cbuf(cbuf *cb,
* 4. Check if leaf is part of list, if key exists mark leaf as key/unique * 4. Check if leaf is part of list, if key exists mark leaf as key/unique
* XXX: extend type search * XXX: extend type search
* *
* @param[in] h Clicon handle
* @param[in] ys The yang statement to populate. * @param[in] ys The yang statement to populate.
* @param[in] arg A void argument not used
* @retval 0 OK * @retval 0 OK
* @retval -1 Error with clicon_err called * @retval -1 Error with clicon_err called
*/ */
@ -1352,17 +1314,17 @@ ys_populate_leaf(clicon_handle h,
char *reason = NULL; char *reason = NULL;
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */ char *restype; /* resolved type */
char *type; /* original type */ char *origtype=NULL; /* original type */
uint8_t fraction_digits; uint8_t fraction_digits;
int options = 0x0; int options = 0x0;
yparent = ys->ys_parent; /* Find parent: list/container */ yparent = ys->ys_parent; /* Find parent: list/container */
/* 1. Find type specification and set cv type accordingly */ /* 1. Find type specification and set cv type accordingly */
if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, NULL, &fraction_digits) if (yang_type_get(ys, &origtype, &yrestype, &options, NULL, NULL, NULL, &fraction_digits)
< 0) < 0)
goto done; goto done;
restype = yrestype?yrestype->ys_argument:NULL; restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, ys, &cvtype) < 0) /* This handles non-resolved also */ if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
goto done; goto done;
/* 2. Create the CV using cvtype and name it */ /* 2. Create the CV using cvtype and name it */
if ((cv = cv_new(cvtype)) == NULL){ if ((cv = cv_new(cvtype)) == NULL){
@ -1402,11 +1364,17 @@ ys_populate_leaf(clicon_handle h,
ys->ys_cv = cv; ys->ys_cv = cv;
retval = 0; retval = 0;
done: done:
if (origtype)
free(origtype);
if (cv && retval < 0) if (cv && retval < 0)
cv_free(cv); cv_free(cv);
return retval; return retval;
} }
/*! Populate list yang statement
* @param[in] h Clicon handle
* @param[in] ys The yang statement (type) to populate.
*/
static int static int
ys_populate_list(clicon_handle h, ys_populate_list(clicon_handle h,
yang_stmt *ys) yang_stmt *ys)
@ -1505,6 +1473,8 @@ range_parse(yang_stmt *ys,
/*! Populate string built-in range statement /*! Populate string built-in range statement
* *
* Create cvec variables "range_min" and "range_max". Assume parent is type. * Create cvec variables "range_min" and "range_max". Assume parent is type.
* @param[in] h Clicon handle
* @param[in] ys The yang statement (range) to populate.
* Actually: bound[..bound] (| bound[..bound])* * Actually: bound[..bound] (| bound[..bound])*
* where bound is integer, decimal or keywords 'min' or 'max. * where bound is integer, decimal or keywords 'min' or 'max.
* RFC 7950 9.2.4: * RFC 7950 9.2.4:
@ -1519,10 +1489,10 @@ ys_populate_range(clicon_handle h,
yang_stmt *ys) yang_stmt *ys)
{ {
int retval = -1; int retval = -1;
yang_stmt *yparent; /* type */ yang_stmt *yparent; /* type */
char *origtype; /* orig type */ char *origtype = NULL; /* orig type */
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */ char *restype; /* resolved type */
int options = 0x0; int options = 0x0;
uint8_t fraction_digits; uint8_t fraction_digits;
enum cv_type cvtype = CGV_ERR; enum cv_type cvtype = CGV_ERR;
@ -1536,7 +1506,8 @@ ys_populate_range(clicon_handle h,
&options, NULL, NULL, NULL, &fraction_digits) < 0) &options, NULL, NULL, NULL, &fraction_digits) < 0)
goto done; goto done;
restype = yrestype?yrestype->ys_argument:NULL; restype = yrestype?yrestype->ys_argument:NULL;
origtype = yarg_id((yang_stmt*)yparent); if (nodeid_split(yang_argument_get(yparent), NULL, &origtype) < 0)
goto done;
/* This handles non-resolved also */ /* This handles non-resolved also */
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done; goto done;
@ -1548,15 +1519,21 @@ ys_populate_range(clicon_handle h,
goto done; goto done;
retval = 0; retval = 0;
done: done:
if (origtype)
free(origtype);
return retval; return retval;
} }
/*! Populate integer built-in length statement /*! Populate integer built-in length statement
* *
* Create cvec variables "range_min" and "range_max". Assume parent is type. * Create cvec variables "range_min" and "range_max". Assume parent is type.
* @param[in] h Clicon handle
* @param[in] ys The yang statement (length) to populate.
*
* Actually: len[..len] (| len[..len])* * Actually: len[..len] (| len[..len])*
* len is unsigned integer or keywords 'min' or 'max. * len is unsigned integer or keywords 'min' or 'max'
* RFC 7950 9.4.4 *
* From RFC 7950 Sec 9.4.4:
* A length range consists of an explicit value, or a lower bound, two * A length range consists of an explicit value, or a lower bound, two
* consecutive dots "..", and an upper bound. Multiple values or ranges * consecutive dots "..", and an upper bound. Multiple values or ranges
* can be given, separated by "|". Length-restricting values MUST NOT * can be given, separated by "|". Length-restricting values MUST NOT
@ -1586,8 +1563,8 @@ ys_populate_length(clicon_handle h,
/*! Sanity check yang type statement /*! Sanity check yang type statement
* XXX: Replace with generic parent/child type-check * XXX: Replace with generic parent/child type-check
* @param[in] h Clicon handle
* @param[in] ys The yang statement (type) to populate. * @param[in] ys The yang statement (type) to populate.
* @
*/ */
static int static int
ys_populate_type(clicon_handle h, ys_populate_type(clicon_handle h,
@ -1620,12 +1597,15 @@ ys_populate_type(clicon_handle h,
return retval; return retval;
} }
/*! Sanity check yang identity statement recursively /*! Sanity check yang identity statement recursively and create derived id list
* *
* Find base identities if any and add this identity to derived list. * Find base identities if any and add this identity to derived identity list.
* Do this recursively * Do this recursively
* @param[in] ys The yang identity to populate. * The derived identity list is a list of <module>:<id> pairs. Prefixes cannot
* @param[in] arg If set contains a derived identifier * be used since they are local in scope.
* @param[in] h Clicon handle
* @param[in] ys The yang identity to populate.
* @param[in] idref If set contains the derived identifier(NULL on top call)
* @see validate_identityref which in runtime validates actual values * @see validate_identityref which in runtime validates actual values
*/ */
static int static int
@ -1636,28 +1616,40 @@ ys_populate_identity(clicon_handle h,
int retval = -1; int retval = -1;
yang_stmt *yc = NULL; yang_stmt *yc = NULL;
yang_stmt *ybaseid; yang_stmt *ybaseid;
// yang_stmt *ymod;
cg_var *cv; cg_var *cv;
char *derid;
char *baseid; char *baseid;
char *prefix = NULL; char *prefix = NULL;
char *id = NULL;
cbuf *cb = NULL; cbuf *cb = NULL;
char *p;
/* Top-call (no recursion) create idref
* The idref is (here) in "canonical form": <module>:<id>
*/
if (idref == NULL){ if (idref == NULL){
/* Create derived identity through prefix:id if not recursively called*/ /* Create derived identity through prefix:id if not recursively called*/
derid = ys->ys_argument; /* derived id */
if ((prefix = yarg_prefix(ys)) == NULL){
if ((p = yang_find_myprefix(ys)) != NULL)
prefix = strdup(yang_find_myprefix(ys));
}
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
} }
if (prefix) #if 0 /* Use module:id instead of prefix:id in derived list */
cprintf(cb, "%s:%s", prefix, derid); if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
else goto done;
cprintf(cb, "%s", derid); if ((ymod = ys_module(ys)) == NULL){
clicon_err(OE_YANG, ENOENT, "No module found");
goto done;
}
cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
#else
{
if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
goto done;
if (prefix)
cprintf(cb, "%s:%s", prefix, id);
else
cprintf(cb, "%s:%s", yang_find_myprefix(ys), id);
}
#endif
idref = cbuf_get(cb); idref = cbuf_get(cb);
} }
/* Iterate through all base statements and check the base identity exists /* Iterate through all base statements and check the base identity exists
@ -1667,9 +1659,9 @@ ys_populate_identity(clicon_handle h,
while ((yc = yn_each(ys, yc)) != NULL) { while ((yc = yn_each(ys, yc)) != NULL) {
if (yc->ys_keyword != Y_BASE) if (yc->ys_keyword != Y_BASE)
continue; continue;
baseid = yc->ys_argument; baseid = yang_argument_get(yc); /* on the form: prefix:id */
if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){ if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){
clicon_err(OE_YANG, 0, "No such identity: %s", baseid); clicon_err(OE_YANG, ENOENT, "No such identity: %s", baseid);
goto done; goto done;
} }
// continue; /* root identity */ // continue; /* root identity */
@ -1696,6 +1688,8 @@ ys_populate_identity(clicon_handle h,
done: done:
if (prefix) if (prefix)
free(prefix); free(prefix);
if (id)
free(id);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
return retval; return retval;
@ -1730,8 +1724,8 @@ if_feature(yang_stmt *yspec,
/*! Populate yang feature statement - set cv to 1 if enabled /*! Populate yang feature statement - set cv to 1 if enabled
* *
* @param[in] ys Feature yang statement to populate.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] ys Feature yang statement to populate.
*/ */
static int static int
ys_populate_feature(clicon_handle h, ys_populate_feature(clicon_handle h,
@ -1792,6 +1786,8 @@ ys_populate_feature(clicon_handle h,
} }
/*! Populate the unique statement with a cvec /*! Populate the unique statement with a cvec
* @param[in] h Clicon handle
* @param[in] ys The yang statement (unique) to populate.
*/ */
static int static int
ys_populate_unique(clicon_handle h, ys_populate_unique(clicon_handle h,
@ -1805,6 +1801,8 @@ ys_populate_unique(clicon_handle h,
} }
/*! Populate unknown node with extension /*! Populate unknown node with extension
* @param[in] h Clicon handle
* @param[in] ys The yang statement (unknown) to populate.
*/ */
static int static int
ys_populate_unknown(clicon_handle h, ys_populate_unknown(clicon_handle h,
@ -1815,19 +1813,19 @@ ys_populate_unknown(clicon_handle h,
char *reason = NULL; char *reason = NULL;
yang_stmt *ymod; yang_stmt *ymod;
char *prefix = NULL; char *prefix = NULL;
char *name; char *id = NULL;
char *extra; char *extra;
if ((extra = ys->ys_extra) == NULL) if ((extra = ys->ys_extra) == NULL)
goto ok; goto ok;
/* Find extension, if found, store it as unknown, if not, /* Find extension, if found, store it as unknown, if not,
break for error */ break for error */
prefix = yarg_prefix(ys); /* And this its prefix */ if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
name = yarg_id(ys); /* This is the type to resolve */ goto done;
if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL) if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL)
goto ok; /* shouldnt happen */ goto ok; /* shouldnt happen */
if (yang_find(ymod, Y_EXTENSION, name) == NULL){ if (yang_find(ymod, Y_EXTENSION, id) == NULL){
clicon_err(OE_YANG, errno, "Extension %s:%s not found", prefix, name); clicon_err(OE_YANG, errno, "Extension %s:%s not found", prefix, id);
goto done; goto done;
} }
if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){ if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
@ -1847,13 +1845,15 @@ ys_populate_unknown(clicon_handle h,
done: done:
if (prefix) if (prefix)
free(prefix); free(prefix);
if (id)
free(id);
return retval; return retval;
} }
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree. /*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
* *
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[in] h Clicon handle * @param[in] arg Argument - in effect Clicon handle
* Preferably run this command using yang_apply * Preferably run this command using yang_apply
* Done in 2nd pass after complete parsing to be sure to have a complete * Done in 2nd pass after complete parsing to be sure to have a complete
* parse-tree * parse-tree
@ -2150,8 +2150,8 @@ yang_expand_grouping(yang_stmt *yn)
int glen; int glen;
int i; int i;
int j; int j;
char *name; char *id = NULL;
char *prefix; char *prefix = NULL;
size_t size; size_t size;
/* Cannot use yang_apply here since child-list is modified (is destructive) */ /* Cannot use yang_apply here since child-list is modified (is destructive) */
@ -2161,14 +2161,18 @@ yang_expand_grouping(yang_stmt *yn)
switch(ys->ys_keyword){ switch(ys->ys_keyword){
case Y_USES: case Y_USES:
/* Split argument into prefix and name */ /* Split argument into prefix and name */
name = yarg_id(ys); /* This is uses/grouping name to resolve */ if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
prefix = yarg_prefix(ys); /* And this its prefix */ goto done;
if (ys_grouping_resolve(ys, prefix, name, &ygrouping) < 0) if (ys_grouping_resolve(ys, prefix, id, &ygrouping) < 0)
goto done; goto done;
if (prefix){ if (prefix){
free(prefix); free(prefix);
prefix = NULL; prefix = NULL;
} }
if (id){
free(id);
id = NULL;
}
if (ygrouping == NULL){ if (ygrouping == NULL){
clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"", clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
__FUNCTION__, ys->ys_argument, ys_module(ys)->ys_argument); __FUNCTION__, ys->ys_argument, ys_module(ys)->ys_argument);
@ -2258,6 +2262,10 @@ yang_expand_grouping(yang_stmt *yn)
} }
retval = 0; retval = 0;
done: done:
if (prefix)
free(prefix);
if (id)
free(id);
return retval; return retval;
} }

View file

@ -96,7 +96,8 @@ struct yang_stmt{
cvec *ys_cvec; /* List of stmt-specific variables cvec *ys_cvec; /* List of stmt-specific variables
Y_RANGE: range_min, range_max Y_RANGE: range_min, range_max
Y_LIST: vector of keys Y_LIST: vector of keys
Y_TYPE & identity: store all derived types Y_TYPE & identity: store all derived
types as <prefix>:<id> list
*/ */
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */ yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
int _ys_vector_i; /* internal use: yn_each */ int _ys_vector_i; /* internal use: yn_each */

View file

@ -1058,7 +1058,7 @@ ys_cv_validate(clicon_handle h,
cvec *patterns = NULL; cvec *patterns = NULL;
cvec *regexps = NULL; cvec *regexps = NULL;
enum cv_type cvtype; enum cv_type cvtype;
char *type; /* orig type */ char *origtype = NULL; /* orig type */
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
char *restype; char *restype;
uint8_t fraction = 0; uint8_t fraction = 0;
@ -1081,13 +1081,13 @@ ys_cv_validate(clicon_handle h,
clicon_err(OE_UNIX, errno, "cvec_new"); clicon_err(OE_UNIX, errno, "cvec_new");
goto done; goto done;
} }
if (yang_type_get(ys, &type, &yrestype, if (yang_type_get(ys, &origtype, &yrestype,
&options, &cvv, &options, &cvv,
patterns, regexps, patterns, regexps,
&fraction) < 0) &fraction) < 0)
goto done; goto done;
restype = yrestype?yrestype->ys_argument:NULL; restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, ys, &cvtype) < 0) if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done; goto done;
if (cv_type_get(ycv) != cvtype){ if (cv_type_get(ycv) != cvtype){
@ -1104,7 +1104,7 @@ ys_cv_validate(clicon_handle h,
if (restype && strcmp(restype, "union") == 0){ if (restype && strcmp(restype, "union") == 0){
assert(cvtype == CGV_REST); assert(cvtype == CGV_REST);
val = cv_string_get(cv); val = cv_string_get(cv);
if ((retval2 = ys_cv_validate_union(h, ys, reason, yrestype, type, val)) < 0) if ((retval2 = ys_cv_validate_union(h, ys, reason, yrestype, origtype, val)) < 0)
goto done; goto done;
retval = retval2; /* invalid (0) with latest reason or valid 1 */ retval = retval2; /* invalid (0) with latest reason or valid 1 */
} }
@ -1127,6 +1127,8 @@ ys_cv_validate(clicon_handle h,
goto done; goto done;
} }
done: done:
if (origtype)
free(origtype);
if (regexps) if (regexps)
cvec_free(regexps); cvec_free(regexps);
if (patterns) if (patterns)
@ -1165,48 +1167,43 @@ ys_typedef_up(yang_stmt *ys)
} }
/*! Find identity yang-stmt /*! Find identity yang-stmt
This is a sanity check of base identity of identity-ref and for identity * This is a sanity check of base identity of identity-ref and for identity
statements when parsing. * statements when parsing.
*
Return true if node is identityref and is derived from identity_name * Return true if node is identityref and is derived from identity_name
The derived-from() function returns true if the (first) node (in * The derived-from() function returns true if the (first) node (in
document order in the argument "nodes") is a node of type identityref, * document order in the argument "nodes") is a node of type identityref,
and its value is an identity that is derived from the identity * and its value is an identity that is derived from the identity
"identity-name" defined in the YANG module "module-name"; otherwise * "identity-name" defined in the YANG module "module-name"; otherwise
it returns false. * it returns false.
*
Valid values for an identityref are any identities derived from the * Valid values for an identityref are any identities derived from the
identityref's base identity. * identityref's base identity.
1. (base) identity must exist (be found). This is a sanity check * 1. (base) identity must exist (be found). This is a sanity check
of the specification and also necessary for identity statements. * of the specification and also necessary for identity statements.
(This is what is done here) * (This is what is done here)
2. Check if a given node has value derived from base identity. This is * 2. Check if a given node has value derived from base identity. This is
a run-time check necessary when validating eg netconf. * a run-time check necessary when validating eg netconf.
(This is validation) * (This is validation)
3. Find all valid derived identities from a identityref base identity. * 3. Find all valid derived identities from a identityref base identity.
(This is for cli generation) * (This is for cli generation)
* @param[in] ys Yang spec of id statement * @param[in] ys Yang spec of id statement
* @param[in] identity Identity string -check if it exists * @param[in] identity Identity string -check if it exists
* @retval 0 OK * @retval 0 OK
* @see validate_identityref for (2) above * @see validate_identityref for (2) above
*/ */
yang_stmt * yang_stmt *
yang_find_identity(yang_stmt *ys, yang_find_identity(yang_stmt *ys,
char *identity) char *identity)
{ {
char *id; char *id = NULL;
char *prefix = NULL; char *prefix = NULL;
yang_stmt *ymodule; yang_stmt *ymodule;
yang_stmt *yid = NULL; yang_stmt *yid = NULL;
yang_stmt *yn; yang_stmt *yn;
if ((id = strchr(identity, ':')) == NULL) if (nodeid_split(identity, &prefix, &id) < 0)
id = identity; goto done;
else{
prefix = strdup(identity);
prefix[id-identity] = '\0';
id++;
}
/* No, now check if identityref is derived from base */ /* No, now check if identityref is derived from base */
if (prefix){ /* Go to top and find import that matches */ if (prefix){ /* Go to top and find import that matches */
if ((ymodule = yang_find_module_by_prefix(ys, prefix)) == NULL) if ((ymodule = yang_find_module_by_prefix(ys, prefix)) == NULL)
@ -1231,6 +1228,8 @@ yang_find_identity(yang_stmt *ys,
} }
} }
done: done:
if (id)
free(id);
if (prefix) if (prefix)
free(prefix); free(prefix);
return yid; return yid;
@ -1334,7 +1333,7 @@ yang_type_resolve(yang_stmt *yorig,
{ {
yang_stmt *rytypedef = NULL; /* Resolved typedef of ytype */ yang_stmt *rytypedef = NULL; /* Resolved typedef of ytype */
yang_stmt *rytype; /* Resolved type of ytype */ yang_stmt *rytype; /* Resolved type of ytype */
char *type; char *type = NULL;
char *prefix = NULL; char *prefix = NULL;
int retval = -1; int retval = -1;
yang_stmt *yn; yang_stmt *yn;
@ -1343,8 +1342,9 @@ yang_type_resolve(yang_stmt *yorig,
if (options) if (options)
*options = 0x0; *options = 0x0;
*yrestype = NULL; /* Initialization of resolved type that may not be necessary */ *yrestype = NULL; /* Initialization of resolved type that may not be necessary */
type = yarg_id(ytype); /* This is the type to resolve */
prefix = yarg_prefix(ytype); /* And this its prefix */ if (nodeid_split(yang_argument_get(ytype), &prefix, &type) < 0)
goto done;
/* Cache does not work for eg string length 32? */ /* Cache does not work for eg string length 32? */
if (ytype->ys_typecache != NULL){ if (ytype->ys_typecache != NULL){
if (yang_type_cache_get(ytype->ys_typecache, yrestype, if (yang_type_cache_get(ytype->ys_typecache, yrestype,
@ -1409,6 +1409,8 @@ yang_type_resolve(yang_stmt *yorig,
done: done:
if (prefix) if (prefix)
free(prefix); free(prefix);
if (type)
free(type);
return retval; return retval;
} }
@ -1432,7 +1434,7 @@ yang_type_resolve(yang_stmt *yorig,
* printf("%d..%d\n", min , max); * printf("%d..%d\n", min , max);
* @endcode * @endcode
* @param[in] ys yang-stmt, leaf or leaf-list * @param[in] ys yang-stmt, leaf or leaf-list
* @param[out] origtype original type may be derived or built-in * @param[out] origtype original type may be derived or built-in (malloced)
* @param[out] yrestype Resolved type. return built-in type or NULL. * @param[out] yrestype Resolved type. return built-in type or NULL.
* @param[out] options Flags field of optional values, see YANG_OPTIONS_* * @param[out] options Flags field of optional values, see YANG_OPTIONS_*
* @param[out] cvv Cvec with min/max range or length. * @param[out] cvv Cvec with min/max range or length.
@ -1465,19 +1467,23 @@ yang_type_get(yang_stmt *ys,
{ {
int retval = -1; int retval = -1;
yang_stmt *ytype; /* type */ yang_stmt *ytype; /* type */
char *type; char *type = NULL;
if (options) if (options)
*options = 0x0; *options = 0x0;
/* Find mandatory type */ /* Find mandatory type */
if ((ytype = yang_find(ys, Y_TYPE, NULL)) == NULL){ if ((ytype = yang_find(ys, Y_TYPE, NULL)) == NULL){
clicon_err(OE_DB, 0, "mandatory type object is not found"); clicon_err(OE_DB, ENOENT, "mandatory type object is not found");
goto done; goto done;
} }
/* XXX: here we seem to have some problems if type is union */ /* XXX: here we seem to have some problems if type is union */
type = yarg_id(ytype); if (nodeid_split(yang_argument_get(ytype), NULL, &type) < 0)
if (origtype) goto done;
*origtype = type; if (origtype &&
(*origtype = strdup(type)) == NULL){
clicon_err(OE_XML, errno, "stdup");
goto done;
}
if (yang_type_resolve(ys, ys, ytype, yrestype, if (yang_type_resolve(ys, ys, ytype, yrestype,
options, cvv, patterns, regexps, fraction) < 0) options, cvv, patterns, regexps, fraction) < 0)
goto done; goto done;
@ -1485,6 +1491,8 @@ yang_type_get(yang_stmt *ys,
*yrestype?(*yrestype)->ys_argument:"null"); *yrestype?(*yrestype)->ys_argument:"null");
retval = 0; retval = 0;
done: done:
if (type)
free(type);
return retval; return retval;
} }
@ -1496,16 +1504,18 @@ yang_type2cv(yang_stmt *ys)
{ {
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */ char *restype; /* resolved type */
char *type; /* original type */ char *origtype=NULL; /* original type */
enum cv_type cvtype = CGV_ERR; enum cv_type cvtype = CGV_ERR;
/* Find type specification */ /* Find type specification */
if (yang_type_get(ys, &type, &yrestype, NULL, NULL, NULL, NULL, NULL) if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL)
< 0) < 0)
goto done; goto done;
restype = yrestype?yrestype->ys_argument:NULL; restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, ys, &cvtype) < 0) /* This handles non-resolved also */ if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
goto done; goto done;
done: done:
if (origtype)
free(origtype);
return cvtype; return cvtype;
} }

View file

@ -226,7 +226,7 @@ new "Netconf set undefined acl-type"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><acls xmlns="urn:example:my-crypto"><acl><name>x</name><type>undefined</type></acl></acls></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><acls xmlns="urn:example:my-crypto"><acl><name>x</name><type>undefined</type></acl></acls></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "netconf validate fail" new "netconf validate fail"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, mc:undefined not derived from acl-base</error-message></rpc-error></rpc-reply>]]>]]>' expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, undefined not derived from acl-base</error-message></rpc-error></rpc-reply>]]>]]>'
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"