From 6df434093e86dbd912fee6ce0550ee6aea38af57 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 24 Aug 2019 15:30:43 +0200 Subject: [PATCH] * Restconf top-level operations GET root resource modified to comply with RFC 8040 Sec 3.1 * non-pretty print remove all spaces, eg `{"operations":{"clixon-example:client-rpc":[null]` * Replaced JSON `null` with `[null]` as proper empty JSON leaf/leaf-list encoding. * [Cannot write to config using restconf example #91](https://github.com/clicon/clixon/issues/91) * Updated restconf documentation (the example was wrong) * [clixon-lib yang revision file name update #92](https://github.com/clicon/clixon/issues/92) * Clixon-lib yang file had conflicting filename and internal yang revision. * This was only detected in the use-case when a whole dir was loaded. * Inserted sanity check in all yang parse routines. * Committed updated clixon-lib yang file that triggered the error --- CHANGELOG.md | 16 ++ apps/restconf/README.md | 61 +++-- apps/restconf/restconf_methods_get.c | 21 +- configure | 4 +- configure.ac | 4 +- doc/FAQ.md | 10 +- example/main/README.md | 6 +- lib/src/clixon_json.c | 5 +- lib/src/clixon_yang.c | 253 ++++++++++-------- test/test_restconf.sh | 4 +- yang/clixon/Makefile.in | 2 +- ...-06-05.yang => clixon-lib@2019-08-13.yang} | 36 +-- 12 files changed, 230 insertions(+), 192 deletions(-) rename yang/clixon/{clixon-lib@2019-06-05.yang => clixon-lib@2019-08-13.yang} (67%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 028bc3fc..d5abb586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Clixon Changelog +## 4.2.0 (Expected: September) + +### API changes on existing features (you may need to change your code) +* Restconf top-level operations GET root resource modified to comply with RFC 8040 Sec 3.1 + * non-pretty print remove all spaces, eg `{"operations":{"clixon-example:client-rpc":[null]` + * Replaced JSON `null` with `[null]` as proper empty JSON leaf/leaf-list encoding. + +### Corrected Bugs +* [Cannot write to config using restconf example #91](https://github.com/clicon/clixon/issues/91) + * Updated restconf documentation (the example was wrong) +* [clixon-lib yang revision file name update #92](https://github.com/clicon/clixon/issues/92) + * Clixon-lib yang file had conflicting filename and internal yang revision. + * This was only detected in the use-case when a whole dir was loaded. + * Inserted sanity check in all yang parse routines. + * Committed updated clixon-lib yang file that triggered the error + ## 4.1.0 (18 August 2019) ### Summary diff --git a/apps/restconf/README.md b/apps/restconf/README.md index e951e18a..dcb57787 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -23,57 +23,62 @@ Download and start nginx. For example on ubuntu: Define nginx config file: /etc/nginx/sites-available/default ``` -server { - ... - location /restconf { - fastcgi_pass unix:/www-data/fastcgi_restconf.sock; - include fastcgi_params; + server { + ... + location /restconf { + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } } -} ``` Start nginx daemon ``` -sudo /etc/init.d nginx start + sudo /etc/init.d nginx start ``` Alternatively, start it via systemd: ``` -sudo /etc/init.d/nginx start -sudo systemctl start start.service + sudo systemctl start nginx.service +``` + +Start clixon backend daemon (if not already started) +``` + sudo clixon_backend -s init -f /usr/local/etc/example.xml ``` Start clixon restconf daemon ``` -> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data + + sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data ``` -Make restconf calls with curl +Make restconf calls with curl (or other http client). Example of writing a new interface specification: ``` -> curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces -[ + curl -sX PUT http://localhost/restconf/data/ietf-interfaces:interfaces -H 'Content-Type: application/yang-data+json' -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth1","type":"clixon-example:eth","enabled":true}}}' +``` + +Get the data +``` + curl -X GET http://127.0.0.1/restconf/data/ietf-interfaces:interfaces { "ietf-interfaces:interfaces": { - "interface":[ + "interface": [ { - "name": "eth9", - "type": "ex:eth", - "enabled": true, - } + "name": "eth1", + "type": "clixon-example:eth", + "enabled": true + } ] } } -] + ``` Get the type of a specific interface: ``` -> curl -G http://127.0.0.1/restconf/data/interfaces/interface=eth9/type -{ - "ietf-interfaces:type": "eth" -} -``` -Example of writing a new interfaces specification: -``` -curl -sX PUT http://localhost/restconf/data -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth1","type":"ex:eth","enabled":true}}}' + curl -X GET http://127.0.0.1/restconf/data/ietf-interfacesinterfaces/interface=eth1/type + { + "ietf-interfaces:type": "clixon-example:eth" + } ``` ## Streams @@ -83,7 +88,7 @@ RFC8040 Section 6 using SSE. One native and one using Nginx nchan. The Nchan alternaitve is described in the next section. -The (example)[../../example/README.md] creates an EXAMPLE stream. +The [example](../../example/main/README.md) creates an EXAMPLE stream. Set the Clixon configuration options: ``` diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 36004540..38757440 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -449,7 +449,10 @@ api_operations_get(clicon_handle h, cprintf(cbx, ""); break; case YANG_DATA_JSON: - cprintf(cbx, "{\"operations\": {"); + if (pretty) + cprintf(cbx, "{\"operations\": {\n"); + else + cprintf(cbx, "{\"operations\":{"); break; default: break; @@ -467,14 +470,19 @@ api_operations_get(clicon_handle h, cprintf(cbx, "<%s xmlns=\"%s\"/>", yang_argument_get(yc), namespace); break; case YANG_DATA_JSON: - if (i++) + if (i++){ cprintf(cbx, ","); - cprintf(cbx, "\"%s:%s\": null", yang_argument_get(ymod), yang_argument_get(yc)); + if (pretty) + cprintf(cbx, "\n\t"); + } + if (pretty) + cprintf(cbx, "\"%s:%s\": [null]", yang_argument_get(ymod), yang_argument_get(yc)); + else + cprintf(cbx, "\"%s:%s\":[null]", yang_argument_get(ymod), yang_argument_get(yc)); break; default: break; } - } } switch (media_out){ @@ -482,7 +490,10 @@ api_operations_get(clicon_handle h, cprintf(cbx, ""); break; case YANG_DATA_JSON: - cprintf(cbx, "}}"); + if (pretty) + cprintf(cbx, "}\n}"); + else + cprintf(cbx, "}}"); break; default: break; diff --git a/configure b/configure index 7b577b86..f615f883 100755 --- a/configure +++ b/configure @@ -2172,9 +2172,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu : ${INSTALLFLAGS="-s"} CLIXON_VERSION_MAJOR="4" -CLIXON_VERSION_MINOR="1" +CLIXON_VERSION_MINOR="2" CLIXON_VERSION_PATCH="0" -CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" +CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\"" # Check CLIgen if test "$prefix" = "NONE"; then diff --git a/configure.ac b/configure.ac index d03a9361..2ebac3e7 100644 --- a/configure.ac +++ b/configure.ac @@ -43,9 +43,9 @@ AC_INIT(lib/clixon/clixon.h.in) : ${INSTALLFLAGS="-s"} CLIXON_VERSION_MAJOR="4" -CLIXON_VERSION_MINOR="1" +CLIXON_VERSION_MINOR="2" CLIXON_VERSION_PATCH="0" -CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" +CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\"" # Check CLIgen if test "$prefix" = "NONE"; then diff --git a/doc/FAQ.md b/doc/FAQ.md index 4c250cdf..2f3a21d7 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -91,11 +91,11 @@ The main example: ## How do I run Clixon example commands? -- Start a backend server: `clixon_backend -F -s init -f /usr/local/etc/example.xml` +- Start a backend server: `sudo clixon_backend -s init -f /usr/local/etc/example.xml` - Start a cli session: `clixon_cli -f /usr/local/etc/example.xml` - Start a netconf session: `clixon_netconf -f /usr/local/etc/example.xml` - Start a restconf daemon: `sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data` -- Send a restconf command: `curl -G http://127.0.0.1/restconf/data` +- Send a restconf command: `curl -X GET http://127.0.0.1/restconf/data` More info in the [example](../example) directory. @@ -195,12 +195,12 @@ Start the clixon restconf daemon sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data ``` -Then acess: +Then access: ``` - curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type + curl -X GET http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth0/type [ { - "ietf-interfaces:type": "ex:eth" + "ietf-interfaces:type": "clixon-example:eth" } ] ``` diff --git a/example/main/README.md b/example/main/README.md index baa4339d..8abd26ee 100644 --- a/example/main/README.md +++ b/example/main/README.md @@ -151,9 +151,11 @@ Start the clixon restconf daemon ``` then access using curl or wget: ``` - curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type + curl -X GET http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth1/type ``` +More info: (restconf)[../../apps/restconf/README.md]. + ## Streams The example has an EXAMPLE stream notification triggering every 5s. To start a notification @@ -178,7 +180,7 @@ cli> no notify cli> ``` -Restconf support is also supported, see (restc)[../../apps/restconf/README.md]. +Restconf support is also supported, see (restconf)[../../apps/restconf/README.md]. ## RPC Operations diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 1ed29621..6d989e42 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -1028,7 +1028,7 @@ xml2json_vec(FILE *f, int retval = 1; cbuf *cb = NULL; - if ((cb = cbuf_new()) ==NULL){ + if ((cb = cbuf_new()) == NULL){ clicon_err(OE_XML, errno, "cbuf_new"); goto done; } @@ -1084,7 +1084,8 @@ json_xmlns_translate(yang_stmt *yspec, if (xml2ns(x, NULL, &namespace0) < 0) goto done; /* Set xmlns="" default namespace attribute (if diff from default) */ - if (namespace0==NULL || strcmp(namespace0, namespace)){ + if (namespace0 == NULL || + strcmp(namespace0, namespace)){ if (xmlns_set(x, NULL, namespace) < 0) goto done; /* and remove prefix */ diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index b3c6e89e..9584732d 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -35,8 +35,11 @@ * @see https://tools.ietf.org/html/rfc6020 YANG 1.0 * @see https://tools.ietf.org/html/rfc7950 YANG 1.1 * - * yang_spec_parse_module - * \ + * CALLING ORDER OF YANG PARSE FILES + * ================================= + * yang_spec_parse_module + * | | + * v v v * yang_spec_parse_file-> yang_parse_post->yang_parse_recurse->yang_parse_module * \ / v * yang_spec_load_dir ------------------------------------> yang_parse_filename @@ -562,8 +565,8 @@ yang_find(yang_stmt *yn, ys = yn->ys_stmt[i]; if (yang_keyword_get(ys) == Y_INCLUDE){ name = yang_argument_get(ys); - ym = yang_find_module_by_name(yspec, name); - if ((yret = yang_find(ym, keyword, argument)) != NULL) + if ((ym = yang_find_module_by_name(yspec, name)) != NULL && + (yret = yang_find(ym, keyword, argument)) != NULL) break; } } @@ -2330,20 +2333,12 @@ yang_expand_grouping(yang_stmt *yn) * Syntax parsing. A string is input and a syntax-tree is returned (or error). * A variable record is also returned containing a list of (global) variable values. * (cloned from cligen) - * @param[in] h CLICON handle * @param[in] str String of yang statements * @param[in] name Log string, typically filename - * @param[in] ysp Yang specification. Should ave been created by caller - * using yspec_new - * @retval ymod Top-level yang (sub)module - * @retval NULL Error encountered - * Calling order: - * yang_parse # Parse top-level yang module. Expand and populate yang tree - * yang_parse_recurse # Parse one yang module, go through its (sub)modules, parse them and then recursively parse them - * yang_parse_filename # Read yang file into a string - * yang_parse_file # Read yang open file descriptor into a string - * yang_parse_str # Set up yacc parser and call it given a string - * clixon_yang_parseparse # Actual yang parsing using yacc + * @param[in] yspec Yang specification. + * @retval ymod Top-level yang (sub)module + * @retval NULL Error encountered + * See top of file for diagram of calling order */ static yang_stmt * yang_parse_str(char *str, @@ -2442,21 +2437,60 @@ yang_parse_file(int fd, return ymod; /* top-level (sub)module */ } +/*! Given a yang filename, extract the revision as an integer as YYYYMMDD + * @param[in] filename Filename on the form: name [+ @rev ] + .yang + * @param[out] basep "Base" filename, stripped: [+ @rev ] + .yang + * @param[out] revp Revision as YYYYMMDD (0 if not found) + */ +static int +filename2revision(const char *filename, + char **basep, + uint32_t *revp) +{ + int retval = -1; + char *base = NULL; + char *p; + + /* base = module name [+ @rev ] + .yang */ + if ((base = strdup(filename)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + clicon_debug(1, "%s %s", __FUNCTION__, base); + if ((p = rindex(base, '.')) != NULL) /* strip postfix .yang */ + *p = '\0'; + if ((p = index(base, '@')) != NULL){ /* extract revision date */ + *p++ = '\0'; + if (ys_parse_date_arg(p, revp) < 0) + goto done; + } + if (basep){ + *basep = base; + base = NULL; + } + retval = 0; + done: + if (base) + free(base); + return retval; +} + /*! No specific revision give. Match a yang file given module * @param[in] h CLICON handle - * @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 + * @param[out] revactual Actual revision (if retval=1) + * @param[out] fbuf Buffer containing filename (if retval=1) + * @retval 1 Match found, Most recent entry returned in fbuf and revactual + * @retval 0 No matching entry found + * @retval -1 Error * @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(clicon_handle h, const char *module, - const char *revision, + const char *revision, + uint32_t *revactual, cbuf *fbuf) { int retval = -1; @@ -2515,21 +2549,13 @@ yang_parse_find_match(clicon_handle h, /*! Open a file, read into a string and invoke yang parsing * * Similar to clicon_yang_str(), just read a file first - * (cloned from cligen) - * @param[in] h CLICON handle * @param[in] filename Name of file * @param[in] ysp Yang specification. Should have been created by caller using yspec_new * @retval ymod Top-level yang (sub)module * @retval NULL Error encountered * The database symbols are inserted in alphabetical order. - * Calling order: - * yang_parse # Parse top-level yang module. Expand and populate yang tree - * yang_parse_recurse # Parse one yang module, go through its (sub)modules, parse them and then recursively parse them - * yang_parse_filename # Read yang file into a string - * yang_parse_file # Read yang open file descriptor into a string - * yang_parse_str # Set up yacc parser and call it given a string - * clixon_yang_parseparse # Actual yang parsing using yacc + * See top of file for diagram of calling order */ yang_stmt * yang_parse_filename(const char *filename, @@ -2556,6 +2582,18 @@ yang_parse_filename(const char *filename, return ymod; /* top-level (sub)module */ } +/*! Given a (sub)module, parse all (sub)modules in turn recursively + * + * Find a yang module file, and then recursively parse all its imported modules. + * @param[in] h CLICON handle + * @param[in] module Module name + * @param[in] revision Revision (or NULL) + * @param[in] ysp Yang statement + * @retval 0 OK + * @retval -1 Error + * + * See top of file for diagram of calling order + */ static yang_stmt * yang_parse_module(clicon_handle h, const char *module, @@ -2563,22 +2601,37 @@ yang_parse_module(clicon_handle h, yang_stmt *ysp) { cbuf *fbuf = NULL; + char *filename; int nr; yang_stmt *ymod = NULL; + yang_stmt *yrev; /* yang revision */ + uint32_t revf = 0; /* revision in filename */ + uint32_t revm = 0; /* revision in parsed new module (should be same as revf) */ if ((fbuf = cbuf_new()) == NULL){ clicon_err(OE_YANG, errno, "cbuf_new"); 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) + if ((nr = yang_parse_find_match(h, module, revision, &revf, 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) + filename = cbuf_get(fbuf); + if ((ymod = yang_parse_filename(filename, ysp)) == NULL) goto done; + if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL) + revm = cv_uint32_get(yrev->ys_cv); + if (filename2revision(filename, NULL, &revf) < 0) + goto done; + /* Sanity check that file revision does not match internal rev stmt */ + if (revf && revm && revm != revf){ + clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s vs %u", filename, revm); + ymod = NULL; + goto done; + } done: if (fbuf) cbuf_free(fbuf); @@ -2587,20 +2640,14 @@ yang_parse_module(clicon_handle h, /*! Given a (sub)module, parse all (sub)modules in turn recursively * - * @param[in] h CLICON handle - * @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 - * @retval ymod Top-level yang (sub)module - * @retval NULL Error encountered * Find a yang module file, and then recursively parse all its imported modules. - * Calling order: - * yang_parse # Parse top-level yang module. Expand and populate yang tree - * yang_parse_recurse # Parse one yang module, go through its (sub)modules, parse them and then recursively parse them - * yang_parse_filename # Read yang file into a string - * yang_parse_file # Read yang open file descriptor into a string - * yang_parse_str # Set up yacc parser and call it given a string - * clixon_yang_parseparse # Actual yang parsing using yacc + * @param[in] h CLICON handle + * @param[in] ymod Yang module. + * @param[in] yspec Yang specification. + * @retval 0 OK + * @retval -1 Error + * + * See top of file for diagram of calling order */ static int yang_parse_recurse(clicon_handle h, @@ -2646,6 +2693,11 @@ yang_parse_recurse(clicon_handle h, return retval; /* top-level (sub)module */ } +/*! + * @param[in] ys Yang statement + * @param[in] dummy Necessary for called in yang_apply + * @see yang_apply_fn + */ static int ys_schemanode_check(yang_stmt *ys, void *dummy) @@ -2716,9 +2768,11 @@ ys_schemanode_check(yang_stmt *ys, } /*! Find feature and if-feature nodes, check features and remove disabled nodes - * @retval -1 Error - * @retval 0 Feature not enabled: remove yt - * @retval 1 OK + * @param[in] h CLICON handle + * @param[in] yt Yang statement + * @retval -1 Error + * @retval 0 Feature not enabled: remove yt + * @retval 1 OK * @note On return 1 the over-lying function need to remove yt from its parent * @note cannot use yang_apply here since child-list is modified (destructive) */ @@ -2794,24 +2848,22 @@ yang_features(clicon_handle h, /*! Parse top yang module including all its sub-modules. Expand and populate yang tree * - * @param[in] h CLICON handle - * @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 - * @param[in,out] ysp Yang specification. Should have been created by caller using yspec_new - * @retval 0 Everything OK - * @retval -1 Error encountered - * The database symbols are inserted in alphabetical order. - * Find a yang module file, and then recursively parse all its imported modules. - * @note if mainmod is filename, revision is not considered. - * Calling order: - * yang_parse # Parse top-level yang module. Expand and populate yang tree - * yang_parse_recurse # Parse one yang module, go through its (sub)modules, - * parse them and then recursively parse them - * yang_parse_filename # Read yang file into a string - * yang_parse_file # Read yang open file descriptor into a string - * yang_parse_str # Set up yacc parser and call it given a string - * clixon_yang_parseparse # Actual yang parsing using yacc + * Perform secondary actions after yang parsing. These actions cannot be made at + * compile-time for various reasons. + * These includes: + * - Detect imported yang specs that are not loaded and load and parse them too + * - Check cardinality of yang (that nr of children match) + * - Check features: remove disabled + * - "Populate" yang, which means things like initiating caches, resolving references + * - Resolve types + * - Augments + * - Defaults + * + * @param[in] h CLICON handle + * @param[in] yspec Yang specification. + * @param[in] modnr Perform checks after this number, prior are already complete + * @retval 0 Everything OK + * @retval -1 Error encountered */ static int yang_parse_post(clicon_handle h, @@ -2884,13 +2936,12 @@ yang_parse_post(clicon_handle h, } /*! Parse yang specification and its dependencies recursively given module - * @param[in] h clicon handle - * @param[in] module Module name, or absolute filename (including dir) - * @param[in] dir Directory where to look for modules and sub-modules - * @param[in] revision Revision, or NULL - * @param[in,out] yspec Modules parse are added to this yangspec - * @retval 0 OK - * @retval -1 Error + * @param[in] h clicon handle + * @param[in] module Module name, or absolute filename (including dir) + * @param[in] revision Revision, or NULL + * @param[in] yspec Modules parse are added to this yangspec + * @retval 0 OK + * @retval -1 Error * @see yang_spec_parse_file */ int @@ -2929,12 +2980,11 @@ yang_spec_parse_module(clicon_handle h, } /*! Parse yang specification and its dependencies recursively given filename - * @param[in] h clicon handle - * @param[in] filename Actual filename (including dir and revision) - * @param[in] dir Directory for sub-modules - * @param[in,out] yspec Modules parse are added to this yangspec - * @retval 0 OK - * @retval -1 Error + * @param[in] h clicon handle + * @param[in] filename Actual filename (including dir and revision) + * @param[in] yspec Modules parse are added to this yangspec + * @retval 0 OK + * @retval -1 Error * @see yang_spec_parse_module for yang dir,module,revision instead of * actual filename * @see yang_spec_load_dir For loading all files in a directory @@ -2976,11 +3026,11 @@ yang_spec_parse_file(clicon_handle h, } /*! Load all yang modules in directory - * @param[in] h Clicon handle - * @param[in] dir Load all yang modules in this directory - * @param[in,out] yspec Modules parse are added to this yangspec - * @retval 0 OK - * @retval -1 Error + * @param[in] h Clicon handle + * @param[in] dir Load all yang modules in this directory + * @param[in] yspec Modules parse are added to this yangspec + * @retval 0 OK + * @retval -1 Error * @see yang_spec_parse_file * Load all yang files in a directory as primary objects. * Some details if several same yang module x exists: @@ -3006,10 +3056,9 @@ yang_spec_load_dir(clicon_handle h, yang_stmt *ym; /* yang module */ yang_stmt *ym0; /* (existing) yang module */ yang_stmt *yrev; /* yang revision */ - uint32_t revf; /* revision in filename */ - uint32_t revm; /* revision in parsed new module (same as revf) */ + uint32_t revf = 0; /* revision in filename */ + uint32_t revm = 0; /* revision in parsed new module (should be same as revf) */ uint32_t rev0; /* revision in existing module */ - char *s; char *oldbase = NULL; int taken = 0; @@ -3031,31 +3080,19 @@ yang_spec_load_dir(clicon_handle h, /* base = module name [+ @rev ] + .yang */ if (oldbase) free(oldbase); - oldbase = base; base = NULL; - if ((base = strdup(dp[i].d_name)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; - } - clicon_debug(1, "%s %s", __FUNCTION__, base); - *rindex(base, '.') = '\0'; /* strip postfix .yang */ - /* base = module name [+ @rev] - * if it hasnt @rev then prefer it (dont check other files w @rev) - */ + oldbase = base; + base = NULL; revf = 0; - if ((s = index(base, '@')) != NULL){ - *s++ = '\0'; - if (ys_parse_date_arg(s, &revf) < 0) - goto done; - } + if (filename2revision(dp[i].d_name, &base, &revf) < 0) + goto done; if (oldbase && strcmp(base, oldbase)) /* new yang file basename */ taken = 0; - if (revf == 0){ /* No revision: a.yang - take that */ + if (revf == 0) /* No revision: a.yang - take that */ taken = 1; - } else{ /* a@xxx.yang */ if (taken) continue; /* skip if already taken */ - /* is there anyone else later? */ + /* Look forward: is there anyone else later? */ if (i+1ys_cv); /* Sanity check that file revision does not match internal rev stmt */ - if (revf && revm && revm != revf){ - clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s vs %u", filename, revm); + if (revf && revm && revm != revf){ /* XXX */ + clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s(%u) vs %u", filename, revf, revm); goto done; } /* If ym0 and ym exists, delete the yang with oldest revision diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 6fa21d4c..dfe21a62 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -74,12 +74,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r # Should be alphabetically ordered new "restconf get restconf/operations. RFC8040 3.3.2 (json)" -expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null,"clixon-lib:ping": null,"clixon-lib:get-state": null,"ietf-netconf:get-config": null,"ietf-netconf:edit-config": null,"ietf-netconf:copy-config": null,"ietf-netconf:delete-config": null,"ietf-netconf:lock": null,"ietf-netconf:unlock": null,"ietf-netconf:get": null,"ietf-netconf:close-session": null,"ietf-netconf:kill-session": null,"ietf-netconf:commit": null,"ietf-netconf:discard-changes": null,"ietf-netconf:validate": null,"clixon-rfc5277:create-subscription": null}} +expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations":{"clixon-example:client-rpc":[null],"clixon-example:empty":[null],"clixon-example:optional":[null],"clixon-example:example":[null],"clixon-lib:debug":[null],"clixon-lib:ping":[null],"ietf-netconf:get-config":[null],"ietf-netconf:edit-config":[null],"ietf-netconf:copy-config":[null],"ietf-netconf:delete-config":[null],"ietf-netconf:lock":[null],"ietf-netconf:unlock":[null],"ietf-netconf:get":[null],"ietf-netconf:close-session":[null],"ietf-netconf:kill-session":[null],"ietf-netconf:commit":[null],"ietf-netconf:discard-changes":[null],"ietf-netconf:validate":[null],"clixon-rfc5277:create-subscription":[null]}} ' new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) -expect='' +expect='' match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index c982ddc2..87d5e65b 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -42,7 +42,7 @@ datarootdir = @datarootdir@ YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANGSPECS = clixon-config@2019-06-05.yang -YANGSPECS += clixon-lib@2019-06-05.yang +YANGSPECS += clixon-lib@2019-08-13.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang diff --git a/yang/clixon/clixon-lib@2019-06-05.yang b/yang/clixon/clixon-lib@2019-08-13.yang similarity index 67% rename from yang/clixon/clixon-lib@2019-06-05.yang rename to yang/clixon/clixon-lib@2019-08-13.yang index 5c163870..20cbc9e3 100644 --- a/yang/clixon/clixon-lib@2019-06-05.yang +++ b/yang/clixon/clixon-lib@2019-08-13.yang @@ -42,7 +42,7 @@ module clixon-lib { revision 2019-08-13 { description - "get-state added for restconf content=nonconfig internal rpc"; + "No changes (reverted change)"; } revision 2019-06-05 { description @@ -52,11 +52,6 @@ module clixon-lib { description "Released in Clixon 3.9"; } - import ietf-netconf { - description "for the get-state extension"; - prefix nc; - } - rpc debug { description "Set debug level of backend."; input { @@ -68,33 +63,4 @@ module clixon-lib { rpc ping { description "Check aliveness of backend daemon."; } - rpc get-state { - description - "Retrieve device state information only. This is a clixon extension - to ietf-netconf to implement RESTCONF GET with attribute - content=nonconfig. - The reason is that netconf only has and neither - which retrieves state only"; - - reference "RFC 8040 4.8.1"; - - input { - anyxml filter { - description - "This parameter specifies the portion of the system - configuration and state data to retrieve."; - nc:get-filter-element-attributes; - } - } - - output { - anyxml data { - description - "Copy of the running datastore subset and/or state - data that matched the filter criteria (if any). - An empty data container indicates that the request did not - produce any results."; - } - } - } }