* Backend plugin returning NULL was still installed - is now logged and skipped.
* [Parent list key is not validated if not provided via RESTCONF #83](https://github.com/clicon/clixon/issues/83), thanks achernavin22.
This commit is contained in:
parent
881dd56ee1
commit
56f32371ce
9 changed files with 62 additions and 38 deletions
|
|
@ -92,7 +92,7 @@
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
* Empty leaf values, eg <a></a> are now checked at vlidation.
|
* Empty leaf values, eg <a></a> are now checked at validation.
|
||||||
* Empty values were skipped in validation.
|
* Empty values were skipped in validation.
|
||||||
* They are now checked and invalid for ints, dec64, etc, but are treated as empty string "" for string types.
|
* They are now checked and invalid for ints, dec64, etc, but are treated as empty string "" for string types.
|
||||||
* Optimized validation by making xml_diff work on raw cache tree (not copies)
|
* Optimized validation by making xml_diff work on raw cache tree (not copies)
|
||||||
|
|
@ -118,6 +118,8 @@
|
||||||
* Added libgen.h for baseline()
|
* Added libgen.h for baseline()
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
* Backend plugin returning NULL was still installed - is now logged and skipped.
|
||||||
|
* [Parent list key is not validated if not provided via RESTCONF #83](https://github.com/clicon/clixon/issues/83), thanks achernavin22.
|
||||||
* [Invalid JSON if GET /operations via RESTCONF #82](https://github.com/clicon/clixon/issues/82), thanks achernavin22
|
* [Invalid JSON if GET /operations via RESTCONF #82](https://github.com/clicon/clixon/issues/82), thanks achernavin22
|
||||||
* List ordering bug - lists with ints as keys behaved wrongly and slow.
|
* List ordering bug - lists with ints as keys behaved wrongly and slow.
|
||||||
* NACM read default rule did not work properly if nacm was enabled AND no groups were defined
|
* NACM read default rule did not work properly if nacm was enabled AND no groups were defined
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ cli_dbxml(clicon_handle h,
|
||||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 1)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,6 @@
|
||||||
* @param[in] name Name of this function (eg "expand_dbvar")
|
* @param[in] name Name of this function (eg "expand_dbvar")
|
||||||
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
||||||
* @param[in] argv Arguments given at the callback ("<db>" "<xmlkeyfmt>")
|
* @param[in] argv Arguments given at the callback ("<db>" "<xmlkeyfmt>")
|
||||||
* @param[out] len len of return commands & helptxt
|
|
||||||
* @param[out] commands vector of function pointers to callback functions
|
* @param[out] commands vector of function pointers to callback functions
|
||||||
* @param[out] helptxt vector of pointers to helptexts
|
* @param[out] helptxt vector of pointers to helptexts
|
||||||
* @see cli_expand_var_generate This is where arg is generated
|
* @see cli_expand_var_generate This is where arg is generated
|
||||||
|
|
@ -171,7 +170,7 @@ expand_dbvar(void *h,
|
||||||
/* This is primarily to get "y",
|
/* This is primarily to get "y",
|
||||||
* xpath2xml would have worked!!
|
* xpath2xml would have worked!!
|
||||||
*/
|
*/
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 1)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
if (y==NULL)
|
if (y==NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,7 @@ api_data_post(clicon_handle h,
|
||||||
/* Translate api_path to xtop/xbot */
|
/* Translate api_path to xtop/xbot */
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
if (api_path){
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y)) < 0)
|
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
|
|
@ -738,7 +738,7 @@ api_data_put(clicon_handle h,
|
||||||
/* Translate api_path to xtop/xbot */
|
/* Translate api_path to xtop/xbot */
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
if (api_path){
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y)) < 0)
|
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
|
|
@ -1011,7 +1011,7 @@ api_data_delete(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
if (api_path){
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y)) < 0)
|
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
|
|
@ -1598,7 +1598,7 @@ api_operations_post(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* Here xtop is: <rpc username="foo"/> */
|
/* Here xtop is: <rpc username="foo"/> */
|
||||||
}
|
}
|
||||||
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y)) < 0)
|
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_stmt *yspec);
|
||||||
int xml_spec_populate(cxobj *x, void *arg);
|
int xml_spec_populate(cxobj *x, void *arg);
|
||||||
int api_path2xpath(yang_stmt *yspec, cvec *cvv, int offset, cbuf *xpath);
|
int api_path2xpath(yang_stmt *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||||
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
||||||
yang_class nodeclass, cxobj **xpathp, yang_stmt **ypathp);
|
yang_class nodeclass, int strict, cxobj **xpathp, yang_stmt **ypathp);
|
||||||
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
|
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
|
||||||
int yang_enum_int_value(cxobj *node, int32_t *val);
|
int yang_enum_int_value(cxobj *node, int32_t *val);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,23 +173,27 @@ clixon_plugin_find(clicon_handle h,
|
||||||
* @param[in] file Which plugin to load
|
* @param[in] file Which plugin to load
|
||||||
* @param[in] function Which function symbol to load and call
|
* @param[in] function Which function symbol to load and call
|
||||||
* @param[in] dlflags See man(3) dlopen
|
* @param[in] dlflags See man(3) dlopen
|
||||||
* @retval cp Clixon plugin structure
|
* @param[out] cpp Clixon plugin structure (if retval is 1)
|
||||||
* @retval NULL Error
|
* @retval 1 OK
|
||||||
|
* @retval 0 Failed load, log, skip and continue with other plugins
|
||||||
|
* @retval -1 Error
|
||||||
* @see clixon_plugins_load Load all plugins
|
* @see clixon_plugins_load Load all plugins
|
||||||
*/
|
*/
|
||||||
static clixon_plugin *
|
static int
|
||||||
plugin_load_one(clicon_handle h,
|
plugin_load_one(clicon_handle h,
|
||||||
char *file,
|
char *file,
|
||||||
char *function,
|
char *function,
|
||||||
int dlflags)
|
int dlflags,
|
||||||
|
clixon_plugin **cpp)
|
||||||
{
|
{
|
||||||
char *error;
|
int retval = -1;
|
||||||
void *handle = NULL;
|
char *error;
|
||||||
plginit2_t *initfn;
|
void *handle = NULL;
|
||||||
|
plginit2_t *initfn;
|
||||||
clixon_plugin_api *api = NULL;
|
clixon_plugin_api *api = NULL;
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
char *name;
|
char *name;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function);
|
clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function);
|
||||||
dlerror(); /* Clear any existing error */
|
dlerror(); /* Clear any existing error */
|
||||||
|
|
@ -201,7 +205,7 @@ plugin_load_one(clicon_handle h,
|
||||||
/* call plugin_init() if defined, eg CLIXON_PLUGIN_INIT or CLIXON_BACKEND_INIT */
|
/* call plugin_init() if defined, eg CLIXON_PLUGIN_INIT or CLIXON_BACKEND_INIT */
|
||||||
if ((initfn = dlsym(handle, function)) == NULL){
|
if ((initfn = dlsym(handle, function)) == NULL){
|
||||||
clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file);
|
clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file);
|
||||||
goto err;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((error = (char*)dlerror()) != NULL) {
|
if ((error = (char*)dlerror()) != NULL) {
|
||||||
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
|
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
|
||||||
|
|
@ -211,11 +215,12 @@ plugin_load_one(clicon_handle h,
|
||||||
if ((api = initfn(h)) == NULL) {
|
if ((api = initfn(h)) == NULL) {
|
||||||
if (!clicon_errno){ /* if clicon_err() is not called then log and continue */
|
if (!clicon_errno){ /* if clicon_err() is not called then log and continue */
|
||||||
clicon_log(LOG_DEBUG, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
clicon_log(LOG_DEBUG, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
||||||
dlclose(handle);
|
retval = 0;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
||||||
goto err;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
|
/* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
|
||||||
|
|
@ -235,15 +240,19 @@ plugin_load_one(clicon_handle h,
|
||||||
|
|
||||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
|
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
|
||||||
(int)strlen(name), name);
|
(int)strlen(name), name);
|
||||||
if (api)
|
cp->cp_api = *api;
|
||||||
cp->cp_api = *api;
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
if (cp){
|
||||||
|
*cpp = cp;
|
||||||
|
cp = NULL;
|
||||||
|
}
|
||||||
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
return cp;
|
if (retval != 1 && handle)
|
||||||
err:
|
|
||||||
if (handle)
|
|
||||||
dlclose(handle);
|
dlclose(handle);
|
||||||
return NULL;
|
if (cp)
|
||||||
|
free(cp);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Load a set of plugin objects from a directory and and call their init-function
|
/*! Load a set of plugin objects from a directory and and call their init-function
|
||||||
|
|
@ -266,6 +275,7 @@ clixon_plugins_load(clicon_handle h,
|
||||||
int i;
|
int i;
|
||||||
char filename[MAXPATHLEN];
|
char filename[MAXPATHLEN];
|
||||||
clixon_plugin *cp;
|
clixon_plugin *cp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
/* Get plugin objects names from plugin directory */
|
/* Get plugin objects names from plugin directory */
|
||||||
|
|
@ -276,8 +286,10 @@ clixon_plugins_load(clicon_handle h,
|
||||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||||
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||||
(int)strlen(filename), filename);
|
(int)strlen(filename), filename);
|
||||||
if ((cp = plugin_load_one(h, filename, function, RTLD_NOW)) == NULL)
|
if ((ret = plugin_load_one(h, filename, function, RTLD_NOW, &cp)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
continue;
|
||||||
_clixon_nplugins++;
|
_clixon_nplugins++;
|
||||||
if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
|
if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "realloc");
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
|
|
|
||||||
|
|
@ -791,7 +791,7 @@ xml_cv_set(cxobj *x,
|
||||||
*
|
*
|
||||||
* @retval xmlobj if found.
|
* @retval xmlobj if found.
|
||||||
* @retval NULL if no such node found.
|
* @retval NULL if no such node found.
|
||||||
* @see xml_find_type wich is a more generic function
|
* @see xml_find_type which is a more generic function
|
||||||
*/
|
*/
|
||||||
cxobj *
|
cxobj *
|
||||||
xml_find(cxobj *x_up,
|
xml_find(cxobj *x_up,
|
||||||
|
|
|
||||||
|
|
@ -1971,6 +1971,7 @@ api_path2xml_vec(char **vec,
|
||||||
cxobj *x0,
|
cxobj *x0,
|
||||||
yang_stmt *y0,
|
yang_stmt *y0,
|
||||||
yang_class nodeclass,
|
yang_class nodeclass,
|
||||||
|
int strict,
|
||||||
cxobj **xpathp,
|
cxobj **xpathp,
|
||||||
yang_stmt **ypathp)
|
yang_stmt **ypathp)
|
||||||
{
|
{
|
||||||
|
|
@ -2053,9 +2054,10 @@ api_path2xml_vec(char **vec,
|
||||||
valvec = NULL;
|
valvec = NULL;
|
||||||
}
|
}
|
||||||
if (restval==NULL){
|
if (restval==NULL){
|
||||||
// XXX patch to allow for lists without restval to be backward compat
|
if (strict){
|
||||||
// clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
|
clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
|
||||||
// goto done;
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
|
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
|
||||||
|
|
@ -2086,9 +2088,11 @@ api_path2xml_vec(char **vec,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: /* eg Y_CONTAINER, Y_LEAF */
|
default: /* eg Y_CONTAINER, Y_LEAF */
|
||||||
if ((x = xml_new(name, x0, y)) == NULL)
|
if ((x = xml_find_type(x0, NULL, name, CX_ELMNT)) == NULL){ /* eg key of list */
|
||||||
goto done;
|
if ((x = xml_new(name, x0, y)) == NULL)
|
||||||
xml_type_set(x, CX_ELMNT);
|
goto done;
|
||||||
|
xml_type_set(x, CX_ELMNT);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (x && namespace){
|
if (x && namespace){
|
||||||
|
|
@ -2097,7 +2101,7 @@ api_path2xml_vec(char **vec,
|
||||||
}
|
}
|
||||||
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
||||||
x, y,
|
x, y,
|
||||||
nodeclass,
|
nodeclass, strict,
|
||||||
xpathp, ypathp)) < 1)
|
xpathp, ypathp)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
ok:
|
ok:
|
||||||
|
|
@ -2144,6 +2148,7 @@ api_path2xml(char *api_path,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
cxobj *xtop,
|
cxobj *xtop,
|
||||||
yang_class nodeclass,
|
yang_class nodeclass,
|
||||||
|
int strict,
|
||||||
cxobj **xbotp,
|
cxobj **xbotp,
|
||||||
yang_stmt **ybotp)
|
yang_stmt **ybotp)
|
||||||
{
|
{
|
||||||
|
|
@ -2169,7 +2174,7 @@ api_path2xml(char *api_path,
|
||||||
}
|
}
|
||||||
nvec--; /* NULL-terminated */
|
nvec--; /* NULL-terminated */
|
||||||
if ((retval = api_path2xml_vec(vec+1, nvec,
|
if ((retval = api_path2xml_vec(vec+1, nvec,
|
||||||
xtop, yspec, nodeclass,
|
xtop, yspec, nodeclass, strict,
|
||||||
xbotp, ybotp)) < 1)
|
xbotp, ybotp)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
xml_yang_root(*xbotp, &xroot);
|
xml_yang_root(*xbotp, &xroot);
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,12 @@ if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
new "restconf Add subtree without key (expected error)"
|
||||||
|
expecteq "$(curl -s -X PUT -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "malformed key, expected '"'"'=restval'"'"'"}}}
'
|
||||||
|
|
||||||
|
new "restconf Add subtree with too many keys (expected error)"
|
||||||
|
expecteq "$(curl -s -X PUT -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "List key interface length mismatch"}}}
'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
stop_restconf
|
stop_restconf
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue