* Support of yangmodels supported, see test_yangmodels.sh

* Added -o "<option>=<value>" command-line option to all programs: backend, cli, netconf, restconf.
* Ignore CR(\r) in yang files for DOS files
This commit is contained in:
Olof hagsand 2019-01-10 20:52:19 +01:00
parent c7e847cd24
commit 207858e20d
26 changed files with 635 additions and 256 deletions

View file

@ -34,6 +34,16 @@
* Yang functions
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*
* yang_spec_parse_module
* \
* yang_spec_parse_file-> yang_parse_post->yang_parse_recurse->yang_parse_module
* \ / v
* yang_spec_load_dir ------------------------------------> yang_parse_filename
* v
* yang_parse_file
* v
* yang_parse_str
*/
#ifdef HAVE_CONFIG_H
@ -1068,7 +1078,8 @@ ys_populate_leaf(yang_stmt *ys,
yparent = ys->ys_parent; /* Find parent: list/container */
/* 1. Find type specification and set cv type accordingly */
if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, &fraction_digits) < 0)
if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, &fraction_digits)
< 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, &cvtype) < 0) /* This handles non-resolved also */
@ -1213,7 +1224,7 @@ ys_populate_range(yang_stmt *ys,
clicon_err(OE_YANG, 0, "parent should be type");
goto done;
}
if (yang_type_resolve(ys, (yang_stmt*)yparent, &yrestype,
if (yang_type_resolve(ys, ys, (yang_stmt*)yparent, &yrestype,
&options, NULL, NULL, &fraction_digits) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
@ -1467,7 +1478,7 @@ ys_populate_feature(clicon_handle h,
cv_name_set(cv, feature);
cv_bool_set(cv, found);
if (found)
clicon_debug(1, "%s %s:%s", __FUNCTION__, module, feature);
clicon_debug(2, "%s %s:%s", __FUNCTION__, module, feature);
ys->ys_cv = cv;
ok:
retval = 0;
@ -2085,7 +2096,7 @@ yang_parse_recurse(clicon_handle h,
subrevision = NULL;
/* if already loaded, ignore, else parse the file */
if (yang_find((yang_node*)ysp,
keyw=Y_IMPORT?Y_MODULE:Y_SUBMODULE,
keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE,
submodule) == NULL){
/* recursive call */
if ((subymod = yang_parse_module(h, submodule, subrevision, ysp)) == NULL)
@ -2249,7 +2260,9 @@ yang_merge_submodules(clicon_handle h,
}
modname = yb->ys_argument;
if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, modname)) == NULL){
clicon_err(OE_YANG, ENOENT, "Module %s which submodule %s belongs to is not found", modname, ysubm->ys_argument);
clicon_err(OE_YANG, ENOENT, "Submodule %s is loaded before/without its main module %s (you need to load the submodule together with or after the main module)",
ysubm->ys_argument,
modname);
goto done;
}
/* Move sub-module statements to modules
@ -2304,64 +2317,29 @@ yang_merge_submodules(clicon_handle h,
* yang_parse_str # Set up yacc parser and call it given a string
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
int
yang_parse(clicon_handle h,
const char *filename,
const char *module,
const char *revision,
yang_spec *yspec)
static int
yang_parse_post(clicon_handle h,
yang_spec *yspec,
int modnr)
{
int retval = -1;
yang_stmt *ymod = NULL; /* Top-level yang (sub)module */
int i;
int modnr; /* Existing number of modules */
char *base = NULL;;
/* Apply steps 2.. on new modules, ie ones after modnr. */
modnr = yspec->yp_len;
if (filename){
/* Find module, and do not load file if module already exists */
if (basename(filename) == NULL){
clicon_err(OE_YANG, errno, "No basename");
goto done;
}
if ((base = strdup(basename(filename))) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
if (index(base, '@') != NULL)
*index(base, '@') = '\0';
if (yang_find((yang_node*)yspec, Y_MODULE, base) != NULL)
goto ok;
if ((ymod = yang_parse_filename(filename, yspec)) == NULL)
goto done;
}
else {
/* Do not load module if it already exists */
if (yang_find((yang_node*)yspec, Y_MODULE, module) != NULL)
goto ok;
if ((ymod = yang_parse_module(h, module, revision, yspec)) == NULL)
goto done;
}
int retval = -1;
int i;
/* 1: Parse from text to yang parse-tree. */
/* Iterate through modules */
if (yang_parse_recurse(h, ymod, yspec) < 0)
goto done;
for (i=modnr; i<yspec->yp_len; i++)
if (yang_parse_recurse(h, yspec->yp_stmt[i], yspec) < 0)
goto done;
/* 2. Check cardinality maybe this should be done after grouping/augment */
for (i=modnr; i<yspec->yp_len; i++) /* XXX */
for (i=modnr; i<yspec->yp_len; i++)
if (yang_cardinality(h, yspec->yp_stmt[i], yspec->yp_stmt[i]->ys_argument) < 0)
goto done;
/* 3: Merge sub-modules with modules - after this step, no submodules exist
* In the merge, remove submodule headers
*/
for (i=modnr; i<yspec->yp_len; i++){
if (yspec->yp_stmt[i]->ys_keyword != Y_SUBMODULE)
continue;
}
i = 0;
i = modnr;
while (i<yspec->yp_len){
int j;
if (yspec->yp_stmt[i]->ys_keyword != Y_SUBMODULE){
@ -2416,7 +2394,48 @@ yang_parse(clicon_handle h,
for (i=modnr; i<yspec->yp_len; i++)
if (yang_apply((yang_node*)yspec->yp_stmt[i], -1, ys_schemanode_check, NULL) < 0)
goto done;
/* Return main module parsed in step 1 */
retval = 0;
done:
return retval;
}
/*! Parse yang specification and its dependencies recursively given module
* @param[in] h clicon handle
* @param[in] module Module name, or absolute filename (including dir)
* @param[in] dir Directory where to look for modules and sub-modules
* @param[in] revision Revision, or NULL
* @param[in,out] yspec Modules parse are added to this yangspec
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_file
*/
int
yang_spec_parse_module(clicon_handle h,
const char *module,
const char *revision,
yang_spec *yspec)
{
int retval = -1;
int modnr; /* Existing number of modules */
char *base = NULL;;
if (yspec == NULL){
clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
goto done;
}
if (module == NULL){
clicon_err(OE_YANG, EINVAL, "yang module not set");
goto done;
}
/* Apply steps 2.. on new modules, ie ones after modnr. */
modnr = yspec->yp_len;
/* Do not load module if it already exists */
if (yang_find((yang_node*)yspec, Y_MODULE, module) != NULL)
goto ok;
if (yang_parse_module(h, module, revision, yspec) == NULL)
goto done;
if (yang_parse_post(h, yspec, modnr) < 0)
goto done;
ok:
retval = 0;
done:
@ -2425,6 +2444,132 @@ yang_parse(clicon_handle h,
return retval;
}
/*! Parse yang specification and its dependencies recursively given filename
* @param[in] h clicon handle
* @param[in] filename Actual filename (including dir and revision)
* @param[in] dir Directory for sub-modules
* @param[in,out] yspec Modules parse are added to this yangspec
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_module for yang dir,module,revision instead of
* actual filename
* @see yang_spec_load_dir For loading all files in a directory
*/
int
yang_spec_parse_file(clicon_handle h,
const char *filename,
yang_spec *yspec)
{
int retval = -1;
int modnr; /* Existing number of modules */
char *base = NULL;;
/* Apply steps 2.. on new modules, ie ones after modnr. */
modnr = yspec->yp_len;
/* Find module, and do not load file if module already exists */
if (basename(filename) == NULL){
clicon_err(OE_YANG, errno, "No basename");
goto done;
}
if ((base = strdup(basename(filename))) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
if (index(base, '@') != NULL)
*index(base, '@') = '\0';
if (yang_find((yang_node*)yspec, Y_MODULE, base) != NULL)
goto ok;
if (yang_parse_filename(filename, yspec) == NULL)
goto done;
if (yang_parse_post(h, yspec, modnr) < 0)
goto done;
ok:
retval = 0;
done:
if (base)
free(base);
return retval;
}
/*! Load all yang modules in directory
* @param[in] h Clicon handle
* @param[in] dir Load all yang modules in this directory
* @param[in,out] yspec Modules parse are added to this yangspec
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_file
*/
int
yang_spec_load_dir(clicon_handle h,
char *dir,
yang_spec *yspec)
{
int retval = -1;
int ndp;
struct dirent *dp = NULL;
int i;
char filename[MAXPATHLEN];
char *base = NULL; /* filename without dir */
char *b;
int j;
int modnr;
/* Get plugin objects names from plugin directory */
if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
goto done;
if (ndp == 0)
clicon_log(LOG_WARNING, "%s: No yang files found in %s",
__FUNCTION__, dir);
/* Apply post steps on new modules, ie ones after modnr. */
modnr = yspec->yp_len;
/* Load all yang files in dir */
for (i = 0; i < ndp; i++) {
/* base = module name [+ @rev ] + .yang */
if (base)
free(base);
if ((base = strdup(dp[i].d_name)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
*rindex(base, '.') = '\0'; /* strip postfix .yang */
/* base = module name [+ @rev]
* if it hasnt @rev then prefer it (dont check other files w @rev)
* Otherwise see if there is a newer
*/
if ((b = index(base, '@')) != NULL){
*b = '\0';
/* base = module name */
/* Entries are sorted, see if later entry exists (include @), if so
* skip this one and take last.
* Assume file without @ is last
*/
for (j = (i+1); j < ndp; j++)
if (strncmp(base, dp[j].d_name, strlen(base)) == 0)
break;
if (j<ndp){ /* exists later entry */
clicon_debug(1, "%s skip %s", __FUNCTION__, dp[i].d_name);
continue;
}
}
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
/* Dont parse if already exists */
if (yang_find((yang_node*)yspec, Y_MODULE, base) != NULL ||
yang_find((yang_node*)yspec, Y_SUBMODULE, base) != NULL)
continue;
if (yang_parse_filename(filename, yspec) == NULL)
goto done;
}
if (yang_parse_post(h, yspec, modnr) < 0)
goto done;
retval = 0;
done:
if (base)
free(base);
return retval;
}
/*! Apply a function call recursively on all yang-stmt s recursively
*
* Recursively traverse all yang-nodes in a parse-tree and apply fn(arg) for
@ -2835,121 +2980,6 @@ yang_config(yang_stmt *ys)
return 1;
}
/*! Parse yang specification and its dependencies recursively given module
* @param[in] h clicon handle
* @param[in] module Module name, or absolute filename (including dir)
* @param[in] dir Directory where to look for modules and sub-modules
* @param[in] revision Revision, or NULL
* @param[in,out] yspec Modules parse are added to this yangspec
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_file
*/
int
yang_spec_parse_module(clicon_handle h,
char *module,
char *revision,
yang_spec *yspec)
{
int retval = -1;
if (yspec == NULL){
clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
goto done;
}
if (module == NULL){
clicon_err(OE_YANG, EINVAL, "yang module not set");
goto done;
}
/* Sanity check - use yang_spec_parse_file instead */
if (strlen(module) && module[0] == '/'){
clicon_err(OE_YANG, EINVAL, "yang module illegal format");
goto done;
}
if (yang_parse(h, NULL, module, revision, yspec) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Parse yang specification and its dependencies recursively given filename
* @param[in] h clicon handle
* @param[in] filename Actual filename (including dir and revision)
* @param[in] dir Directory for sub-modules
* @param[in,out] yspec Modules parse are added to this yangspec
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_module for yang dir,module,revision instead of actual filename
*/
int
yang_spec_parse_file(clicon_handle h,
char *filename,
yang_spec *yspec)
{
int retval = -1;
if (yspec == NULL){
clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
goto done;
}
if (yang_parse(h, filename, NULL, NULL, yspec) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Load all yang modules in directory
* @param[in] h Clicon handle
* @param[in] dir Load all yang modules in this directory
* @param[in,out] yspec Modules parse are added to this yangspec
* @retval 0 OK
* @retval -1 Error
*/
int
yang_spec_load_dir(clicon_handle h,
char *dir,
yang_spec *yspec)
{
int retval = -1;
int ndp;
struct dirent *dp = NULL;
int i;
char filename[MAXPATHLEN];
char *base;
char *b;
int j;
int len;
/* Get plugin objects names from plugin directory */
if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
goto done;
/* Load all yang files */
for (i = 0; i < ndp; i++) {
base = dp[i].d_name;
/* Entries are sorted, see if later entry exists (include @), if so skip
* this one and take last.
*/
if ((b = index(base, '@')) != NULL)
len = b-base;
else
len = strlen(base);
/* remove duplicates: there may be cornercases that dont work, eg
* mix of revisions and not? */
for (j = (i+1); j < ndp; j++)
if (strncmp(base, dp[j].d_name, len) == 0)
break;
if (j<ndp) /* exists later entry */
continue;
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
if (yang_parse(h, filename, NULL, NULL, yspec) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Given a yang node, translate the argument string to a cv vector
*