C-API: Exposed diff function
Test: double leaf validate test
This commit is contained in:
parent
d358387d39
commit
fcf9a8b0b0
8 changed files with 115 additions and 89 deletions
|
|
@ -733,82 +733,6 @@ cli_validate(clicon_handle h,
|
||||||
return retval;
|
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
|
/*! Compare two dbs using XML. Write to file and run diff
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cvv
|
* @param[in] cvv
|
||||||
|
|
@ -845,7 +769,7 @@ compare_dbs(clicon_handle h,
|
||||||
clixon_netconf_error(xerr, "Get configuration", NULL);
|
clixon_netconf_error(xerr, "Get configuration", NULL);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (compare_xmls(xc1, xc2, format) < 0) /* astext? */
|
if (clixon_compare_xmls(xc1, xc2, format, cligen_output) < 0) /* astext? */
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,6 @@
|
||||||
/*
|
/*
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
enum format_enum{
|
|
||||||
FORMAT_XML,
|
|
||||||
FORMAT_JSON,
|
|
||||||
FORMAT_TEXT,
|
|
||||||
FORMAT_CLI,
|
|
||||||
FORMAT_NETCONF
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Protocol message header */
|
/* Protocol message header */
|
||||||
struct clicon_msg {
|
struct clicon_msg {
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
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:
|
* xml_flag() flags:
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -75,5 +75,6 @@ int yang_xml_mandatory(cxobj *xt, yang_stmt *ys);
|
||||||
int xml_rpc_isaction(cxobj *xn);
|
int xml_rpc_isaction(cxobj *xn);
|
||||||
int xml_find_action(cxobj *xn, int top, cxobj **xap);
|
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 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_ */
|
#endif /* _CLIXON_XML_MAP_H_ */
|
||||||
|
|
|
||||||
|
|
@ -1101,7 +1101,7 @@ xml_yang_validate_list_key_only(cxobj *xt,
|
||||||
int ret;
|
int ret;
|
||||||
cxobj *x;
|
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 */
|
and !Node has a config sub-statement and it is false */
|
||||||
if ((yt = xml_spec(xt)) != NULL &&
|
if ((yt = xml_spec(xt)) != NULL &&
|
||||||
yang_config(yt) != 0 &&
|
yang_config(yt) != 0 &&
|
||||||
|
|
@ -1230,7 +1230,7 @@ xml_yang_validate_all(clicon_handle h,
|
||||||
if ((ret = xml_yang_mount_get(xt, NULL)) < 0)
|
if ((ret = xml_yang_mount_get(xt, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 1)
|
if (ret == 1)
|
||||||
goto ok;
|
goto ok; /* Actually this may be somewhat too strict */
|
||||||
#endif
|
#endif
|
||||||
/* if not given by argument (overide) use default link
|
/* if not given by argument (overide) use default link
|
||||||
and !Node has a config sub-statement and it is false */
|
and !Node has a config sub-statement and it is false */
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
#include "clixon_xml_sort.h"
|
#include "clixon_xml_sort.h"
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
|
#include "clixon_text_syntax.h"
|
||||||
|
#include "clixon_xml_io.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
|
|
||||||
/* Local types
|
/* Local types
|
||||||
|
|
@ -1703,3 +1705,84 @@ purge_tagged_nodes(cxobj *xn,
|
||||||
return retval;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ xml_yang_mount_get(cxobj *xt,
|
||||||
goto ok;
|
goto ok;
|
||||||
if (xml2xpath(xt, NULL, 1, &xpath) < 0)
|
if (xml2xpath(xt, NULL, 1, &xpath) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Special value in yang unknown node for mount-points: mapping from xpath->mounted yspec */
|
||||||
if ((cvv = yang_cvec_get(yu)) == NULL)
|
if ((cvv = yang_cvec_get(yu)) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
if ((cv = cvec_find(cvv, xpath)) == NULL)
|
if ((cv = cvec_find(cvv, xpath)) == NULL)
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,9 @@ module clixon-example{
|
||||||
leaf name{
|
leaf name{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
leaf value{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* State data (not config) for the example application*/
|
/* 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" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><interface nc:operation=\"delete\"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>" "<rpc-reply $DEFAULTNS><rpc-error>" ""
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><interface nc:operation=\"delete\"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>" "<rpc-reply $DEFAULTNS><rpc-error>" ""
|
||||||
|
|
||||||
new "Add interface without key"
|
new "Add interface without key"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><interface nc:operation=\"create\"><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>name</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory key in 'list interface' in ietf-interfaces.yang:107</error-message></rpc-error></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><interface nc:operation=\"create\"><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>name</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory key in 'list interface' in ietf-interfaces.yang:[0-9]\+</error-message></rpc-error></rpc-reply>" ""
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><discard-changes/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><discard-changes/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
@ -430,10 +433,22 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
|
||||||
new "netconf client-side rpc"
|
new "netconf client-side rpc"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><client-rpc xmlns=\"urn:example:clixon\"><x>val42</x></client-rpc></rpc>" "" "<rpc-reply $DEFAULTNS><x xmlns=\"urn:example:clixon\">val42</x></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><client-rpc xmlns=\"urn:example:clixon\"><x>val42</x></client-rpc></rpc>" "" "<rpc-reply $DEFAULTNS><x xmlns=\"urn:example:clixon\">val42</x></rpc-reply>"
|
||||||
|
|
||||||
|
# Negative tests
|
||||||
new "netconf extra leaf in leaf should fail"
|
new "netconf extra leaf in leaf should fail"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface><name>e0<name>e1</name></name></interface></interfaces></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>name</bad-element></error-info><error-severity>error</error-severity><error-message>Failed to find YANG spec of XML node: name with parent: name in namespace: urn:ietf:params:xml:ns:yang:ietf-interfaces</error-message></rpc-error></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface><name>e0<name>e1</name></name></interface></interfaces></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>name</bad-element></error-info><error-severity>error</error-severity><error-message>Failed to find YANG spec of XML node: name with parent: name in namespace: urn:ietf:params:xml:ns:yang:ietf-interfaces</error-message></rpc-error></rpc-reply>"
|
||||||
|
|
||||||
# Negative tests
|
new "netconf duplicate keys"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>xx</name><name>yy</name></parameter></table></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
||||||
|
new "netconf validate duplicate keys expect fail"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-many-elements</error-app-tag>" ""
|
||||||
|
|
||||||
|
new "netconf duplicate values"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>xx</name><value>foo</value><value>bar</value></parameter></table></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
||||||
|
new "netconf validate duplicate values expect fail"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-many-elements</error-app-tag>" ""
|
||||||
|
|
||||||
new "netconf xpath syntax error (api-path not xpath) should fail"
|
new "netconf xpath syntax error (api-path not xpath) should fail"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/if:interfaces/interface=eth2f0,foo/fii\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>xpath parser on line 1: syntax error at or before: ','</error-message></rpc-error></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/if:interfaces/interface=eth2f0,foo/fii\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>xpath parser on line 1: syntax error at or before: ','</error-message></rpc-error></rpc-reply>"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue