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.";
- }
- }
- }
}