* Top-level default leafs assigned.
* Enforcing RFC 7950 Sec 7.6.1 means unassigned top-level leafs (or leafs under non-presence containers) are assigned default values. * NACM default behaviour is read-only (empty configs are dead-lockedd) * This applies if NACM is loaded and `CLICON_NACM_MODE` is `internal` * Fixed: [default values don't show up in datastores #111](https://github.com/clicon/clixon/issues/111)
This commit is contained in:
parent
65733ffe69
commit
794d51a365
30 changed files with 593 additions and 167 deletions
31
CHANGELOG.md
31
CHANGELOG.md
|
|
@ -42,6 +42,34 @@ Expected: July 2020
|
||||||
|
|
||||||
### API changes on existing protocol/config features (For users)
|
### API changes on existing protocol/config features (For users)
|
||||||
|
|
||||||
|
* Top-level default leafs assigned.
|
||||||
|
* Enforcing RFC 7950 Sec 7.6.1 means unassigned top-level leafs (or leafs under non-presence containers) are assigned default values.
|
||||||
|
* In this process non-presence containers may be created.
|
||||||
|
* See also [default values don't show up in datastores #111](https://github.com/clicon/clixon/issues/111).
|
||||||
|
* NACM default behaviour is read-only (empty configs are dead-lockedd)
|
||||||
|
* This applies if NACM is loaded and `CLICON_NACM_MODE` is `internal`
|
||||||
|
* Due to the previous bult (top-level default leafs)
|
||||||
|
* This means that empty configs or empty NACM configs are not writable (deadlocked).
|
||||||
|
* Workarounds:
|
||||||
|
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
|
||||||
|
|
||||||
|
* This means that empty configs or empty NACM configs are not writable (deadlocked).
|
||||||
|
* Workarounds:
|
||||||
|
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
|
||||||
|
|
||||||
|
* This means that empty configs or empty NACM configs are not writable (deadlocked).
|
||||||
|
* Workarounds:
|
||||||
|
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
|
||||||
|
|
||||||
|
* This means that empty configs or empty NACM configs are not writable (deadlocked).
|
||||||
|
* Workarounds:
|
||||||
|
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
|
||||||
|
|
||||||
|
* This means that empty configs or empty NACM configs are not writable (deadlocked).
|
||||||
|
* Workarounds:
|
||||||
|
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
|
||||||
|
2. Edit the startup-db with a valid NACM config and restart the system
|
||||||
|
3. Set clixon option CLICON_NACM_DISABLED_ON_EMPTY to true which means that if the config is (completely) empty, you can add a NACM config as a first edit.
|
||||||
* Netconf lock/unlock behaviour changed to adhere to RFC 6241
|
* Netconf lock/unlock behaviour changed to adhere to RFC 6241
|
||||||
* Changed commit lock error tag from "lock denied" to "in-use".
|
* Changed commit lock error tag from "lock denied" to "in-use".
|
||||||
* Changed unlock error message from "lock is already held" to #lock not active" or "lock held by other session".
|
* Changed unlock error message from "lock is already held" to #lock not active" or "lock held by other session".
|
||||||
|
|
@ -53,6 +81,7 @@ Expected: July 2020
|
||||||
* CLICON_SSL_SERVER_CERT
|
* CLICON_SSL_SERVER_CERT
|
||||||
* CLICON_SSL_SERVER_KEY
|
* CLICON_SSL_SERVER_KEY
|
||||||
* CLICON_SSL_CA_CERT
|
* CLICON_SSL_CA_CERT
|
||||||
|
* Added CLICON_NACM_DISABLED_ON_EMPTY to mitigate read-only "dead-lock" of empty startup configs.
|
||||||
* Restconf FCGI (eg via nginx) have changed reply message syntax slightly as follows (due to refactoring and common code with evhtp):
|
* Restconf FCGI (eg via nginx) have changed reply message syntax slightly as follows (due to refactoring and common code with evhtp):
|
||||||
* Bodies in error retuns including html code have been removed
|
* Bodies in error retuns including html code have been removed
|
||||||
* Some (extra) CRLF:s have been removed
|
* Some (extra) CRLF:s have been removed
|
||||||
|
|
@ -99,6 +128,8 @@ Expected: July 2020
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
||||||
|
* Fixed: [default values don't show up in datastores #111](https://github.com/clicon/clixon/issues/111).
|
||||||
|
* See also API changes since this changes NACM behavior for example.
|
||||||
* Fixed: Don't call upgrade callbacks if no revision defined so there's no way to determine right way 'from' and 'to'
|
* Fixed: Don't call upgrade callbacks if no revision defined so there's no way to determine right way 'from' and 'to'
|
||||||
* Fixed: [lock candidate succeeded even though it is modified #110](https://github.com/clicon/clixon/issues/110)
|
* Fixed: [lock candidate succeeded even though it is modified #110](https://github.com/clicon/clixon/issues/110)
|
||||||
* Fixed: [Need to add the possibility to use anchors around patterns #51](https://github.com/clicon/cligen/issues/51):
|
* Fixed: [Need to add the possibility to use anchors around patterns #51](https://github.com/clicon/cligen/issues/51):
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ startup_extraxml(clicon_handle h,
|
||||||
/* Clear tmp db */
|
/* Clear tmp db */
|
||||||
if (xmldb_db_reset(h, tmp_db) < 0)
|
if (xmldb_db_reset(h, tmp_db) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Application may define extra xml in its reset function*/
|
/* Application may define extra xml in its reset function */
|
||||||
if (clixon_plugin_reset_all(h, tmp_db) < 0)
|
if (clixon_plugin_reset_all(h, tmp_db) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Extra XML can also be added via file */
|
/* Extra XML can also be added via file */
|
||||||
|
|
@ -238,13 +238,13 @@ startup_extraxml(clicon_handle h,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Check if tmp db is empty.
|
* Check if tmp db is empty. XXX no this is not possible.
|
||||||
* It should be empty if extra-xml is null and reset plugins did nothing
|
* It should be empty if extra-xml is null and reset plugins did nothing
|
||||||
* then skip validation.
|
* then skip validation.
|
||||||
*/
|
*/
|
||||||
if (xmldb_get(h, tmp_db, NULL, NULL, &xt0) < 0)
|
if (xmldb_get(h, tmp_db, NULL, NULL, &xt0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xt0==NULL || xml_child_nr(xt0)==0)
|
if (xmldb_empty_get(h, tmp_db))
|
||||||
goto ok;
|
goto ok;
|
||||||
xt = NULL;
|
xt = NULL;
|
||||||
/* Validate the tmp db and return possibly upgraded xml in xt
|
/* Validate the tmp db and return possibly upgraded xml in xt
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t de_id; /* session id */
|
uint32_t de_id; /* session id */
|
||||||
cxobj *de_xml; /* cache */
|
cxobj *de_xml; /* cache */
|
||||||
int de_modified;
|
int de_modified; /* Dirty since loaded/copied/committed/etc XXX:nocache? */
|
||||||
|
int de_empty; /* Empty on read from file, xmldb_readfile and xmldb_put sets it */
|
||||||
} db_elmnt;
|
} db_elmnt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ cxobj *xmldb_cache_get(clicon_handle h, const char *db);
|
||||||
|
|
||||||
int xmldb_modified_get(clicon_handle h, const char *db);
|
int xmldb_modified_get(clicon_handle h, const char *db);
|
||||||
int xmldb_modified_set(clicon_handle h, const char *db, int value);
|
int xmldb_modified_set(clicon_handle h, const char *db, int value);
|
||||||
|
int xmldb_empty_get(clicon_handle h, const char *db);
|
||||||
int xmldb_dump(clicon_handle h, FILE *f, cxobj *xt);
|
int xmldb_dump(clicon_handle h, FILE *f, cxobj *xt);
|
||||||
|
|
||||||
#endif /* _CLIXON_DATASTORE_H */
|
#endif /* _CLIXON_DATASTORE_H */
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,9 @@ int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
|
||||||
int xml_namespace_change(cxobj *x, char *ns, char *prefix);
|
int xml_namespace_change(cxobj *x, char *ns, char *prefix);
|
||||||
int xml_default(cxobj *x);
|
int xml_default(cxobj *x);
|
||||||
int xml_default_recurse(cxobj *xn);
|
int xml_default_recurse(cxobj *xn);
|
||||||
|
int xml_default_yspec(yang_stmt *yspec, cxobj *xn);
|
||||||
|
int xml_nopresence_default(cxobj *xt);
|
||||||
|
int xml_nopresence_default_mark(cxobj *x, void *arg);
|
||||||
int xml_sanity(cxobj *x, void *arg);
|
int xml_sanity(cxobj *x, void *arg);
|
||||||
int xml_non_config_data(cxobj *xt, void *arg);
|
int xml_non_config_data(cxobj *xt, void *arg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,13 @@
|
||||||
|
|
||||||
/* Struct containing module state differences between two modules or two
|
/* Struct containing module state differences between two modules or two
|
||||||
* revisions of same module.
|
* revisions of same module.
|
||||||
* This is in state of flux so it needs to be contained and easily changed.
|
* The most significant usecase is one module-state is a loaded datastore and the other
|
||||||
|
* is the one loaded by the server by its YANG files.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int md_status; /* 0 if no module-state in a datastore, 1 if there is */
|
int md_status; /* 0 if no module-state in a datastore, 1 if there is */
|
||||||
char *md_set_id; /* server-specific identifier */
|
char *md_set_id; /* server-specific identifier */
|
||||||
cxobj *md_diff; /* yang module state containing revisions and XML_FLAG_ADD|DEL|CHANGE */
|
cxobj *md_diff; /* yang module state containing revisions and XML_FLAG_ADD|DEL|CHANGE */
|
||||||
} modstate_diff_t;
|
} modstate_diff_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -636,12 +636,11 @@ clicon_argv_set(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Get xml database element including pid and xml cache
|
/*! Get xml database element including id, xml cache, empty on startup and dirty bit
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] db Name of database
|
* @param[in] db Name of database
|
||||||
* @retval de Database element
|
* @retval de Database element
|
||||||
* @retval NULL None found
|
* @retval NULL None found
|
||||||
* @note these use db_elmnt hash, not data
|
|
||||||
*/
|
*/
|
||||||
db_elmnt *
|
db_elmnt *
|
||||||
clicon_db_elmnt_get(clicon_handle h,
|
clicon_db_elmnt_get(clicon_handle h,
|
||||||
|
|
@ -655,14 +654,12 @@ clicon_db_elmnt_get(clicon_handle h,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Set xml database element including pid and xml cache
|
/*! Set xml database element including id, xml cache, empty on startup and dirty bit
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] db Name of database
|
* @param[in] db Name of database
|
||||||
* @param[in] de Database element
|
* @param[in] de Database element
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* XXX add prefix to db to ensure uniqueness?
|
|
||||||
* @note these use db_elmnt hash, not data
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_db_elmnt_set(clicon_handle h,
|
clicon_db_elmnt_set(clicon_handle h,
|
||||||
|
|
|
||||||
|
|
@ -518,6 +518,26 @@ xmldb_modified_get(clicon_handle h,
|
||||||
return de->de_modified;
|
return de->de_modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get empty flag from datastore (the datastore was empty ON LOAD)
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] db Database name
|
||||||
|
* @retval -1 Error (datastore does not exist)
|
||||||
|
* @retval 0 Db was not empty on load
|
||||||
|
* @retval 1 Db was empty on load
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xmldb_empty_get(clicon_handle h,
|
||||||
|
const char *db)
|
||||||
|
{
|
||||||
|
db_elmnt *de;
|
||||||
|
|
||||||
|
if ((de = clicon_db_elmnt_get(h, db)) == NULL){
|
||||||
|
clicon_err(OE_CFG, EFAULT, "datastore %s does not exist", db);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return de->de_empty;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Get modified flag from datastore
|
/*! Get modified flag from datastore
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] db Database name
|
* @param[in] db Database name
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
#include "clixon_xpath.h"
|
#include "clixon_xpath.h"
|
||||||
#include "clixon_json.h"
|
#include "clixon_json.h"
|
||||||
#include "clixon_nacm.h"
|
#include "clixon_nacm.h"
|
||||||
|
#include "clixon_path.h"
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
|
|
@ -104,7 +105,7 @@ singleconfigroot(cxobj *xt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i != 1){
|
if (i != 1){
|
||||||
clicon_err(OE_DB, ENOENT, "Top-element is not unique, expecting single config");
|
clicon_err(OE_DB, ENOENT, "Top-element is not unique, expecting single config");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
x = NULL;
|
x = NULL;
|
||||||
|
|
@ -443,33 +444,63 @@ text_read_modstate(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
disable_nacm_on_empty(cxobj *x0,
|
||||||
|
yang_stmt *yspec)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *ymod;
|
||||||
|
cxobj **vec = NULL;
|
||||||
|
int len = 0;
|
||||||
|
cxobj *xb;
|
||||||
|
|
||||||
|
if ((ymod = yang_find(yspec, Y_MODULE, "ietf-netconf-acm")) == NULL)
|
||||||
|
goto ok;
|
||||||
|
if (clixon_xml_find_instance_id(x0, yspec, &vec, &len, "/nacm:nacm/nacm:enable-nacm") < 1)
|
||||||
|
goto done;
|
||||||
|
if (len){
|
||||||
|
if ((xb = xml_body_get(vec[0])) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xml_value_set(xb, "false") < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (vec)
|
||||||
|
free(vec);
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Common read function that reads an XML tree from file
|
/*! Common read function that reads an XML tree from file
|
||||||
* @param[in] th Datastore text handle
|
* @param[in] th Datastore text handle
|
||||||
* @param[in] db Symbolic database name, eg "candidate", "running"
|
* @param[in] db Symbolic database name, eg "candidate", "running"
|
||||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||||
* @param[in] yspec Top-level yang spec
|
* @param[in] yspec Top-level yang spec
|
||||||
* @param[out] xp XML tree read from file
|
* @param[out] xp XML tree read from file
|
||||||
|
* @param[out] de If set, return db-element status (eg empty flag)
|
||||||
* @param[out] msdiff If set, return modules-state differences
|
* @param[out] msdiff If set, return modules-state differences
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
|
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* @note retval 0 is NYI becaues of functions calling this function
|
* @note retval 0 is NYI because of functions calling this function cannot handle it yet
|
||||||
*/
|
*/
|
||||||
#undef XMLDB_READFILE_FAIL
|
#undef XMLDB_READFILE_FAIL /* See comment on retval = 0 above */
|
||||||
int
|
int
|
||||||
xmldb_readfile(clicon_handle h,
|
xmldb_readfile(clicon_handle h,
|
||||||
const char *db,
|
const char *db,
|
||||||
yang_bind yb,
|
yang_bind yb,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
cxobj **xp,
|
cxobj **xp,
|
||||||
|
db_elmnt *de,
|
||||||
modstate_diff_t *msdiff)
|
modstate_diff_t *msdiff)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x0 = NULL;
|
cxobj *x0 = NULL;
|
||||||
char *dbfile = NULL;
|
char *dbfile = NULL;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
char *format;
|
char *format;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (xmldb_db2file(h, db, &dbfile) < 0)
|
if (xmldb_db2file(h, db, &dbfile) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -497,8 +528,9 @@ xmldb_readfile(clicon_handle h,
|
||||||
goto fail;
|
goto fail;
|
||||||
#endif
|
#endif
|
||||||
/* Always assert a top-level called "config".
|
/* Always assert a top-level called "config".
|
||||||
To ensure that, deal with two cases:
|
* To ensure that, deal with two cases:
|
||||||
1. File is empty <top/> -> rename top-level to "config" */
|
* 1. File is empty <top/> -> rename top-level to "config"
|
||||||
|
*/
|
||||||
if (xml_child_nr(x0) == 0){
|
if (xml_child_nr(x0) == 0){
|
||||||
if (xml_name_set(x0, "config") < 0)
|
if (xml_name_set(x0, "config") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -509,7 +541,10 @@ xmldb_readfile(clicon_handle h,
|
||||||
if (singleconfigroot(x0, &x0) < 0)
|
if (singleconfigroot(x0, &x0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* From Clixon 3.10,datastore files may contain module-state defining
|
if (xml_child_nr(x0) == 0 && de)
|
||||||
|
de->de_empty = 1;
|
||||||
|
|
||||||
|
/* Datastore files may contain module-state defining
|
||||||
* which modules are used in the file.
|
* which modules are used in the file.
|
||||||
*/
|
*/
|
||||||
if (text_read_modstate(h, yspec, x0, msdiff) < 0)
|
if (text_read_modstate(h, yspec, x0, msdiff) < 0)
|
||||||
|
|
@ -569,16 +604,36 @@ xmldb_get_nocache(clicon_handle h,
|
||||||
size_t xlen;
|
size_t xlen;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
db_elmnt de0 = {0,};
|
||||||
|
int empty = 0;
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &xt, msdiff)) < 0)
|
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &xt, &de0, msdiff)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
clicon_db_elmnt_set(h, db, &de0); /* Content is copied */
|
||||||
|
|
||||||
|
/* Check if empty */
|
||||||
|
if (xml_child_nr(xt) == 0)
|
||||||
|
empty = 1;
|
||||||
|
/* Add global defaults.
|
||||||
|
* Must do it before xpath check, since globals may be filtered out
|
||||||
|
*/
|
||||||
|
if (xml_default_yspec(yspec, xt) < 0)
|
||||||
|
goto done;
|
||||||
|
/* If empty database, then disable NACM if loaded
|
||||||
|
* This has some drawbacks. One is that a config may become empty at a later stage
|
||||||
|
* and then this does not hold.
|
||||||
|
*/
|
||||||
|
if (empty &&
|
||||||
|
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
|
||||||
|
if (disable_nacm_on_empty(xt, yspec) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
/* Here xt looks like: <config>...</config> */
|
/* Here xt looks like: <config>...</config> */
|
||||||
/* Given the xpath, return a vector of matches in xvec */
|
/* Given the xpath, return a vector of matches in xvec */
|
||||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||||
|
|
@ -664,6 +719,7 @@ xmldb_get_cache(clicon_handle h,
|
||||||
cxobj *x1t = NULL;
|
cxobj *x1t = NULL;
|
||||||
db_elmnt de0 = {0,};
|
db_elmnt de0 = {0,};
|
||||||
int ret;
|
int ret;
|
||||||
|
int empty = 0;
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
|
@ -672,7 +728,7 @@ xmldb_get_cache(clicon_handle h,
|
||||||
de = clicon_db_elmnt_get(h, db);
|
de = clicon_db_elmnt_get(h, db);
|
||||||
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
|
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
|
||||||
/* If there is no xml x0 tree (in cache), then read it from file */
|
/* If there is no xml x0 tree (in cache), then read it from file */
|
||||||
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, msdiff)) < 0)
|
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, &de0, msdiff)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -680,10 +736,30 @@ xmldb_get_cache(clicon_handle h,
|
||||||
* No, argument against: we may want to have a semantically wrong file and wish to edit?
|
* No, argument against: we may want to have a semantically wrong file and wish to edit?
|
||||||
*/
|
*/
|
||||||
de0.de_xml = x0t;
|
de0.de_xml = x0t;
|
||||||
clicon_db_elmnt_set(h, db, &de0);
|
clicon_db_elmnt_set(h, db, &de0); /* Content is copied */
|
||||||
} /* x0t == NULL */
|
} /* x0t == NULL */
|
||||||
else
|
else
|
||||||
x0t = de->de_xml;
|
x0t = de->de_xml;
|
||||||
|
|
||||||
|
/* Check if empty, must be before add global defaults */
|
||||||
|
if (xml_child_nr(x0t) == 0)
|
||||||
|
empty = 1;
|
||||||
|
|
||||||
|
/* Add global defaults.
|
||||||
|
* Cant do it to x1t since that is after xpath check, since globals may be filtered out
|
||||||
|
*/
|
||||||
|
if (xml_default_yspec(yspec, x0t) < 0)
|
||||||
|
goto done;
|
||||||
|
/* If empty database, then disable NACM if loaded
|
||||||
|
* This has some drawbacks. One is that a config may become empty at a later stage
|
||||||
|
* and then this does not hold.
|
||||||
|
*/
|
||||||
|
if (empty &&
|
||||||
|
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
|
||||||
|
if (disable_nacm_on_empty(x0t, yspec) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Here x0t looks like: <config>...</config> */
|
/* Here x0t looks like: <config>...</config> */
|
||||||
/* Given the xpath, return a vector of matches in xvec
|
/* Given the xpath, return a vector of matches in xvec
|
||||||
* Can we do everything in one go?
|
* Can we do everything in one go?
|
||||||
|
|
@ -693,8 +769,6 @@ xmldb_get_cache(clicon_handle h,
|
||||||
* a) for every node that is found, copy to new tree
|
* a) for every node that is found, copy to new tree
|
||||||
* b) if config dont dont state data
|
* b) if config dont dont state data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Here xt looks like: <config>...</config> */
|
|
||||||
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
@ -703,6 +777,7 @@ xmldb_get_cache(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
xml_spec_set(x1t, xml_spec(x0t));
|
xml_spec_set(x1t, xml_spec(x0t));
|
||||||
|
|
||||||
|
|
||||||
if (xlen < 1000){
|
if (xlen < 1000){
|
||||||
/* This is optimized for the case when the tree is large and xlen is small
|
/* This is optimized for the case when the tree is large and xlen is small
|
||||||
* If the tree is large and xlen too, then the other is better.
|
* If the tree is large and xlen too, then the other is better.
|
||||||
|
|
@ -786,6 +861,7 @@ xmldb_get_zerocopy(clicon_handle h,
|
||||||
db_elmnt *de = NULL;
|
db_elmnt *de = NULL;
|
||||||
db_elmnt de0 = {0,};
|
db_elmnt de0 = {0,};
|
||||||
int ret;
|
int ret;
|
||||||
|
int empty = 0;
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
|
@ -794,7 +870,7 @@ xmldb_get_zerocopy(clicon_handle h,
|
||||||
de = clicon_db_elmnt_get(h, db);
|
de = clicon_db_elmnt_get(h, db);
|
||||||
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
|
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
|
||||||
/* If there is no xml x0 tree (in cache), then read it from file */
|
/* If there is no xml x0 tree (in cache), then read it from file */
|
||||||
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, msdiff)) < 0)
|
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, &de0, msdiff)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -806,6 +882,25 @@ xmldb_get_zerocopy(clicon_handle h,
|
||||||
} /* x0t == NULL */
|
} /* x0t == NULL */
|
||||||
else
|
else
|
||||||
x0t = de->de_xml;
|
x0t = de->de_xml;
|
||||||
|
/* Check if empty, must be before add global defaults */
|
||||||
|
if (xml_child_nr(x0t) == 0)
|
||||||
|
empty = 1;
|
||||||
|
|
||||||
|
/* Add global defaults.
|
||||||
|
* Cant do it to x1t since that is after xpath check, since globals may be filtered out
|
||||||
|
*/
|
||||||
|
if (xml_default_yspec(yspec, x0t) < 0)
|
||||||
|
goto done;
|
||||||
|
/* If empty database, then disable NACM if loaded
|
||||||
|
* This has some drawbacks. One is that a config may become empty at a later stage
|
||||||
|
* and then this does not hold.
|
||||||
|
*/
|
||||||
|
if (empty &&
|
||||||
|
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
|
||||||
|
if (disable_nacm_on_empty(x0t, yspec) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Here xt looks like: <config>...</config> */
|
/* Here xt looks like: <config>...</config> */
|
||||||
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -874,7 +969,7 @@ xmldb_get(clicon_handle h,
|
||||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
|
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
|
||||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||||
* @param[out] msdiff If set, return modules-state differences (upgrade code)
|
* @param[out] msdiff If set, return modules-state differences (upgrade code)
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
|
|
@ -944,12 +1039,16 @@ xmldb_get0_clear(clicon_handle h,
|
||||||
|
|
||||||
if (x == NULL)
|
if (x == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
/* clear XML tree of defaults */
|
/* Mark non-presence containers as XML_FLAG_DEFAULT */
|
||||||
|
if (xml_apply(x, CX_ELMNT, xml_nopresence_default_mark, (void*)XML_FLAG_DEFAULT) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Clear XML tree of defaults */
|
||||||
if (xml_tree_prune_flagged(x, XML_FLAG_DEFAULT, 1) < 0)
|
if (xml_tree_prune_flagged(x, XML_FLAG_DEFAULT, 1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* clear mark and change */
|
/* clear mark and change */
|
||||||
xml_apply0(x, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
xml_apply0(x, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||||
(void*)(0xff));
|
(void*)(0xffff));
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -972,3 +1071,4 @@ xmldb_get0_free(clicon_handle h,
|
||||||
*xp = NULL;
|
*xp = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int xmldb_readfile(clicon_handle h, const char *db, yang_bind yb, yang_stmt *yspec, cxobj **xp, modstate_diff_t *msd);
|
int xmldb_readfile(clicon_handle h, const char *db, yang_bind yb, yang_stmt *yspec,
|
||||||
|
cxobj **xp, db_elmnt *de, modstate_diff_t *msd);
|
||||||
|
|
||||||
#endif /* _CLIXON_DATASTORE_READ_H */
|
#endif /* _CLIXON_DATASTORE_READ_H */
|
||||||
|
|
|
||||||
|
|
@ -481,9 +481,11 @@ text_modify(clicon_handle h,
|
||||||
switch(op){
|
switch(op){
|
||||||
case OP_CREATE:
|
case OP_CREATE:
|
||||||
if (x0){
|
if (x0){
|
||||||
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
|
if (xml_nopresence_default(x0) == 0){
|
||||||
goto done;
|
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
|
||||||
goto fail;
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case OP_REPLACE: /* fall thru */
|
case OP_REPLACE: /* fall thru */
|
||||||
case OP_MERGE:
|
case OP_MERGE:
|
||||||
|
|
@ -928,7 +930,7 @@ xmldb_put(clicon_handle h,
|
||||||
/* If there is no xml x0 tree (in cache), then read it from file */
|
/* If there is no xml x0 tree (in cache), then read it from file */
|
||||||
if (x0 == NULL){
|
if (x0 == NULL){
|
||||||
firsttime++; /* to avoid leakage on error, see fail from text_modify */
|
firsttime++; /* to avoid leakage on error, see fail from text_modify */
|
||||||
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &x0, NULL)) < 0)
|
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &x0, de, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -987,10 +989,10 @@ xmldb_put(clicon_handle h,
|
||||||
db_elmnt de0 = {0,};
|
db_elmnt de0 = {0,};
|
||||||
if (de != NULL)
|
if (de != NULL)
|
||||||
de0 = *de;
|
de0 = *de;
|
||||||
if (de0.de_xml == NULL){
|
if (de0.de_xml == NULL)
|
||||||
de0.de_xml = x0;
|
de0.de_xml = x0;
|
||||||
clicon_db_elmnt_set(h, db, &de0);
|
de0.de_empty = (xml_child_nr(de0.de_xml) == 0);
|
||||||
}
|
clicon_db_elmnt_set(h, db, &de0);
|
||||||
}
|
}
|
||||||
if (xmldb_db2file(h, db, &dbfile) < 0)
|
if (xmldb_db2file(h, db, &dbfile) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1057,6 +1059,9 @@ xmldb_dump(clicon_handle h,
|
||||||
char *format;
|
char *format;
|
||||||
int pretty;
|
int pretty;
|
||||||
|
|
||||||
|
/* clear XML tree of defaults */
|
||||||
|
if (xml_tree_prune_flagged(xt, XML_FLAG_DEFAULT, 1) < 0)
|
||||||
|
goto done;
|
||||||
/* Add modstate first */
|
/* Add modstate first */
|
||||||
if ((x = clicon_modst_cache_get(h, 1)) != NULL){
|
if ((x = clicon_modst_cache_get(h, 1)) != NULL){
|
||||||
if ((xmodst = xml_dup(x)) == NULL)
|
if ((xmodst = xml_dup(x)) == NULL)
|
||||||
|
|
@ -1079,3 +1084,4 @@ xmldb_dump(clicon_handle h,
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1100,7 +1100,9 @@ nacm_access(clicon_handle h,
|
||||||
/* Do initial nacm processing common to all access validation in
|
/* Do initial nacm processing common to all access validation in
|
||||||
* RFC8341 3.4 */
|
* RFC8341 3.4 */
|
||||||
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
|
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
|
||||||
operation is permitted. */
|
* operation is permitted.
|
||||||
|
* note option CLICON_NACM_DISABLED_ON_EMPTY
|
||||||
|
*/
|
||||||
if ((x = xpath_first(xnacm, nsc, "enable-nacm")) == NULL)
|
if ((x = xpath_first(xnacm, nsc, "enable-nacm")) == NULL)
|
||||||
goto permit;
|
goto permit;
|
||||||
enabled = xml_body(x);
|
enabled = xml_body(x);
|
||||||
|
|
@ -1155,6 +1157,7 @@ nacm_access_pre(clicon_handle h,
|
||||||
cxobj *xnacm = NULL;
|
cxobj *xnacm = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
|
|
||||||
|
/* Check clixon option: disabled, external tree or internal */
|
||||||
mode = clicon_option_str(h, "CLICON_NACM_MODE");
|
mode = clicon_option_str(h, "CLICON_NACM_MODE");
|
||||||
if (mode == NULL)
|
if (mode == NULL)
|
||||||
goto permit;
|
goto permit;
|
||||||
|
|
|
||||||
|
|
@ -1883,6 +1883,7 @@ xml_copy_one(cxobj *x0,
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
xml_flag_set(x1, xml_flag(x0, XML_FLAG_DEFAULT)); /* Maybe more flags */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -979,9 +979,223 @@ xml_namespace_change(cxobj *x,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Add default values (if not set)
|
int
|
||||||
* @param[in] xt XML tree with some node marked
|
xml_default_create1(yang_stmt *y,
|
||||||
* Typically called in a recursive apply function:
|
cxobj *xt,
|
||||||
|
int top,
|
||||||
|
cxobj **xcp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *namespace;
|
||||||
|
char *prefix;
|
||||||
|
int ret;
|
||||||
|
cxobj *xc = NULL;
|
||||||
|
|
||||||
|
if ((xc = xml_new(yang_argument_get(y), NULL, CX_ELMNT)) == NULL)
|
||||||
|
goto done;
|
||||||
|
xml_spec_set(xc, y);
|
||||||
|
|
||||||
|
/* assign right prefix */
|
||||||
|
if ((namespace = yang_find_mynamespace(y)) != NULL){
|
||||||
|
prefix = NULL;
|
||||||
|
if ((ret = xml2prefix(xt, namespace, &prefix)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret){ /* Namespace found, prefix returned in prefix */
|
||||||
|
if (xml_prefix_set(xc, prefix) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else{ /* Namespace does not exist in target, must add it w xmlns attr.
|
||||||
|
use source prefix */
|
||||||
|
if (!top){
|
||||||
|
if ((prefix = yang_find_myprefix(y)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (add_namespace(xc, xc, prefix, namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Add prefix to x, if any */
|
||||||
|
if (prefix && xml_prefix_set(xc, prefix) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xml_addsub(xt, xc) < 0)
|
||||||
|
goto done;
|
||||||
|
*xcp = xc;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Create leaf from default value
|
||||||
|
*
|
||||||
|
* @param[in] yt Yang spec
|
||||||
|
* @param[in] xt XML tree
|
||||||
|
* @param[in] top Use default namespace (if you create xmlns statement)
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xml_default_create(yang_stmt *y,
|
||||||
|
cxobj *xt,
|
||||||
|
int top)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xc = NULL;
|
||||||
|
cxobj *xb;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
if (xml_default_create1(y, xt, top, &xc) < 0)
|
||||||
|
goto done;
|
||||||
|
xml_flag_set(xc, XML_FLAG_DEFAULT);
|
||||||
|
if ((xb = xml_new("body", xc, CX_BODY)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((str = cv2str_dup(yang_cv_get(y))) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xml_value_set(xb, str) < 0)
|
||||||
|
goto done;
|
||||||
|
free(str);
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Try to see if intermediate nodes are necessary for default values, create if so
|
||||||
|
*
|
||||||
|
* @param[in] yt Yang container (no-presence)
|
||||||
|
* @param[out] createp Need to create XML container
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xml_nopresence_try(yang_stmt *yt,
|
||||||
|
int *createp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *y;
|
||||||
|
|
||||||
|
if (yt == NULL || yang_keyword_get(yt) != Y_CONTAINER){
|
||||||
|
clicon_err(OE_XML, EINVAL, "yt argument is not container");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*createp = 0;
|
||||||
|
y = NULL;
|
||||||
|
while ((y = yn_each(yt, y)) != NULL) {
|
||||||
|
switch (yang_keyword_get(y)){
|
||||||
|
case Y_LEAF:
|
||||||
|
if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
|
||||||
|
/* Need to create container */
|
||||||
|
*createp = 1;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Y_CONTAINER:
|
||||||
|
if (yang_find(y, Y_PRESENCE, NULL) == NULL){
|
||||||
|
/* If this is non-presence, (and it does not exist in xt) call recursively
|
||||||
|
* and create nodes if any default value exist first. Then continue and populate?
|
||||||
|
*/
|
||||||
|
if (xml_nopresence_try(y, createp) < 0)
|
||||||
|
goto done;
|
||||||
|
if (*createp)
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
} /* switch */
|
||||||
|
}
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Ensure default values are set on (children of) one single xml node
|
||||||
|
*
|
||||||
|
* Not recursive, except in one case with one or several non-presence containers, in which case
|
||||||
|
* XML containers may be created to host default values. That code may be a little too recursive.
|
||||||
|
* @param[in] yt Yang spec
|
||||||
|
* @param[in] xt XML tree (with yt as spec of xt, informally)
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xml_default1(yang_stmt *yt,
|
||||||
|
cxobj *xt)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *yc;
|
||||||
|
cxobj *xc;
|
||||||
|
int top=0; /* Top symbol (set default namespace) */
|
||||||
|
int create = 0;
|
||||||
|
|
||||||
|
if (xt == NULL){ /* No xml */
|
||||||
|
clicon_err(OE_XML, EINVAL, "No XML argument");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
switch (yang_keyword_get(yt)){
|
||||||
|
case Y_MODULE:
|
||||||
|
case Y_SUBMODULE:
|
||||||
|
top++;
|
||||||
|
case Y_CONTAINER: /* XXX maybe check for non-presence here as well */
|
||||||
|
case Y_LIST:
|
||||||
|
case Y_INPUT:
|
||||||
|
case Y_OUTPUT:
|
||||||
|
yc = NULL;
|
||||||
|
while ((yc = yn_each(yt, yc)) != NULL) {
|
||||||
|
switch (yang_keyword_get(yc)){
|
||||||
|
case Y_LEAF:
|
||||||
|
if (!cv_flag(yang_cv_get(yc), V_UNSET)){ /* Default value exists */
|
||||||
|
if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
|
||||||
|
/* No such child exist, create this leaf */
|
||||||
|
if (xml_default_create(yc, xt, top) < 0)
|
||||||
|
goto done;
|
||||||
|
xml_sort(xt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Y_CONTAINER:
|
||||||
|
if (yang_find(yc, Y_PRESENCE, NULL) == NULL){
|
||||||
|
/* If this is non-presence, (and it does not exist in xt) call
|
||||||
|
* recursively and create nodes if any default value exist first.
|
||||||
|
* Then continue and populate?
|
||||||
|
*/
|
||||||
|
if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
|
||||||
|
/* No such container exist, recursively try if needed */
|
||||||
|
if (xml_nopresence_try(yc, &create) < 0)
|
||||||
|
goto done;
|
||||||
|
if (create){
|
||||||
|
/* Retval shows there is a default value need to create the
|
||||||
|
* container */
|
||||||
|
if (xml_default_create1(yc, xt, top, &xc) < 0)
|
||||||
|
goto done;
|
||||||
|
xml_sort(xt);
|
||||||
|
/* Then call it recursively */
|
||||||
|
if (xml_default1(yc, xc) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
} /* switch */
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Ensure default values are set on existing leaf children of this node
|
||||||
|
*
|
||||||
|
* Assume yang is bound to the tree
|
||||||
|
* @param[in] xt XML tree
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
|
|
@ -990,79 +1204,22 @@ xml_default(cxobj *xt)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
yang_stmt *y;
|
|
||||||
// int i; // XXX
|
|
||||||
cxobj *xc;
|
|
||||||
cxobj *xb;
|
|
||||||
char *str;
|
|
||||||
int added=0;
|
|
||||||
char *namespace;
|
|
||||||
char *prefix;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Check leaf defaults */
|
if (xml_default1(ys, xt) < 0)
|
||||||
if (yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST ||
|
goto done;
|
||||||
yang_keyword_get(ys) == Y_INPUT){
|
|
||||||
y = NULL;
|
|
||||||
while ((y = yn_each(ys, y)) != NULL) {
|
|
||||||
if (yang_keyword_get(y) != Y_LEAF)
|
|
||||||
continue;
|
|
||||||
if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
|
|
||||||
if (!xml_find(xt, yang_argument_get(y))){
|
|
||||||
if ((xc = xml_new(yang_argument_get(y), NULL, CX_ELMNT)) == NULL)
|
|
||||||
goto done;
|
|
||||||
xml_spec_set(xc, y);
|
|
||||||
|
|
||||||
/* assign right prefix */
|
|
||||||
if ((namespace = yang_find_mynamespace(y)) != NULL){
|
|
||||||
prefix = NULL;
|
|
||||||
if ((ret = xml2prefix(xt, namespace, &prefix)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret){
|
|
||||||
if (xml_prefix_set(xc, prefix) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
else{ /* namespace does not exist in target, use source prefix */
|
|
||||||
if ((prefix = yang_find_myprefix(y)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (add_namespace(xc, xc, prefix, namespace) < 0)
|
|
||||||
goto done;
|
|
||||||
/* Add prefix to x, if any */
|
|
||||||
if (prefix && xml_prefix_set(xc, prefix) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xml_flag_set(xc, XML_FLAG_DEFAULT);
|
|
||||||
if ((xb = xml_new("body", xc, CX_BODY)) == NULL)
|
|
||||||
goto done;
|
|
||||||
if ((str = cv2str_dup(yang_cv_get(y))) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (xml_value_set(xb, str) < 0)
|
|
||||||
goto done;
|
|
||||||
free(str);
|
|
||||||
added++;
|
|
||||||
if (xml_insert(xt, xc, INS_LAST, NULL, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Recursively fill in default values in a tree
|
/*! Recursively fill in default values in an XML tree
|
||||||
* Alt to use xml_apply
|
* @param[in] xt XML tree
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_default_recurse(cxobj *xn)
|
xml_default_recurse(cxobj *xn)
|
||||||
|
|
@ -1082,6 +1239,85 @@ xml_default_recurse(cxobj *xn)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Ensure default values are set on top-level
|
||||||
|
*
|
||||||
|
* Not recursive, except in one case with one or several non-presence containers
|
||||||
|
* @param[in] xt XML tree
|
||||||
|
* Typically called in a recursive apply function:
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_default_yspec(yang_stmt *yspec,
|
||||||
|
cxobj *xt)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *ymod = NULL;
|
||||||
|
|
||||||
|
if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){
|
||||||
|
clicon_err(OE_XML, EINVAL, "yspec argument is not yang spec");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
while ((ymod = yn_each(yspec, ymod)) != NULL)
|
||||||
|
if (xml_default1(ymod, xt) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! This node is a default set value or (recursively) a non-presence container
|
||||||
|
* @retval 1 xt is a nopresence/default node (ie "virtual")
|
||||||
|
* @retval 0 xt is not such a node
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_nopresence_default(cxobj *xt)
|
||||||
|
{
|
||||||
|
cxobj *xc;
|
||||||
|
yang_stmt *yt;
|
||||||
|
|
||||||
|
if ((yt = xml_spec(xt)) == NULL)
|
||||||
|
return 0;
|
||||||
|
switch (yang_keyword_get(yt)){
|
||||||
|
case Y_CONTAINER:
|
||||||
|
if (yang_find(yt, Y_PRESENCE, NULL))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case Y_LEAF:
|
||||||
|
return xml_flag(xt, XML_FLAG_DEFAULT)?1:0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||||
|
if (xml_nopresence_default(xc) == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Remove xml container if it is non-presence and only contains default leafs
|
||||||
|
* Called from xml_apply. Reason for marking is to delete it afterwords.
|
||||||
|
* @param[in] x
|
||||||
|
* @param[in] arg (flag value)
|
||||||
|
* @code
|
||||||
|
* if (xml_apply(xt, CX_ELMNT, xml_nopresence_default_mark, (void*)XML_FLAG_DEFAULT) < 0)
|
||||||
|
* err;
|
||||||
|
* if (xml_tree_prune_flagged(xt, XML_FLAG_DEFAULT, 1) < 0)
|
||||||
|
* goto done;
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_nopresence_default_mark(cxobj *x,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (xml_nopresence_default(x))
|
||||||
|
xml_flag_set(x, (intptr_t)arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer
|
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer
|
||||||
* @param[in] xt XML top of tree
|
* @param[in] xt XML top of tree
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1029,6 +1029,7 @@ xml_insert2(cxobj *xp,
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see xml_addsub where xc is appended. xml_insert is xml_addsub();xml_sort()
|
* @see xml_addsub where xc is appended. xml_insert is xml_addsub();xml_sort()
|
||||||
|
* @note It is assumed that all siblings of xi are YANG bound
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_insert(cxobj *xp,
|
xml_insert(cxobj *xp,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
# Authentication and authorization and IETF NACM
|
# Authentication and authorization and IETF NACM
|
||||||
# See RFC 8341 A.2
|
# See RFC 8341 A.2
|
||||||
# But replaced ietf-netconf-monitoring with *
|
# But replaced ietf-netconf-monitoring with *
|
||||||
# Note credenials check set to none since USER poses as different users.
|
# Note:
|
||||||
|
# 1. credenials check set to none since USER poses as different users.
|
||||||
|
# 2. CLICON_NACM_DISABLE_ON_EMPTY: start with empty config and add nacm config
|
||||||
|
|
||||||
# Magic line must be first in script (see README.md)
|
# Magic line must be first in script (see README.md)
|
||||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
@ -31,6 +33,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -140,7 +143,7 @@ expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/da
|
||||||
|
|
||||||
# explicitly disable nacm (regression on netgate bug)
|
# explicitly disable nacm (regression on netgate bug)
|
||||||
new "disable nacm"
|
new "disable nacm"
|
||||||
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": false}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 201 Created"
|
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": false}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content"
|
||||||
|
|
||||||
new "auth set authentication config"
|
new "auth set authentication config"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
<CLICON_NACM_CREDENTIALS>$mode</CLICON_NACM_CREDENTIALS>
|
<CLICON_NACM_CREDENTIALS>$mode</CLICON_NACM_CREDENTIALS>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -62,7 +63,6 @@ EOF
|
||||||
# 6: expected return value of test2
|
# 6: expected return value of test2
|
||||||
# 7: expected return value of test3
|
# 7: expected return value of test3
|
||||||
# 8: startup mode: startup or init
|
# 8: startup mode: startup or init
|
||||||
# 9: Dont set default values (nullify them)
|
|
||||||
testrun(){
|
testrun(){
|
||||||
enablenacm=$1
|
enablenacm=$1
|
||||||
readdefault=$2
|
readdefault=$2
|
||||||
|
|
@ -72,24 +72,9 @@ testrun(){
|
||||||
ret2=$6
|
ret2=$6
|
||||||
ret3=$7
|
ret3=$7
|
||||||
db=$8
|
db=$8
|
||||||
nulldef=$9
|
|
||||||
|
|
||||||
# Set default values (or not)
|
NACM=$(cat <<EOF
|
||||||
if [ $nulldef -ne 0 ]; then
|
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||||
# Defaults should be: true permit deny permit:
|
|
||||||
# nacm enabled, exec default permit, read permit (expect fail)"
|
|
||||||
# which means results should be 0 1 3
|
|
||||||
# Also enable-nacm is present since otherwise the nacm container would be removed
|
|
||||||
# since it is non-presence
|
|
||||||
NACM=$(cat <<EOF
|
|
||||||
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
|
||||||
<enable-nacm>${enablenacm}</enable-nacm>
|
|
||||||
</nacm>
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
else
|
|
||||||
NACM=$(cat <<EOF
|
|
||||||
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
|
||||||
<enable-nacm>${enablenacm}</enable-nacm>
|
<enable-nacm>${enablenacm}</enable-nacm>
|
||||||
<read-default>${readdefault}</read-default>
|
<read-default>${readdefault}</read-default>
|
||||||
<write-default>${writedefault}</write-default>
|
<write-default>${writedefault}</write-default>
|
||||||
|
|
@ -98,7 +83,6 @@ EOF
|
||||||
</nacm>
|
</nacm>
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
fi
|
|
||||||
# Initial data
|
# Initial data
|
||||||
XML='<x xmlns="urn:example:nacm">42</x>'
|
XML='<x xmlns="urn:example:nacm">42</x>'
|
||||||
|
|
||||||
|
|
@ -117,6 +101,7 @@ EOF
|
||||||
start_backend -s $db -f $cfg
|
start_backend -s $db -f $cfg
|
||||||
else
|
else
|
||||||
new "Restart backend as eg follows: -Ff $cfg -s $db"
|
new "Restart backend as eg follows: -Ff $cfg -s $db"
|
||||||
|
sleep 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
|
|
@ -132,12 +117,16 @@ EOF
|
||||||
wait_restconf
|
wait_restconf
|
||||||
|
|
||||||
# Use POST (instead of startup)
|
# Use POST (instead of startup)
|
||||||
|
# Note this only works because CLICON_NACM_DISABLED_ON_EMPTY is true
|
||||||
if [ $db = init ]; then
|
if [ $db = init ]; then
|
||||||
new "Set Initial data using POST"
|
# Must set NACM first
|
||||||
expectpart "$(curl -u guest:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$XML" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created"
|
new "Set NACM using PATCH"
|
||||||
|
expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" -d "<data>$NACM$XML</data>" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content"
|
||||||
|
|
||||||
|
# new "Set Initial data using POST"
|
||||||
|
# expectpart "$(curl -u guest:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$XML" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created"
|
||||||
|
|
||||||
new "Set NACM using POST"
|
|
||||||
expectpart "$(curl -u guest:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$NACM" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#----------- First get
|
#----------- First get
|
||||||
|
|
@ -204,39 +193,36 @@ EOF
|
||||||
# Run a lot of tests with different settings of default read/write/exec
|
# Run a lot of tests with different settings of default read/write/exec
|
||||||
# Outer loop either starts from startup or inits config via restconf POST
|
# Outer loop either starts from startup or inits config via restconf POST
|
||||||
for db in startup init; do
|
for db in startup init; do
|
||||||
new "nacm enabled and all defaults permit"
|
new "1. nacm enabled and all defaults permit"
|
||||||
testrun true permit permit permit 0 0 0 $db 0
|
testrun true permit permit permit 0 0 0 $db
|
||||||
|
|
||||||
new "nacm disabled and all defaults permit"
|
new "2. nacm disabled and all defaults permit"
|
||||||
testrun false permit permit permit 0 0 0 $db 0
|
testrun false permit permit permit 0 0 0 $db
|
||||||
|
|
||||||
new "nacm disabled and all defaults deny"
|
new "3. nacm disabled and all defaults deny"
|
||||||
testrun false deny deny deny 0 0 0 $db 0
|
testrun false deny deny deny 0 0 0 $db
|
||||||
|
|
||||||
new "nacm enabled, all defaults deny (expect fail)"
|
new "4. nacm enabled, all defaults deny (expect fail)"
|
||||||
testrun true deny deny deny 1 1 1 $db 0
|
testrun true deny deny deny 1 1 1 $db
|
||||||
|
|
||||||
new "nacm enabled, exec default deny - read permit (expect fail)"
|
new "5. nacm enabled, exec default deny - read permit (expect fail)"
|
||||||
testrun true permit deny deny 1 1 1 $db 0
|
testrun true permit deny deny 1 1 1 $db
|
||||||
|
|
||||||
new "nacm enabled, exec default deny - write permit (expect fail)"
|
new "6. nacm enabled, exec default deny - write permit (expect fail)"
|
||||||
testrun true deny permit deny 1 1 1 $db 0
|
testrun true deny permit deny 1 1 1 $db
|
||||||
|
|
||||||
new "nacm enabled, exec default deny read/write permit (expect fail)"
|
new "7. nacm enabled, exec default deny read/write permit (expect fail)"
|
||||||
testrun true permit permit deny 1 1 1 $db 0
|
testrun true permit permit deny 1 1 1 $db
|
||||||
|
|
||||||
new "nacm enabled, exec default permit, all others deny (expect fail)"
|
new "8. nacm enabled, exec default permit, all others deny (expect fail)"
|
||||||
testrun true deny deny permit 2 1 2 $db 0
|
testrun true deny deny permit 2 1 2 $db
|
||||||
|
|
||||||
new "nacm enabled, exec default permit, read permit (expect fail)"
|
new "9. nacm enabled, exec default permit, read permit (expect fail)"
|
||||||
testrun true permit deny permit 0 1 3 $db 0 # This is yang default
|
testrun true permit deny permit 0 1 3 $db # This is yang default
|
||||||
|
|
||||||
new "nacm enabled, with default values (no settings - should be same as previous)"
|
new "10. nacm enabled, exec default permit, write permit (expect fail)"
|
||||||
# note last 1 means nullify all default values)
|
testrun true deny permit permit 2 0 2 $db
|
||||||
testrun true xxx xxx xxx 0 1 3 init 1
|
|
||||||
|
|
||||||
new "nacm enabled, exec default permit, write permit (expect fail)"
|
|
||||||
testrun true deny permit permit 2 0 2 $db 0
|
|
||||||
done
|
done
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,8 @@ module nacm-example{
|
||||||
}
|
}
|
||||||
prefix nacm;
|
prefix nacm;
|
||||||
container authentication {
|
container authentication {
|
||||||
description "Example code for enabling www basic auth and some example
|
presence "To keep this from auto-expanding";
|
||||||
|
description "Example code for enabling www basic auth and some example
|
||||||
users";
|
users";
|
||||||
leaf basic_auth{
|
leaf basic_auth{
|
||||||
description "Basic user / password authentication as in HTTP basic auth";
|
description "Basic user / password authentication as in HTTP basic auth";
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,6 @@ module scaling{
|
||||||
yang-version 1.1;
|
yang-version 1.1;
|
||||||
namespace "urn:example:clixon";
|
namespace "urn:example:clixon";
|
||||||
prefix ex;
|
prefix ex;
|
||||||
import "clixon-config" {
|
|
||||||
prefix cc;
|
|
||||||
}
|
|
||||||
container x {
|
container x {
|
||||||
list y {
|
list y {
|
||||||
key "a";
|
key "a";
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_NACM_DISABLED_ON_EMPTY>true</CLICON_NACM_DISABLED_ON_EMPTY>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
|
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
|
||||||
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Create running-db containin the interface "run" OK
|
# Create running-db containin the interface "run" OK
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,11 @@ module interfaces{
|
||||||
reference
|
reference
|
||||||
"RFC 2863: The Interfaces Group MIB";
|
"RFC 2863: The Interfaces Group MIB";
|
||||||
}
|
}
|
||||||
|
leaf foo{
|
||||||
|
description "Should not appear";
|
||||||
|
type string;
|
||||||
|
default "bar";
|
||||||
|
}
|
||||||
container interfaces {
|
container interfaces {
|
||||||
description
|
description
|
||||||
"Interface configuration parameters.";
|
"Interface configuration parameters.";
|
||||||
|
|
@ -52,8 +57,11 @@ module interfaces{
|
||||||
leaf description {
|
leaf description {
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
leaf foo{
|
||||||
|
description "Should not appear";
|
||||||
|
type string;
|
||||||
|
default "bar";
|
||||||
|
}
|
||||||
leaf type {
|
leaf type {
|
||||||
type string;
|
type string;
|
||||||
mandatory true;
|
mandatory true;
|
||||||
|
|
@ -124,6 +132,11 @@ module interfaces{
|
||||||
reference
|
reference
|
||||||
"RFC 2863: The Interfaces Group MIB";
|
"RFC 2863: The Interfaces Group MIB";
|
||||||
}
|
}
|
||||||
|
leaf foo{
|
||||||
|
description "Should not appear";
|
||||||
|
type string;
|
||||||
|
default "fie";
|
||||||
|
}
|
||||||
container interfaces {
|
container interfaces {
|
||||||
description
|
description
|
||||||
"Interface configuration parameters.";
|
"Interface configuration parameters.";
|
||||||
|
|
@ -133,6 +146,11 @@ module interfaces{
|
||||||
leaf name {
|
leaf name {
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
leaf foo{
|
||||||
|
description "Should not appear";
|
||||||
|
type string;
|
||||||
|
default "bar";
|
||||||
|
}
|
||||||
container docs{
|
container docs{
|
||||||
description "Original description is wrapped and renamed";
|
description "Original description is wrapped and renamed";
|
||||||
leaf descr {
|
leaf descr {
|
||||||
|
|
@ -290,7 +308,6 @@ XML='<interfaces xmlns="urn:example:interfaces"><interface><name>e0</name><type>
|
||||||
ALL="<config>$MODSTATE$XML</config>"
|
ALL="<config>$MODSTATE$XML</config>"
|
||||||
|
|
||||||
# -u means trigger example upgrade
|
# -u means trigger example upgrade
|
||||||
new "test params: -s startup -f $cfg -- -u"
|
|
||||||
|
|
||||||
# kill old backend (if any)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
|
|
@ -301,6 +318,7 @@ fi
|
||||||
new "start backend -s startup -f $cfg -q -- -u"
|
new "start backend -s startup -f $cfg -q -- -u"
|
||||||
output=$(sudo $clixon_backend -F -D $DBG -s startup -f $cfg -q -- -u)
|
output=$(sudo $clixon_backend -F -D $DBG -s startup -f $cfg -q -- -u)
|
||||||
#echo "$output"
|
#echo "$output"
|
||||||
|
|
||||||
if [ "$ALL" != "$output" ]; then
|
if [ "$ALL" != "$output" ]; then
|
||||||
err "$ALL" "$output"
|
err "$ALL" "$output"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,8 @@ module clixon-config {
|
||||||
description
|
description
|
||||||
"Added: CLICON_CLI_LINES_DEFAULT
|
"Added: CLICON_CLI_LINES_DEFAULT
|
||||||
Added enum HIDE to CLICON_CLI_GENMODEL
|
Added enum HIDE to CLICON_CLI_GENMODEL
|
||||||
Added CLICON_SSL_SERVER_CERT
|
Added CLICON_SSL_SERVER_CERT, CLICON_SSL_SERVER_KEY, CLICON_SSL_CA_CERT
|
||||||
Added CLICON_SSL_SERVER_KEY
|
Added CLICON_NACM_DISABLED_ON_EMPTY";
|
||||||
Added CLICON_SSL_CA_CERT";
|
|
||||||
}
|
}
|
||||||
revision 2020-04-23 {
|
revision 2020-04-23 {
|
||||||
description
|
description
|
||||||
|
|
@ -719,6 +718,20 @@ module clixon-config {
|
||||||
exact for example, this user must exist and be used, otherwise
|
exact for example, this user must exist and be used, otherwise
|
||||||
another user (such as root or www) can pose as it.";
|
another user (such as root or www) can pose as it.";
|
||||||
}
|
}
|
||||||
|
leaf CLICON_NACM_DISABLED_ON_EMPTY {
|
||||||
|
type boolean;
|
||||||
|
default false;
|
||||||
|
description
|
||||||
|
"RFC 8341 and ietf-netconf-acm@2018-02-14.yang defines enable-nacm as true by
|
||||||
|
default. Since also write-default is deny by default it leads to that empty
|
||||||
|
configs can not be edited.
|
||||||
|
This means that a startup config must always have a NACM configuration or
|
||||||
|
that the NACM recovery session is used to edit an empty config.
|
||||||
|
If this option is set, Clixon disables NACM if a datastore is empty on load.
|
||||||
|
Note that it only makes the check on initial load, not if a store 'becomes'
|
||||||
|
empty, but enables a clixon nacm system to start empty and add an NACM
|
||||||
|
config after boot.";
|
||||||
|
}
|
||||||
leaf CLICON_MODULE_LIBRARY_RFC7895 {
|
leaf CLICON_MODULE_LIBRARY_RFC7895 {
|
||||||
type boolean;
|
type boolean;
|
||||||
default true;
|
default true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue