Very large commit for upcoming 4.4 release

Major New features

* New and updated search functions using xpath, api-path and instance-id
  * New search functions using api-path and instance_id:
    * C search functions: `clixon_find_instance_id()` and `clixon_find_api_path()`
  * Binary search optimization in lists for indexed leafs in all three formats.
    * This improves search performance to O(logN) which is drastical improvements for large lists.
  * You can also register explicit indexes for making binary search (not only list keys)
  * For more info, see docs at [paths](https://clixon-docs.readthedocs.io/en/latest/paths.html) and
[search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml)

API changes on existing features (you may need to change your code)
* On failed validation of leafrefs, error message changed from: `No such leaf` to `No leaf <name> matching path <path>`.
* CLI Error message (clicon_rpc_generate_error()) changed when backend returns netconf error to be more descriptive:
  * Original: `Config error: Validate failed. Edit and try again or discard changes: Invalid argument`
  * New (example): `Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"

Minor changes

* Test framework
  * Added `-- -S <file>` command-line to main example to be able to return any state to main example.
  * Added `test/cicd` test scripts for running on a set of other hosts
* C-code restructuring
  * clixon_yang.c partitioned and moved code into clixon_yang_parse_lib.c and clixon_yang_module.c and move back some code from clixon_yang_type.c.
    * partly to reduce size, but most important to limit code that accesses internal yang structures, only clixon_yang.c does this now.
This commit is contained in:
Olof hagsand 2020-02-02 15:52:30 +01:00
parent e8ae628d06
commit 19e21be0bc
132 changed files with 6241 additions and 2332 deletions

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -77,8 +77,8 @@
#include "clixon_plugin.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_yang_module.h"
#include "clixon_yang_internal.h" /* internal */
modstate_diff_t *
modstate_diff_new(void)
@ -155,7 +155,7 @@ yang_modules_revision(clicon_handle h)
if ((ymod = yang_find(yspec, Y_MODULE, "ietf-yang-library")) != NULL ||
(ymod = yang_find(yspec, Y_SUBMODULE, "ietf-yang-library")) != NULL){
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL){
revision = yrev->ys_argument;
revision = yang_argument_get(yrev);
}
}
return revision;
@ -189,25 +189,25 @@ yms_build(clicon_handle h,
goto done;
}
cprintf(cb,"<modules-state xmlns=\"%s\">", yns->ys_argument);
cprintf(cb,"<modules-state xmlns=\"%s\">", yang_argument_get(yns));
cprintf(cb,"<module-set-id>%s</module-set-id>", msid);
ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (ymod->ys_keyword != Y_MODULE &&
ymod->ys_keyword != Y_SUBMODULE)
if (yang_keyword_get(ymod) != Y_MODULE &&
yang_keyword_get(ymod) != Y_SUBMODULE)
continue;
cprintf(cb,"<module>");
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
cprintf(cb,"<name>%s</name>", yang_argument_get(ymod));
if ((ys = yang_find(ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
else{
/* RFC7895 1 If no (such) revision statement exists, the module's or
submodule's revision is the zero-length string. */
cprintf(cb,"<revision></revision>");
}
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
cprintf(cb,"<namespace>%s</namespace>", yang_argument_get(ys));
else
cprintf(cb,"<namespace></namespace>");
/* This follows order in rfc 7895: feature, conformance-type,
@ -215,10 +215,10 @@ yms_build(clicon_handle h,
if (!brief){
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
switch(yc->ys_keyword){
switch(yang_keyword_get(yc)){
case Y_FEATURE:
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
if (yang_cv_get(yc) && cv_bool_get(yang_cv_get(yc)))
cprintf(cb,"<feature>%s</feature>", yang_argument_get(yc));
break;
default:
break;
@ -228,12 +228,12 @@ yms_build(clicon_handle h,
}
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
switch(yc->ys_keyword){
switch(yang_keyword_get(yc)){
case Y_SUBMODULE:
cprintf(cb,"<submodule>");
cprintf(cb,"<name>%s</name>", yc->ys_argument);
cprintf(cb,"<name>%s</name>", yang_argument_get(yc));
if ((ys = yang_find(yc, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
else
cprintf(cb,"<revision></revision>");
cprintf(cb,"</submodule>");
@ -413,7 +413,7 @@ mod_ns_upgrade(clicon_handle h,
goto fail;
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) == NULL)
goto fail;
if (ys_parse_date_arg(yrev->ys_argument, &to) < 0)
if (ys_parse_date_arg(yang_argument_get(yrev), &to) < 0)
goto done;
}
if ((ret = upgrade_callback_call(h, xt, ns, from, to, cbret)) < 0)
@ -482,3 +482,125 @@ clixon_module_upgrade(clicon_handle h,
retval = 0;
goto done;
}
/*! 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
* @param[in] ys A yang statement
* @param[in] prefix prefix
* @retval ymod Yang module statement if found
* @retval NULL not found
* @note Prefixes are relative to the module they are defined
* @see yang_find_module_by_name
* @see yang_find_module_by_namespace
*/
yang_stmt *
yang_find_module_by_prefix(yang_stmt *ys,
char *prefix)
{
yang_stmt *yimport;
yang_stmt *yprefix;
yang_stmt *my_ymod;
yang_stmt *ymod = NULL;
yang_stmt *yspec;
char *myprefix;
if ((yspec = ys_spec(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang spec not found");
goto done;
}
if ((my_ymod = ys_module(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang module not found");
goto done;
}
myprefix = yang_find_myprefix(ys);
if (myprefix && strcmp(myprefix, prefix) == 0){
ymod = my_ymod;
goto done;
}
yimport = NULL;
while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
if (yang_keyword_get(yimport) != Y_IMPORT)
continue;
if ((yprefix = yang_find(yimport, Y_PREFIX, NULL)) != NULL &&
strcmp(yang_argument_get(yprefix), prefix) == 0){
break;
}
}
if (yimport){
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yimport))) == NULL){
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
prefix);
yimport = NULL;
goto done; /* unresolved */
}
}
done:
return ymod;
}
/* Get module from its own prefix
* This is really not a valid usecase, a kludge for the identityref derived
* list workaround (IDENTITYREF_KLUDGE)
* Actually, for canonical prefixes it is!
*/
yang_stmt *
yang_find_module_by_prefix_yspec(yang_stmt *yspec,
char *prefix)
{
yang_stmt *ymod = NULL;
yang_stmt *yprefix;
while ((ymod = yn_each(yspec, ymod)) != NULL)
if (yang_keyword_get(ymod) == Y_MODULE &&
(yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL &&
strcmp(yang_argument_get(yprefix), prefix) == 0)
return ymod;
return NULL;
}
/*! Given a yang spec and a namespace, return yang module
*
* @param[in] yspec A yang specification
* @param[in] namespace namespace
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_name
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_namespace(yang_stmt *yspec,
char *namespace)
{
yang_stmt *ymod = NULL;
if (namespace == NULL)
goto done;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (yang_find(ymod, Y_NAMESPACE, namespace) != NULL)
break;
}
done:
return ymod;
}
/*! Given a yang spec and a module name, return yang module or submodule
*
* @param[in] yspec A yang specification
* @param[in] name Name of module
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_namespace
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_name(yang_stmt *yspec,
char *name)
{
yang_stmt *ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL)
if ((yang_keyword_get(ymod) == Y_MODULE || yang_keyword_get(ymod) == Y_SUBMODULE) &&
strcmp(yang_argument_get(ymod), name)==0)
return ymod;
return NULL;
}