diff --git a/CHANGELOG.md b/CHANGELOG.md index a80cdd9a..8ed77983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,16 @@ * More complete Yang parser * YANG parser cardinality checked (only modules level yet) * See https://github.com/clicon/clixon/issues/84 + * Support of submodule, include and belongs-to. * Openconfig yang specs parsed: https://github.com/openconfig/public + * Improved unknown handling + * `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 ### 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. ### Minor changes * XML parser conformance to W3 spec diff --git a/README.md b/README.md index 9cbb2e32..278374e5 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ support. * [Tests](test/) * [Docker](docker/) * [Roadmap](ROADMAP.md) - * [Reference manual](http://www.clicon.org/doxygen/index.html) (Note: the link may not be up-to-date. It is better to build your own: `cd doc; make doc`) + * [Reference manual](#reference) Background ========== @@ -124,6 +124,10 @@ However, the following YANG syntax modules are not implemented: - action - belongs-to +Restrictions on Yang types are as follows: +- The range statement does not support multiple values (RFC7895 sec 9.2.4) +- Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. + Netconf ======= Clixon implements the following NETCONF proposals or standards: @@ -217,7 +221,6 @@ The tests outlines an example of three groups (taken from the RFC): admin, limit * limited: Read access (get and get-config) * guest: No access - Runtime ======= @@ -225,3 +228,12 @@ Runtime The figure shows the SDK runtime of Clixon. +Reference +========= +A reference manual can be built using [Doxygen](http://www.doxygen.nl/index.html). 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 +``` \ No newline at end of file diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 3856eb0e..76a5dca9 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", CLIXON_DATADIR, NULL, yspec, NULL) < 0) + if (yang_parse(h, NULL, "ietf-netconf-acm", NULL, yspec, NULL) < 0) goto done; fd = fileno(f); /* Read configfile */ @@ -753,11 +753,10 @@ main(int argc, /* 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, clicon_yang_dir(h), yspec, NULL) < 0) + if (yang_spec_parse_file(h, yang_filename, yspec, NULL) < 0) goto done; } else if (yang_spec_parse_module(h, clicon_yang_module_main(h), - clicon_yang_dir(h), clicon_yang_module_revision(h), yspec, NULL) < 0) goto done; @@ -770,11 +769,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", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec, NULL)< 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", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec, NULL)< 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_generate.c b/apps/cli/cli_generate.c index 22f76883..022e41b8 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -51,7 +51,7 @@ #include #include #include - +#include /* For pow() kludge in cvtype_max2str_dup2 */ /* cligen */ #include @@ -169,6 +169,28 @@ static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, cbuf *cb, static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype, yang_stmt *ytype, cbuf *cb, char *helptext); +/*! Patched maxstring to account for DEC64 types + * @note kludge to fix overflow error -> Fix the original error in cvtype_max2str + * by adding a fraction_digits argument. + */ +static char * +cvtype_max2str_dup2(enum cv_type type, + int fraction_digits) +{ + int len; + char *str; + + if (type!=CGV_DEC64 || fraction_digits==0) + return cvtype_max2str_dup(type); + if ((len = cvtype_max2str(type, NULL, 0)) < 0) + return NULL; + if ((str = (char *)malloc(len+1)) == NULL) + return NULL; + memset(str, '\0', len+1); + len = snprintf(str, len+1, "%" PRId64 ".0", (INT64_MAX/((int)pow(10,fraction_digits)))); + return str; +} + /*! Generate CLI code for Yang leaf statement to CLIgen variable of specific type * Check for completion (of already existent values), ranges (eg range[min:max]) and * patterns, (eg regexp:"[0.9]*"). @@ -282,10 +304,12 @@ yang2cli_var_sub(clicon_handle h, } snprintf(r, 512, "%d", MAXPATHLEN); } - else if ((r = cvtype_max2str_dup(cvtype)) == NULL){ + else { + if ((r = cvtype_max2str_dup2(cvtype, fraction_digits)) == NULL){ clicon_err(OE_UNIX, errno, "cvtype_max2str"); goto done; } + } } cprintf(cb, "%s]", r); /* range */ free(r); @@ -410,7 +434,7 @@ yang2cli_var(clicon_handle h, cbuf *cb, char *helptext) { - int retval = -1; + int retval = -1; char *origtype; yang_stmt *yrestype; /* resolved type */ char *restype; /* resolved type */ @@ -513,10 +537,12 @@ yang2cli_leaf(clicon_handle h, if (helptext) cprintf(cbuf, "(\"%s\")", helptext); cprintf(cbuf, " "); - yang2cli_var(h, ys, cbuf, helptext); + if (yang2cli_var(h, ys, cbuf, helptext) < 0) + goto done; } else - yang2cli_var(h, ys, cbuf, helptext); + if (yang2cli_var(h, ys, cbuf, helptext) < 0) + goto done; if (callback){ if (cli_callback_generate(h, ys, cbuf) < 0) goto done; diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index c651127e..5e887543 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -433,11 +433,10 @@ main(int argc, char **argv) /* 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, clicon_yang_dir(h), yspec, &ymod) < 0) + if (yang_spec_parse_file(h, yang_filename, yspec, &ymod) < 0) goto done; } else if (yang_spec_parse_module(h, clicon_yang_module_main(h), - clicon_yang_dir(h), clicon_yang_module_revision(h), yspec, &ymod) < 0) goto done; diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 6f3f30bc..6326c283 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -447,11 +447,10 @@ main(int argc, /* 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, clicon_yang_dir(h), yspec, NULL) < 0) + if (yang_spec_parse_file(h, yang_filename, yspec, NULL) < 0) goto done; } else if (yang_spec_parse_module(h, clicon_yang_module_main(h), - clicon_yang_dir(h), clicon_yang_module_revision(h), yspec, NULL) < 0) goto done; diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 838f0c73..a8627b66 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -32,7 +32,13 @@ ***** END LICENSE BLOCK ***** * - * Code for handling netconf rpc messages according to RFC 4741 and RFC 5277 + * Code for handling netconf rpc messages according to RFC 4741,5277,6241 + * All NETCONF protocol elements are defined in the following namespace: + * urn:ietf:params:xml:ns:netconf:base:1.0 + * YANG defines an XML namespace for NETCONF operations, + * content, and the element. The name of this + * namespace is "urn:ietf:params:xml:ns:yang:1". + * *****************************************************************************/ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ @@ -894,7 +900,7 @@ netconf_application_rpc(clicon_handle h, } cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn)); /* Find yang rpc statement, return yang rpc statement if found */ - if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), Y_RPC, &yrpc) < 0) + if (yang_abs_schema_nodeid(yspec, xml_spec(xn), cbuf_get(cb), Y_RPC, &yrpc) < 0) goto done; /* Check if found */ if (yrpc != NULL){ diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index ed7fdc43..5909e8db 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -517,7 +517,6 @@ main(int argc, char *sockpath; char *path; clicon_handle h; - char *yangspec=NULL; char *dir; char *tmp; int logdst = CLICON_LOG_SYSLOG; @@ -619,10 +618,6 @@ main(int argc, argc -= optind; argv += optind; - /* Overwrite yang module with -y option */ - if (yangspec) - clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", yangspec); - /* Initialize plugins group */ if ((dir = clicon_restconf_dir(h)) != NULL) if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0) @@ -635,12 +630,11 @@ main(int argc, /* 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, clicon_yang_dir(h), yspec, NULL) < 0) + if (yang_spec_parse_file(h, yang_filename, yspec, NULL) < 0) goto done; } else if (yang_spec_parse_module(h, clicon_yang_module_main(h), - clicon_yang_dir(h), - clicon_yang_module_revision(h), + clicon_yang_module_revision(h), yspec, NULL) < 0) goto done; @@ -649,10 +643,10 @@ main(int argc, goto done; /* Add system modules */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && - yang_spec_parse_module(h, "ietf-restconf-monitoring", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec, NULL)< 0) goto done; if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && - yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec, NULL)< 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/configure b/configure index 9428e91f..7eba05ec 100755 --- a/configure +++ b/configure @@ -4391,15 +4391,14 @@ fi done -# This is to find clixon system files in the source code and Makefile +# CLIXON_DATADIR is where clixon installs the "system" yang files in yang/Makfile +# This directory should most probably be included in each application, +# so each application designer may need to place CLIXON_DATADIR in their config +# (last in yang dir list): +# $CLIXON_DATADIR CLIXON_DATADIR="${prefix}/share/clixon" -cat >>confdefs.h <<_ACEOF -#define CLIXON_DATADIR "${CLIXON_DATADIR}" -_ACEOF - - # Default location for config file cat >>confdefs.h <<_ACEOF diff --git a/configure.ac b/configure.ac index c46628fb..ebdccaf6 100644 --- a/configure.ac +++ b/configure.ac @@ -199,10 +199,13 @@ AC_CHECK_LIB(dl, dlopen) AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp) -# This is to find clixon system files in the source code and Makefile +# CLIXON_DATADIR is where clixon installs the "system" yang files in yang/Makfile +# This directory should most probably be included in each application, +# so each application designer may need to place CLIXON_DATADIR in their config +# (last in yang dir list): +# $CLIXON_DATADIR AC_SUBST(CLIXON_DATADIR) CLIXON_DATADIR="${prefix}/share/clixon" -AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${CLIXON_DATADIR}", [Clixon data dir for system yang files etc]) # Default location for config file AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${CLIXON_DEFAULT_CONFIG}",[Location for apps to find default config file]) diff --git a/datastore/datastore_client.c b/datastore/datastore_client.c index 82c0263e..e5c5440c 100644 --- a/datastore/datastore_client.c +++ b/datastore/datastore_client.c @@ -71,7 +71,7 @@ sudo ./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src #include /* Command line options to be passed to getopt(3) */ -#define DATASTORE_OPTS "hDd:p:b:y:m:" +#define DATASTORE_OPTS "hDd:p:b:y:" /*! usage */ @@ -85,8 +85,7 @@ usage(char *argv0) "\t-d \t\tDatabase name. Default: running. Alt: candidate,startup\n" "\t-b \tDatabase directory. Mandatory\n" "\t-p \tDatastore plugin. Mandatory\n" - "\t-y \tYang directory (where modules are stored). Mandatory\n" - "\t-m \tYang module. Mandatory\n" + "\t-y \tYang file. Mandatory\n" "and command is either:\n" "\tget []\n" "\tmget []\n" @@ -115,7 +114,6 @@ main(int argc, char **argv) char *plugin = NULL; char *cmd = NULL; yang_spec *yspec = NULL; - char *yangdir = NULL; char *yangmodule = NULL; char *dbdir = NULL; int ret; @@ -158,12 +156,7 @@ main(int argc, char **argv) usage(argv0); dbdir = optarg; break; - case 'y': /* Yang directory */ - if (!optarg) - usage(argv0); - yangdir = optarg; - break; - case 'm': /* Yang module */ + case 'y': /* Yang file */ if (!optarg) usage(argv0); yangmodule = optarg; @@ -188,10 +181,6 @@ main(int argc, char **argv) clicon_err(OE_DB, 0, "Missing dbdir -b option"); goto done; } - if (yangdir == NULL){ - clicon_err(OE_YANG, 0, "Missing yangdir -y option"); - goto done; - } if (yangmodule == NULL){ clicon_err(OE_YANG, 0, "Missing yang module -m option"); goto done; @@ -206,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, NULL, yangmodule, yangdir, NULL, yspec, NULL) < 0) + if (yang_parse(h, yangmodule, NULL, NULL, yspec, NULL) < 0) goto done; /* Set database directory option */ if (xmldb_setopt(h, "dbdir", dbdir) < 0) diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index bee21bd0..5d1edaf2 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -1367,8 +1367,7 @@ main(int argc, enum operation_type op; char *cmd; char *db; - char *yangdir; - char *yangmod; + char *yangmod; /* yang file */ yang_spec *yspec = NULL; clicon_handle h; @@ -1381,12 +1380,11 @@ main(int argc, } cmd = argv[1]; db = argv[2]; - yangdir = argv[3]; yangmod = argv[4]; db_init(db); if ((yspec = yspec_new()) == NULL) goto done - if (yang_parse(h, NULL, yangmod, yangdir, NULL, yspec) < 0) + if (yang_parse(h, NULL, yangmod, NULL, yspec) < 0) goto done; if (strcmp(cmd, "get")==0){ if (argc < 5) diff --git a/doc/FAQ.md b/doc/FAQ.md index 25727e98..a5539c5a 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -97,13 +97,31 @@ configuration file is /usr/local/etc/clixon.xml. The example configuration file is installed at /usr/local/etc/example.xml. The YANG specification for the configuration file is clixon-config.yang. -You can change where Clixon looks for the configuration FILE as follows: +## How are Clixon configuration files found? + +Clixon by default finds its configuration file at `/usr/local/etc/clixon.xml`. However, you can modify this location as follows: - Provide -f FILE option when starting a program (eg clixon_backend -f FILE) - Provide --with-configfile=FILE when configuring - - Provide --with-sysconfig= when configuring, then FILE is /clixon.xml - - Provide --sysconfig= when configuring then FILE is /etc/clixon.xml + - Provide --with-sysconfig= when configuring. Then FILE is /clixon.xml + - Provide --sysconfig= when configuring. Then FILE is /etc/clixon.xml - FILE is /usr/local/etc/clixon.xml +## How are Yang files found? + +Yang files contain the configuration specification. A Clixon +application loads yang files and clixon itself loads system yang +files. When Yang files are loaded modules are imported and submodules +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_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. + ## How do I enable Yang features? Yang models have features, and parts of a specification can be diff --git a/doc/Makefile.in b/doc/Makefile.in index 7b1ce519..ad0f4203 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -44,11 +44,13 @@ all: $(SUBDIRS) doc echo "Build doxygen doc: make doc" # Regular doxygen documentation +# Need to install doxygen doc: doxygen Doxyfile # generates html dir echo "Build doxygen graphs: make graphs" # doxygen documentation with callgraphs +# Need to install graphviz graphs: doxygen Doxyfile.graphs # generates html dir + call graphs (takes time) diff --git a/example/example.xml b/example/example.xml index c222f6ce..b6b42cdb 100644 --- a/example/example.xml +++ b/example/example.xml @@ -2,6 +2,7 @@ /usr/local/etc/example.xml *:* /usr/local/share/example/yang + /usr/local/share/clixon example example /usr/local/lib/example/backend diff --git a/include/clixon_config.h.in b/include/clixon_config.h.in index 1cc881df..f8678e33 100644 --- a/include/clixon_config.h.in +++ b/include/clixon_config.h.in @@ -1,8 +1,5 @@ /* include/clixon_config.h.in. Generated from configure.ac by autoheader. */ -/* Clixon data dir for system yang files etc */ -#undef CLIXON_DATADIR - /* Location for apps to find default config file */ #undef CLIXON_DEFAULT_CONFIG @@ -42,6 +39,9 @@ /* Define to 1 if you have the `crypt' library (-lcrypt). */ #undef HAVE_LIBCRYPT +/* Define to 1 if you have the `curl' library (-lcurl). */ +#undef HAVE_LIBCURL + /* Define to 1 if you have the `dl' library (-ldl). */ #undef HAVE_LIBDL @@ -133,4 +133,4 @@ `char[]'. */ #undef YYTEXT_POINTER -#include "clixon_custom.h" +#include diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index f06660f0..d774fe29 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -105,9 +105,6 @@ 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_dir(clicon_handle h){ - return clicon_option_str(h, "CLICON_YANG_DIR"); -} static inline char *clicon_yang_module_main(clicon_handle h){ return clicon_option_str(h, "CLICON_YANG_MODULE_MAIN"); } diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 5b3b01e2..222f3f91 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -95,6 +95,7 @@ enum rfc_6020{ Y_MANDATORY, Y_MAX_ELEMENTS, Y_MIN_ELEMENTS, + Y_MODIFIER, Y_MODULE, Y_MUST, Y_NAMESPACE, @@ -193,6 +194,8 @@ struct yang_stmt{ char *ys_argument; /* String / argument depending on keyword */ int ys_flags; /* Flags according to YANG_FLAG_* above */ + /*--------------here common for all -------*/ + char *ys_extra; /* For unknown */ cg_var *ys_cv; /* cligen variable. See ys_populate() Following stmts have cv:s: leaf: for default value @@ -200,7 +203,7 @@ struct yang_stmt{ config: boolean true or false mandatory: boolean true or false fraction-digits for fraction-digits - unkown-stmt (argument) + unknown-stmt (argument) */ cvec *ys_cvec; /* List of stmt-specific variables Y_RANGE: range_min, range_max @@ -265,11 +268,12 @@ 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, + const char *module, const char *revision, yang_spec *ysp, yang_stmt **ymodp); int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn, void *arg); -int yang_abs_schema_nodeid(yang_spec *yspec, char *schema_nodeid, +int yang_abs_schema_nodeid(yang_spec *yspec, yang_stmt *ys, + char *schema_nodeid, enum rfc_6020 keyword, yang_stmt **yres); int yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid, enum rfc_6020 keyword, yang_stmt **yres); @@ -277,8 +281,8 @@ 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 *dir, char *revision, yang_spec *yspec, yang_stmt **ymodp); -int yang_spec_parse_file(clicon_handle h, char *filename, char *dir, yang_spec *yspec, yang_stmt **ymodp); +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); 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 d832ffc9..c2a25f25 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", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec, NULL)< 0) goto done; - if (yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + if (yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec, NULL)< 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 62202def..5804f182 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -178,8 +178,16 @@ parse_configfile(clicon_handle h, __FUNCTION__, name, body); continue; } + /* hard-coded exceptions for configure options that are leaf-lists (not leaf) + * They must be accessed directly by looping over clicon_conf_xml(h) + */ if (strcmp(name,"CLICON_FEATURE")==0) continue; + if (strcmp(name,"CLICON_YANG_DIR")==0) + continue; + /* Used as an arg to this fn */ + if (strcmp(name,"CLICON_CONFIGFILE")==0) + continue; if (hash_add(copt, name, body, @@ -236,14 +244,25 @@ clicon_options_main(clicon_handle h, clicon_err(OE_CFG, 0, "%s: suffix %s not recognized (Run ./configure --with-config-compat?)", configfile, suffix); goto done; } - /* Parse clixon yang spec */ - if (yang_parse(h, NULL, "clixon-config", CLIXON_DATADIR, NULL, yspec, NULL) < 0) - goto done; - /* Read configfile */ + /* Read configfile first without yangspec, for bootstrapping */ if (parse_configfile(h, configfile, yspec, &xconfig) < 0) goto done; if (xml_rootchild(xconfig, 0, &xconfig) < 0) goto done; + /* 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) + goto done; + clicon_conf_xml_set(h, NULL); + if (xconfig) + xml_free(xconfig); + /* Read configfile second time now with check yang spec */ + if (parse_configfile(h, configfile, yspec, &xconfig) < 0) + goto done; + if (xml_rootchild(xconfig, 0, &xconfig) < 0) + goto done; + /* Set clixon_conf pointer to handle */ clicon_conf_xml_set(h, xconfig); /* Specific option handling */ if (clicon_option_bool(h, "CLICON_XML_SORT") == 1) @@ -638,7 +657,7 @@ clicon_conf_xml(clicon_handle h) return NULL; } -/*! Set YANG specification for Clixon system options and features + /*! Set YANG specification for Clixon system options and features * ys must be a malloced pointer */ int diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 2afb0b6b..a7033ce2 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -122,6 +122,7 @@ static const map_str2int ykmap[] = { {"mandatory", Y_MANDATORY}, {"max-elements", Y_MAX_ELEMENTS}, {"min-elements", Y_MIN_ELEMENTS}, + {"modifier", Y_MODIFIER}, {"module", Y_MODULE}, {"must", Y_MUST}, {"namespace", Y_NAMESPACE}, @@ -206,6 +207,8 @@ ys_free1(yang_stmt *ys) { if (ys->ys_argument) free(ys->ys_argument); + if (ys->ys_extra) + free(ys->ys_extra); if (ys->ys_cv) cv_free(ys->ys_cv); if (ys->ys_cvec) @@ -233,7 +236,8 @@ ys_free(yang_stmt *ys) return 0; } -/*! Free a yang specification recursively */ +/*! Free a yang specification recursively + */ int yspec_free(yang_spec *yspec) { @@ -294,6 +298,11 @@ ys_cp(yang_stmt *ynew, clicon_err(OE_YANG, errno, "strdup"); goto done; } + if (yold->ys_extra) + if ((ynew->ys_extra = strdup(yold->ys_extra)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } if (yold->ys_cv) if ((ynew->ys_cv = cv_dup(yold->ys_cv)) == NULL){ clicon_err(OE_YANG, errno, "cv_dup"); @@ -672,7 +681,8 @@ yang_find_topnode(yang_spec *ysp, if (yang_nodeid_split(nodeid, &prefix, &id) < 0) goto done; if (prefix){ - if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL){ + if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL || + (ymod = yang_find((yang_node*)ysp, Y_SUBMODULE, prefix)) != NULL){ if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL) goto ok; goto done; @@ -771,7 +781,7 @@ yang_order(yang_stmt *y) int j=0; yp = y->ys_parent; - if (yp->yn_keyword == Y_MODULE ||yp->yn_keyword == Y_SUBMODULE){ + if (yp->yn_keyword == Y_MODULE || yp->yn_keyword == Y_SUBMODULE){ ypp = yp->yn_parent; for (i=0; iyn_len; i++){ yn = (yang_node*)ypp->yn_stmt[i]; @@ -800,7 +810,8 @@ yang_key2str(int keyword) return (char*)clicon_int2str(ykmap, keyword); } -/*! Find top module or sub-module given a statement. Ultimate top is yang spec +/*! Find top module or sub-module given a statement. + * Ultimate top is yang spec, dont return that * The routine recursively finds ancestors. * @param[in] ys Any yang statement in a yang tree * @retval ymod The top module or sub-module @@ -811,14 +822,13 @@ ys_module(yang_stmt *ys) { yang_node *yn; -#if 1 if (ys==NULL || ys->ys_keyword==Y_SPEC) return NULL; -#else - if (ys==NULL || ys->ys_keyword==Y_SPEC) + if (ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE) return ys; -#endif - while (ys != NULL && ys->ys_keyword != Y_MODULE && ys->ys_keyword != Y_SUBMODULE){ + while (ys != NULL && + ys->ys_keyword != Y_MODULE && + ys->ys_keyword != Y_SUBMODULE){ yn = ys->ys_parent; /* Some extra stuff to ensure ys is a stmt */ if (yn && yn->yn_keyword == Y_SPEC) @@ -973,7 +983,8 @@ yang_find_module_by_prefix(yang_stmt *ys, } yimport = NULL; while ((yimport = yn_each((yang_node*)my_ymod, yimport)) != NULL) { - if (yimport->ys_keyword != Y_IMPORT) + if (yimport->ys_keyword != Y_IMPORT && + yimport->ys_keyword != Y_INCLUDE) continue; if ((yprefix = yang_find((yang_node*)yimport, Y_PREFIX, NULL)) != NULL && strcmp(yprefix->ys_argument, prefix) == 0){ @@ -981,9 +992,10 @@ yang_find_module_by_prefix(yang_stmt *ys, } } if (yimport){ - if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL){ + if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL && + (ymod = yang_find((yang_node*)yspec, Y_SUBMODULE, yimport->ys_argument)) == NULL){ clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s", - yimport->ys_argument); + prefix); yimport = NULL; goto done; /* unresolved */ } @@ -1411,9 +1423,9 @@ ys_populate_feature(clicon_handle h, cg_var *cv; char *module; char *feature; - cxobj *x1; + cxobj *xc; - /* Eg, when parsing the config xml itself */ + /* get clicon config file in xml form */ if ((x = clicon_conf_xml(h)) == NULL) goto ok; if ((ymod = ys_module(ys)) == NULL){ @@ -1422,14 +1434,14 @@ ys_populate_feature(clicon_handle h, } module = ymod->ys_argument; feature = ys->ys_argument; - x1 = NULL; - while ((x1 = xml_child_each(x, x1, CX_ELMNT)) != NULL && found == 0) { + xc = NULL; + while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL && found == 0) { char *m = NULL; char *f = NULL; - if (strcmp(xml_name(x1), "CLICON_FEATURE") != 0) + if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0) continue; /* get m and f from configuration feature rules */ - if (yang_nodeid_split(xml_body(x1), &m, &f) < 0) + if (yang_nodeid_split(xml_body(xc), &m, &f) < 0) goto done; if (m && f && (strcmp(m,"*")==0 || @@ -1454,6 +1466,51 @@ ys_populate_feature(clicon_handle h, return retval; } +/*! Populate unknown node with extension + */ +static int +ys_populate_unknown(yang_stmt *ys) +{ + int retval = -1; + int cvret; + char *reason = NULL; + yang_stmt *ymod; + char *prefix = NULL; + char *name; + char *extra; + + if ((extra = ys->ys_extra) == NULL) + goto ok; + /* Find extension, if found, store it as unknown, if not, + break for error */ + prefix = yarg_prefix(ys); /* And this its prefix */ + name = yarg_id(ys); /* This is the type to resolve */ + if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL) + goto ok; /* shouldnt happen */ + if (yang_find((yang_node*)ymod, Y_EXTENSION, name) == NULL){ + clicon_err(OE_YANG, errno, "Extension %s:%s not found", prefix, name); + goto done; + } + if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){ + clicon_err(OE_YANG, errno, "cv_new"); + goto done; + } + if ((cvret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */ + clicon_err(OE_YANG, errno, "parsing cv"); + goto done; + } + if (cvret == 0){ /* parsing failed */ + clicon_err(OE_YANG, errno, "Parsing CV: %s", reason); + goto done; + } + ok: + retval = 0; + done: + if (prefix) + free(prefix); + 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 @@ -1495,6 +1552,10 @@ ys_populate(yang_stmt *ys, if (ys_populate_identity(ys, NULL) < 0) goto done; break; + case Y_UNKNOWN: + if (ys_populate_unknown(ys) < 0) + goto done; + break; default: break; } @@ -1555,12 +1616,11 @@ yang_augment_node(yang_stmt *ys, yang_stmt *yss = NULL; yang_stmt *yc; int i; - + schema_nodeid = ys->ys_argument; clicon_debug(1, "%s %s", __FUNCTION__, schema_nodeid); - /* Find the target */ - if (yang_abs_schema_nodeid(ysp, schema_nodeid, -1, &yss) < 0) + if (yang_abs_schema_nodeid(ysp, ys, schema_nodeid, -1, &yss) < 0) goto done; if (yss == NULL) goto ok; @@ -1646,14 +1706,15 @@ yang_expand(yang_node *yn) prefix = yarg_prefix(ys); /* And this its prefix */ if (ys_grouping_resolve(ys, prefix, name, &ygrouping) < 0) goto done; - if (prefix) - free(prefix); + if (ygrouping == NULL){ - clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found", - __FUNCTION__, ys->ys_argument); + clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"", + __FUNCTION__, ys->ys_argument, ys_module(ys)->ys_argument); goto done; break; } + if (prefix) + free(prefix); /* XXX move up */ /* Check mark flag to see if this grouping (itself) has been expanded If not, this needs to be done before we can insert it into the 'uses' place */ @@ -1825,48 +1886,69 @@ yang_parse_file(int fd, return ymod; /* top-level (sub)module */ } -/*! No specific revision give. Match a yang file given dir and module +/*! No specific revision give. Match a yang file given module * @param[in] h CLICON handle - * @param[in] yang_dir Directory where all YANG module files reside + * @param[in] dir Directory, if NULL, look in YANG_DIR path * @param[in] module Name of main YANG module. + * @param[in] revision Revision or NULL * @param[out] fbuf Buffer containing filename - * + * @note for bootstrapping, dir may have to be set. * @retval 1 Match found, Most recent entry returned in fbuf * @retval 0 No matching entry found * @retval -1 Error */ static int -yang_parse_find_match(const char *yang_dir, - const char *module, +yang_parse_find_match(clicon_handle h, + const char *module, + const char *revision, cbuf *fbuf) { - int retval = -1; + int retval = -1; struct dirent *dp = NULL; int ndp; cbuf *regex = NULL; + cxobj *x; + cxobj *xc; + char *dir; + /* get clicon config file in xml form */ + if ((x = clicon_conf_xml(h)) == NULL) + goto ok; if ((regex = cbuf_new()) == NULL){ clicon_err(OE_YANG, errno, "cbuf_new"); goto done; } /* RFC 6020: The name of the file SHOULD be of the form: - module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' ) - revision-date ::= 4DIGIT "-" 2DIGIT "-" 2DIGIT - */ - cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$", - module); - if ((ndp = clicon_file_dirent(yang_dir, - &dp, - cbuf_get(regex), - S_IFREG)) < 0) - goto done; - /* Entries are sorted, last entry should be most recent date */ - if (ndp != 0){ - cprintf(fbuf, "%s/%s", yang_dir, dp[ndp-1].d_name); - retval = 1; - } + * module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' ) + * revision-date ::= 4DIGIT "-" 2DIGIT "-" 2DIGIT + */ + if (revision) + cprintf(regex, "^%s@%s(.yang)$", module, revision); else - retval = 0; + cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$", + module); + xc = NULL; + while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) { + if (strcmp(xml_name(xc), "CLICON_YANG_DIR") != 0) + continue; + dir = xml_body(xc); + /* get all matching files in this directory */ + if ((ndp = clicon_file_dirent(dir, + &dp, + cbuf_get(regex), + 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); + retval = 1; + goto done; + } + } + ok: + retval = 0; done: if (regex) cbuf_free(regex); @@ -1875,7 +1957,6 @@ yang_parse_find_match(const char *yang_dir, return retval; } - /*! Open a file, read into a string and invoke yang parsing * * Similar to clicon_yang_str(), just read a file first @@ -1903,7 +1984,6 @@ yang_parse_filename(const char *filename, int fd = -1; struct stat st; - clicon_log(LOG_DEBUG, "Parsing yang file: %s", filename); if (stat(filename, &st) < 0){ clicon_err(OE_YANG, errno, "%s not found", filename); goto done; @@ -1921,8 +2001,8 @@ yang_parse_filename(const char *filename, } static yang_stmt * -yang_parse_module(const char *module, - const char *dir, +yang_parse_module(clicon_handle h, + const char *module, const char *revision, yang_spec *ysp) { @@ -1934,16 +2014,12 @@ yang_parse_module(const char *module, clicon_err(OE_YANG, errno, "cbuf_new"); goto done; } - if (revision) - cprintf(fbuf, "%s/%s@%s.yang", dir, module, revision); - else{ - /* No specific revision, Match a yang file */ - if ((nr = yang_parse_find_match(dir, module, fbuf)) < 0) - goto done; - if (nr == 0){ - clicon_err(OE_YANG, errno, "No matching %s yang files found in %s (expected module name or absolute filename)", module, dir); - goto done; - } + /* Match a yang file with or without revision in yang-dir list */ + if ((nr = yang_parse_find_match(h, module, revision, fbuf)) < 0) + goto done; + if (nr == 0){ + clicon_err(OE_YANG, errno, "No yang files found matching \"%s\" in the list of CLICON_YANG_DIRs", module); + goto done; } if ((ymod = yang_parse_filename(cbuf_get(fbuf), ysp)) == NULL) goto done; @@ -1953,10 +2029,9 @@ yang_parse_module(const char *module, return ymod; /* top-level (sub)module */ } -/*! Parse one yang module then go through (sub)modules and parse them recursively +/*! Given a (sub)module, parse all (sub)modules in turn recursively * * @param[in] h CLICON handle - * @param[in] yang_dir Directory where all YANG module files reside * @param[in] module Name of main YANG module. Or absolute file name. * @param[in] revision Module revision date or NULL * @param[in] ysp Yang specification. Should have been created by caller using yspec_new @@ -1972,8 +2047,8 @@ yang_parse_module(const char *module, * clixon_yang_parseparse # Actual yang parsing using yacc */ static int -yang_parse_recurse(yang_stmt *ymod, - const char *dir, +yang_parse_recurse(clicon_handle h, + yang_stmt *ymod, yang_spec *ysp) { int retval = -1; @@ -1982,21 +2057,29 @@ yang_parse_recurse(yang_stmt *ymod, char *submodule; char *subrevision; yang_stmt *subymod; + enum rfc_6020 keyw; - /* go through all import statements of ysp (or its module) */ + /* go through all import (modules) and include(submodules) of ysp */ while ((yi = yn_each((yang_node*)ymod, yi)) != NULL){ - if (yi->ys_keyword != Y_IMPORT) + keyw = yi->ys_keyword; + if (keyw != Y_IMPORT && keyw != Y_INCLUDE) continue; + /* common part */ submodule = yi->ys_argument; + /* Is there a specific revision (or just latest)? */ if ((yrev = yang_find((yang_node*)yi, Y_REVISION_DATE, NULL)) != NULL) subrevision = yrev->ys_argument; else subrevision = NULL; - if (yang_find((yang_node*)ysp, Y_MODULE, submodule) == NULL){ + /* if already loaded, ignore, else parse the file */ + if (yang_find((yang_node*)ysp, + keyw=Y_IMPORT?Y_MODULE:Y_SUBMODULE, + submodule) == NULL){ /* recursive call */ - if ((subymod = yang_parse_module(submodule, dir, subrevision, ysp)) == NULL) + if ((subymod = yang_parse_module(h, submodule, subrevision, ysp)) == NULL) goto done; - if (yang_parse_recurse(subymod, dir, ysp) < 0){ + /* Go through its sub-modules recursively */ + if (yang_parse_recurse(h, subymod, ysp) < 0){ ymod = NULL; goto done; } @@ -2019,7 +2102,8 @@ ys_schemanode_check(yang_stmt *ys, yp = ys->ys_parent; switch (ys->ys_keyword){ case Y_AUGMENT: - if (yp->yn_keyword == Y_MODULE) /* Not top-level */ + if (yp->yn_keyword == Y_MODULE || /* Not top-level */ + yp->yn_keyword == Y_SUBMODULE) break; /* fallthru */ case Y_REFINE: @@ -2035,7 +2119,7 @@ ys_schemanode_check(yang_stmt *ys, break; case Y_DEVIATION: yspec = ys_spec(ys); - if (yang_abs_schema_nodeid(yspec, ys->ys_argument, -1, &yres) < 0) + if (yang_abs_schema_nodeid(yspec, ys, ys->ys_argument, -1, &yres) < 0) goto done; if (yres == NULL){ clicon_err(OE_YANG, 0, "schemanode sanity check of %s", ys->ys_argument); @@ -2127,10 +2211,69 @@ yang_features(clicon_handle h, return retval; } +/*! Merge yang submodule into the module it belongs to + * Skip submodule header fields + * @param[in] h Clicon handle + * @param[in] yspec Yang spec + * @param[in] ysubm Yang submodule + */ +static int +yang_merge_submodules(clicon_handle h, + yang_spec *yspec, + yang_stmt *ysubm) +{ + int retval = -1; + yang_stmt *yb; /* belongs-to */ + yang_stmt *ymod; /* parent yang module */ + yang_stmt *yc; /* yang child */ + char *modname; + int i; + + assert(ysubm->ys_keyword == Y_SUBMODULE); + /* Get parent name (via belongs-to) and find parent module */ + if ((yb = yang_find((yang_node*)ysubm, Y_BELONGS_TO, NULL)) == NULL){ + clicon_err(OE_YANG, ENOENT, "submodule %s does not have a mandatory belongs-to statement", ysubm->ys_argument); + goto done; + } + modname = yb->ys_argument; + if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, modname)) == NULL){ + clicon_err(OE_YANG, ENOENT, "Module %s which submodule %s belongs to is not found", modname, ysubm->ys_argument); + goto done; + } + /* Move sub-module statements to modules + * skip belongs-to, revision, organization, reference, yang-version) + * since main module has its own and may only have one + * XXX: use queue,... + */ + for (i=0; iys_len; i++){ + yc = ysubm->ys_stmt[i]; + if (yc->ys_keyword == Y_BELONGS_TO || + yc->ys_keyword == Y_CONTACT || + yc->ys_keyword == Y_DESCRIPTION || + yc->ys_keyword == Y_ORGANIZATION || + yc->ys_keyword == Y_REVISION || + yc->ys_keyword == Y_REFERENCE || + yc->ys_keyword == Y_YANG_VERSION) + ys_free(yc); + else{ + if (yn_insert((yang_node*)ymod, yc) < 0) + goto done; + } + } + if (ysubm->ys_stmt){ + free(ysubm->ys_stmt); + ysubm->ys_stmt = NULL; + } + ysubm->ys_len = 0; + ys_free(ysubm); + retval = 0; + done: + return retval; +} + /*! Parse top yang module including all its sub-modules. Expand and populate yang tree * * @param[in] h CLICON handle - * @param[in] yang_dir Directory where all YANG module files reside (except mainfile) * @param[in] filename File name containing Yang specification. Overrides module * @param[in] module Name of main YANG module. Or absolute file name. * @param[in] revision Main module revision date string or NULL @@ -2154,7 +2297,6 @@ int yang_parse(clicon_handle h, const char *filename, const char *module, - const char *dir, const char *revision, yang_spec *ysp, yang_stmt **ymodp) @@ -2164,63 +2306,89 @@ yang_parse(clicon_handle h, int i; int modnr; /* Existing number of modules */ + /* Apply steps 2.. on new modules, ie ones after modnr. */ modnr = ysp->yp_len; if (filename){ if ((ymod = yang_parse_filename(filename, ysp)) == NULL) goto done; } else - if ((ymod = yang_parse_module(module, dir, revision, ysp)) == NULL) + if ((ymod = yang_parse_module(h, module, revision, ysp)) == NULL) goto done; - /* From here on, apply actions on new modules, ie ones after modnr. */ - /* Step 1: parse from text to yang parse-tree. */ + /* 1: Parse from text to yang parse-tree. */ /* Iterate through modules */ - if (yang_parse_recurse(ymod, dir, ysp) < 0) + if (yang_parse_recurse(h, ymod, ysp) < 0) goto done; - /* Check cardinality maybe this should be done after grouping/augment */ + /* 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) goto done; - /* Step 2: check features: check if enabled and remove disabled features */ + /* 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) + continue; + } + i = 0; + while (iyp_len){ + int j; + if (ysp->yp_stmt[i]->ys_keyword != Y_SUBMODULE){ + i++; + continue; + } + if (yang_merge_submodules(h, ysp, ysp->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--; + } + + /* 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) goto done; - /* Step 3: Go through parse tree and populate it with cv types */ + /* 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) goto done; - - /* Step 4: Resolve all types: populate type caches. Requires eg length/range cvecs - * from ys_populate step + /* 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++) - yang_apply((yang_node*)ysp->yp_stmt[i], Y_TYPE, ys_resolve_type, NULL); + if (yang_apply((yang_node*)ysp->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 than the - context they are used. Like static scoping. After this we expand all - grouping/uses and unfold all macros into a single tree as they are used. - */ + /* Up to here resolving is made in the context they are defined, rather + * than the context they are used (except for submodules being merged w + * modules). Like static scoping. + * After this we expand all grouping/uses and unfold all macros into a + *single tree as they are used. + */ - /* Step 5: Macro expansion of all grouping/uses pairs. Expansion needs marking */ + /* 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) goto done; yang_apply((yang_node*)ysp->yp_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK); } - /* Step 6: Top-level augmentation of all modules XXX: only new modules? */ + /* 8: Top-level augmentation of all modules XXX: only new modules? */ if (yang_augment_spec(ysp) < 0) goto done; - /* Step 7: sanity check of schemanode references, need more here */ + /* 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) goto done; + /* Return main module parsed in step 1 */ if (ymodp) *ymodp = ymod; retval = 0; @@ -2367,6 +2535,7 @@ schema_nodeid_vec(yang_node *yn, /*! Given an absolute schema-nodeid (eg /a/b/c) find matching yang spec * @param[in] yspec Yang specification. + * @param[in] yn Original yang stmt (where call is made) if any * @param[in] schema_nodeid Absolute schema-node-id, ie /a/b * @param[in] keyword A schemode of this type, or -1 if any * @param[out] yres Result yang statement node, or NULL if not found @@ -2382,6 +2551,7 @@ schema_nodeid_vec(yang_node *yn, */ int yang_abs_schema_nodeid(yang_spec *yspec, + yang_stmt *yn, char *schema_nodeid, enum rfc_6020 keyword, yang_stmt **yres) @@ -2389,7 +2559,7 @@ yang_abs_schema_nodeid(yang_spec *yspec, int retval = -1; char **vec = NULL; int nvec; - yang_stmt *ymod; + yang_stmt *ymod = NULL; char *id; char *prefix = NULL; yang_stmt *yprefix; @@ -2421,16 +2591,20 @@ yang_abs_schema_nodeid(yang_spec *yspec, } prefix[id-vec[1]] = '\0'; id++; - ymod = NULL; - while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { - if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL && - strcmp(yprefix->ys_argument, prefix) == 0){ - break; + if (yn) /* Find module using local prefix definition */ + ymod = yang_find_module_by_prefix(yn, prefix); + if (ymod == NULL){ /* Try (global) prefix the module itself uses */ + ymod = NULL; + while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { + if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL && + strcmp(yprefix->ys_argument, prefix) == 0){ + break; + } } } - if (ymod == NULL){ /* Try with topnode */ + if (ymod == NULL){ /* Try find id from topnode without prefix XXX remove?*/ if ((ys = yang_find_topnode(yspec, id, YC_SCHEMANODE)) == NULL){ - clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id); + clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id); goto done; } if ((ymod = ys_module(ys)) == NULL){ @@ -2439,7 +2613,7 @@ yang_abs_schema_nodeid(yang_spec *yspec, } if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL && strcmp(yprefix->ys_argument, prefix) != 0){ - clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id); + clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id); goto done; } } @@ -2546,12 +2720,7 @@ ys_parse_sub(yang_stmt *ys, char *extra) { int retval = -1; - int cvret; - char *reason = NULL; - yang_stmt *ymod; uint8_t fd; - char *prefix = NULL; - char *name; switch (ys->ys_keyword){ case Y_FRACTION_DIGITS: @@ -2563,40 +2732,17 @@ ys_parse_sub(yang_stmt *ys, goto done; } break; - case Y_UNKNOWN: + case Y_UNKNOWN: /* XXX This code assumes ymod already loaded + but it may not be */ if (extra == NULL) break; - /* Find extension, if found, store it as unknown, if not, - break for error */ - prefix = yarg_prefix(ys); /* And this its prefix */ - name = yarg_id(ys); /* This is the type to resolve */ - if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL) - goto ok; /* shouldnt happen */ - if (yang_find((yang_node*)ymod, Y_EXTENSION, name) == NULL){ - clicon_err(OE_YANG, errno, "Extension %s:%s not found", prefix, name); - goto done; - } - if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){ - clicon_err(OE_YANG, errno, "cv_new"); - goto done; - } - if ((cvret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */ - clicon_err(OE_YANG, errno, "parsing cv"); - goto done; - } - if (cvret == 0){ /* parsing failed */ - clicon_err(OE_YANG, errno, "Parsing CV: %s", reason); - goto done; - } + ys->ys_extra = extra; break; default: break; } - ok: retval = 0; done: - if (prefix) - free(prefix); return retval; } @@ -2654,7 +2800,6 @@ yang_config(yang_stmt *ys) int yang_spec_parse_module(clicon_handle h, char *module, - char *dir, char *revision, yang_spec *yspec, yang_stmt **ymodp) @@ -2674,11 +2819,7 @@ yang_spec_parse_module(clicon_handle h, clicon_err(OE_YANG, EINVAL, "yang module illegal format"); goto done; } - if (dir == NULL){ - clicon_err(OE_YANG, EINVAL, "yang dir not set"); - goto done; - } - if (yang_parse(h, NULL, module, dir, revision, yspec, ymodp) < 0) + if (yang_parse(h, NULL, module, revision, yspec, ymodp) < 0) goto done; retval = 0; done: @@ -2698,7 +2839,6 @@ yang_spec_parse_module(clicon_handle h, int yang_spec_parse_file(clicon_handle h, char *filename, - char *dir, yang_spec *yspec, yang_stmt **ymodp) { @@ -2708,11 +2848,7 @@ yang_spec_parse_file(clicon_handle h, clicon_err(OE_YANG, EINVAL, "yang spec is NULL"); goto done; } - if (dir == NULL){ - clicon_err(OE_YANG, EINVAL, "yang dir not set"); - goto done; - } - if (yang_parse(h, filename, NULL, dir, NULL, yspec, ymodp) < 0) + if (yang_parse(h, filename, NULL, NULL, yspec, ymodp) < 0) goto done; retval = 0; done: diff --git a/lib/src/clixon_yang_cardinality.c b/lib/src/clixon_yang_cardinality.c index ab6a43d6..60964c86 100644 --- a/lib/src/clixon_yang_cardinality.c +++ b/lib/src/clixon_yang_cardinality.c @@ -193,7 +193,8 @@ yang_cardinality(clicon_handle h, pk = yt->ys_keyword; /* 0) Find parent sub-parts of cardinality vector */ - ycplist = ycard_find(pk, 0, yclist, 0); + if ((ycplist = ycard_find(pk, 0, yclist, 0)) == NULL) + goto ok; /* skip */ /* 1) For all children, if neither in 0..n, 0..1, 1 or 1..n ->ERROR */ i = 0; while (iys_len){ @@ -231,14 +232,15 @@ yang_cardinality(clicon_handle h, } if (0) { /* Notyet */ - /* 4) Recurse */ - i = 0; - while (iys_len){ /* Note, children may be removed */ - ys = yt->ys_stmt[i++]; - if (yang_cardinality(h, ys, modname) < 0) - goto done; - } + /* 4) Recurse */ + i = 0; + while (iys_len){ /* Note, children may be removed */ + ys = yt->ys_stmt[i++]; + if (yang_cardinality(h, ys, modname) < 0) + goto done; + } } + ok: retval = 0; done: return retval; diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index cf2f3208..539126df 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -77,7 +77,6 @@ * * Load RFC7895 yang spec, module-set-id, etc. * @param[in] h Clicon handle - * @note CLIXON_DATADIR is hardcoded */ int yang_modules_init(clicon_handle h) @@ -94,7 +93,7 @@ yang_modules_init(clicon_handle h) goto done; } /* Ensure revision exists is set */ - if (yang_spec_parse_module(h, "ietf-yang-library", CLIXON_DATADIR, NULL, yspec, NULL)< 0) + if (yang_spec_parse_module(h, "ietf-yang-library", NULL, yspec, NULL)< 0) goto done; /* Find revision */ if (yang_modules_revision(h) == NULL){ @@ -121,7 +120,8 @@ yang_modules_revision(clicon_handle h) char *revision = NULL; yspec = clicon_dbspec_yang(h); - if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, "ietf-yang-library")) != NULL){ + if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, "ietf-yang-library")) != NULL || + (ymod = yang_find((yang_node*)yspec, Y_SUBMODULE, "ietf-yang-library")) != NULL){ if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL){ revision = yrev->ys_argument; } @@ -172,7 +172,8 @@ yang_modules_state_get(clicon_handle h, char *module = "ietf-yang-library"; module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID"); - if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){ + if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL && + (ylib = yang_find((yang_node*)yspec, Y_SUBMODULE, module)) == NULL){ clicon_err(OE_YANG, 0, "%s not found", module); goto done; } @@ -189,7 +190,8 @@ yang_modules_state_get(clicon_handle h, ymod = NULL; while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { - if (ymod->ys_keyword != Y_MODULE) + if (ymod->ys_keyword != Y_MODULE && + ymod->ys_keyword != Y_SUBMODULE) continue; cprintf(cb,""); cprintf(cb,"%s", ymod->ys_argument); diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index f947c833..0af79687 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -42,9 +42,6 @@ * identifier_ref = prefix : IDENTIFIER * node_identier = prefix : IDENTIFIER * - * Missing top-level statements (will break parser if in yang spec): - * - error-app-tag-stmt - * - any-data-stmt * Missing args (just strings); * - length-arg-str * - path-arg-str @@ -124,6 +121,7 @@ %token K_MANDATORY %token K_MAX_ELEMENTS %token K_MIN_ELEMENTS +%token K_MODIFIER %token K_MODULE %token K_MUST %token K_NAMESPACE @@ -369,29 +367,28 @@ file : module_stmt MY_EOF /* For extensions */ unknown_stmt : ustring ':' ustring ';' - { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("0"); - if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("0"); + { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt"); + if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt"); clicon_debug(2,"unknown-stmt -> ustring : ustring"); } | ustring ':' ustring ' ' string ';' - { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("0"); - if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("0"); } + { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknwon_stmt"); + if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("unknwon_stmt"); } clicon_debug(2,"unknown-stmt -> ustring : ustring string"); - if ($5) free($5); } ; /* module identifier-arg-str */ module_stmt : K_MODULE identifier_str - { if ((_YY->yy_module = ysp_add_push(_yy, Y_MODULE, $2)) == NULL) _YYERROR("1"); + { if ((_YY->yy_module = ysp_add_push(_yy, Y_MODULE, $2)) == NULL) _YYERROR("module_stmt"); } '{' module_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("2"); - clicon_debug(2,"module_stmt -> id-arg-str { module-substmts }");} + { if (ystack_pop(_yy) < 0) _YYERROR("module_stmt"); + clicon_debug(2,"module_stmt -> id-arg-str { module-substmts }");} ; module_substmts : module_substmts module_substmt - { clicon_debug(2,"module-substmts -> module-substmts module-substm");} + {clicon_debug(2,"module-substmts -> module-substmts module-substm");} | module_substmt { clicon_debug(2,"module-substmts ->");} ; @@ -406,10 +403,11 @@ module_substmt : module_header_stmts { clicon_debug(2,"module-substmt -> module- ; /* submodule */ -submodule_stmt : K_SUBMODULE identifier_str '{' submodule_substmts '}' - { if ((_YY->yy_module = ysp_add_push(_yy, Y_SUBMODULE, $2)) == NULL) _YYERROR("3"); - clicon_debug(2,"submodule -> id-arg-str { submodule-stmts }"); - } +submodule_stmt : K_SUBMODULE identifier_str + { if ((_YY->yy_module = ysp_add_push(_yy, Y_SUBMODULE, $2)) == NULL) _YYERROR("submodule_stmt"); } + '{' submodule_substmts '}' + { if (ystack_pop(_yy) < 0) _YYERROR("submodule_stmt"); + clicon_debug(2,"submodule_stmt -> id-arg-str { submodule-substmts }");} ; submodule_substmts : submodule_substmts submodule_substmt @@ -464,16 +462,16 @@ submodule_header_stmt : yang_version_stmt ; /* yang-version-stmt = yang-version-keyword yang-version-arg-str */ -yang_version_stmt : K_YANG_VERSION string ';' - { if (ysp_add(_yy, Y_YANG_VERSION, $2, NULL) == NULL) _YYERROR("83"); +yang_version_stmt : K_YANG_VERSION string stmtend + { if (ysp_add(_yy, Y_YANG_VERSION, $2, NULL) == NULL) _YYERROR("yang_version_stmt"); clicon_debug(2,"yang-version-stmt -> YANG-VERSION string"); } ; /* import */ import_stmt : K_IMPORT identifier_str - { if (ysp_add_push(_yy, Y_IMPORT, $2) == NULL) _YYERROR("81"); } + { if (ysp_add_push(_yy, Y_IMPORT, $2) == NULL) _YYERROR("import_stmt"); } '{' import_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("82"); + { if (ystack_pop(_yy) < 0) _YYERROR("import_stmt"); clicon_debug(2,"import-stmt -> IMPORT id-arg-str { import-substmts }");} ; @@ -484,69 +482,79 @@ import_substmts : import_substmts import_substmt ; import_substmt : prefix_stmt { clicon_debug(2,"import-stmt -> prefix-stmt"); } - | revision_date_stmt { clicon_debug(2,"import-stmt -> revision-date-stmt"); } + | revision_date_stmt { clicon_debug(2,"import-stmt -> revision-date-stmt"); } + | description_stmt { clicon_debug(2,"import-stmt -> description-stmt"); } + | reference_stmt { clicon_debug(2,"import-stmt -> reference-stmt"); } ; include_stmt : K_INCLUDE identifier_str ';' - { if (ysp_add(_yy, Y_INCLUDE, $2, NULL)== NULL) _YYERROR("97"); - clicon_debug(2,"include-stmt -> id-arg-str"); } - | K_INCLUDE identifier_str '{' revision_date_stmt '}' - { if (ysp_add(_yy, Y_INCLUDE, $2, NULL)== NULL) _YYERROR("98"); - clicon_debug(2,"include-stmt -> id-arg-str { revision-date-stmt }"); } + { if (ysp_add(_yy, Y_INCLUDE, $2, NULL)== NULL) _YYERROR("include_stmt"); + clicon_debug(2,"include-stmt -> id-str"); } + | K_INCLUDE identifier_str '{' include_substmts '}' + { if (ysp_add(_yy, Y_INCLUDE, $2, NULL)== NULL) _YYERROR("include_stmt"); + clicon_debug(2,"include-stmt -> id-str { include-substmts }"); } ; +include_substmts : include_substmts include_substmt + { clicon_debug(2,"include-substmts -> include-substmts include-substm");} + | include_substmt + { clicon_debug(2,"include-substmts ->");} + ; + +include_substmt : revision_date_stmt { clicon_debug(2,"include-stmt -> revision-date-stmt"); } + | description_stmt { clicon_debug(2,"include-stmt -> description-stmt"); } + | reference_stmt { clicon_debug(2,"include-stmt -> reference-stmt"); } + ; + + /* namespace-stmt = namespace-keyword sep uri-str */ namespace_stmt : K_NAMESPACE string stmtend - { if (ysp_add(_yy, Y_NAMESPACE, $2, NULL)== NULL) _YYERROR("99"); + { if (ysp_add(_yy, Y_NAMESPACE, $2, NULL)== NULL) _YYERROR("namespace_stmt"); clicon_debug(2,"namespace-stmt -> NAMESPACE string"); } ; -prefix_stmt : K_PREFIX identifier_str ';' /* XXX prefix-arg-str */ - { if (ysp_add(_yy, Y_PREFIX, $2, NULL)== NULL) _YYERROR("100"); +prefix_stmt : K_PREFIX identifier_str stmtend /* XXX prefix-arg-str */ + { if (ysp_add(_yy, Y_PREFIX, $2, NULL)== NULL) _YYERROR("prefix_stmt"); clicon_debug(2,"prefix-stmt -> PREFIX string ;");} ; -belongs_to_stmt : K_BELONGS_TO identifier_str ';' - - { if (ysp_add(_yy, Y_BELONGS_TO, $2, NULL)== NULL) _YYERROR("100"); - clicon_debug(2,"belongs-to-stmt -> BELONGS-TO id-arg-str ;");} - | K_BELONGS_TO identifier_str '{' prefix_stmt '}' - { if (ysp_add(_yy, Y_BELONGS_TO, $2, NULL)== NULL) _YYERROR("98"); +belongs_to_stmt : K_BELONGS_TO identifier_str '{' prefix_stmt '}' + { if (ysp_add(_yy, Y_BELONGS_TO, $2, NULL)== NULL) _YYERROR("belongs_to_stmt"); clicon_debug(2,"belongs-to-stmt -> BELONGS-TO id-arg-str { prefix-stmt } ");} ; -organization_stmt: K_ORGANIZATION string ';' - { if (ysp_add(_yy, Y_ORGANIZATION, $2, NULL)== NULL) _YYERROR("102"); +organization_stmt: K_ORGANIZATION string stmtend + { if (ysp_add(_yy, Y_ORGANIZATION, $2, NULL)== NULL) _YYERROR("belongs_to_stmt"); clicon_debug(2,"organization-stmt -> ORGANIZATION string ;");} ; -contact_stmt : K_CONTACT string ';' - { if (ysp_add(_yy, Y_CONTACT, $2, NULL)== NULL) _YYERROR("95"); +contact_stmt : K_CONTACT string stmtend + { if (ysp_add(_yy, Y_CONTACT, $2, NULL)== NULL) _YYERROR("contact_stmt"); clicon_debug(2,"contact-stmt -> CONTACT string"); } ; -description_stmt: K_DESCRIPTION string ';' - { if (ysp_add(_yy, Y_DESCRIPTION, $2, NULL)== NULL) _YYERROR("101"); +description_stmt : K_DESCRIPTION string stmtend + { if (ysp_add(_yy, Y_DESCRIPTION, $2, NULL)== NULL) _YYERROR("description_stmt"); clicon_debug(2,"description-stmt -> DESCRIPTION string ;");} ; -reference_stmt: K_REFERENCE string ';' - { if (ysp_add(_yy, Y_REFERENCE, $2, NULL)== NULL) _YYERROR("105"); +reference_stmt : K_REFERENCE string stmtend + { if (ysp_add(_yy, Y_REFERENCE, $2, NULL)== NULL) _YYERROR("reference_stmt"); clicon_debug(2,"reference-stmt -> REFERENCE string ;");} ; units_stmt : K_UNITS string ';' - { if (ysp_add(_yy, Y_UNITS, $2, NULL)== NULL) _YYERROR("93"); + { if (ysp_add(_yy, Y_UNITS, $2, NULL)== NULL) _YYERROR("units_stmt"); clicon_debug(2,"units-stmt -> UNITS string"); } ; revision_stmt : K_REVISION string ';' /* XXX date-arg-str */ - { if (ysp_add(_yy, Y_REVISION, $2, NULL) == NULL) _YYERROR("4"); + { if (ysp_add(_yy, Y_REVISION, $2, NULL) == NULL) _YYERROR("revision_stmt"); clicon_debug(2,"revision-stmt -> date-arg-str ;"); } | K_REVISION string - { if (ysp_add_push(_yy, Y_REVISION, $2) == NULL) _YYERROR("5"); } + { if (ysp_add_push(_yy, Y_REVISION, $2) == NULL) _YYERROR("revision_stmt"); } '{' revision_substmts '}' /* XXX date-arg-str */ - { if (ystack_pop(_yy) < 0) _YYERROR("6"); + { if (ystack_pop(_yy) < 0) _YYERROR("revision_stmt"); clicon_debug(2,"revision-stmt -> date-arg-str { revision-substmts }"); } ; @@ -570,19 +578,18 @@ revision_stmts : revision_stmts revision_stmt { clicon_debug(2,"revision-stmts -> "); } ; -revision_date_stmt : K_REVISION_DATE string ';' /* XXX date-arg-str */ - { if (ysp_add(_yy, Y_REVISION_DATE, $2, NULL) == NULL) _YYERROR("96"); +revision_date_stmt : K_REVISION_DATE string stmtend /* XXX date-arg-str */ + { if (ysp_add(_yy, Y_REVISION_DATE, $2, NULL) == NULL) _YYERROR("revision_date_stmt"); clicon_debug(2,"revision-date-stmt -> date;"); } ; -/* Extension */ -extension_stmt: K_EXTENSION identifier_str ';' - { if (ysp_add(_yy, Y_EXTENSION, $2, NULL) == NULL) _YYERROR("59"); +extension_stmt : K_EXTENSION identifier_str ';' + { if (ysp_add(_yy, Y_EXTENSION, $2, NULL) == NULL) _YYERROR("extension_stmt"); clicon_debug(2,"extenstion-stmt -> EXTENSION id-arg-str ;"); } | K_EXTENSION identifier_str - { if (ysp_add_push(_yy, Y_EXTENSION, $2) == NULL) _YYERROR("60"); } + { if (ysp_add_push(_yy, Y_EXTENSION, $2) == NULL) _YYERROR("extension_stmt"); } '{' extension_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("61"); + { if (ystack_pop(_yy) < 0) _YYERROR("extension_stmt"); clicon_debug(2,"extension-stmt -> FEATURE id-arg-str { extension-substmts }"); } ; @@ -612,13 +619,13 @@ yin_element_stmt1 : K_YIN_ELEMENT bool_str stmtend {free($2);} /* Identity */ identity_stmt : K_IDENTITY identifier_str ';' - { if (ysp_add(_yy, Y_IDENTITY, $2, NULL) == NULL) _YYERROR("65"); + { if (ysp_add(_yy, Y_IDENTITY, $2, NULL) == NULL) _YYERROR("identity_stmt"); clicon_debug(2,"identity-stmt -> IDENTITY string ;"); } | K_IDENTITY identifier_str - { if (ysp_add_push(_yy, Y_IDENTITY, $2) == NULL) _YYERROR("66"); } + { if (ysp_add_push(_yy, Y_IDENTITY, $2) == NULL) _YYERROR("identity_stmt"); } '{' identity_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("67"); + { if (ystack_pop(_yy) < 0) _YYERROR("identity_stmt"); clicon_debug(2,"identity-stmt -> IDENTITY string { identity-substmts }"); } ; @@ -636,19 +643,19 @@ identity_substmt : base_stmt { clicon_debug(2,"identity-substmt -> base- | { clicon_debug(2,"identity-substmt -> "); } ; -base_stmt : K_BASE identifier_ref_str ';' - { if (ysp_add(_yy, Y_BASE, $2, NULL)== NULL) _YYERROR("90"); +base_stmt : K_BASE identifier_ref_str stmtend + { if (ysp_add(_yy, Y_BASE, $2, NULL)== NULL) _YYERROR("base_stmt"); clicon_debug(2,"base-stmt -> BASE identifier-ref-arg-str"); } ; /* Feature */ feature_stmt : K_FEATURE identifier_str ';' - { if (ysp_add(_yy, Y_FEATURE, $2, NULL) == NULL) _YYERROR("62"); + { if (ysp_add(_yy, Y_FEATURE, $2, NULL) == NULL) _YYERROR("feature_stmt"); clicon_debug(2,"feature-stmt -> FEATURE id-arg-str ;"); } | K_FEATURE identifier_str - { if (ysp_add_push(_yy, Y_FEATURE, $2) == NULL) _YYERROR("63"); } + { if (ysp_add_push(_yy, Y_FEATURE, $2) == NULL) _YYERROR("feature_stmt"); } '{' feature_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("64"); + { if (ystack_pop(_yy) < 0) _YYERROR("feature_stmt"); clicon_debug(2,"feature-stmt -> FEATURE id-arg-str { feature-substmts }"); } ; @@ -669,15 +676,15 @@ feature_substmt : if_feature_stmt { clicon_debug(2,"feature-substmt -> if-fea /* if-feature-stmt = if-feature-keyword sep if-feature-expr-str */ if_feature_stmt : K_IF_FEATURE string stmtend - { if (ysp_add(_yy, Y_IF_FEATURE, $2, NULL) == NULL) _YYERROR("85"); + { if (ysp_add(_yy, Y_IF_FEATURE, $2, NULL) == NULL) _YYERROR("if_feature_stmt"); clicon_debug(2,"if-feature-stmt -> IF-FEATURE identifier-ref-arg-str"); } ; /* Typedef */ typedef_stmt : K_TYPEDEF identifier_str - { if (ysp_add_push(_yy, Y_TYPEDEF, $2) == NULL) _YYERROR("46"); } + { if (ysp_add_push(_yy, Y_TYPEDEF, $2) == NULL) _YYERROR("typedef_stmt"); } '{' typedef_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("47"); + { if (ystack_pop(_yy) < 0) _YYERROR("typedef_stmt"); clicon_debug(2,"typedef-stmt -> TYPEDEF id-arg-str { typedef-substmts }"); } ; @@ -699,13 +706,13 @@ typedef_substmt : type_stmt { clicon_debug(2,"typedef-substmt -> type-s /* Type */ type_stmt : K_TYPE identifier_ref_str ';' - { if (ysp_add(_yy, Y_TYPE, $2, NULL) == NULL) _YYERROR("48"); + { if (ysp_add(_yy, Y_TYPE, $2, NULL) == NULL) _YYERROR("type_stmt"); clicon_debug(2,"type-stmt -> TYPE identifier-ref-arg-str ;");} | K_TYPE identifier_ref_str - { if (ysp_add_push(_yy, Y_TYPE, $2) == NULL) _YYERROR("49"); + { if (ysp_add_push(_yy, Y_TYPE, $2) == NULL) _YYERROR("type_stmt"); } '{' type_body_stmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("50"); + { if (ystack_pop(_yy) < 0) _YYERROR("type_stmt"); clicon_debug(2,"type-stmt -> TYPE identifier-ref-arg-str { type-body-stmts }");} ; @@ -742,13 +749,13 @@ type_body_stmt/* numerical-restrictions */ /* range-stmt */ range_stmt : K_RANGE string ';' /* XXX range-arg-str */ - { if (ysp_add(_yy, Y_RANGE, $2, NULL) == NULL) _YYERROR("68"); + { if (ysp_add(_yy, Y_RANGE, $2, NULL) == NULL) _YYERROR("range_stmt"); clicon_debug(2,"range-stmt -> RANGE string ;"); } | K_RANGE string - { if (ysp_add_push(_yy, Y_RANGE, $2) == NULL) _YYERROR("69"); } + { if (ysp_add_push(_yy, Y_RANGE, $2) == NULL) _YYERROR("range_stmt"); } '{' range_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("70"); + { if (ystack_pop(_yy) < 0) _YYERROR("range_stmt"); clicon_debug(2,"range-stmt -> RANGE string { range-substmts }"); } ; @@ -767,7 +774,7 @@ range_substmt : error_message_stmt { clicon_debug(2,"range-substmt -> error-me /* fraction-digits-stmt = fraction-digits-keyword fraction-digits-arg-str */ fraction_digits_stmt : K_FRACTION_DIGITS string stmtend - { if (ysp_add(_yy, Y_FRACTION_DIGITS, $2, NULL) == NULL) _YYERROR("84"); + { if (ysp_add(_yy, Y_FRACTION_DIGITS, $2, NULL) == NULL) _YYERROR("fraction_digits_stmt"); clicon_debug(2,"fraction-digits-stmt -> FRACTION-DIGITS string"); } ; @@ -776,15 +783,22 @@ meta_stmts : meta_stmts meta_stmt { clicon_debug(2,"meta-stmts -> meta-stmts | meta_stmt { clicon_debug(2,"meta-stmts -> meta-stmt"); } ; +meta_stmt : organization_stmt { clicon_debug(2,"meta-stmt -> organization-stmt"); } + | contact_stmt { clicon_debug(2,"meta-stmt -> contact-stmt"); } + | description_stmt { clicon_debug(2,"meta-stmt -> description-stmt"); } + | reference_stmt { clicon_debug(2,"meta-stmt -> reference-stmt"); } + ; + + /* length-stmt */ length_stmt : K_LENGTH string ';' /* XXX length-arg-str */ - { if (ysp_add(_yy, Y_LENGTH, $2, NULL) == NULL) _YYERROR("53"); + { if (ysp_add(_yy, Y_LENGTH, $2, NULL) == NULL) _YYERROR("length_stmt"); clicon_debug(2,"length-stmt -> LENGTH string ;"); } | K_LENGTH string - { if (ysp_add_push(_yy, Y_LENGTH, $2) == NULL) _YYERROR("54"); } + { if (ysp_add_push(_yy, Y_LENGTH, $2) == NULL) _YYERROR("length_stmt"); } '{' length_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("55"); + { if (ystack_pop(_yy) < 0) _YYERROR("length_stmt"); clicon_debug(2,"length-stmt -> LENGTH string { length-substmts }"); } ; @@ -803,13 +817,13 @@ length_substmt : error_message_stmt { clicon_debug(2,"length-substmt -> error-m /* Pattern */ pattern_stmt : K_PATTERN string ';' - { if (ysp_add(_yy, Y_PATTERN, $2, NULL) == NULL) _YYERROR("56"); + { if (ysp_add(_yy, Y_PATTERN, $2, NULL) == NULL) _YYERROR("pattern_stmt"); clicon_debug(2,"pattern-stmt -> PATTERN string ;"); } | K_PATTERN string - { if (ysp_add_push(_yy, Y_PATTERN, $2) == NULL) _YYERROR("57"); } + { if (ysp_add_push(_yy, Y_PATTERN, $2) == NULL) _YYERROR("pattern_stmt"); } '{' pattern_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("58"); + { if (ystack_pop(_yy) < 0) _YYERROR("pattern_stmt"); clicon_debug(2,"pattern-stmt -> PATTERN string { pattern-substmts }"); } ; @@ -819,27 +833,34 @@ pattern_substmts : pattern_substmts pattern_substmt { clicon_debug(2,"pattern-substmts -> pattern-substmt"); } ; -pattern_substmt : reference_stmt { clicon_debug(2,"pattern-substmt -> reference-stmt"); } - | error_message_stmt { clicon_debug(2,"pattern-substmt -> error-message-stmt");} - | unknown_stmt { clicon_debug(2,"pattern-substmt -> unknown-stmt");} +pattern_substmt : modifier_stmt { clicon_debug(2,"pattern-substmt -> modifier-stmt");} + | error_message_stmt { clicon_debug(2,"pattern-substmt -> error-message-stmt");} + | error_app_tag_stmt { clicon_debug(2,"pattern-substmt -> error-app-tag-stmt");} + | description_stmt { clicon_debug(2,"pattern-substmt -> description-stmt");} + | reference_stmt { clicon_debug(2,"pattern-substmt -> reference-stmt"); } + | unknown_stmt { clicon_debug(2,"pattern-substmt -> unknown-stmt");} + | { clicon_debug(2,"pattern-substmt -> "); } ; -default_stmt : K_DEFAULT string ';' - { if (ysp_add(_yy, Y_DEFAULT, $2, NULL)== NULL) _YYERROR("94"); +modifier_stmt : K_MODIFIER string stmtend + { if (ysp_add(_yy, Y_DEFAULT, $2, NULL)== NULL) _YYERROR("modifier_stmt"); + clicon_debug(2,"modifier-stmt -> MODIFIER string"); } + ; + +default_stmt : K_DEFAULT string stmtend + { if (ysp_add(_yy, Y_DEFAULT, $2, NULL)== NULL) _YYERROR("default_stmt"); clicon_debug(2,"default-stmt -> DEFAULT string"); } ; - - /* enum-stmt */ enum_stmt : K_ENUM string ';' - { if (ysp_add(_yy, Y_ENUM, $2, NULL) == NULL) _YYERROR("71"); + { if (ysp_add(_yy, Y_ENUM, $2, NULL) == NULL) _YYERROR("enum_stmt"); clicon_debug(2,"enum-stmt -> ENUM string ;"); } | K_ENUM string - { if (ysp_add_push(_yy, Y_ENUM, $2) == NULL) _YYERROR("72"); } + { if (ysp_add_push(_yy, Y_ENUM, $2) == NULL) _YYERROR("enum_stmt"); } '{' enum_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("73"); + { if (ystack_pop(_yy) < 0) _YYERROR("enum_stmt"); clicon_debug(2,"enum-stmt -> ENUM string { enum-substmts }"); } ; @@ -857,24 +878,24 @@ enum_substmt : value_stmt { clicon_debug(2,"enum-substmt -> value-stm | { clicon_debug(2,"enum-substmt -> "); } ; -path_stmt : K_PATH string ';' /* XXX: path-arg-str */ - { if (ysp_add(_yy, Y_PATH, $2, NULL)== NULL) _YYERROR("91"); +path_stmt : K_PATH string stmtend /* XXX: path-arg-str */ + { if (ysp_add(_yy, Y_PATH, $2, NULL)== NULL) _YYERROR("path_stmt"); clicon_debug(2,"path-stmt -> PATH string"); } ; -require_instance_stmt : K_REQUIRE_INSTANCE bool_str ';' - { if (ysp_add(_yy, Y_REQUIRE_INSTANCE, $2, NULL)== NULL) _YYERROR("92"); +require_instance_stmt : K_REQUIRE_INSTANCE bool_str stmtend + { if (ysp_add(_yy, Y_REQUIRE_INSTANCE, $2, NULL)== NULL) _YYERROR("require_instance_stmt"); clicon_debug(2,"require-instance-stmt -> REQUIRE-INSTANCE string"); } ; /* bit-stmt */ bit_stmt : K_BIT identifier_str ';' - { if (ysp_add(_yy, Y_BIT, $2, NULL) == NULL) _YYERROR("74"); + { if (ysp_add(_yy, Y_BIT, $2, NULL) == NULL) _YYERROR("bit_stmt"); clicon_debug(2,"bit-stmt -> BIT string ;"); } | K_BIT identifier_str - { if (ysp_add_push(_yy, Y_BIT, $2) == NULL) _YYERROR("75"); } + { if (ysp_add_push(_yy, Y_BIT, $2) == NULL) _YYERROR("bit_stmt"); } '{' bit_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("76"); + { if (ystack_pop(_yy) < 0) _YYERROR("bit_stmt"); clicon_debug(2,"bit-stmt -> BIT string { bit-substmts }"); } ; @@ -893,50 +914,50 @@ bit_substmt : position_stmt { clicon_debug(2,"bit-substmt -> positition /* position-stmt = position-keyword position-value-arg-str */ position_stmt : K_POSITION integer_value_str stmtend - { if (ysp_add(_yy, Y_POSITION, $2, NULL) == NULL) _YYERROR("87"); + { if (ysp_add(_yy, Y_POSITION, $2, NULL) == NULL) _YYERROR("position_stmt"); clicon_debug(2,"position-stmt -> POSITION integer-value"); } ; /* status-stmt = status-keyword sep status-arg-str XXX: current-keyword*/ status_stmt : K_STATUS string stmtend - { if (ysp_add(_yy, Y_STATUS, $2, NULL) == NULL) _YYERROR("88"); + { if (ysp_add(_yy, Y_STATUS, $2, NULL) == NULL) _YYERROR("status_stmt"); clicon_debug(2,"status-stmt -> STATUS string"); } ; -config_stmt : K_CONFIG bool_str ';' - { if (ysp_add(_yy, Y_CONFIG, $2, NULL) == NULL) _YYERROR("89"); +config_stmt : K_CONFIG bool_str stmtend + { if (ysp_add(_yy, Y_CONFIG, $2, NULL) == NULL) _YYERROR("config_stmt"); clicon_debug(2,"config-stmt -> CONFIG config-arg-str"); } ; /* mandatory-stmt = mandatory-keyword mandatory-arg-str */ -mandatory_stmt: K_MANDATORY bool_str ';' +mandatory_stmt : K_MANDATORY bool_str stmtend { yang_stmt *ys; - if ((ys = ysp_add(_yy, Y_MANDATORY, $2, NULL))== NULL) _YYERROR("106"); + if ((ys = ysp_add(_yy, Y_MANDATORY, $2, NULL))== NULL) _YYERROR("mandatory_stmt"); clicon_debug(2,"mandatory-stmt -> MANDATORY mandatory-arg-str ;");} ; -presence_stmt: K_PRESENCE string ';' +presence_stmt : K_PRESENCE string stmtend { yang_stmt *ys; - if ((ys = ysp_add(_yy, Y_PRESENCE, $2, NULL))== NULL) _YYERROR("107"); + if ((ys = ysp_add(_yy, Y_PRESENCE, $2, NULL))== NULL) _YYERROR("presence_stmt"); clicon_debug(2,"presence-stmt -> PRESENCE string ;");} ; /* ordered-by-stmt = ordered-by-keyword sep ordered-by-arg-str */ -ordered_by_stmt: K_ORDERED_BY string stmtend +ordered_by_stmt : K_ORDERED_BY string stmtend { yang_stmt *ys; - if ((ys = ysp_add(_yy, Y_ORDERED_BY, $2, NULL))== NULL) _YYERROR("108"); + if ((ys = ysp_add(_yy, Y_ORDERED_BY, $2, NULL))== NULL) _YYERROR("ordered_by_stmt"); clicon_debug(2,"ordered-by-stmt -> ORDERED-BY ordered-by-arg ;");} ; /* must-stmt */ must_stmt : K_MUST string ';' - { if (ysp_add(_yy, Y_MUST, $2, NULL) == NULL) _YYERROR("77"); + { if (ysp_add(_yy, Y_MUST, $2, NULL) == NULL) _YYERROR("must_stmt"); clicon_debug(2,"must-stmt -> MUST string ;"); } | K_MUST string - { if (ysp_add_push(_yy, Y_MUST, $2) == NULL) _YYERROR("78"); } + { if (ysp_add_push(_yy, Y_MUST, $2) == NULL) _YYERROR("must_stmt"); } '{' must_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("79"); + { if (ystack_pop(_yy) < 0) _YYERROR("must_stmt"); clicon_debug(2,"must-stmt -> MUST string { must-substmts }"); } ; @@ -947,42 +968,47 @@ must_substmts : must_substmts must_substmt ; must_substmt : error_message_stmt { clicon_debug(2,"must-substmt -> error-message-stmt"); } + | error_app_tag_stmt { clicon_debug(2,"must-substmt -> error-app-tag-stmt"); } | description_stmt { clicon_debug(2,"must-substmt -> description-stmt"); } | reference_stmt { clicon_debug(2,"must-substmt -> reference-stmt"); } | { clicon_debug(2,"must-substmt -> "); } ; /* error-message-stmt */ -error_message_stmt : K_ERROR_MESSAGE string ';' - { if (ysp_add(_yy, Y_ERROR_MESSAGE, $2, NULL) == NULL) _YYERROR("80"); } +error_message_stmt : K_ERROR_MESSAGE string stmtend + { if (ysp_add(_yy, Y_ERROR_MESSAGE, $2, NULL) == NULL) _YYERROR("error_message_stmt"); + clicon_debug(2,"error-message-stmt -> ERROR-MESSAGE string"); } ; -/* XXX error-app-tag-stmt */ +error_app_tag_stmt : K_ERROR_APP_TAG string stmtend + { if (ysp_add(_yy, Y_ERROR_MESSAGE, $2, NULL) == NULL) _YYERROR("error_message_stmt"); + clicon_debug(2,"error-app-tag-stmt -> ERROR-APP-TAG string"); } + ; /* min-elements-stmt = min-elements-keyword min-value-arg-str */ -min_elements_stmt: K_MIN_ELEMENTS integer_value_str stmtend - { if (ysp_add(_yy, Y_MIN_ELEMENTS, $2, NULL)== NULL) _YYERROR("103"); +min_elements_stmt : K_MIN_ELEMENTS integer_value_str stmtend + { if (ysp_add(_yy, Y_MIN_ELEMENTS, $2, NULL)== NULL) _YYERROR("min_elements_stmt"); clicon_debug(2,"min-elements-stmt -> MIN-ELEMENTS integer ;");} ; /* max-elements-stmt = max-elements-keyword ("unbounded"|integer-value) * XXX cannot use integer-value */ -max_elements_stmt: K_MAX_ELEMENTS string ';' - { if (ysp_add(_yy, Y_MAX_ELEMENTS, $2, NULL)== NULL) _YYERROR("104"); +max_elements_stmt : K_MAX_ELEMENTS string stmtend + { if (ysp_add(_yy, Y_MAX_ELEMENTS, $2, NULL)== NULL) _YYERROR("max_elements_stmt"); clicon_debug(2,"max-elements-stmt -> MIN-ELEMENTS integer ;");} ; -value_stmt : K_VALUE integer_value_str ';' - { if (ysp_add(_yy, Y_VALUE, $2, NULL) == NULL) _YYERROR("86"); +value_stmt : K_VALUE integer_value_str stmtend + { if (ysp_add(_yy, Y_VALUE, $2, NULL) == NULL) _YYERROR("value_stmt"); clicon_debug(2,"value-stmt -> VALUE integer-value"); } ; /* Grouping */ grouping_stmt : K_GROUPING identifier_str - { if (ysp_add_push(_yy, Y_GROUPING, $2) == NULL) _YYERROR("51"); } + { if (ysp_add_push(_yy, Y_GROUPING, $2) == NULL) _YYERROR("grouping_stmt"); } '{' grouping_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("52"); + { if (ystack_pop(_yy) < 0) _YYERROR("grouping_stmt"); clicon_debug(2,"grouping-stmt -> GROUPING id-arg-str { grouping-substmts }"); } ; @@ -1006,12 +1032,12 @@ grouping_substmt : status_stmt { clicon_debug(2,"grouping-substmt -> st /* container */ container_stmt : K_CONTAINER identifier_str ';' - { if (ysp_add(_yy, Y_CONTAINER, $2, NULL) == NULL) _YYERROR("7"); + { if (ysp_add(_yy, Y_CONTAINER, $2, NULL) == NULL) _YYERROR("container_stmt"); clicon_debug(2,"container-stmt -> CONTAINER id-arg-str ;");} | K_CONTAINER identifier_str - { if (ysp_add_push(_yy, Y_CONTAINER, $2) == NULL) _YYERROR("8"); } + { if (ysp_add_push(_yy, Y_CONTAINER, $2) == NULL) _YYERROR("container_stmt"); } '{' container_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("9"); + { if (ystack_pop(_yy) < 0) _YYERROR("container_stmt"); clicon_debug(2,"container-stmt -> CONTAINER id-arg-str { container-substmts }");} ; @@ -1036,14 +1062,13 @@ container_substmt : when_stmt { clicon_debug(2,"container-substmt -> when- | { clicon_debug(2,"container-substmt ->");} ; -/* leaf */ leaf_stmt : K_LEAF identifier_str ';' - { if (ysp_add(_yy, Y_LEAF, $2, NULL) == NULL) _YYERROR("10"); + { if (ysp_add(_yy, Y_LEAF, $2, NULL) == NULL) _YYERROR("leaf_stmt"); clicon_debug(2,"leaf-stmt -> LEAF id-arg-str ;");} | K_LEAF identifier_str - { if (ysp_add_push(_yy, Y_LEAF, $2) == NULL) _YYERROR("11"); } + { if (ysp_add_push(_yy, Y_LEAF, $2) == NULL) _YYERROR("leaf_stmt"); } '{' leaf_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("12"); + { if (ystack_pop(_yy) < 0) _YYERROR("leaf_stmt"); clicon_debug(2,"leaf-stmt -> LEAF id-arg-str { lead-substmts }");} ; @@ -1068,12 +1093,12 @@ leaf_substmt : when_stmt { clicon_debug(2,"leaf-substmt -> when-stmt /* leaf-list */ leaf_list_stmt : K_LEAF_LIST identifier_str ';' - { if (ysp_add(_yy, Y_LEAF_LIST, $2, NULL) == NULL) _YYERROR("13"); + { if (ysp_add(_yy, Y_LEAF_LIST, $2, NULL) == NULL) _YYERROR("leaf_list_stmt"); clicon_debug(2,"leaf-list-stmt -> LEAF id-arg-str ;");} | K_LEAF_LIST identifier_str - { if (ysp_add_push(_yy, Y_LEAF_LIST, $2) == NULL) _YYERROR("14"); } + { if (ysp_add_push(_yy, Y_LEAF_LIST, $2) == NULL) _YYERROR("leaf_list_stmt"); } '{' leaf_list_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("15"); + { if (ystack_pop(_yy) < 0) _YYERROR("leaf_list_stmt"); clicon_debug(2,"leaf-list-stmt -> LEAF-LIST id-arg-str { lead-substmts }");} ; @@ -1097,14 +1122,13 @@ leaf_list_substmt : when_stmt { clicon_debug(2,"leaf-list-substmt -> when | { clicon_debug(2,"leaf-list-stmt ->"); } ; -/* list */ list_stmt : K_LIST identifier_str ';' - { if (ysp_add(_yy, Y_LIST, $2, NULL) == NULL) _YYERROR("16"); + { if (ysp_add(_yy, Y_LIST, $2, NULL) == NULL) _YYERROR("list_stmt"); clicon_debug(2,"list-stmt -> LIST id-arg-str ;"); } | K_LIST identifier_str - { if (ysp_add_push(_yy, Y_LIST, $2) == NULL) _YYERROR("17"); } + { if (ysp_add_push(_yy, Y_LIST, $2) == NULL) _YYERROR("list_stmt"); } '{' list_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("18"); + { if (ystack_pop(_yy) < 0) _YYERROR("list_stmt"); clicon_debug(2,"list-stmt -> LIST id-arg-str { list-substmts }"); } ; @@ -1137,24 +1161,24 @@ list_substmt : when_stmt { clicon_debug(2,"list-substmt -> when-stmt /* key-stmt = key-keyword sep key-arg-str */ key_stmt : K_KEY string stmtend - { if (ysp_add(_yy, Y_KEY, $2, NULL)== NULL) _YYERROR("109"); + { if (ysp_add(_yy, Y_KEY, $2, NULL)== NULL) _YYERROR("key_stmt"); clicon_debug(2,"key-stmt -> KEY id-arg-str ;");} ; /* unique-stmt = unique-keyword unique-arg-str */ unique_stmt : K_UNIQUE string stmtend - { if (ysp_add(_yy, Y_UNIQUE, $2, NULL)== NULL) _YYERROR("110"); + { if (ysp_add(_yy, Y_UNIQUE, $2, NULL)== NULL) _YYERROR("unique_stmt"); clicon_debug(2,"key-stmt -> KEY id-arg-str ;");} ; /* choice */ choice_stmt : K_CHOICE identifier_str ';' - { if (ysp_add(_yy, Y_CHOICE, $2, NULL) == NULL) _YYERROR("19"); + { if (ysp_add(_yy, Y_CHOICE, $2, NULL) == NULL) _YYERROR("choice_stmt"); clicon_debug(2,"choice-stmt -> CHOICE id-arg-str ;"); } | K_CHOICE identifier_str - { if (ysp_add_push(_yy, Y_CHOICE, $2) == NULL) _YYERROR("20"); } + { if (ysp_add_push(_yy, Y_CHOICE, $2) == NULL) _YYERROR("choice_stmt"); } '{' choice_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("21"); + { if (ystack_pop(_yy) < 0) _YYERROR("choice_stmt"); clicon_debug(2,"choice-stmt -> CHOICE id-arg-str { choice-substmts }"); } ; @@ -1180,12 +1204,12 @@ choice_substmt : when_stmt { clicon_debug(2,"choice-substmt -> when-st /* case */ case_stmt : K_CASE identifier_str ';' - { if (ysp_add(_yy, Y_CASE, $2, NULL) == NULL) _YYERROR("22"); + { if (ysp_add(_yy, Y_CASE, $2, NULL) == NULL) _YYERROR("case_stmt"); clicon_debug(2,"case-stmt -> CASE id-arg-str ;"); } | K_CASE identifier_str - { if (ysp_add_push(_yy, Y_CASE, $2) == NULL) _YYERROR("23"); } + { if (ysp_add_push(_yy, Y_CASE, $2) == NULL) _YYERROR("case_stmt"); } '{' case_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("24"); + { if (ystack_pop(_yy) < 0) _YYERROR("case_stmt"); clicon_debug(2,"case-stmt -> CASE id-arg-str { case-substmts }"); } ; @@ -1206,23 +1230,23 @@ case_substmt : when_stmt { clicon_debug(2,"case-substmt -> when-stmt ; anydata_stmt : K_ANYDATA identifier_str ';' - { if (ysp_add(_yy, Y_ANYDATA, $2, NULL) == NULL) _YYERROR("25"); + { if (ysp_add(_yy, Y_ANYDATA, $2, NULL) == NULL) _YYERROR("anydata_stmt"); clicon_debug(2,"anydata-stmt -> ANYDATA id-arg-str ;"); } | K_ANYDATA identifier_str - { if (ysp_add_push(_yy, Y_ANYDATA, $2) == NULL) _YYERROR("26"); } + { if (ysp_add_push(_yy, Y_ANYDATA, $2) == NULL) _YYERROR("anydata_stmt"); } '{' anyxml_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("27"); + { if (ystack_pop(_yy) < 0) _YYERROR("anydata_stmt"); clicon_debug(2,"anydata-stmt -> ANYDATA id-arg-str { anyxml-substmts }"); } ; /* anyxml */ anyxml_stmt : K_ANYXML identifier_str ';' - { if (ysp_add(_yy, Y_ANYXML, $2, NULL) == NULL) _YYERROR("25"); + { if (ysp_add(_yy, Y_ANYXML, $2, NULL) == NULL) _YYERROR("anyxml_stmt"); clicon_debug(2,"anyxml-stmt -> ANYXML id-arg-str ;"); } | K_ANYXML identifier_str - { if (ysp_add_push(_yy, Y_ANYXML, $2) == NULL) _YYERROR("26"); } + { if (ysp_add_push(_yy, Y_ANYXML, $2) == NULL) _YYERROR("anyxml_stmt"); } '{' anyxml_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("27"); + { if (ystack_pop(_yy) < 0) _YYERROR("anyxml_stmt"); clicon_debug(2,"anyxml-stmt -> ANYXML id-arg-str { anyxml-substmts }"); } ; @@ -1245,12 +1269,12 @@ anyxml_substmt : when_stmt { clicon_debug(2,"anyxml-substmt -> when-st /* uses-stmt = uses-keyword identifier-ref-arg-str */ uses_stmt : K_USES identifier_ref_str ';' - { if (ysp_add(_yy, Y_USES, $2, NULL) == NULL) _YYERROR("28"); + { if (ysp_add(_yy, Y_USES, $2, NULL) == NULL) _YYERROR("uses_stmt"); clicon_debug(2,"uses-stmt -> USES id-arg-str ;"); } | K_USES identifier_ref_str - { if (ysp_add_push(_yy, Y_USES, $2) == NULL) _YYERROR("29"); } + { if (ysp_add_push(_yy, Y_USES, $2) == NULL) _YYERROR("uses_stmt"); } '{' uses_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("30"); + { if (ystack_pop(_yy) < 0) _YYERROR("uses_stmt"); clicon_debug(2,"uses-stmt -> USES id-arg-str { uses-substmts }"); } ; @@ -1273,12 +1297,12 @@ uses_substmt : when_stmt { clicon_debug(2,"uses-substmt -> when-stmt /* refine-stmt = refine-keyword sep refine-arg-str */ refine_stmt : K_REFINE desc_schema_node_str ';' - { if (ysp_add(_yy, Y_REFINE, $2, NULL) == NULL) _YYERROR("31"); + { if (ysp_add(_yy, Y_REFINE, $2, NULL) == NULL) _YYERROR("refine_stmt"); clicon_debug(2,"refine-stmt -> REFINE id-arg-str ;"); } | K_REFINE desc_schema_node_str - { if (ysp_add_push(_yy, Y_REFINE, $2) == NULL) _YYERROR("32"); } + { if (ysp_add_push(_yy, Y_REFINE, $2) == NULL) _YYERROR("refine_stmt"); } '{' refine_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("33"); + { if (ystack_pop(_yy) < 0) _YYERROR("refine_stmt"); clicon_debug(2,"refine-stmt -> REFINE id-arg-str { refine-substmts }"); } ; @@ -1297,9 +1321,9 @@ refine_substmt : must_stmt { clicon_debug(2,"refine-substmt -> must-stmt"); /* uses-augment-stmt = augment-keyword augment-arg-str */ uses_augment_stmt : K_AUGMENT desc_schema_node_str - { if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("34"); } + { if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("uses_augment_stmt"); } '{' augment_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("35"); + { if (ystack_pop(_yy) < 0) _YYERROR("uses_augment_stmt"); clicon_debug(2,"uses-augment-stmt -> AUGMENT desc-schema-node-str { augment-substmts }"); } @@ -1307,9 +1331,9 @@ uses_augment_stmt : K_AUGMENT desc_schema_node_str * XXX abs-schema-nodeid-str is too difficult, it needs the + semantics */ augment_stmt : K_AUGMENT string - { if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("34"); } + { if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("augment_stmt"); } '{' augment_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("35"); + { if (ystack_pop(_yy) < 0) _YYERROR("augment_stmt"); clicon_debug(2,"augment-stmt -> AUGMENT abs-schema-node-str { augment-substmts }"); } ; @@ -1333,12 +1357,12 @@ augment_substmt : when_stmt { clicon_debug(2,"augment-substmt -> when-s /* when */ when_stmt : K_WHEN string ';' - { if (ysp_add(_yy, Y_WHEN, $2, NULL) == NULL) _YYERROR("36"); + { if (ysp_add(_yy, Y_WHEN, $2, NULL) == NULL) _YYERROR("when_stmt"); clicon_debug(2,"when-stmt -> WHEN string ;"); } | K_WHEN string - { if (ysp_add_push(_yy, Y_WHEN, $2) == NULL) _YYERROR("37"); } + { if (ysp_add_push(_yy, Y_WHEN, $2) == NULL) _YYERROR("when_stmt"); } '{' when_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("38"); + { if (ystack_pop(_yy) < 0) _YYERROR("when_stmt"); clicon_debug(2,"when-stmt -> WHEN string { when-substmts }"); } ; @@ -1355,12 +1379,12 @@ when_substmt : description_stmt { clicon_debug(2,"when-substmt -> description-s /* rpc */ rpc_stmt : K_RPC identifier_str ';' - { if (ysp_add(_yy, Y_RPC, $2, NULL) == NULL) _YYERROR("39"); + { if (ysp_add(_yy, Y_RPC, $2, NULL) == NULL) _YYERROR("rpc_stmt"); clicon_debug(2,"rpc-stmt -> RPC id-arg-str ;"); } | K_RPC identifier_str - { if (ysp_add_push(_yy, Y_RPC, $2) == NULL) _YYERROR("40"); } + { if (ysp_add_push(_yy, Y_RPC, $2) == NULL) _YYERROR("rpc_stmt"); } '{' rpc_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("41"); + { if (ystack_pop(_yy) < 0) _YYERROR("rpc_stmt"); clicon_debug(2,"rpc-stmt -> RPC id-arg-str { rpc-substmts }"); } ; @@ -1383,23 +1407,23 @@ rpc_substmt : if_feature_stmt { clicon_debug(2,"rpc-substmt -> if-feature-stm /* action */ action_stmt : K_ACTION identifier_str ';' - { if (ysp_add(_yy, Y_ACTION, $2, NULL) == NULL) _YYERROR("39"); + { if (ysp_add(_yy, Y_ACTION, $2, NULL) == NULL) _YYERROR("action_stmt"); clicon_debug(2,"action-stmt -> ACTION id-arg-str ;"); } | K_ACTION identifier_str - { if (ysp_add_push(_yy, Y_ACTION, $2) == NULL) _YYERROR("40"); } + { if (ysp_add_push(_yy, Y_ACTION, $2) == NULL) _YYERROR("action_stmt"); } '{' rpc_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("41"); + { if (ystack_pop(_yy) < 0) _YYERROR("action_stmt"); clicon_debug(2,"action-stmt -> ACTION id-arg-str { rpc-substmts }"); } ; /* notification */ notification_stmt : K_NOTIFICATION identifier_str ';' - { if (ysp_add(_yy, Y_NOTIFICATION, $2, NULL) == NULL) _YYERROR("46"); + { if (ysp_add(_yy, Y_NOTIFICATION, $2, NULL) == NULL) _YYERROR("notification_stmt"); clicon_debug(2,"notification-stmt -> NOTIFICATION id-arg-str ;"); } | K_NOTIFICATION identifier_str - { if (ysp_add_push(_yy, Y_NOTIFICATION, $2) == NULL) _YYERROR("47"); } + { if (ysp_add_push(_yy, Y_NOTIFICATION, $2) == NULL) _YYERROR("notification_stmt"); } '{' notification_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("48"); + { if (ystack_pop(_yy) < 0) _YYERROR("notification_stmt"); clicon_debug(2,"notification-stmt -> NOTIFICATION id-arg-str { notification-substmts }"); } ; @@ -1427,9 +1451,9 @@ notification_substmt : if_feature_stmt { clicon_debug(2,"notification-substmt - */ deviation_stmt : K_DEVIATION string - { if (ysp_add_push(_yy, Y_DEVIATION, $2) == NULL) _YYERROR("47"); } + { if (ysp_add_push(_yy, Y_DEVIATION, $2) == NULL) _YYERROR("deviation_stmt"); } '{' deviation_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("48"); + { if (ystack_pop(_yy) < 0) _YYERROR("deviation_stmt"); clicon_debug(2,"deviation-stmt -> DEVIATION id-arg-str { notification-substmts }"); } ; @@ -1444,14 +1468,10 @@ deviation_substmt : description_stmt { clicon_debug(2,"deviation-substmt -> des | deviate_not_supported_stmt { clicon_debug(2,"deviation-substmt -> deviate-not-supported-stmt"); } ; -deviate_not_supported_stmt : K_DEVIATE string { clicon_debug(2,"deviate-not-supported-stmt -> DEVIATE string"); } +deviate_not_supported_stmt : K_DEVIATE string stmtend + { clicon_debug(2,"deviate-not-supported-stmt -> DEVIATE string"); } ; -meta_stmt : organization_stmt { clicon_debug(2,"meta-stmt -> organization-stmt"); } - | contact_stmt { clicon_debug(2,"meta-stmt -> contact-stmt"); } - | description_stmt { clicon_debug(2,"meta-stmt -> description-stmt"); } - | reference_stmt { clicon_debug(2,"meta-stmt -> reference-stmt"); } - ; /* body */ body_stmts : body_stmts body_stmt { clicon_debug(2,"body-stmts -> body-stmts body-stmt"); } @@ -1492,9 +1512,9 @@ short_case_stmt : container_stmt { clicon_debug(2,"short-case-substmt -> conta /* input */ input_stmt : K_INPUT - { if (ysp_add_push(_yy, Y_INPUT, NULL) == NULL) _YYERROR("42"); } + { if (ysp_add_push(_yy, Y_INPUT, NULL) == NULL) _YYERROR("input_stmt"); } '{' input_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("43"); + { if (ystack_pop(_yy) < 0) _YYERROR("input_stmt"); clicon_debug(2,"input-stmt -> INPUT { input-substmts }"); } ; @@ -1512,15 +1532,12 @@ input_substmt : typedef_stmt { clicon_debug(2,"input-substmt -> typedef- /* output */ output_stmt : K_OUTPUT /* XXX reuse input-substatements since they are same */ - { if (ysp_add_push(_yy, Y_OUTPUT, NULL) == NULL) _YYERROR("44"); } + { if (ysp_add_push(_yy, Y_OUTPUT, NULL) == NULL) _YYERROR("output_stmt"); } '{' input_substmts '}' - { if (ystack_pop(_yy) < 0) _YYERROR("45"); + { if (ystack_pop(_yy) < 0) _YYERROR("output_stmt"); clicon_debug(2,"output-stmt -> OUTPUT { input-substmts }"); } ; - - - string : qstrings { $$=$1; clicon_debug(2,"string -> qstrings (%s)", $1); } | ustring { $$=$1; clicon_debug(2,"string -> ustring (%s)", $1); } ; @@ -1554,12 +1571,11 @@ ustring : ustring CHAR {$$=$1; } ; - abs_schema_nodeid : abs_schema_nodeid '/' node_identifier - { if (($$=string_del_join($1, "/", $3)) == NULL) _YYERROR("0"); + { if (($$=string_del_join($1, "/", $3)) == NULL) _YYERROR("abs_schema_nodeid"); clicon_debug(2,"absolute-schema-nodeid -> absolute-schema-nodeid / node-identifier"); } | '/' node_identifier - { if (($$=string_del_join(NULL, "/", $2)) == NULL) _YYERROR("0"); + { if (($$=string_del_join(NULL, "/", $2)) == NULL) _YYERROR("abs_schema_nodeid"); clicon_debug(2,"absolute-schema-nodeid -> / node-identifier"); } ; @@ -1572,7 +1588,7 @@ desc_schema_node_str : desc_schema_nodeid desc_schema_nodeid : node_identifier { $$= $1; clicon_debug(2,"descendant-schema-nodeid -> node_identifier"); } | node_identifier abs_schema_nodeid - { if (($$=string_del_join($1, " ", $2)) == NULL) _YYERROR("0");clicon_debug(2,"descendant-schema-nodeid -> node_identifier abs_schema_nodeid"); } + { if (($$=string_del_join($1, " ", $2)) == NULL) _YYERROR("desc_schema_nodeid");clicon_debug(2,"descendant-schema-nodeid -> node_identifier abs_schema_nodeid"); } ; identifier_str : '"' IDENTIFIER '"' { $$ = $2; @@ -1602,7 +1618,7 @@ bool_str : '"' BOOL '"' { $$ = $2; node_identifier : IDENTIFIER { $$=$1; clicon_debug(2,"identifier-ref-arg-str -> string"); } | IDENTIFIER ':' IDENTIFIER - { if (($$=string_del_join($1, ":", $3)) == NULL) _YYERROR("112"); + { if (($$=string_del_join($1, ":", $3)) == NULL) _YYERROR("node_identifier"); clicon_debug(2,"identifier-ref-arg-str -> prefix : string"); } ; diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 68204c9b..788af4f7 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -150,7 +150,7 @@ yang_type_cache_set(yang_type_cache **ycache0, return retval; } -/*! Get individual fields (direct/destrucively) from yang type cache. */ +/*! Get individual fields (direct/destructively) from yang type cache. */ int yang_type_cache_get(yang_type_cache *ycache, yang_stmt **resolved, @@ -227,19 +227,17 @@ ys_resolve_type(yang_stmt *ys, yang_stmt *resolved = NULL; assert(ys->ys_keyword == Y_TYPE); + /* Recursively resolve ys -> resolve with restrictions(options, etc) + * Note that the resolved type could be ys itself. + */ if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved, &options, &mincv, &maxcv, &pattern, &fraction) < 0) goto done; - if (resolved && strcmp(resolved->ys_argument, "union")==0) - ; - /* skip unions since they may have different sets of options, mincv, etc - * You would have to resolve all sub-types also recursively - */ - else - if (yang_type_cache_set(&ys->ys_typecache, - resolved, options, mincv, maxcv, pattern, fraction) < 0) - goto done; + /* Cache the resolve locally */ + if (yang_type_cache_set(&ys->ys_typecache, + resolved, options, mincv, maxcv, pattern, fraction) < 0) + goto done; retval = 0; done: return retval; @@ -660,10 +658,12 @@ ys_cv_validate_union_one(yang_stmt *ys, clicon_err(OE_UNIX, errno, "cv_new"); goto done; } - if (cv_parse(val, cvt) <0){ + if ((retval = cv_parse1(val, cvt, reason)) < 0){ clicon_err(OE_UNIX, errno, "cv_parse"); goto done; } + if (retval == 0) + goto done; if ((retval = cv_validate1(cvt, cvtype, options, range_min, range_max, pattern, yrt, restype, reason)) < 0) goto done; @@ -688,16 +688,32 @@ ys_cv_validate_union(yang_stmt *ys, { int retval = 1; /* valid */ yang_stmt *yt = NULL; + char *reason1 = NULL; /* saved reason */ while ((yt = yn_each((yang_node*)yrestype, yt)) != NULL){ if (yt->ys_keyword != Y_TYPE) continue; if ((retval = ys_cv_validate_union_one(ys, reason, yt, type, val)) < 0) goto done; + /* If validation failed, save reason, reset error and continue, + * save latest reason if noithing validates. + */ + if (retval == 0 && reason && *reason != NULL){ + if (reason1) + free(reason1); + reason1 = *reason; + *reason = NULL; + } if (retval == 1) /* Enough that one type validates value */ break; } done: + if (retval == 0 && reason1){ + *reason = reason1; + reason1 = NULL; + } + if (reason1) + free(reason1); return retval; } @@ -908,7 +924,7 @@ resolve_restrictions(yang_stmt *yrange, } /*! Recursively resolve a yang type to built-in type with optional restrictions - * @param[in] ys yang-stmt from where the current search is based + * @param[in] ys (original) type yang-stmt where the current search is based * @param[in] ytype yang-stmt object containing currently resolving type * @param[out] yrestype resolved type. return built-in type or NULL. mandatory * @param[out] options pointer to flags field of optional values. optional @@ -945,14 +961,14 @@ yang_type_resolve(yang_stmt *ys, char *prefix = NULL; int retval = -1; yang_node *yn; - yang_stmt *ymod; + yang_stmt *yrmod; /* module where resolved type is looked for */ if (options) *options = 0x0; *yrestype = NULL; /* Initialization of resolved type that may not be necessary */ type = yarg_id(ytype); /* This is the type to resolve */ prefix = yarg_prefix(ytype); /* And this its prefix */ - /* Cache does not work for eg string length 32 */ + /* Cache does not work for eg string length 32? */ if (!yang_builtin(type) && ytype->ys_typecache != NULL){ if (yang_type_cache_get(ytype->ys_typecache, yrestype, options, mincv, maxcv, pattern, fraction) < 0) @@ -974,11 +990,12 @@ yang_type_resolve(yang_stmt *ys, /* Not basic type. Now check if prefix which means we look in other module */ if (prefix){ /* Go to top and find import that matches */ - if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL){ - clicon_err(OE_DB, 0, "Type not resolved: %s:%s", prefix, type); + if ((yrmod = yang_find_module_by_prefix(ytype, prefix)) == NULL){ + clicon_err(OE_DB, 0, "Type not resolved: \"%s:%s\" in module %s", + prefix, type, ys_module(ys)->ys_argument); goto done; } - if ((rytypedef = yang_find((yang_node*)ymod, Y_TYPEDEF, type)) == NULL) + if ((rytypedef = yang_find((yang_node*)yrmod, Y_TYPEDEF, type)) == NULL) goto ok; /* unresolved */ } else diff --git a/test/lib.sh b/test/lib.sh index ab0a5ed9..716b6c83 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -78,14 +78,24 @@ expectfn(){ expect2= fi ret=$($cmd) + r=$? # echo "cmd:\"$cmd\"" # echo "retval:\"$retval\"" # echo "ret:\"$ret\"" - if [ $? -ne $retval ]; then - echo -e "\e[31m\nError in Test$testnr [$testname]:" +# echo "r:\"$r\"" + if [ $r != $retval ]; then + echo -e "\e[31m\nError ($r != $retval) in Test$testnr [$testname]:" echo -e "\e[0m:" exit -1 - fi + fi + if [ $r != 0 ]; then + return + fi +# if [ $ret -ne $retval ]; then +# echo -e "\e[31m\nError in Test$testnr [$testname]:" +# echo -e "\e[0m:" +# exit -1 +# fi # Match if both are empty string if [ -z "$ret" -a -z "$expect" ]; then return diff --git a/test/test_cli.sh b/test/test_cli.sh index 1304bf81..a93cf0fb 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -17,6 +17,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $APPNAME /usr/local/lib/$APPNAME/backend /usr/local/lib/$APPNAME/clispec diff --git a/test/test_datastore.sh b/test/test_datastore.sh index 27f3b4bf..122b3add 100755 --- a/test/test_datastore.sh +++ b/test/test_datastore.sh @@ -57,7 +57,7 @@ run(){ fi rm -rf $mydir/* - conf="-d candidate -b $mydir -p ../datastore/$name/$name.so -y $dir -m ietf-ip" + conf="-d candidate -b $mydir -p ../datastore/$name/$name.so -y $dir/ietf-ip.yang" new "datastore $name init" expectfn "$datastore $conf init" 0 "" @@ -144,7 +144,7 @@ run(){ expectfn "$datastore $conf put create 13newentry" 0 "" new "datastore other db init" - expectfn "$datastore -d kalle -b $mydir -p ../datastore/$name/$name.so -y $dir -m ietf-ip init" 0 "" + expectfn "$datastore -d kalle -b $mydir -p ../datastore/$name/$name.so -y $dir/ietf-ip.yang init" 0 "" new "datastore other db copy" expectfn "$datastore $conf copy kalle" 0 "" diff --git a/test/test_feature.sh b/test/test_feature.sh index e5279f6c..86f3ffd2 100755 --- a/test/test_feature.sh +++ b/test/test_feature.sh @@ -13,6 +13,7 @@ cat < $cfg ietf-routing:router-id $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $APPNAME /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli diff --git a/test/test_identity.sh b/test/test_identity.sh index e006ca34..f9498013 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -11,6 +11,7 @@ cat < $cfg $cfg $dir + /usr/local/share/clixon /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend example_backend.so$ diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 6a146916..719a77e6 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -10,6 +10,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon example /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli diff --git a/test/test_list.sh b/test/test_list.sh index 67c3f953..ff12015a 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -12,6 +12,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $APPNAME /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli diff --git a/test/test_nacm.sh b/test/test_nacm.sh index 586e5538..afac1df7 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -15,6 +15,7 @@ cat < $cfg $cfg /usr/local/share/clixon + /usr/local/share/clixon /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index ee5ae60b..997965ff 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -18,6 +18,7 @@ cat < $cfg $cfg /usr/local/share/example/yang + /usr/local/share/clixon /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend example_backend.so$ diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 2d3d2b0c..e66aae53 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -13,6 +13,7 @@ cat < $cfg $cfg 42 /usr/local/share/$APPNAME/yang + /usr/local/share/clixon /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend example_backend.so$ @@ -200,7 +201,6 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "eth/0/0true]]>]]>" - new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" diff --git a/test/test_openconfig.sh b/test/test_openconfig.sh index 6ed5423b..efb68c54 100755 --- a/test/test_openconfig.sh +++ b/test/test_openconfig.sh @@ -1,24 +1,104 @@ #!/bin/bash # Parse yang openconfig tests +# Note that the openconfig test suites are patched to counter CLixon issues as follows: +# - release/models/mpls/openconfig-mpls-te.yang +# issue: https://github.com/clicon/clixon/issues/60 +# - release/models/wifi/types/openconfig-wifi-types.yang +# issue: https://github.com/clicon/clixon/issues/59 +# #PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_yang" PROG=../util/clixon_util_yang -OPENCONFIG=~/syssrc/openconfig +OPENCONFIG=public +OCDIR=$OPENCONFIG/release/models + +# Clone openconfig dir if not there +if [ ! -d public ]; then + git clone https://github.com/openconfig/public +else + (cd public; git pull) +fi # include err() and new() functions and creates $dir . ./lib.sh -# Openconfig -# Files not parseable: -# - openconfig-access-points.yang -# - openconfig-access-points.yang -new "Openconfig" +# Yang specifics: multi-keys and empty type +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh + +cfg=$dir/conf_yang.xml +fyang=$dir/test.yang + +cat < $cfg + + $cfg + $OCDIR + $OCDIR/acl + $OCDIR/aft + $OCDIR/bfd + $OCDIR/bgp + $OCDIR/catalog + $OCDIR/interfaces + $OCDIR/isis + $OCDIR/lacp + $OCDIR/lldp + $OCDIR/local-routing + $OCDIR/mpls + $OCDIR/multicast + $OCDIR/network-instance + $OCDIR/openflow + $OCDIR/optical-transport + $OCDIR/ospf + $OCDIR/platform + $OCDIR/policy + $OCDIR/policy-forwarding + $OCDIR/probes + $OCDIR/qos + $OCDIR/relay-agent + $OCDIR/rib + $OCDIR/segment-routing + $OCDIR/stp + $OCDIR/system + $OCDIR/telemetry + $OCDIR/types + $OCDIR/vlan + $OCDIR/wifi + $OCDIR/wifi/access-points + $OCDIR/wifi/ap-manager + $OCDIR/wifi/mac + $OCDIR/wifi/phy + $OCDIR/wifi/types + /usr/local/share/$APPNAME/yang + /usr/local/share/clixon + $APPNAME + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + true + +EOF + files=$(find $OPENCONFIG -name "*.yang") +# Just cound nr of modules (exclude submodule) +let m=0; # Nr of modules for f in $files; do - new "$f" - YANG=$(cat $f) - # NYI - expecteof "$PROG" 0 "$YANG" "module" + if [ -n "$(head -1 $f|grep '^module')" ]; then + let m++; + fi done +echo "Number of modules:$m" +for f in $files; do + if [ -n "$(head -1 $f|grep '^module')" ]; then + new "cli $f" + expectfn "$clixon_cli -1f $cfg -y $f show version" 0 "3." + fi + +done + rm -rf $dir - diff --git a/test/test_order.sh b/test/test_order.sh index da1ecb87..a44586cd 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -26,6 +26,7 @@ cat < $cfg /tmp/conf_yang.xml /usr/local/share/$APPNAME/yang + /usr/local/share/clixon example /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli diff --git a/test/test_perf.sh b/test/test_perf.sh index e6dde054..a2c36cf8 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -23,7 +23,7 @@ fyang=$dir/scaling.yang fconfig=$dir/config cat < $fyang -module ietf-ip{ +module scaling{ yang-version 1.1; namespace "urn:example:clixon"; prefix ip; @@ -47,8 +47,9 @@ EOF cat < $cfg $cfg - $fyang - ietf-ip + $dir + /usr/local/share/clixon + scaling /usr/local/var/$APPNAME/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile false diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 260d7eb9..f3b8fa72 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -11,6 +11,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon example /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index 52364f2f..938aa232 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -12,6 +12,7 @@ cat < $cfg $cfg /usr/local/var + /usr/local/share/clixon false /usr/local/var/$APPNAME/$APPNAME.sock $dir/restconf.pidfile diff --git a/test/test_startup.sh b/test/test_startup.sh index ba4960f7..d9ec5b07 100755 --- a/test/test_startup.sh +++ b/test/test_startup.sh @@ -14,6 +14,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon example $APPNAME /usr/local/lib/$APPNAME/backend diff --git a/test/test_stream.sh b/test/test_stream.sh index 7e5d0e48..ce0aefc5 100755 --- a/test/test_stream.sh +++ b/test/test_stream.sh @@ -25,8 +25,7 @@ UTIL=../util/clixon_util_stream NCWAIT=5 # Wait (netconf valgrind may need more time) if [ ! -x $UTIL ]; then - echo "$UTIL not found. To build: (cd ../util; make clixon_util_stream)" - exit 1 + (cd ../util; make clixon_util_stream) fi DATE=$(date +"%Y-%m-%d") # include err() and new() functions and creates $dir diff --git a/test/test_type.sh b/test/test_type.sh index a2126e1c..d23d13cb 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -7,11 +7,15 @@ APPNAME=example cfg=$dir/conf_yang.xml fyang=$dir/type.yang +fyang2=$dir/example2.yang +fyang3=$dir/example3.yang cat < $cfg $cfg + $dir /usr/local/share/$APPNAME/yang + /usr/local/share/clixon example /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli @@ -21,14 +25,59 @@ cat < $cfg 1 /usr/local/var/$APPNAME /usr/local/lib/xmldb/text.so + false EOF +# transitive type, exists in fyang3, referenced from fyang2, but not declared in fyang +cat < $fyang3 +module example3{ + prefix ex3; + namespace "urn:example:example3"; + typedef w{ + type union{ + type int32{ + range "4..44"; + } + } + } + typedef u{ + type union { + type w; + type enumeration { + enum "bounded"; + enum "unbounded"; + } + } + } + typedef t{ + type string{ + pattern '[a-z][0-9]*'; + } + } +} +EOF +cat < $fyang2 +module example2{ + import example3 { prefix ex3; } + namespace "urn:example:example2"; + prefix ex2; + grouping gr2 { + leaf talle{ + type ex3:t; + } + leaf ulle{ + type ex3:u; + } + } +} +EOF cat < $fyang module example{ yang-version 1.1; namespace "urn:example:clixon"; prefix ex; + import example2 { prefix ex2; } typedef ab { type string { pattern @@ -136,6 +185,10 @@ module example{ leaf mbits{ type mybits; } + container c{ + description "transitive type- exists in ex3"; + uses ex2:gr2; + } } EOF @@ -151,6 +204,50 @@ if [ $? -ne 0 ]; then err fi +new "cli set transitive string" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle x99" 0 "^$" + +new "cli set transitive string error" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle 9xx" 255 "^$" + +new "netconf set transitive string error" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "9xx]]>]]>" "^]]>]]>" + +new "netconf validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorvalidation of talle failed regexp match fail" + +new "netconf discard-changes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "cli set transitive union int" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 33" 0 "^$" + +new "cli validate" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o validate" 0 "^$" + +new "cli set transitive union string" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle unbounded" 0 "^$" + +new "cli validate" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o validate" 0 "^$" + +new "cli set transitive union error. should fail" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle kalle" 255 "" + +new "cli set transitive union error int" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 55" 255 "" + +new "netconf set transitive union error int" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "55]]>]]>" "^]]>]]>" + +new "netconf validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorvalidation of ulle failed" + +new "netconf discard-changes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +#----------- + new "cli set ab" expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.a.b" 0 "^$" diff --git a/test/test_when_must.sh b/test/test_when_must.sh index da629784..f83edd78 100755 --- a/test/test_when_must.sh +++ b/test/test_when_must.sh @@ -13,6 +13,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $APPNAME /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli diff --git a/test/test_yang.sh b/test/test_yang.sh index a15bf6cb..d81277a4 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -6,12 +6,16 @@ APPNAME=example cfg=$dir/conf_yang.xml fyang=$dir/test.yang +fsubmod=$dir/example-types.yang fyangerr=$dir/err.yang +# /usr/local/share/$APPNAME/yang cat < $cfg $cfg - /usr/local/share/$APPNAME/yang + + $dir + /usr/local/share/clixon $APPNAME /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli @@ -30,6 +34,7 @@ module $APPNAME{ yang-version 1.1; prefix ex; namespace "urn:example:clixon"; + include example-types; extension c-define { description "Example from RFC 6020"; argument "name"; @@ -85,6 +90,41 @@ module $APPNAME{ type string; } } + list mylist{ /* uses submodule */ + key x; + leaf x{ + type string; + } + uses ex:subm-group; + } +} +EOF + +# Submodule Example from rfc7950 sec 7.2.3 +cat < $fsubmod +submodule example-types { + yang-version 1.1; + belongs-to $APPNAME { + prefix "sys"; + } + import ietf-yang-types { + prefix "yang"; + } + organization "Example Inc."; + contact "Joe L. User"; + description + "This submodule defines common Example types."; + revision "2007-06-09" { + description "Initial revision."; + } + grouping subm-group { + description "Defined in submodule"; + container subm-container{ + leaf subm-leaf{ + type string; + } + } + } } EOF @@ -128,7 +168,8 @@ new "netconf get config" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^a]]>]]>$" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + #new "cli not defined extension" #new "netconf not defined extension" @@ -182,7 +223,7 @@ new "netconf get presence only" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf anyxml" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" @@ -191,7 +232,7 @@ new "netconf validate anyxml" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf delete candidate" -expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" # Check 3-keys new "netconf add one 3-key entry" @@ -218,6 +259,25 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '121two]]>]]>' +# clear db for next test +new "netconf delete candidate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "netconf commit empty candidate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "netconfig config submodule" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "afoo]]>]]>" "^]]>]]>$" + +new "netconf submodule get config" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^afoo]]>]]>$" + +new "netconf submodule validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "netconf submodule discard-changes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + new "Kill backend" # Check if premature kill pid=`pgrep -u root -f clixon_backend` diff --git a/util/clixon_util_yang.c b/util/clixon_util_yang.c index 9232b535..13b78743 100644 --- a/util/clixon_util_yang.c +++ b/util/clixon_util_yang.c @@ -31,6 +31,8 @@ ***** END LICENSE BLOCK ***** + * Parse a SINGLE yang file - no dependencies - utility function only useful + * for basic syntactic checks. */ #ifdef HAVE_CONFIG_H diff --git a/yang/clixon-config@2018-10-21.yang b/yang/clixon-config@2018-10-21.yang index 839e16b0..a0376c21 100644 --- a/yang/clixon-config@2018-10-21.yang +++ b/yang/clixon-config@2018-10-21.yang @@ -133,11 +133,14 @@ module clixon-config { description "Location of configuration-file for default values (this file)"; } - leaf CLICON_YANG_DIR { + leaf-list CLICON_YANG_DIR { type string; - mandatory true; description - "Location of YANG module and submodule files."; + "Yang directory path for finding module and submodule files. + A list of these options should be in the configuration. + When loading a Yang module, Clixon searches this list in the order + they appear. Ensure that CLIXON_DATADIR(default + /usr/local/share/clixon) is present in the path"; } leaf CLICON_YANG_MODULE_MAIN { type string;