diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13d7981c..cfa85e95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,15 @@
## 3.8.0 (Upcoming)
### Major New features
+* YANG Features
+ * Yang 1.1 feature and if-feature according to RFC 7950 7.20.1 and 7.20.2.
+ * Features are declared via CLICON_FEATURE in the configuration file. Examples showing enabling (1) a specific feature; (2) all features in a module; (3) all features in all modules:
+```
+ ietf-routing:router-id
+ ietf-routing:*
+ *:*
+```
+ * logical combination of features not implemented, eg if-feature "not foo or bar and baz";
* YANG Module Library support
* According to RFC 7895 and implemented by ietf-yang-library.yang
* Supported: module, name, revision, namespace
@@ -44,11 +53,13 @@
### Minor changes
+* New function: clicon_conf_xml() returns configuration tree
* Obsoleted COMPAT_CLIV and COMPAT_XSL that were optional in 3.7
* Added timeout option -t for clixon_netconf - quit after max time.
* Added -l option for clixon_backend for directing syslog to stderr or stdout if running in foreground
### Corrected Bugs
+* Identity without any identityref:s caused SEGV
* Memory error in backend transaction revert
* Set dir /www-data with www-data as owner, see https://github.com/clicon/clixon/issues/37
diff --git a/README.md b/README.md
index e422bb35..b821d658 100644
--- a/README.md
+++ b/README.md
@@ -108,10 +108,9 @@ used to generate an interactive CLI, netconf and restconf clients. It
also manages an XML datastore.
Clixon mainly follows [YANG 1.0 RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) with some exceptions:
-- conformance: feature, if-feature, deviation
+- conformance: deviation
- list features: min/max-elements, unique
- action statements
-- notifications
The aim is also to cover new features in YANG 1.1 [YANG RFC 7950](https://www.rfc-editor.org/rfc/rfc7950.txt)
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 43e0cdbb..2cc110f2 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -81,13 +81,16 @@
static int
backend_terminate(clicon_handle h)
{
- yang_spec *yspec;
- char *pidfile = clicon_backend_pidfile(h);
- char *sockpath = clicon_sock(h);
+ yang_spec *yspec;
+ char *pidfile = clicon_backend_pidfile(h);
+ char *sockpath = clicon_sock(h);
+ cxobj *x;
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
+ if ((x = clicon_conf_xml(h)) != NULL)
+ xml_free(x);
clixon_plugin_exit(h);
/* Delete all backend plugin RPC callbacks */
rpc_callback_delete_all();
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index 98dbc3e3..4876fcbb 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -86,7 +86,7 @@ struct backend_handle {
int bh_magic; /* magic (HDR)*/
clicon_hash_t *bh_copt; /* clicon option list (HDR) */
clicon_hash_t *bh_data; /* internal clicon data (HDR) */
- event_stream_t *ch_stream; /* notification streams, see clixon_stream.[ch] */
+ event_stream_t *bh_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */
struct client_entry *bh_ce_list; /* The client list */
diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c
index 4ffb55ca..22f76883 100644
--- a/apps/cli/cli_generate.c
+++ b/apps/cli/cli_generate.c
@@ -232,19 +232,21 @@ yang2cli_var_sub(clicon_handle h,
cprintf(cb, ">");
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
- cprintf(cb, "|<%s:%s choice:", ys->ys_argument, cvtypestr);
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) != NULL &&
(ybaseid = yang_find_identity(ys, ybaseref->ys_argument)) != NULL){
- i = 0;
- while ((cv = cvec_each(ybaseid->ys_cvec, cv)) != NULL){
- if (i++)
- cprintf(cb, "|");
- name = strdup(cv_name_get(cv));
- if ((id=strchr(name, ':')) != NULL)
- *id = '\0';
- cprintf(cb, "%s:%s", name, id+1);
- if (name)
- free(name);
+ if (cvec_len(ybaseid->ys_cvec) > 0){
+ cprintf(cb, "|<%s:%s choice:", ys->ys_argument, cvtypestr);
+ i = 0;
+ while ((cv = cvec_each(ybaseid->ys_cvec, cv)) != NULL){
+ if (i++)
+ cprintf(cb, "|");
+ name = strdup(cv_name_get(cv));
+ if ((id=strchr(name, ':')) != NULL)
+ *id = '\0';
+ cprintf(cb, "%s:%s", name, id+1);
+ if (name)
+ free(name);
+ }
}
}
}
@@ -792,7 +794,7 @@ yang2cli(clicon_handle h,
if (yang2cli_stmt(h, ymod, cbuf, gt, 0) < 0)
goto done;
}
- clicon_debug(1, "%s: buf\n%s\n", __FUNCTION__, cbuf_get(cbuf));
+ clicon_debug(0, "%s: buf\n%s\n", __FUNCTION__, cbuf_get(cbuf));
/* Parse the buffer using cligen parser. XXX why this?*/
if ((globals = cvec_new(0)) == NULL)
goto done;
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index f5881713..c63b6e98 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -79,11 +79,14 @@
static int
cli_terminate(clicon_handle h)
{
- yang_spec *yspec;
+ yang_spec *yspec;
+ cxobj *x;
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
+ if ((x = clicon_conf_xml(h)) != NULL)
+ xml_free(x);
cli_plugin_finish(h);
cli_handle_exit(h);
clicon_log_exit();
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index f34f572c..7617beac 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -269,13 +269,16 @@ send_hello(int s)
static int
netconf_terminate(clicon_handle h)
{
- yang_spec *yspec;
-
+ yang_spec *yspec;
+ cxobj *x;
+
clixon_plugin_exit(h);
rpc_callback_delete_all();
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
+ if ((x = clicon_conf_xml(h)) != NULL)
+ xml_free(x);
event_exit();
clicon_handle_exit(h);
clicon_log_exit();
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index 3bd4584c..a38cf164 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -445,13 +445,16 @@ api_restconf(clicon_handle h,
static int
restconf_terminate(clicon_handle h)
{
- yang_spec *yspec;
+ yang_spec *yspec;
+ cxobj *x;
clixon_plugin_exit(h);
rpc_callback_delete_all();
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
+ if ((x = clicon_conf_xml(h)) != NULL)
+ xml_free(x);
clicon_handle_exit(h);
clicon_log_exit();
return 0;
diff --git a/doc/FAQ.md b/doc/FAQ.md
index 2d1800b3..11e68214 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -100,6 +100,19 @@ You can change where CLixon looks for the configuration FILE as follows:
- Provide --sysconfig=
when configuring then FILE is /etc/clixon.xml
- FILE is /usr/local/etc/clixon.xml
+## How do I enable Yang features?
+
+Yang models have features, and parts of a specification can be
+conditional using the if-feature statement. In Clixon, features are
+enabled in the configuration file using .
+
+The example below shows enabling a specific feature; enabling all features in module; and enabling all features in all modules, respectively:
+```
+ ietf-routing:router-id
+ ietf-routing:*
+ *:*
+```
+
## Can I run Clixon as docker containers?
Yes, the example works as docker containers as well. There should be a
diff --git a/example/example.xml b/example/example.xml
index 337048bb..c222f6ce 100644
--- a/example/example.xml
+++ b/example/example.xml
@@ -1,5 +1,6 @@
/usr/local/etc/example.xml
+ *:*
/usr/local/share/example/yang
example
example
diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h
index 738b2249..3eef5507 100644
--- a/lib/clixon/clixon_options.h
+++ b/lib/clixon/clixon_options.h
@@ -164,6 +164,9 @@ int clicon_quiet_mode_set(clicon_handle h, int val);
yang_spec * clicon_dbspec_yang(clicon_handle h);
int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
+cxobj *clicon_conf_xml(clicon_handle h);
+int clicon_conf_xml_set(clicon_handle h, cxobj *x);
+
plghndl_t clicon_xmldb_plugin_get(clicon_handle h);
int clicon_xmldb_plugin_set(clicon_handle h, plghndl_t handle);
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index 465fa1a6..3996b806 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -259,7 +259,6 @@ char *yang_find_myprefix(yang_stmt *ys);
int yang_order(yang_stmt *y);
int yang_print(FILE *f, yang_node *yn);
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
-int ys_populate(yang_stmt *ys, void *arg);
yang_stmt *yang_parse_file(int fd, const char *name, yang_spec *ysp);
int yang_parse(clicon_handle h, const char *filename,
const char *module, const char *dir,
diff --git a/lib/src/clixon_handle.c b/lib/src/clixon_handle.c
index 940d7cf6..64d58628 100644
--- a/lib/src/clixon_handle.c
+++ b/lib/src/clixon_handle.c
@@ -129,13 +129,12 @@ int
clicon_handle_exit(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
- clicon_hash_t *copt;
- clicon_hash_t *data;
+ clicon_hash_t *ha;
- if ((copt = clicon_options(h)) != NULL)
- hash_free(copt);
- if ((data = clicon_data(h)) != NULL)
- hash_free(data);
+ if ((ha = clicon_options(h)) != NULL)
+ hash_free(ha);
+ if ((ha = clicon_data(h)) != NULL)
+ hash_free(ha);
stream_delete_all(clicon_stream(h));
free(ch);
return 0;
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index d44f4df6..2070cda2 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -64,8 +64,8 @@
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
-#include "clixon_options.h"
#include "clixon_xml.h"
+#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
@@ -116,12 +116,16 @@ clicon_option_dump(clicon_handle h,
}
/*! Read filename and set values to global options registry. XML variant.
- * @see clicon_option_readfile
+ *
+ * @param[out] xconfig Pointer to xml config tree. Should be freed by caller
+ * @retval 0 OK
+ * @retval -1 Error
*/
-static int
-clicon_option_readfile_xml(clicon_hash_t *copt,
- const char *filename,
- yang_spec *yspec)
+static int
+parse_configfile(clicon_handle h,
+ const char *filename,
+ yang_spec *yspec,
+ cxobj **xconfig)
{
struct stat st;
FILE *f = NULL;
@@ -132,6 +136,7 @@ clicon_option_readfile_xml(clicon_hash_t *copt,
cxobj *x = NULL;
char *name;
char *body;
+ clicon_hash_t *copt = clicon_options(h);
if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified");
@@ -168,14 +173,22 @@ clicon_option_readfile_xml(clicon_hash_t *copt,
while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
body = xml_body(x);
- if (name && body &&
- (hash_add(copt,
- name,
- body,
- strlen(body)+1)) == NULL)
+ if (name==NULL || body == NULL){
+ clicon_log(LOG_WARNING, "%s option NULL: name:%s body:%s",
+ __FUNCTION__, name, body);
+ continue;
+ }
+ if (strcmp(name,"CLICON_FEATURE")==0)
+ continue;
+ if (hash_add(copt,
+ name,
+ body,
+ strlen(body)+1) == NULL)
goto done;
}
retval = 0;
+ *xconfig = xt;
+ xt = NULL;
done:
if (xt)
xml_free(xt);
@@ -199,6 +212,7 @@ clicon_options_main(clicon_handle h)
char *suffix;
char xml = 0; /* Configfile is xml, otherwise legacy */
yang_spec *yspec = NULL;
+ cxobj *xconfig = NULL;
/*
* Set configure file if not set by command-line above
@@ -213,29 +227,30 @@ clicon_options_main(clicon_handle h)
suffix++;
xml = strcmp(suffix, "xml") == 0;
}
- if (xml){ /* Read clixon yang file */
- if ((yspec = yspec_new()) == NULL)
- goto done;
- if (yang_parse(h, NULL, "clixon-config", CLIXON_DATADIR, NULL, yspec) < 0)
- goto done;
- /* Read configfile */
- if (clicon_option_readfile_xml(copt, configfile, yspec) < 0)
- goto done;
- /* Specific option handling */
- if (clicon_option_bool(h, "CLICON_XML_SORT") == 1)
- xml_child_sort = 1;
- else
- xml_child_sort = 0;
- }
- else {
+ if (xml == 0){
clicon_err(OE_CFG, 0, "%s: suffix %s not recognized (Run ./configure --with-config-compat?)", configfile, suffix);
goto done;
}
+ /* Parse clixon yang spec */
+ if ((yspec = yspec_new()) == NULL)
+ goto done;
+ if (yang_parse(h, NULL, "clixon-config", CLIXON_DATADIR, NULL, yspec) < 0)
+ goto done;
+ /* Read configfile */
+ if (parse_configfile(h, configfile, yspec, &xconfig) < 0)
+ goto done;
+ if (xml_rootchild(xconfig, 0, &xconfig) < 0)
+ goto done;
+ clicon_conf_xml_set(h, xconfig);
+ /* Specific option handling */
+ if (clicon_option_bool(h, "CLICON_XML_SORT") == 1)
+ xml_child_sort = 1;
+ else
+ xml_child_sort = 0;
retval = 0;
done:
if (yspec) /* The clixon yang-spec is not used after this */
yspec_free(yspec);
-
return retval;
}
@@ -571,6 +586,38 @@ clicon_dbspec_yang_set(clicon_handle h,
return 0;
}
+/*! Get YANG specification for Clixon system options and features
+ * Must use hash functions directly since they are not strings.
+ */
+cxobj *
+clicon_conf_xml(clicon_handle h)
+{
+ clicon_hash_t *cdat = clicon_data(h);
+ size_t len;
+ void *p;
+
+ if ((p = hash_value(cdat, "clixon_conf", &len)) != NULL)
+ return *(cxobj **)p;
+ return NULL;
+}
+
+/*! Set YANG specification for Clixon system options and features
+ * ys must be a malloced pointer
+ */
+int
+clicon_conf_xml_set(clicon_handle h,
+ cxobj *x)
+{
+ clicon_hash_t *cdat = clicon_data(h);
+
+ /* It is the pointer to x that should be copied by hash,
+ * so we send a ptr to the ptr to indicate what to copy.
+ */
+ if (hash_add(cdat, "clixon_conf", &x, sizeof(x)) == NULL)
+ return -1;
+ return 0;
+}
+
/*! Get xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
plghndl_t
clicon_xmldb_plugin_get(clicon_handle h)
@@ -664,7 +711,6 @@ clicon_xmldb_handle_set(clicon_handle h,
return 0;
}
-
/*! Get authorized user name
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c
index f5246109..2798e42b 100644
--- a/lib/src/clixon_proto_client.c
+++ b/lib/src/clixon_proto_client.c
@@ -61,8 +61,8 @@
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
-#include "clixon_options.h"
#include "clixon_xml.h"
+#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_string.h"
#include "clixon_xpath_ctx.h"
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 4576fc82..ce9716b2 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -80,8 +80,8 @@
#include "clixon_string.h"
#include "clixon_yang.h"
#include "clixon_yang_type.h"
-#include "clixon_options.h"
#include "clixon_xml.h"
+#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 8adbfdc1..e8c30ede 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -672,6 +672,10 @@ yang_find_topnode(yang_spec *ysp,
* @param[in] ys Yang statement
* @retval NULL Not found
* @retval prefix Prefix as char* pointer into yang tree
+ * @code
+ * char *myprefix;
+ * myprefix = yang_find_myprefix(ys);
+ * @endcode
*/
char *
yang_find_myprefix(yang_stmt *ys)
@@ -862,6 +866,15 @@ yarg_prefix(yang_stmt *ys)
* @param[out] id Malloced identifier.
* @retval 0 OK
* @retval -1 Error
+ * @code
+ * char *prefix = NULL;
+ * char *id = NULL;
+ * if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
+ * goto done;
+ * if (prefix)
+ * free(prefix);
+ * if (id)
+ * free(id);
* @note caller need to free id and prefix after use
*/
int
@@ -1284,11 +1297,11 @@ ys_populate_type(yang_stmt *ys,
* Do this recursively
* @param[in] ys The yang identity to populate.
* @param[in] arg If set contains a derived identifier
- * @see validate_identityref which in runtime validates actual valoues
+ * @see validate_identityref which in runtime validates actual values
*/
static int
ys_populate_identity(yang_stmt *ys,
- void *arg)
+ char *idref)
{
int retval = -1;
yang_stmt *yc = NULL;
@@ -1298,7 +1311,6 @@ ys_populate_identity(yang_stmt *ys,
char *baseid;
char *prefix = NULL;
cbuf *cb = NULL;
- char *idref = (char*)arg;
char *p;
if (idref == NULL){
@@ -1358,31 +1370,91 @@ ys_populate_identity(yang_stmt *ys,
return retval;
}
+/*! Populate yang feature statement - set cv to 1 if enabled
+ *
+ * @param[in] ys Feature yang statement to populate.
+ * @param[in] h Clicon handle
+ */
+static int
+ys_populate_feature(clicon_handle h,
+ yang_stmt *ys)
+{
+ int retval = -1;
+ cxobj *x;
+ yang_stmt *ymod;
+ int found = 0;
+ cg_var *cv;
+ char *module;
+ char *feature;
+ cxobj *x1;
+
+ /* Eg, when parsing the config xml itself */
+ if ((x = clicon_conf_xml(h)) == NULL)
+ goto ok;
+ if ((ymod = ys_module(ys)) == NULL){
+ clicon_err(OE_YANG, 0, "module not found");
+ goto done;
+ }
+ module = ymod->ys_argument;
+ feature = ys->ys_argument;
+ x1 = NULL;
+ while ((x1 = xml_child_each(x, x1, CX_ELMNT)) != NULL && found == 0) {
+ char *m = NULL;
+ char *f = NULL;
+ if (strcmp(xml_name(x1), "CLICON_FEATURE") != 0)
+ continue;
+ /* get m and f from configuration feature rules */
+ if (yang_nodeid_split(xml_body(x1), &m, &f) < 0)
+ goto done;
+ if (m && f &&
+ (strcmp(m,"*")==0 ||
+ strcmp(m, module)==0) &&
+ (strcmp(f,"*")==0 ||
+ strcmp(f, feature)==0))
+ found = 1;
+ if (m) free(m);
+ if (f) free(f);
+ }
+ if ((cv = cv_new(CGV_BOOL)) == NULL){
+ clicon_err(OE_YANG, errno, "cv_new");
+ goto done;
+ }
+ cv_name_set(cv, feature);
+ cv_bool_set(cv, found);
+ clicon_debug(1, "%s %s:%s %d", __FUNCTION__, module, feature, found);
+ ys->ys_cv = cv;
+ ok:
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
*
* We do this in 2nd pass after complete parsing to be sure to have a complete parse-tree
* See ys_parse_sub for first pass and what can be assumed
* After this pass, cv:s are set for LEAFs and LEAF-LISTs
*/
-int
+static int
ys_populate(yang_stmt *ys,
void *arg)
{
int retval = -1;
-
+ // clicon_handle h = (clicon_handle)arg;
+
switch(ys->ys_keyword){
case Y_LEAF:
case Y_LEAF_LIST:
- if (ys_populate_leaf(ys, arg) < 0)
+ if (ys_populate_leaf(ys, NULL) < 0)
goto done;
break;
case Y_LIST:
- if (ys_populate_list(ys, arg) < 0)
+ if (ys_populate_list(ys, NULL) < 0)
goto done;
break;
case Y_RANGE:
case Y_LENGTH:
- if (ys_populate_range(ys, arg) < 0)
+ if (ys_populate_range(ys, NULL) < 0)
goto done;
break;
case Y_MANDATORY: /* call yang_mandatory() to check if set */
@@ -1391,11 +1463,11 @@ ys_populate(yang_stmt *ys,
goto done;
break;
case Y_TYPE:
- if (ys_populate_type(ys, arg) < 0)
+ if (ys_populate_type(ys, NULL) < 0)
goto done;
break;
case Y_IDENTITY:
- if (ys_populate_identity(ys, arg) < 0)
+ if (ys_populate_identity(ys, NULL) < 0)
goto done;
break;
default:
@@ -1406,7 +1478,6 @@ ys_populate(yang_stmt *ys,
return retval;
}
-
/*! Resolve a grouping name from a point in the yang tree
* @retval 0 OK, but ygrouping determines if a grouping was resolved or not
* @retval -1 Error, with clicon_err called
@@ -1852,6 +1923,8 @@ yang_parse_module(const char *module,
if ((ymod = yang_parse_filename(cbuf_get(fbuf), ysp)) == NULL)
goto done;
done:
+ if (fbuf)
+ cbuf_free(fbuf);
return ymod; /* top-level (sub)module */
}
@@ -1894,7 +1967,7 @@ yang_parse_recurse(yang_stmt *ymod,
subrevision = yrev->ys_argument;
else
subrevision = NULL;
- if (yang_find((yang_node*)ysp, Y_MODULE, submodule) == NULL)
+ if (yang_find((yang_node*)ysp, Y_MODULE, submodule) == NULL){
/* recursive call */
if ((subymod = yang_parse_module(submodule, dir, subrevision, ysp)) == NULL)
goto done;
@@ -1902,6 +1975,7 @@ yang_parse_recurse(yang_stmt *ymod,
ymod = NULL;
goto done;
}
+ }
}
retval = 0;
done:
@@ -1951,6 +2025,83 @@ ys_schemanode_check(yang_stmt *ys,
return retval;
}
+/*! Find feature and if-feature nodes, check features and remove disabled nodes
+ * @retval -1 Error
+ * @retval 0 Feature not enabled: remove yt
+ * @retval 1 OK
+ * @note On return 1 the over-lying function need to remove yt from its parent
+ * @note cannot use yang_apply here since child-list is modified (destructive)
+ */
+static int
+yang_features(clicon_handle h,
+ yang_stmt *yt)
+{
+ int retval = -1;
+ int i;
+ int j;
+ yang_stmt *ys = NULL;
+ char *prefix = NULL;
+ char *feature = NULL;
+ yang_stmt *ymod; /* module yang node */
+ yang_stmt *yfeat; /* feature yang node */
+
+ i = 0;
+ while (iys_len){ /* Note, children may be removed */
+ ys = yt->ys_stmt[i];
+ if (ys->ys_keyword == Y_IF_FEATURE){
+ if (yang_nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
+ goto done;
+ /* Specifically need to handle? strcmp(prefix, myprefix)) */
+ if (prefix == NULL)
+ ymod = ys_module(ys);
+ else
+ ymod = yang_find_module_by_prefix(yt, prefix);
+
+ /* Check if feature exists, and is set, otherwise remove */
+ if ((yfeat = yang_find((yang_node*)ymod, Y_FEATURE, feature)) == NULL ||
+ yfeat->ys_cv == NULL || !cv_bool_get(yfeat->ys_cv)){
+ retval = 0; /* feature not enabled */
+ goto done;
+ }
+ if (prefix){
+ free(prefix);
+ prefix = NULL;
+ }
+ if (feature){
+ free(feature);
+ feature = NULL;
+ }
+ }
+ else
+ if (ys->ys_keyword == Y_FEATURE){
+ if (ys_populate_feature(h, ys) < 0)
+ goto done;
+ } else switch (yang_features(h, ys)){
+ case -1: /* error */
+ goto done;
+ break;
+ case 0: /* disabled: remove ys */
+ for (j=i+1; jys_len; j++)
+ yt->ys_stmt[j-1] = yt->ys_stmt[j];
+ yt->ys_len--;
+ yt->ys_stmt[yt->ys_len] = NULL;
+ ys_free(ys);
+ continue; /* Don't increment i */
+ break;
+ default: /* ok */
+ break;
+ }
+ i++;
+ }
+ retval = 1;
+ done:
+ if (prefix)
+ free(prefix);
+ if (feature)
+ free(feature);
+ return retval;
+}
+
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
*
* @param[in] h CLICON handle
@@ -2001,12 +2152,18 @@ yang_parse(clicon_handle h,
if (yang_parse_recurse(ymod, dir, ysp) < 0)
goto done;
- /* Step 2: Go through parse tree and populate it with cv types */
+ /* Step 2: check features: check if enabled and remove disabled features */
+ for (i=modnr; iyp_len; i++) /* XXX */
+ if (yang_features(h, ysp->yp_stmt[i]) < 0)
+ goto done;
+
+ /* Step 3: Go through parse tree and populate it with cv types */
for (i=modnr; iyp_len; i++)
- if (yang_apply((yang_node*)ysp->yp_stmt[i], -1, ys_populate, NULL) < 0)
+ if (yang_apply((yang_node*)ysp->yp_stmt[i], -1, ys_populate, (void*)h) < 0)
goto done;
- /* Step 3: Resolve all types: populate type caches. Requires eg length/range cvecs
+
+ /* Step 4: Resolve all types: populate type caches. Requires eg length/range cvecs
* from ys_populate step
*/
for (i=modnr; iyp_len; i++)
@@ -2017,18 +2174,18 @@ yang_parse(clicon_handle h,
grouping/uses and unfold all macros into a single tree as they are used.
*/
- /* Step 4: Macro expansion of all grouping/uses pairs. Expansion needs marking */
+ /* Step 5: Macro expansion of all grouping/uses pairs. Expansion needs marking */
for (i=modnr; iyp_len; i++){
if (yang_expand((yang_node*)ysp->yp_stmt[i]) < 0)
goto done;
yang_apply((yang_node*)ysp->yp_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
}
- /* Step 4: Top-level augmentation of all modules XXX: only new modules? */
+ /* Step 6: Top-level augmentation of all modules XXX: only new modules? */
if (yang_augment_spec(ysp) < 0)
goto done;
- /* sanity check of schemanode references, need more here */
+ /* Step 7: sanity check of schemanode references, need more here */
for (i=modnr; iyp_len; i++)
if (yang_apply((yang_node*)ysp->yp_stmt[i], -1, ys_schemanode_check, NULL) < 0)
goto done;
@@ -2486,11 +2643,8 @@ yang_spec_parse_module(clicon_handle h,
clicon_err(OE_YANG, EINVAL, "yang dir not set");
goto done;
}
- if (yang_parse(h, NULL, module, dir, revision, yspec) < 0){
- yspec_free(yspec);
- yspec = NULL;
+ if (yang_parse(h, NULL, module, dir, revision, yspec) < 0)
goto done;
- }
retval = 0;
done:
return retval;
@@ -2521,11 +2675,8 @@ yang_spec_parse_file(clicon_handle h,
clicon_err(OE_YANG, EINVAL, "yang dir not set");
goto done;
}
- if (yang_parse(h, filename, NULL, dir, NULL, yspec) < 0){
- yspec_free(yspec);
- yspec = NULL;
+ if (yang_parse(h, filename, NULL, dir, NULL, yspec) < 0)
goto done;
- }
retval = 0;
done:
return retval;
diff --git a/test/test_cli.sh b/test/test_cli.sh
index 716fb9d3..5b2574bf 100755
--- a/test/test_cli.sh
+++ b/test/test_cli.sh
@@ -42,7 +42,6 @@ sudo $clixon_backend -s init -f $cfg
if [ $? -ne 0 ]; then
err
fi
-new "cli tests"
new "cli configure top"
expectfn "$clixon_cli -1 -f $cfg set interfaces" 0 "^$"
diff --git a/test/test_event.sh b/test/test_event.sh
index 61c198b7..ce403bc7 100755
--- a/test/test_event.sh
+++ b/test/test_event.sh
@@ -131,4 +131,4 @@ if [ -n "$pid" ]; then
sudo kill $pid
fi
-#rm -rf $dir
+rm -rf $dir
diff --git a/yang/clixon-config@2018-04-30.yang b/yang/clixon-config@2018-04-30.yang
index 676276d1..1535a156 100644
--- a/yang/clixon-config@2018-04-30.yang
+++ b/yang/clixon-config@2018-04-30.yang
@@ -117,6 +117,16 @@ module clixon-config {
}
}
container config {
+ leaf-list CLICON_FEATURE {
+ description
+ "Supported features as used by YANG feature/if-feature
+ value is: :, where and
+ are either names, or the special character '*'.
+ *:an* means enable all features
+ :* means enable all features in the specified module
+ *: means enable the specific feature in all modules";
+ type string;
+ }
leaf CLICON_CONFIGFILE{
type string;
description