C-API: Exposed diff function

Test: double leaf validate test
This commit is contained in:
Olof Hagsand 2023-02-13 20:21:44 +01:00
parent d358387d39
commit fcf9a8b0b0
8 changed files with 115 additions and 89 deletions

View file

@ -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:

View file

@ -43,13 +43,6 @@
/*
* Types
*/
enum format_enum{
FORMAT_XML,
FORMAT_JSON,
FORMAT_TEXT,
FORMAT_CLI,
FORMAT_NETCONF
};
/* Protocol message header */
struct clicon_msg {

View file

@ -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:
*/

View file

@ -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_ */

View file

@ -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 */

View file

@ -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;
}

View file

@ -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)

View file

@ -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" "<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"
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"
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"
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"
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"
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>"