diff --git a/CHANGELOG.md b/CHANGELOG.md
index 500ebf44..00a27c0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,7 @@
Users may have to change how they access the system
+* Error-type changed from protocol to application for data-not-unique netconf/restconf errors
* New clixon-config@2020-11-03.yang revision
* Moved to clixon-restconf.yang and marked as obsolete:
- CLICON_RESTCONF_IPV4_ADDR
@@ -63,6 +64,7 @@ Developers may need to change their code
### Minor changes
+* Added new revision of main example yang: `clixon-example@2020-12-01.yang`
* Support for building static lib: `LINKAGE=static configure`
* Change comment character to be active anywhere to beginning of _word_ only.
* See [Change CLIgen comments](https://github.com/clicon/cligen/issues/55)
@@ -72,6 +74,9 @@ Developers may need to change their code
### Corrected Bugs
+* Fixed [YANG: key statement in rpc/notification list #148](https://github.com/clicon/clixon/issues/148)
+ * Do not check uniqueness among lists without keys
+
* Fixed typo: [False Header Content_type in restconf error #152](https://github.com/clicon/clixon/issues/152)
* Added message-id attributes in error and hello replies
* See [namespace prefix nc is not supported in full #154](https://github.com/clicon/clixon/issues/154)
diff --git a/example/main/Makefile.in b/example/main/Makefile.in
index 2e27c993..afc2d062 100644
--- a/example/main/Makefile.in
+++ b/example/main/Makefile.in
@@ -81,7 +81,7 @@ all: $(PLUGINS)
CLISPECS = $(APPNAME)_cli.cli
-YANGSPECS = clixon-example@2020-03-11.yang
+YANGSPECS = clixon-example@2020-12-01.yang
# Backend plugin
BE_SRC = $(APPNAME)_backend.c
diff --git a/example/main/clixon-example@2020-03-11.yang b/example/main/clixon-example@2020-03-11.yang
index 58435d0c..a4b0c23b 100644
--- a/example/main/clixon-example@2020-03-11.yang
+++ b/example/main/clixon-example@2020-03-11.yang
@@ -26,9 +26,6 @@ module clixon-example {
import iana-if-type {
prefix ianaift;
}
- import ietf-datastores {
- prefix ds;
- }
/* Example interface type for tests, local callbacks, etc */
identity eth {
base if:interface-type;
@@ -36,21 +33,17 @@ module clixon-example {
identity loopback {
base if:interface-type;
}
- /* Generic config data */
- container table{
- list parameter{
- key name;
- leaf name{
+ /* Translation function example - See also example_cli */
+ container translate{
+ description "dont have lists directly under top since restconf cant address list directly";
+ list translate{
+ key k;
+ leaf k{
type string;
}
leaf value{
type string;
}
- leaf stat{
- description "Inline state data for example application";
- config false;
- type int32;
- }
}
}
/* State data (not config) for the example application*/
@@ -89,6 +82,7 @@ module clixon-example {
ex:e4 arg1{
uses bar;
}
+
/* Example notification as used in RFC 5277 and RFC 8040 */
notification event {
description "Example notification event.";
diff --git a/example/main/clixon-example@2020-12-01.yang b/example/main/clixon-example@2020-12-01.yang
new file mode 100644
index 00000000..fe5a4619
--- /dev/null
+++ b/example/main/clixon-example@2020-12-01.yang
@@ -0,0 +1,223 @@
+module clixon-example {
+ yang-version 1.1;
+ namespace "urn:example:clixon";
+ prefix ex;
+ description
+ "Clixon example used as a part of the Clixon test suite.
+ It can be used as a basis for making new Clixon applications.
+ Note, may change without updating revision, just for testing current master.
+ ";
+ revision 2020-12-01 {
+ description "Added table/paramater/value as the primary data example";
+ }
+ revision 2020-03-11 {
+ description "Added container around translation list. Released in Clixon 4.4.0";
+ }
+ revision 2019-11-05 {
+ description "Augment interface. Released in Clixon 4.3.0";
+ }
+ revision 2019-07-23 {
+ description "Extension e4. Released in Clixon 4.1.0";
+ }
+ revision 2019-01-13 {
+ description "Released in Clixon 3.9";
+ }
+ import ietf-interfaces {
+ prefix if;
+ }
+ import ietf-ip {
+ prefix ip;
+ }
+ import iana-if-type {
+ prefix ianaift;
+ }
+ import ietf-datastores {
+ prefix ds;
+ }
+ /* Example interface type for tests, local callbacks, etc */
+ identity eth {
+ base if:interface-type;
+ }
+ identity loopback {
+ base if:interface-type;
+ }
+ /* Generic config data */
+ container table{
+ list parameter{
+ key name;
+ leaf name{
+ type string;
+ }
+ leaf value{
+ type string;
+ }
+ leaf stat{
+ description "Inline state data for example application";
+ config false;
+ type int32;
+ }
+ }
+ }
+ /* State data (not config) for the example application*/
+ container state {
+ config false;
+ description "state data for the example application (must be here for example get operation)";
+ leaf-list op {
+ type string;
+ }
+ }
+ augment "/if:interfaces/if:interface" {
+ container my-status {
+ config false;
+ description "For testing augment+state";
+ leaf int {
+ type int32;
+ }
+ leaf str {
+ type string;
+ }
+ }
+ }
+ /* yang extension implemented by the example backend code. */
+ extension e4 {
+ description
+ "The first child of the ex:e4 (unknown) statement is inserted into
+ the module as a regular data statement. This means that 'uses bar;'
+ in the ex:e4 statement below is a valid data node";
+ argument arg;
+ }
+ grouping bar {
+ leaf bar{
+ type string;
+ }
+ }
+ ex:e4 arg1{
+ uses bar;
+ }
+ /* Example notification as used in RFC 5277 and RFC 8040 */
+ notification event {
+ description "Example notification event.";
+ leaf event-class {
+ type string;
+ description "Event class identifier.";
+ }
+ container reportingEntity {
+ description "Event specific information.";
+ leaf card {
+ type string;
+ description "Line card identifier.";
+ }
+ }
+ leaf severity {
+ type string;
+ description "Event severity description.";
+ }
+ }
+ rpc client-rpc {
+ description "Example local client-side RPC that is processed by the
+ the netconf/restconf and not sent to the backend.
+ This is a clixon implementation detail: some rpc:s
+ are better processed by the client for API or perf reasons";
+ input {
+ leaf x {
+ type string;
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ }
+ }
+ rpc empty {
+ description "Smallest possible RPC with no input or output sections";
+ }
+ rpc optional {
+ description "Small RPC with optional input and output";
+ input {
+ leaf x {
+ type string;
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ }
+ }
+ rpc example {
+ description "Some example input/output for testing RFC7950 7.14.
+ RPC simply echoes the input for debugging.";
+ input {
+ leaf x {
+ description
+ "If a leaf in the input tree has a 'mandatory' statement with
+ the value 'true', the leaf MUST be present in an RPC invocation.";
+ type string;
+ mandatory true;
+ }
+ leaf y {
+ description
+ "If a leaf in the input tree has a 'mandatory' statement with the
+ value 'true', the leaf MUST be present in an RPC invocation.";
+ type string;
+ default "42";
+ }
+ leaf-list z {
+ description
+ "If a leaf-list in the input tree has one or more default
+ values, the server MUST use these values (XXX not supported)";
+ type string;
+ }
+ leaf w {
+ description
+ "If any node has a 'when' statement that would evaluate to
+ 'false',then this node MUST NOT be present in the input tree.
+ (XXX not supported)";
+ type string;
+ when "/translate/k=5/value='w'";
+ }
+ list u0 {
+ description "list without key";
+ leaf uk{
+ type string;
+ }
+ }
+ list u1 {
+ description "list with key";
+ key uk;
+ leaf uk{
+ type string;
+ }
+ leaf val{
+ type string;
+ }
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ leaf y {
+ type string;
+ }
+ leaf z {
+ type string;
+ }
+ leaf w {
+ type string;
+ }
+ list u0 {
+ leaf uk{
+ type string;
+ }
+ }
+ list u1 {
+ key uk;
+ leaf uk{
+ type string;
+ }
+ }
+ }
+ }
+}
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index 197ba0df..3c9dccc9 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -1184,7 +1184,7 @@ netconf_data_not_unique_xml(cxobj **xret,
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
- "protocol"
+ "application"
"operation-failed"
"data-not-unique"
"error") < 0)
diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c
index fa9bec6f..b9163a7b 100644
--- a/lib/src/clixon_validate.c
+++ b/lib/src/clixon_validate.c
@@ -654,11 +654,15 @@ check_insert_duplicate(char **vec,
* @param[in] xt The parent of x
* @param[in] y Its yang spec (Y_LIST)
* @param[in] yu A yang unique spec (Y_UNIQUE) for unique keyword or (Y_LIST) for list keys
- * @param[out] xret Error XML tree. Free with xml_free after use
+ * @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
* @note It would be possible to cache the vector built below
+ * All key leafs MUST be present for all list entries.
+ * The combined values of all the leafs specified in the key are used to
+ * uniquely identify a list entry. All key leafs MUST be given values
+ * when a list entry is created.
*/
static int
check_unique_list(cxobj *x,
@@ -666,7 +670,6 @@ check_unique_list(cxobj *x,
yang_stmt *y,
yang_stmt *yu,
cxobj **xret)
-
{
int retval = -1;
cvec *cvk; /* unique vector */
@@ -686,7 +689,11 @@ check_unique_list(cxobj *x,
sorted = (yang_keyword_get(yu) == Y_LIST &&
yang_find(y, Y_ORDERED_BY, "user") == NULL);
cvk = yang_cvec_get(yu);
- vlen = cvec_len(cvk); /* nr of unique elements to check */
+ /* nr of unique elements to check */
+ if ((vlen = cvec_len(cvk)) == 0){
+ /* No keys: no checks necessary */
+ goto ok;
+ }
if ((vec = calloc(vlen*xml_child_nr(xt), sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
@@ -718,6 +725,7 @@ check_unique_list(cxobj *x,
x = xml_child_each(xt, x, CX_ELMNT);
i++;
} while (x && y == xml_spec(x)); /* stop if list ends, others may follow */
+ ok:
/* It would be possible to cache vec here as an optimization */
retval = 1;
done:
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index c80fa34d..d986426d 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -2853,7 +2853,7 @@ yang_config(yang_stmt *ys)
*
* config statement is default true.
* @param[in] ys Yang statement
- * @retval 0 Node or one of its ancestor has config false
+ * @retval 0 Node or one of its ancestor has config false or is RPC or notification
* @retval 1 Neither node nor any of its ancestors has config false
*/
int
@@ -2865,6 +2865,8 @@ yang_config_ancestor(yang_stmt *ys)
do {
if (yang_config(yp) == 0)
return 0;
+ if (yang_keyword_get(yp) == Y_INPUT || yang_keyword_get(yp) == Y_OUTPUT || yang_keyword_get(yp) == Y_NOTIFICATION)
+ return 0;
} while((yp = yang_parent_get(yp)) != NULL);
return 1;
}
diff --git a/lib/src/clixon_yang_parse_lib.c b/lib/src/clixon_yang_parse_lib.c
index fe2dba69..54ca4200 100644
--- a/lib/src/clixon_yang_parse_lib.c
+++ b/lib/src/clixon_yang_parse_lib.c
@@ -1091,7 +1091,7 @@ ys_schemanode_check(yang_stmt *ys,
return retval;
}
-/*! Check lists: non-config lists MUST have keys
+/*! Check lists: config lists MUST have keys
* @param[in] h Clicon handle
* @param[in] ys Yang statement
* Verify the following rule:
diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh
index 5d2c110b..7312fde4 100755
--- a/test/test_restconf2.sh
+++ b/test/test_restconf2.sh
@@ -144,7 +144,7 @@ new "restconf DELETE"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 204 No Content"
new "restconf POST from top containing duplicate keys expect error"
-expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":[{"name":"TEST","type":"eth0"},{"name":"TEST","type":"eth0"}]}}')" 0 "HTTP/1.1 412 Precondition Failed" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-app-tag":"data-not-unique","error-severity":"error","error-info":{"non-unique":{"name":"TEST"}}}}}'
+expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":[{"name":"TEST","type":"eth0"},{"name":"TEST","type":"eth0"}]}}')" 0 "HTTP/1.1 412 Precondition Failed" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"operation-failed","error-app-tag":"data-not-unique","error-severity":"error","error-info":{"non-unique":{"name":"TEST"}}}}}'
new "restconf GET null datastore"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 404 Not Found" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
diff --git a/test/test_rpc.sh b/test/test_rpc.sh
index 3bbd3242..d5d2fc41 100755
--- a/test/test_rpc.sh
+++ b/test/test_rpc.sh
@@ -161,7 +161,24 @@ new "netconf wrong rpc namespace: should fail"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationunknown-elementgeterrorUnrecognized RPC (wrong namespace?)]]>]]>$"
new "restconf wrong rpc: should fail"
-expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" u $RCPROTO://localhost/restconf/operations/clixon-foo:get)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}'
+expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-foo:get)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}'
+
+# test rpc lists with / without keys
+LIST='foobarbar'
+new "netconf example rpc input list without key with non-unique entries"
+expecteof "$clixon_netconf -qf $cfg" 0 "mandatory$LIST]]>]]>" "^mandatory42$LIST]]>]]>$"
+
+LIST='bar1foo2'
+new "netconf example rpc input list with key"
+expecteof "$clixon_netconf -qf $cfg" 0 "mandatory$LIST]]>]]>" "^mandatory42$LIST]]>]]>$"
+
+LIST='bar12'
+new "netconf example rpc input key list without key (should fail)"
+expecteof "$clixon_netconf -qf $cfg" 0 "mandatory$LIST]]>]]>" "^applicationmissing-elementukerrorMandatory key]]>]]>$"
+
+LIST='bar1bar2'
+new "netconf example rpc input list with non-unique keys (should fail)"
+expecteof "$clixon_netconf -qf $cfg" 0 "mandatory$LIST]]>]]>" "^applicationoperation-faileddata-not-uniqueerrorbar]]>]]>$"
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
diff --git a/test/test_unique.sh b/test/test_unique.sh
index caf66609..6050633a 100755
--- a/test/test_unique.sh
+++ b/test/test_unique.sh
@@ -109,7 +109,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^protocoloperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -138,7 +138,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^protocoloperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
new "make it valid by deleting port from smtp entry"
expecteof "$clixon_netconf -qf $cfg" 0 "nonesmtp25
@@ -163,7 +163,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^protocoloperation-faileddata-not-uniqueerror192.0.2.1]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.1]]>]]>$"
new "make valid by replacing IP of http entry"
expecteof "$clixon_netconf -qf $cfg" 0 "nonehttp178.23.34.1
@@ -204,7 +204,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^protocoloperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"