* 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:
parent
ea13727e97
commit
7e4e1d6deb
27 changed files with 2124 additions and 203 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
Så 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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue