* Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10

* Previous support did no validation of values.
  * Validation of types and CLI expansion
  * Example extended with inclusion of iana-if-type RFC 7224 interface identities
This commit is contained in:
Olof hagsand 2018-06-03 15:36:05 +02:00
parent ea13727e97
commit 7e4e1d6deb
27 changed files with 2124 additions and 203 deletions

View file

@ -186,6 +186,7 @@ clicon_option_readfile_xml(clicon_hash_t *copt,
/*! Initialize option values
*
* Set default options, Read config-file, Check that all values are set.
* Read clixon system config files
* @param[in] h clicon handle
*/
int

View file

@ -226,7 +226,7 @@ xml2cli(FILE *f,
return retval;
}
/*! Validate an xml node of type leafref, ensure the value is one of that path's reference
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
* @param[in] xt XML leaf node of type leafref
* @param[in] ytype Yang type statement belonging to the XML node
*/
@ -270,6 +270,70 @@ validate_leafref(cxobj *xt,
return retval;
}
/*! Validate xml node of type identityref, ensure value is a defined identity
* Check if a given node has value derived from base identity. This is
* a run-time check necessary when validating eg netconf.
* Valid values for an identityref are any identities derived from all
* the identityref's base identities.
* Example:
* b0 --> b1 --> b2 (b1 & b2 are derived)
* identityref b2
* base b0;
* This function does: derived_from(b2, b0);
* @param[in] xt XML leaf node of type identityref
* @param[in] ys Yang spec of leaf
* @param[in] ytype Yang type field of type identityref
* @see ys_populate_identity where the derived types are set
* @see RFC7950 Sec 9.10.2:
*/
static int
validate_identityref(cxobj *xt,
yang_stmt *ys,
yang_stmt *ytype)
{
int retval = -1;
char *node;
yang_stmt *ybaseref; /* This is the type's base reference */
yang_stmt *ybaseid;
char *prefix = NULL;
cbuf *cb = 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 ((node = xml_body(xt)) == NULL)
return 0;
if (strchr(node, ':') == NULL){
prefix = yang_find_myprefix(ys);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "%s:%s", prefix, node);
node = cbuf_get(cb);
}
/* This is the type's base reference */
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, no base");
goto done;
}
/* This is the actual base identity */
if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, no base identity");
goto done;
}
/* Here check if node is in the derived node list of the base identity */
if (cvec_find(ybaseid->ys_cvec, node) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, %s not derived from %s", node, ybaseid->ys_argument);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Validate a single XML node with yang specification for added entry
* 1. Check if mandatory leafs present as subs.
* 2. Check leaf values, eg int ranges and string regexps.
@ -374,10 +438,16 @@ xml_yang_validate_all(cxobj *xt,
/* Special case if leaf is leafref, then first check against
current xml tree
*/
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL &&
strcmp(ytype->ys_argument, "leafref") == 0)
if (validate_leafref(xt, ytype) < 0)
goto done;
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(ytype->ys_argument, "leafref") == 0){
if (validate_leafref(xt, ytype) < 0)
goto done;
}
else if (strcmp(ytype->ys_argument, "identityref") == 0){
if (validate_identityref(xt, ys, ytype) < 0)
goto done;
}
}
break;
default:
break;

View file

@ -817,8 +817,6 @@ yarg_prefix(yang_stmt *ys)
return prefix;
}
/*! Given a yang statement and a prefix, return yang module to that prefix
* Note, not the other module but the proxy import statement only
* @param[in] ys A yang statement
@ -1168,6 +1166,8 @@ ys_populate_range(yang_stmt *ys,
/*! Sanity check yang type statement
* XXX: Replace with generic parent/child type-check
* @param[in] ys The yang statement (type) to populate.
* @
*/
static int
ys_populate_type(yang_stmt *ys,
@ -1199,28 +1199,82 @@ ys_populate_type(yang_stmt *ys,
return retval;
}
/*! Sanity check yang type statement
/*! Sanity check yang identity statement recursively
*
* Find base identities if any and add this identity to derived list.
* Do this recursively
* @param[in] ys The yang identity to populate.
* @param[in] arg If set contains a derived identifier
* @see validate_identityref which in runtime validates actual valoues
*/
static int
ys_populate_identity(yang_stmt *ys,
void *arg)
{
int retval = -1;
yang_stmt *ybase;
yang_stmt *yc = NULL;
yang_stmt *ybaseid;
cg_var *cv;
char *derid;
char *baseid;
char *prefix = NULL;
cbuf *cb = NULL;
char *idref = (char*)arg;
char *p;
if ((ybase = yang_find((yang_node*)ys, Y_BASE, NULL)) == NULL)
return 0;
if ((yang_find_identity(ys, ybase->ys_argument)) == NULL){
clicon_err(OE_YANG, 0, "Identity %s not found (base type of %s)",
ybase->ys_argument, ys->ys_argument);
goto done;
if (idref == NULL){
/* 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){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (prefix)
cprintf(cb, "%s:%s", prefix, derid);
else
cprintf(cb, "%s", derid);
idref = cbuf_get(cb);
}
/* Iterate through all base statements and check the base identity exists
* AND populate the base identity recursively
*/
while ((yc = yn_each((yang_node*)ys, yc)) != NULL) {
if (yc->ys_keyword != Y_BASE)
continue;
baseid = yc->ys_argument;
if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){
clicon_err(OE_YANG, 0, "No such identity: %s", baseid);
goto done;
}
// continue; /* root identity */
/* Check if derived id is already in base identifier */
if (cvec_find(ybaseid->ys_cvec, idref) != NULL)
continue;
/* Add derived id to ybaseid */
if ((cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
/* add prefix */
cv_name_set(cv, idref);
cvec_append_var(ybaseid->ys_cvec, cv);
/* Transitive to the root */
if (ys_populate_identity(ybaseid, idref) < 0)
goto done;
}
retval = 0;
done:
if (prefix)
free(prefix);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
*
* We do this in 2nd pass after complete parsing to be sure to have a complete parse-tree

View file

@ -380,7 +380,7 @@ clicon_type2cv(char *origtype,
(rmax && (i) > cv_##type##_get(rmax)))
/*!
/*! Validate CLIgen variable
* @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
@ -764,9 +764,9 @@ ys_typedef_up(yang_stmt *ys)
return (yang_stmt*)ys;
}
/*! Return yang-stmt of identity
/*! Find identity yang-stmt
This is a sanity check of base identity of identity-ref and for identity
statements.
statements when parsing.
Return true if node is identityref and is derived from identity_name
The derived-from() function returns true if the (first) node (in
@ -779,12 +779,17 @@ ys_typedef_up(yang_stmt *ys)
identityref's base identity.
1. (base) identity must exist (be found). This is a sanity check
of the specification and also necessary for identity statements.
(This is what is dine here)
2. Check if a given node has value derived from base identity. This is
a run-time check necessary when validating eg netconf.
(This is validation)
3. Find all valid derived identities from a identityref base identity.
This is for cli generation.
vad är det denna function ska göra? Svar: 1
*/
(This is for cli generation)
* @param[in] ys Yang spec of id statement
* @param[in] identity Identity string -check if it exists
* @retval 0 OK
* @see validate_identityref for (2) above
*/
yang_stmt *
yang_find_identity(yang_stmt *ys,
char *identity)