diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 1b4b781a..e0d1500c 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -733,82 +733,6 @@ cli_validate(clicon_handle h,
return retval;
}
-/*! Compare two dbs using XML. Write to file and run diff
- */
-static int
-compare_xmls(cxobj *xc1,
- cxobj *xc2,
- enum format_enum format)
-{
- int fd;
- FILE *f;
- char filename1[MAXPATHLEN];
- char filename2[MAXPATHLEN];
- int retval = -1;
- cbuf *cb = NULL;
-
- snprintf(filename1, sizeof(filename1), "/tmp/cliconXXXXXX");
- snprintf(filename2, sizeof(filename2), "/tmp/cliconXXXXXX");
- if ((fd = mkstemp(filename1)) < 0){
- clicon_err(OE_UNDEF, errno, "tmpfile");
- goto done;
- }
- if ((f = fdopen(fd, "w")) == NULL)
- goto done;
- switch(format){
- case FORMAT_TEXT:
- if (clixon_txt2file(f, xc1, 0, cligen_output, 1, 1) < 0)
- goto done;
- break;
- case FORMAT_XML:
- default:
- if (clixon_xml2file(f, xc1, 0, 1, cligen_output, 1, 1) < 0)
- goto done;
- break;
- }
- fclose(f);
- close(fd);
-
- if ((fd = mkstemp(filename2)) < 0){
- clicon_err(OE_UNDEF, errno, "mkstemp: %s", strerror(errno));
- goto done;
- }
- if ((f = fdopen(fd, "w")) == NULL)
- goto done;
-
- switch(format){
- case FORMAT_TEXT:
- if (clixon_txt2file(f, xc2, 0, cligen_output, 1, 1) < 0)
- goto done;
- break;
- case FORMAT_XML:
- default:
- if (clixon_xml2file(f, xc2, 0, 1, cligen_output, 1, 1) < 0)
- goto done;
- break;
- }
-
- fclose(f);
- close(fd);
-
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_CFG, errno, "cbuf_new");
- goto done;
- }
- cprintf(cb, "diff -dU 1 %s %s | grep -v @@ | sed 1,2d",
- filename1, filename2);
- if (system(cbuf_get(cb)) < 0)
- goto done;
-
- retval = 0;
- done:
- if (cb)
- cbuf_free(cb);
- unlink(filename1);
- unlink(filename2);
- return retval;
-}
-
/*! Compare two dbs using XML. Write to file and run diff
* @param[in] h Clicon handle
* @param[in] cvv
@@ -845,7 +769,7 @@ compare_dbs(clicon_handle h,
clixon_netconf_error(xerr, "Get configuration", NULL);
goto done;
}
- if (compare_xmls(xc1, xc2, format) < 0) /* astext? */
+ if (clixon_compare_xmls(xc1, xc2, format, cligen_output) < 0) /* astext? */
goto done;
retval = 0;
done:
diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h
index 74f92f40..076b6021 100644
--- a/lib/clixon/clixon_proto.h
+++ b/lib/clixon/clixon_proto.h
@@ -43,13 +43,6 @@
/*
* Types
*/
-enum format_enum{
- FORMAT_XML,
- FORMAT_JSON,
- FORMAT_TEXT,
- FORMAT_CLI,
- FORMAT_NETCONF
-};
/* Protocol message header */
struct clicon_msg {
diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h
index d70e2a02..a44060c8 100644
--- a/lib/clixon/clixon_xml.h
+++ b/lib/clixon/clixon_xml.h
@@ -177,6 +177,15 @@ typedef int (xml_applyfn_t)(cxobj *x, void *arg);
typedef struct clixon_xml_vec clixon_xvec; /* struct defined in clicon_xml_vec.c */
+/* Alternative formats */
+enum format_enum{
+ FORMAT_XML,
+ FORMAT_JSON,
+ FORMAT_TEXT,
+ FORMAT_CLI,
+ FORMAT_NETCONF
+};
+
/*
* xml_flag() flags:
*/
diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h
index af6b3dbb..6beda9ce 100644
--- a/lib/clixon/clixon_xml_map.h
+++ b/lib/clixon/clixon_xml_map.h
@@ -75,5 +75,6 @@ int yang_xml_mandatory(cxobj *xt, yang_stmt *ys);
int xml_rpc_isaction(cxobj *xn);
int xml_find_action(cxobj *xn, int top, cxobj **xap);
int purge_tagged_nodes(cxobj *xn, char *ns, char *name, char *value, int keepnode);
+int clixon_compare_xmls(cxobj *xc1, cxobj *xc2, enum format_enum format, clicon_output_cb *fn);
#endif /* _CLIXON_XML_MAP_H_ */
diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c
index 6dd21830..1af42b14 100644
--- a/lib/src/clixon_validate.c
+++ b/lib/src/clixon_validate.c
@@ -1101,7 +1101,7 @@ xml_yang_validate_list_key_only(cxobj *xt,
int ret;
cxobj *x;
- /* if not given by argument (overide) use default link
+ /* if not given by argument (override) use default link
and !Node has a config sub-statement and it is false */
if ((yt = xml_spec(xt)) != NULL &&
yang_config(yt) != 0 &&
@@ -1229,8 +1229,8 @@ xml_yang_validate_all(clicon_handle h,
/* Do not validate beyond mountpoints */
if ((ret = xml_yang_mount_get(xt, NULL)) < 0)
goto done;
- if (ret == 1)
- goto ok;
+ if (ret == 1)
+ goto ok; /* Actually this may be somewhat too strict */
#endif
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 58b620dd..f2f9f1ab 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -77,6 +77,8 @@
#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h"
#include "clixon_yang_type.h"
+#include "clixon_text_syntax.h"
+#include "clixon_xml_io.h"
#include "clixon_xml_map.h"
/* Local types
@@ -1703,3 +1705,84 @@ purge_tagged_nodes(cxobj *xn,
return retval;
}
+/*! Compare two dbs using XML. Write to file and run diff
+ *
+ * @param[in] xc1 XML tree 1
+ * @param[in] xc2 XML tree 2
+ * @param[in] format "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
+ * @param[in] fn File print function (if NULL, use fprintf)
+ */
+int
+clixon_compare_xmls(cxobj *xc1,
+ cxobj *xc2,
+ enum format_enum format,
+ clicon_output_cb *fn)
+{
+ int fd;
+ FILE *f;
+ char filename1[MAXPATHLEN];
+ char filename2[MAXPATHLEN];
+ int retval = -1;
+ cbuf *cb = NULL;
+
+ snprintf(filename1, sizeof(filename1), "/tmp/cliconXXXXXX");
+ snprintf(filename2, sizeof(filename2), "/tmp/cliconXXXXXX");
+ if ((fd = mkstemp(filename1)) < 0){
+ clicon_err(OE_UNDEF, errno, "tmpfile");
+ goto done;
+ }
+ if ((f = fdopen(fd, "w")) == NULL)
+ goto done;
+ switch(format){
+ case FORMAT_TEXT:
+ if (clixon_txt2file(f, xc1, 0, cligen_output, 1, 1) < 0)
+ goto done;
+ break;
+ case FORMAT_XML:
+ default:
+ if (clixon_xml2file(f, xc1, 0, 1, cligen_output, 1, 1) < 0)
+ goto done;
+ break;
+ }
+ fclose(f);
+ close(fd);
+
+ if ((fd = mkstemp(filename2)) < 0){
+ clicon_err(OE_UNDEF, errno, "mkstemp: %s", strerror(errno));
+ goto done;
+ }
+ if ((f = fdopen(fd, "w")) == NULL)
+ goto done;
+
+ switch(format){
+ case FORMAT_TEXT:
+ if (clixon_txt2file(f, xc2, 0, cligen_output, 1, 1) < 0)
+ goto done;
+ break;
+ case FORMAT_XML:
+ default:
+ if (clixon_xml2file(f, xc2, 0, 1, cligen_output, 1, 1) < 0)
+ goto done;
+ break;
+ }
+
+ fclose(f);
+ close(fd);
+
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_CFG, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "diff -dU 1 %s %s | grep -v @@ | sed 1,2d",
+ filename1, filename2);
+ if (system(cbuf_get(cb)) < 0)
+ goto done;
+
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ unlink(filename1);
+ unlink(filename2);
+ return retval;
+}
diff --git a/lib/src/clixon_yang_schema_mount.c b/lib/src/clixon_yang_schema_mount.c
index adb1b09e..0f2ecad0 100644
--- a/lib/src/clixon_yang_schema_mount.c
+++ b/lib/src/clixon_yang_schema_mount.c
@@ -162,6 +162,7 @@ xml_yang_mount_get(cxobj *xt,
goto ok;
if (xml2xpath(xt, NULL, 1, &xpath) < 0)
goto done;
+ /* Special value in yang unknown node for mount-points: mapping from xpath->mounted yspec */
if ((cvv = yang_cvec_get(yu)) == NULL)
goto ok;
if ((cv = cvec_find(cvv, xpath)) == NULL)
diff --git a/test/test_netconf.sh b/test/test_netconf.sh
index dc4de610..8687961b 100755
--- a/test/test_netconf.sh
+++ b/test/test_netconf.sh
@@ -62,6 +62,9 @@ module clixon-example{
leaf name{
type string;
}
+ leaf value{
+ type string;
+ }
}
}
/* State data (not config) for the example application*/
@@ -223,7 +226,7 @@ new "Re-Delete eth/0/0 using none should generate error"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "eth/0/0ex:ethnone " "" ""
new "Add interface without key"
-expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "ex:ethnone " "" "applicationmissing-elementnameerrorMandatory key in 'list interface' in ietf-interfaces.yang:107"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "ex:ethnone " "applicationmissing-elementnameerrorMandatory key in 'list interface' in ietf-interfaces.yang:[0-9]\+" ""
new "netconf discard-changes"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" ""
@@ -430,10 +433,22 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "
new "netconf client-side rpc"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "val42" "" "val42"
+# Negative tests
new "netconf extra leaf in leaf should fail"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "e0e1" "" "applicationunknown-elementnameerrorFailed to find YANG spec of XML node: name with parent: name in namespace: urn:ietf:params:xml:ns:yang:ietf-interfaces"
-# Negative tests
+new "netconf duplicate keys"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" ""
+
+new "netconf validate duplicate keys expect fail"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "protocoloperation-failedtoo-many-elements" ""
+
+new "netconf duplicate values"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" ""
+
+new "netconf validate duplicate values expect fail"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "protocoloperation-failedtoo-many-elements" ""
+
new "netconf xpath syntax error (api-path not xpath) should fail"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorxpath parser on line 1: syntax error at or before: ','"