diff --git a/CHANGELOG.md b/CHANGELOG.md
index a70162a9..d71d1ec7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,15 +12,23 @@
* Support of submodule, include and belongs-to.
* Openconfig yang specs parsed: https://github.com/openconfig/public
* Improved unknown handling
- * Configure option `CLICON_YANG_DIR` is changed from a single directory to a path of directories
- * Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
+ * Yang Configure options changed
+ * `CLICON_YANG_DIR` is changed from a single directory to a path of directories
+ * Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
+ * CLICON_YANG_MAIN_FILE Provides a filename with a single module filename.
+ * CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded.
### API changes on existing features (you may need to change your code)
* Yang parser is stricter (see above) which may break parsing of existing yang specs.
* Yang parser functions have changed signatures. Please check the source if you call these functions.
* Add `/usr/local/share/clixon` to your configuration file, or corresponding CLICON_DATADIR directory for Clixon system yang files.
+* Change all @datamodel:tree to @datamodel in all CLI specification files
+ * If you generate CLI code from the model (CLIXON_CLI_GENMODEL).
+ * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
### Minor changes
+* Removed return value ymodp from yang parse functions (eg yang_parse()).
+* New config option: CLICON_CLI_MODEL_TREENAME defining name of generated syntax tree if CLIXON_CLI_GENMODEL is set.
* XML parser conformance to W3 spec
* Names lexically correct (NCName)
* Syntactically Correct handling of '' (processing instructions) and ' cd doc
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index db66647e..1359e984 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -264,7 +264,7 @@ nacm_load_external(clicon_handle h)
}
if ((yspec = yspec_new()) == NULL)
goto done;
- if (yang_parse(h, NULL, "ietf-netconf-acm", NULL, yspec, NULL) < 0)
+ if (yang_parse(h, NULL, "ietf-netconf-acm", NULL, yspec) < 0)
goto done;
fd = fileno(f);
/* Read configfile */
@@ -527,7 +527,7 @@ main(int argc,
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
yang_spec *yspec = NULL;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
- char *yang_filename = NULL;
+ char *str;
/* In the startup, logs to stderr & syslog and debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@@ -652,8 +652,8 @@ main(int argc,
case 'g': /* config socket group */
clicon_option_str_set(h, "CLICON_SOCK_GROUP", optarg);
break;
- case 'y' :{ /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ case 'y' :{ /* Load yang absolute filename */
+ clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
}
case 'x' :{ /* xmldb plugin */
@@ -750,17 +750,20 @@ main(int argc,
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, yspec, NULL) < 0)
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL)
+ if (yang_spec_parse_file(h, str, yspec) < 0)
+ goto done;
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL)
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ /* 3. Load all modules in a directory */
+ if ((str = clicon_yang_main_dir(h)) != NULL)
+ if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
- }
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_module_revision(h),
- yspec, NULL) < 0)
- goto done;
-
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
@@ -769,11 +772,11 @@ main(int argc,
goto done;
/* Load yang Restconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
- yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec, NULL)< 0)
+ yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
/* Load yang Netconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
- yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec, NULL)< 0)
+ yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
goto done;
/* Set options: database dir and yangspec (could be hidden in connect?)*/
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index feb5f158..b22e1c9f 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -250,15 +250,14 @@ main(int argc, char **argv)
int logclisyntax = 0;
int help = 0;
char *treename = NULL;
- int len;
+ // int len;
int logdst = CLICON_LOG_STDERR;
char *restarg = NULL; /* what remains after options */
int dump_configfile_xml = 0;
yang_spec *yspec;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
struct passwd *pw;
- char *yang_filename = NULL;
- yang_stmt *ymod = NULL; /* Main module */
+ char *str;
/* Defaults */
once = 0;
@@ -390,8 +389,8 @@ main(int argc, char **argv)
case 'L' : /* Debug print dynamic CLI syntax */
logclisyntax++;
break;
- case 'y' :{ /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ case 'y' :{ /* Load yang absolute filename */
+ clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
}
case 'c' :{ /* Overwrite clispec with absolute filename */
@@ -430,16 +429,24 @@ main(int argc, char **argv)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, yspec, &ymod) < 0)
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL){
+ if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_module_revision(h),
- yspec, &ymod) < 0)
- goto done;
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL){
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ }
+ /* 3. Load all modules in a directory */
+ if ((str = clicon_yang_main_dir(h)) != NULL){
+ if (yang_spec_load_dir(h, str, yspec) < 0)
+ goto done;
+ }
+
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
@@ -448,25 +455,22 @@ main(int argc, char **argv)
/* Create tree generated from dataspec. If no other trees exists, this is
* the only one.
+ * The following code creates the tree @datamodel
+ * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR)
+ * using the "tree reference"
+ * syntax, ie @datamodel
+ * But note that yang2cli generates syntax for ALL modules, not just for
+ * .
*/
if (clicon_cli_genmodel(h)){
parse_tree pt = {0,}; /* cli parse tree */
- char *name; /* main module name */
+ char *treeref;
+ treeref = clicon_cli_model_treename(h);
/* Create cli command tree from dbspec */
if (yang2cli(h, yspec, &pt, clicon_cli_genmodel_type(h)) < 0)
goto done;
-
- /* name of main module */
- name = ymod->ys_argument;
-
- len = strlen("datamodel:") + strlen(name) + 1;
- if ((treename = malloc(len)) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- snprintf(treename, len, "datamodel:%s", name);
- cligen_tree_add(cli_cligen(h), treename, pt);
+ cligen_tree_add(cli_cligen(h), treeref, pt);
if (printgen)
cligen_print(stdout, pt, 1);
diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c
index 0fb58736..633c46ae 100644
--- a/apps/cli/cli_plugin.c
+++ b/apps/cli/cli_plugin.c
@@ -209,6 +209,30 @@ clixon_str2fn(char *name,
return NULL;
}
+#ifdef CLICON_CLI_MODEL_TREENAME_PATCH
+/*! Patch all CLI spec calls to @datamodel:tree to @datamodel.
+ * This is a backward compatible fix for 3.9 for CLIgen specification files
+ * using model generation (CLIXON_CLI_GENMODEL).
+ * All new references should use @datamodel (or CLICON_CLI_MODEL_TREENAME).
+ * whereas older code used @datamodel:tree.
+ */
+static int
+mask_datamodel_fn(cg_obj *co,
+ void *arg)
+{
+ char *str = "datamodel:";
+ int len = strlen(str);
+ if (co->co_type == CO_REFERENCE){
+
+ if (strlen(co->co_command) > len &&
+ strncmp(co->co_command, "datamodel:", len)==0){
+ co->co_command[len-1] = '\0';
+ }
+ }
+ return 0;
+}
+#endif /* CLICON_CLI_MODEL_TREENAME_PATCH */
+
/*! Append to syntax mode from file
* @param[in] h Clixon handle
* @param[in] filename Name of file where syntax is specified (in syntax-group dir)
@@ -253,7 +277,10 @@ cli_load_syntax(clicon_handle h,
goto done;
}
fclose(f);
-
+#ifdef CLICON_CLI_MODEL_TREENAME_PATCH
+ if (pt_apply(pt, mask_datamodel_fn, h) < 0)
+ goto done;
+#endif
/* Get CLICON specific global variables */
prompt = cvec_find_str(cvv, "CLICON_PROMPT");
plgnam = cvec_find_str(cvv, "CLICON_PLUGIN");
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index aee1cf80..cc51d966 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -341,7 +341,7 @@ main(int argc,
struct timeval tv = {0,}; /* timeout */
yang_spec *yspec = NULL;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
- char *yang_filename = NULL;
+ char *str;
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
@@ -420,7 +420,7 @@ main(int argc,
clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg);
break;
case 'y' :{ /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
}
case 'U': /* Clixon 'pseudo' user */
@@ -444,16 +444,23 @@ main(int argc,
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, yspec, NULL) < 0)
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL){
+ if (yang_spec_parse_file(h, str, yspec) < 0)
+ goto done;
+ }
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL){
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ }
+ /* 3. Load all modules in a directory */
+ if ((str = clicon_yang_main_dir(h)) != NULL){
+ if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_module_revision(h),
- yspec, NULL) < 0)
- goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index 76fe7aab..d5067c4f 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -522,9 +522,9 @@ main(int argc,
int logdst = CLICON_LOG_SYSLOG;
yang_spec *yspec = NULL;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
- char *yang_filename = NULL;
char *stream_path;
int finish;
+ char *str;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@@ -601,7 +601,7 @@ main(int argc,
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
break;
case 'y' : /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
case 'a': /* internal backend socket address family */
clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
@@ -627,26 +627,34 @@ main(int argc,
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, yspec, NULL) < 0)
+
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL){
+ if (yang_spec_parse_file(h, str, yspec) < 0)
+ goto done;
+ }
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL){
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ }
+ /* 3. Load all modules in a directory */
+ if ((str = clicon_yang_main_dir(h)) != NULL){
+ if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_module_revision(h),
- yspec, NULL) < 0)
- goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
/* Add system modules */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
- yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec, NULL)< 0)
+ yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
- yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec, NULL)< 0)
+ yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
goto done;
/* Call start function in all plugins before we go interactive
Pass all args after the standard options to plugin_start
diff --git a/datastore/datastore_client.c b/datastore/datastore_client.c
index 165b4238..5722d497 100644
--- a/datastore/datastore_client.c
+++ b/datastore/datastore_client.c
@@ -195,7 +195,7 @@ main(int argc, char **argv)
if ((yspec = yspec_new()) == NULL)
goto done;
/* Parse yang spec from given file */
- if (yang_parse(h, yangmodule, NULL, NULL, yspec, NULL) < 0)
+ if (yang_parse(h, yangmodule, NULL, NULL, yspec) < 0)
goto done;
/* Set database directory option */
if (xmldb_setopt(h, "dbdir", dbdir) < 0)
diff --git a/doc/FAQ.md b/doc/FAQ.md
index a5539c5a..9e54e304 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -73,8 +73,14 @@ clicon:x:1001:,www-data
```
## What about reference documentation?
-Clixon uses Doxygen for reference documentation.
-Build using 'make doc' and aim your browser at doc/html/index.html.
+Clixon uses [Doxygen](http://www.doxygen.nl/index.html) for reference documentation.
+You need to install doxygen and graphviz on your system.
+Build it in the doc directory and point the browser to `.../clixon/doc/html/index.html` as follows:
+```
+> cd doc
+> make doc
+> make graphs # detailed callgraphs
+```
## How is configuration data stored?
Configuration data is stored in an XML datastore. In the example the
@@ -115,12 +121,15 @@ are included.
The following configuration file options control the loading of Yang files:
- `CLICON_YANG_DIR` - A list of directories (yang dir path) where Clixon searches for module and submodules.
+- `CLICON_YANG_MAIN_DIR` - Load all yang modules in this directory.
+- `CLICON_YANG_MAIN_FILE` - Load a specific Yang module fiven by a file.
- `CLICON_YANG_MODULE_MAIN` - Specifies a single module to load. The module is searched for in the yang dir path.
- `CLICON_YANG_MODULE_REVISION` : Specifies a revision to the main module.
Note that the special `CLIXON_DATADIR`, by default `/usr/local/share/clixon` should be included in the yang dir path for Clixon system files to be found.
-Application also has a command-line option `-y` to include a single Yang using absolute file path. This is mainly for debugging.
+You can combine the options, however, more specific options override
+less specific. For example, `CLICON_YANG_MAIN_FILE` overrides `CLICON_YANG_MODULE_MAIN`.
## How do I enable Yang features?
diff --git a/example/example_cli.cli b/example/example_cli.cli
index 1856c8c7..302a6cf3 100644
--- a/example/example_cli.cli
+++ b/example/example_cli.cli
@@ -7,10 +7,10 @@ CLICON_PLUGIN="example_cli";
translate value (),cli_set("/translate/value");
# Note, when switching to PT, change datamodel to only @datamodel
-set @datamodel:example, cli_set();
-merge @datamodel:example, cli_merge();
-create @datamodel:example, cli_create();
-delete("Delete a configuration item") @datamodel:example, cli_del();
+set @datamodel, cli_set();
+merge @datamodel, cli_merge();
+create @datamodel, cli_create();
+delete("Delete a configuration item") @datamodel, cli_del();
validate("Validate changes"), cli_validate();
commit("Commit the changes"), cli_commit();
@@ -40,19 +40,19 @@ show("Show a particular state of the system"){
}
configuration("Show configuration"), cli_show_config("candidate", "text", "/");{
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");{
- @datamodel:example, cli_show_auto("candidate", "text");
+ @datamodel, cli_show_auto("candidate", "text");
}
cli("Show configuration as CLI commands"), cli_show_config("candidate", "cli", "/");{
- @datamodel:example, cli_show_auto("candidate", "cli");
+ @datamodel, cli_show_auto("candidate", "cli");
}
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");{
- @datamodel:example, cli_show_auto("candidate", "netconf");
+ @datamodel, cli_show_auto("candidate", "netconf");
}
text("Show configuration as text"), cli_show_config("candidate","text","/");{
- @datamodel:example, cli_show_auto("candidate", "text");
+ @datamodel, cli_show_auto("candidate", "text");
}
json("Show configuration as JSON"), cli_show_config("candidate", "json", "/");{
- @datamodel:example, cli_show_auto("candidate", "json");
+ @datamodel, cli_show_auto("candidate", "json");
}
}
}
diff --git a/include/clixon_custom.h b/include/clixon_custom.h
index 8bad0f92..cbb3f1df 100644
--- a/include/clixon_custom.h
+++ b/include/clixon_custom.h
@@ -51,3 +51,15 @@ int strverscmp (__const char *__s1, __const char *__s2);
*/
#define XMLNS_YANG_ONLY 1
+/* Set for full XML namespace code in XML, NETCONF and YANG
+ * Experimental
+ */
+#undef ENABLE_XMLNS
+
+/* If set, patch all CLI spec calls to @datamodel:tree to @datamodel.
+ * This is a backward compatible fix for 3.9 for CLIgen specification files
+ * using model generation (CLIXON_CLI_GENMODEL).
+ * All new references should use @datamodel (or CLICON_CLI_MODEL_TREENAME).
+ * whereas older code used @datamodel:tree.
+ */
+#define CLICON_CLI_MODEL_TREENAME_PATCH
diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h
index d774fe29..1f4069fc 100644
--- a/lib/clixon/clixon_options.h
+++ b/lib/clixon/clixon_options.h
@@ -105,6 +105,12 @@ int clicon_option_del(clicon_handle h, const char *name);
static inline char *clicon_configfile(clicon_handle h){
return clicon_option_str(h, "CLICON_CONFIGFILE");
}
+static inline char *clicon_yang_main_file(clicon_handle h){
+ return clicon_option_str(h, "CLICON_YANG_MAIN_FILE");
+}
+static inline char *clicon_yang_main_dir(clicon_handle h){
+ return clicon_option_str(h, "CLICON_YANG_MAIN_DIR");
+}
static inline char *clicon_yang_module_main(clicon_handle h){
return clicon_option_str(h, "CLICON_YANG_MODULE_MAIN");
}
@@ -129,6 +135,9 @@ static inline char *clicon_clispec_dir(clicon_handle h){
static inline char *clicon_cli_mode(clicon_handle h){
return clicon_option_str(h, "CLICON_CLI_MODE");
}
+static inline char *clicon_cli_model_treename(clicon_handle h){
+ return clicon_option_str(h, "CLICON_CLI_MODEL_TREENAME");
+}
static inline char *clicon_sock(clicon_handle h){
return clicon_option_str(h, "CLICON_SOCK");
}
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index 222f3f91..a49d1e94 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -256,7 +256,7 @@ int yang_nodeid_split(char *nodeid, char **prefix, char **id);
yang_stmt *ys_module(yang_stmt *ys);
yang_spec *ys_spec(yang_stmt *ys);
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
-yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
+yang_stmt *yang_find(yang_node *yn, int keyword, const char *argument);
int yang_match(yang_node *yn, int keyword, char *argument);
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
@@ -269,7 +269,7 @@ 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 *revision, yang_spec *ysp, yang_stmt **ymodp);
+ const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
void *arg);
int yang_abs_schema_nodeid(yang_spec *yspec, yang_stmt *ys,
@@ -281,8 +281,9 @@ cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys, char *extra);
int yang_mandatory(yang_stmt *ys);
int yang_config(yang_stmt *ys);
-int yang_spec_parse_module(clicon_handle h, char *module, char *revision, yang_spec *yspec, yang_stmt **ymodp);
-int yang_spec_parse_file(clicon_handle h, char *filename, yang_spec *yspec, yang_stmt **ymodp);
+int yang_spec_parse_module(clicon_handle h, char *module, char *revision, yang_spec *yspec);
+int yang_spec_parse_file(clicon_handle h, char *filename, yang_spec *yspec);
+int yang_spec_load_dir(clicon_handle h, char *dir, yang_spec *yspec);
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
int yang_key_match(yang_node *yn, char *name);
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index c2a25f25..27c0acc0 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -988,9 +988,9 @@ netconf_module_load(clicon_handle h)
yspec = clicon_dbspec_yang(h);
/* Load yang spec */
- if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec, NULL)< 0)
+ if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
goto done;
- if (yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec, NULL)< 0)
+ if (yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
goto done;
if ((xc = clicon_conf_xml(h)) == NULL){
clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded");
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 5804f182..9c737b18 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -252,7 +252,7 @@ clicon_options_main(clicon_handle h,
/* Set clixon_conf pointer to handle */
clicon_conf_xml_set(h, xconfig);
/* Parse clixon yang spec */
- if (yang_parse(h, NULL, "clixon-config", NULL, yspec, NULL) < 0)
+ if (yang_parse(h, NULL, "clixon-config", NULL, yspec) < 0)
goto done;
clicon_conf_xml_set(h, NULL);
if (xconfig)
diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c
index 7710ae80..03ed2b29 100644
--- a/lib/src/clixon_plugin.c
+++ b/lib/src/clixon_plugin.c
@@ -269,8 +269,7 @@ clixon_plugins_load(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__);
/* Get plugin objects names from plugin directory */
- if((ndp = clicon_file_dirent(dir, &dp,
- regexp?regexp:"(.so)$", S_IFREG))<0)
+ if((ndp = clicon_file_dirent(dir, &dp, regexp?regexp:"(.so)$", S_IFREG)) < 0)
goto done;
/* Load all plugins */
for (i = 0; i < ndp; i++) {
diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y
index a86c7276..7072ed01 100644
--- a/lib/src/clixon_xml_parse.y
+++ b/lib/src/clixon_xml_parse.y
@@ -315,7 +315,7 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya,
int retval = -1;
cxobj *xa;
-#ifdef notyet
+#ifdef ENABLE_XMLNS
if (prefix && strcmp(prefix,"xmlns")==0)
fprintf(stderr, "PrefixedAttName NCNAME:%s = %s\n", name, attval);
if (prefix==NULL && strcmp(name,"xmlns")==0)
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index a7033ce2..2a6b1572 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -46,7 +46,7 @@
#include
#include
#include
-#define __USE_GNU /* strverscmp */
+#define __USE_GNU /* strverscmp */
#include
#include
#include
@@ -56,6 +56,7 @@
#include
#include
#include
+#include
#include
/* cligen */
@@ -416,7 +417,7 @@ yn_each(yang_node *yn,
yang_stmt *
yang_find(yang_node *yn,
int keyword,
- char *argument)
+ const char *argument)
{
yang_stmt *ys = NULL;
int i;
@@ -1939,7 +1940,6 @@ yang_parse_find_match(clicon_handle h,
S_IFREG)) < 0)
goto done;
/* Entries are sorted, last entry should be most recent date
- * Found
*/
if (ndp != 0){
cprintf(fbuf, "%s/%s", dir, dp[ndp-1].d_name);
@@ -2278,7 +2278,6 @@ yang_merge_submodules(clicon_handle h,
* @param[in] module Name of main YANG module. Or absolute file name.
* @param[in] revision Main module revision date string or NULL
* @param[in,out] ysp Yang specification. Should have been created by caller using yspec_new
- * @param[out] ymodp Yang module of first, topmost Yang module, if given.
* @retval 0 Everything OK
* @retval -1 Error encountered
* The database symbols are inserted in alphabetical order.
@@ -2298,72 +2297,89 @@ yang_parse(clicon_handle h,
const char *filename,
const char *module,
const char *revision,
- yang_spec *ysp,
- yang_stmt **ymodp)
+ yang_spec *yspec)
{
int retval = -1;
yang_stmt *ymod = NULL; /* Top-level yang (sub)module */
int i;
int modnr; /* Existing number of modules */
+ char *base = NULL;;
/* Apply steps 2.. on new modules, ie ones after modnr. */
- modnr = ysp->yp_len;
+ modnr = yspec->yp_len;
if (filename){
- if ((ymod = yang_parse_filename(filename, ysp)) == NULL)
+ /* Find module, and do not load file if module already exists */
+ if (basename(filename) == NULL){
+ clicon_err(OE_YANG, errno, "No basename");
+ goto done;
+ }
+ if ((base = strdup(basename(filename))) == NULL){
+ clicon_err(OE_YANG, errno, "strdup");
+ goto done;
+ }
+ if (index(base, '@') != NULL)
+ *index(base, '@') = '\0';
+ if (yang_find((yang_node*)yspec, Y_MODULE, base) != NULL)
+ goto ok;
+ if ((ymod = yang_parse_filename(filename, yspec)) == NULL)
goto done;
}
- else
- if ((ymod = yang_parse_module(h, module, revision, ysp)) == NULL)
+ else {
+ /* Do not load module if it already exists */
+ if (yang_find((yang_node*)yspec, Y_MODULE, module) != NULL)
+ goto ok;
+ if ((ymod = yang_parse_module(h, module, revision, yspec)) == NULL)
goto done;
+ }
/* 1: Parse from text to yang parse-tree. */
/* Iterate through modules */
- if (yang_parse_recurse(h, ymod, ysp) < 0)
+ if (yang_parse_recurse(h, ymod, yspec) < 0)
goto done;
/* 2. Check cardinality maybe this should be done after grouping/augment */
- for (i=modnr; iyp_len; i++) /* XXX */
- if (yang_cardinality(h, ysp->yp_stmt[i], ysp->yp_stmt[i]->ys_argument) < 0)
+ for (i=modnr; iyp_len; i++) /* XXX */
+ if (yang_cardinality(h, yspec->yp_stmt[i], yspec->yp_stmt[i]->ys_argument) < 0)
goto done;
/* 3: Merge sub-modules with modules - after this step, no submodules exist
* In the merge, remove submodule headers
*/
- for (i=modnr; iyp_len; i++){
- if (ysp->yp_stmt[i]->ys_keyword != Y_SUBMODULE)
+ for (i=modnr; iyp_len; i++){
+ if (yspec->yp_stmt[i]->ys_keyword != Y_SUBMODULE)
continue;
}
i = 0;
- while (iyp_len){
+ while (iyp_len){
int j;
- if (ysp->yp_stmt[i]->ys_keyword != Y_SUBMODULE){
+ if (yspec->yp_stmt[i]->ys_keyword != Y_SUBMODULE){
i++;
continue;
}
- if (yang_merge_submodules(h, ysp, ysp->yp_stmt[i]) < 0)
+ if (yang_merge_submodules(h, yspec, yspec->yp_stmt[i]) < 0)
goto done;
/* shift down one step */
- for (j=i; jyp_len-1; j++)
- ysp->yp_stmt[j] = ysp->yp_stmt[j+1];
- ysp->yp_len--;
+ for (j=i; jyp_len-1; j++)
+ yspec->yp_stmt[j] = yspec->yp_stmt[j+1];
+ yspec->yp_len--;
}
/* 4: 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)
+ for (i=modnr; iyp_len; i++) /* XXX */
+ if (yang_features(h, yspec->yp_stmt[i]) < 0)
goto done;
/* 5: 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, (void*)h) < 0)
+ for (i=modnr; iyp_len; i++)
+ if (yang_apply((yang_node*)yspec->yp_stmt[i], -1, ys_populate, (void*)h) < 0)
goto done;
/* 6: Resolve all types: populate type caches. Requires eg length/range cvecs
* from ys_populate step.
* Must be done using static binding.
*/
- for (i=modnr; iyp_len; i++)
- if (yang_apply((yang_node*)ysp->yp_stmt[i], Y_TYPE, ys_resolve_type, NULL) < 0)
+ for (i=modnr; iyp_len; i++)
+ if (yang_apply((yang_node*)yspec->yp_stmt[i], Y_TYPE, ys_resolve_type, NULL) < 0)
goto done;
/* Up to here resolving is made in the context they are defined, rather
@@ -2374,25 +2390,26 @@ yang_parse(clicon_handle h,
*/
/* 7: 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)
+ for (i=modnr; iyp_len; i++){
+ if (yang_expand((yang_node*)yspec->yp_stmt[i]) < 0)
goto done;
- yang_apply((yang_node*)ysp->yp_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
+ yang_apply((yang_node*)yspec->yp_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
}
/* 8: Top-level augmentation of all modules XXX: only new modules? */
- if (yang_augment_spec(ysp) < 0)
+ if (yang_augment_spec(yspec) < 0)
goto done;
/* 9: 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)
+ for (i=modnr; iyp_len; i++)
+ if (yang_apply((yang_node*)yspec->yp_stmt[i], -1, ys_schemanode_check, NULL) < 0)
goto done;
/* Return main module parsed in step 1 */
- if (ymodp)
- *ymodp = ymod;
+ ok:
retval = 0;
- done:
+ done:
+ if (base)
+ free(base);
return retval;
}
@@ -2792,7 +2809,6 @@ yang_config(yang_stmt *ys)
* @param[in] dir Directory where to look for modules and sub-modules
* @param[in] revision Revision, or NULL
* @param[in,out] yspec Modules parse are added to this yangspec
- * @param[out] ymodp Yang module of first, topmost Yang module, if given.
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_file
@@ -2801,8 +2817,7 @@ int
yang_spec_parse_module(clicon_handle h,
char *module,
char *revision,
- yang_spec *yspec,
- yang_stmt **ymodp)
+ yang_spec *yspec)
{
int retval = -1;
@@ -2819,7 +2834,7 @@ yang_spec_parse_module(clicon_handle h,
clicon_err(OE_YANG, EINVAL, "yang module illegal format");
goto done;
}
- if (yang_parse(h, NULL, module, revision, yspec, ymodp) < 0)
+ if (yang_parse(h, NULL, module, revision, yspec) < 0)
goto done;
retval = 0;
done:
@@ -2831,7 +2846,6 @@ yang_spec_parse_module(clicon_handle h,
* @param[in] filename Actual filename (including dir and revision)
* @param[in] dir Directory for sub-modules
* @param[in,out] yspec Modules parse are added to this yangspec
- * @param[out] ymodp Yang module of first, topmost Yang module, if given.
* @retval 0 OK
* @retval -1 Error
* @see yang_spec_parse_module for yang dir,module,revision instead of actual filename
@@ -2839,8 +2853,7 @@ yang_spec_parse_module(clicon_handle h,
int
yang_spec_parse_file(clicon_handle h,
char *filename,
- yang_spec *yspec,
- yang_stmt **ymodp)
+ yang_spec *yspec)
{
int retval = -1;
@@ -2848,13 +2861,62 @@ yang_spec_parse_file(clicon_handle h,
clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
goto done;
}
- if (yang_parse(h, filename, NULL, NULL, yspec, ymodp) < 0)
+ if (yang_parse(h, filename, NULL, NULL, yspec) < 0)
goto done;
retval = 0;
done:
return retval;
}
+/*! Load all yang modules in directory
+ * @param[in] h Clicon handle
+ * @param[in] dir Load all yang modules in this directory
+ * @param[in,out] yspec Modules parse are added to this yangspec
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+int
+yang_spec_load_dir(clicon_handle h,
+ char *dir,
+ yang_spec *yspec)
+{
+ int retval = -1;
+ int ndp;
+ struct dirent *dp = NULL;
+ int i;
+ char filename[MAXPATHLEN];
+ char *base;
+ char *b;
+ int j;
+ int len;
+
+ /* Get plugin objects names from plugin directory */
+ if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
+ goto done;
+ /* Load all yang files */
+ for (i = 0; i < ndp; i++) {
+ base = dp[i].d_name;
+ /* Entries are sorted, see if later entry exists (include @), if so skip
+ * this one and take last.
+ */
+ if ((b = index(base, '@')) != NULL)
+ len = b-base;
+ else
+ len = strlen(base);
+ for (j = (i+1); j < ndp; j++)
+ if (strncmp(base, dp[j].d_name, len) == 0)
+ break;
+ if (j[@]";
@@ -224,9 +234,18 @@ module clixon-config {
type int32;
default 1;
description
- "Generate code for CLI completion of existing db symbols.
- Example: Add name=\"myspec\" in datamodel spec and reference
- as @myspec";
+ "If set, generate CLI specification for CLI completion of
+ loaded Yang modules. This CLI tree can be accessed in CLI
+ spec files using the tree reference syntax (eg @datamodel).
+ See also CLICON_CLI_MODEL_TREENAME.";
+ }
+ leaf CLICON_CLI_MODEL_TREENAME {
+ type string;
+ default "datamodel";
+ description
+ "If CLICON_CLI_GENMODEL is set, CLI specs can reference the
+ model syntax using this reference.
+ Example: set @datamodel, cli_set();";
}
leaf CLICON_CLI_GENMODEL_COMPLETION {
type int32;