diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 12bf047a..c1aee865 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -3,7 +3,8 @@
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
- Copyright (C) 2017-2020 Olof Hagsand
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -168,22 +169,28 @@ client_get_capabilities(clicon_handle h,
char *xpath,
cxobj **xret)
{
- int retval = -1;
- cxobj *xrstate = NULL; /* xml restconf-state node */
- cxobj *xcap = NULL; /* xml capabilities node */
-
+ int retval = -1;
+ cxobj *xrstate = NULL; /* xml restconf-state node */
+ cbuf *cb = NULL;
+
if ((xrstate = xpath_first(*xret, NULL, "restconf-state")) == NULL){
clicon_err(OE_YANG, ENOENT, "restconf-state not found in config node");
goto done;
}
- if ((xcap = xml_new("capabilities", xrstate, yspec)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
- if (xml_parse_va(&xcap, yspec, "urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit") < 0)
- goto done;
- if (xml_parse_va(&xcap, yspec, "urn:ietf:params:restconf:capability:depth:1.0") < 0)
+ }
+ cprintf(cb, "");
+ cprintf(cb, "urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit");
+ cprintf(cb, "urn:ietf:params:restconf:capability:depth:1.0");
+ cprintf(cb, "");
+ if (xml_parse_string2(cbuf_get(cb), YB_PARENT, NULL, &xrstate, NULL) < 0)
goto done;
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
return retval;
}
@@ -217,10 +224,10 @@ client_get_streams(clicon_handle h,
goto done;
}
if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, 0, "clicon buffer");
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
- cprintf(cb,"<%s xmlns=\"%s\">", top, yang_argument_get(yns));
+ cprintf(cb, "<%s xmlns=\"%s\">", top, yang_argument_get(yns));
/* Second argument is a hack to have the same function for the
* RFC5277 and 8040 stream cases
*/
@@ -271,11 +278,16 @@ client_statedata(clicon_handle h,
yang_stmt *ymod;
int ret;
char *namespace;
+ cbuf *cb = NULL;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277")){
if ((ymod = yang_find_module_by_name(yspec, "clixon-rfc5277")) == NULL){
clicon_err(OE_YANG, ENOENT, "yang module clixon-rfc5277 not found");
@@ -285,7 +297,9 @@ client_statedata(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "clixon-rfc5277 namespace not found");
goto done;
}
- if (xml_parse_va(xret, yspec, "", namespace) < 0)
+
+ cprintf(cb, "", namespace);
+ if (xml_parse_string2(cbuf_get(cb), YB_TOP, yspec, xret, NULL) < 0)
goto done;
if ((ret = client_get_streams(h, yspec, xpath, ymod, "netconf", xret)) < 0)
goto done;
@@ -301,7 +315,9 @@ client_statedata(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "ietf-restconf-monitoring namespace not found");
goto done;
}
- if (xml_parse_va(xret, yspec, "", namespace) < 0)
+ cbuf_reset(cb);
+ cprintf(cb, "", namespace);
+ if (xml_parse_string2(cbuf_get(cb), YB_TOP, yspec, xret, NULL) < 0)
goto done;
if ((ret = client_get_streams(h, yspec, xpath, ymod, "restconf-state", xret)) < 0)
goto done;
@@ -323,6 +339,8 @@ client_statedata(clicon_handle h,
retval = 1; /* OK */
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
+ if (cb)
+ cbuf_free(cb);
return retval;
fail:
retval = 0;
@@ -574,7 +592,7 @@ from_client_edit_config(clicon_handle h,
xml_spec_set(xc, NULL);
/* Populate XML with Yang spec (why not do this in parser?)
*/
- if (xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(xc, yspec, NULL) < 0)
goto done;
/* Maybe validate xml here as in text_modify_top? */
if (xml_apply(xc, CX_ELMNT, xml_non_config_data, &non_config) < 0)
@@ -980,15 +998,22 @@ from_client_get(clicon_handle h,
}
/* If not only-state, then read running config
* Note xret can be pruned by nacm below and change name and
- * metrged with state data, so zero-copy cant be used
+ * merged with state data, so zero-copy cant be used
* Also, must use external namespace context here due to ", yspec, &xt) < 0)
+ if (xml_parse_file(fd, yspec, &xt) < 0)
goto done;
if (xt == NULL){
clicon_err(OE_XML, 0, "No xml tree in %s", filename);
@@ -748,6 +749,10 @@ main(int argc,
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
+ /* Load yang YANG module state */
+ if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE") &&
+ yang_spec_parse_module(h, "ietf-yang-library", NULL, yspec)< 0)
+ goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c
index edfe51c8..f2c28e66 100644
--- a/apps/backend/backend_plugin.c
+++ b/apps/backend/backend_plugin.c
@@ -133,7 +133,7 @@ clixon_plugin_statedata(clicon_handle h,
cbuf_free(ccc);
}
#endif
- if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(x, yspec, NULL) < 0)
goto done;
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
goto done;
diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c
index 55c0aa1c..5ef7e9cd 100644
--- a/apps/backend/backend_startup.c
+++ b/apps/backend/backend_startup.c
@@ -176,7 +176,7 @@ load_extraxml(clicon_handle h,
goto done;
}
yspec = clicon_dbspec_yang(h);
- if (xml_parse_file(fd, "", yspec, &xt) < 0)
+ if (xml_parse_file(fd, yspec, &xt) < 0)
goto done;
/* Replace parent w first child */
if (xml_rootchild(xt, 0, &xt) < 0)
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index c55e7358..d3bf9d05 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -796,7 +796,7 @@ load_config_file(clicon_handle h,
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
}
- if (xml_parse_file(fd, "", NULL, &xt) < 0)
+ if (xml_parse_file(fd, NULL, &xt) < 0)
goto done;
if (xt == NULL)
goto done;
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index 66786725..4885ba0c 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -541,7 +541,7 @@ main(int argc, char **argv)
cligen_tree_add(cli_cligen(h), treeref, pt);
if (printgen)
- cligen_print(stdout, pt, 1); /* pt_print */
+ pt_print(stdout, pt, 1); /* pt_print */
}
/* Initialize cli syntax */
diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c
index b9a3bd46..6eb7dbb6 100644
--- a/apps/cli/cli_show.c
+++ b/apps/cli/cli_show.c
@@ -2,7 +2,9 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -431,6 +433,7 @@ cli_show_config1(clicon_handle h,
cvec *argv)
{
int retval = -1;
+ int ret;
char *db;
char *formatstr;
char *xpath;
@@ -450,6 +453,10 @@ cli_show_config1(clicon_handle h,
goto done;
}
+ if ((yspec = clicon_dbspec_yang(h)) == NULL){
+ clicon_err(OE_FATAL, 0, "No DB_SPEC");
+ goto done;
+ }
/* First argv argument: Database */
db = cv_string_get(cvec_i(argv, 0));
/* Second argv argument: Format */
@@ -489,13 +496,13 @@ cli_show_config1(clicon_handle h,
clicon_rpc_generate_error(xerr, "Get configuration", NULL);
goto done;
}
- if ((yspec = clicon_dbspec_yang(h)) == NULL){
- clicon_err(OE_FATAL, 0, "No DB_SPEC");
+ /* Some formats (eg cli) require yang */
+ if ((ret = xml_spec_populate(xt, yspec, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ clicon_rpc_generate_error(xerr, "Get configuration", NULL);
goto done;
}
- /* Some formats (eg cli) require yang */
- if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
- goto done;
/* Print configuration according to format */
switch (format){
case FORMAT_XML:
@@ -681,6 +688,7 @@ cli_show_auto1(clicon_handle h,
cvec *argv)
{
int retval = 1;
+ int ret;
yang_stmt *yspec;
char *api_path_fmt; /* xml key format */
// char *api_path = NULL; /* xml key */
@@ -740,6 +748,13 @@ cli_show_auto1(clicon_handle h,
clicon_rpc_generate_error(xerr, "Get configuration", NULL);
goto done;
}
+ /* Some formats (eg cli) require yang */
+ if ((ret = xml_spec_populate(xt, yspec, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ clicon_rpc_generate_error(xerr, "Get configuration", NULL);
+ goto done;
+ }
if ((xp = xpath_first(xt, nsc, "%s", xpath)) != NULL)
/* Print configuration according to format */
switch (format){
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index a540628e..c92ac114 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -103,7 +103,6 @@ netconf_hello_dispatch(cxobj *xn)
return retval;
}
-
/*! Process incoming packet
* @param[in] h Clicon handle
* @param[in] cb Packet buffer
@@ -149,7 +148,7 @@ netconf_input_packet(clicon_handle h,
free(str0);
if ((xrpc=xpath_first(xreq, NULL, "//rpc")) != NULL){
isrpc++;
- if (xml_spec_populate_rpc_input(h, xrpc, yspec) < 0)
+ if (xml_spec_populate_rpc(xrpc, yspec, NULL) < 0)
goto done;
if ((ret = xml_yang_validate_rpc(h, xrpc, &xret)) < 0)
goto done;
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 8f3315f5..74d3f3ee 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -2,7 +2,9 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -157,9 +159,11 @@ netconf_get_config(clicon_handle h,
cxobj *xn,
cxobj **xret)
{
- cxobj *xfilter; /* filter */
- int retval = -1;
- char *ftype = NULL;
+ int retval = -1;
+ cxobj *xfilter; /* filter */
+ char *ftype = NULL;
+ cxobj *xdata;
+ yang_stmt *yspec;
/* ie ... */
if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL)
@@ -354,9 +358,11 @@ netconf_get(clicon_handle h,
cxobj *xn,
cxobj **xret)
{
- cxobj *xfilter; /* filter */
- int retval = -1;
- char *ftype = NULL;
+ int retval = -1;
+ cxobj *xfilter; /* filter */
+ char *ftype = NULL;
+ cxobj *xdata;
+ yang_stmt *yspec;
/* ie ... */
if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL)
@@ -471,7 +477,6 @@ netconf_notification_cb(int s,
}
/*
-
RESULT # If not present, events in the default NETCONF stream will be sent.
@@ -586,7 +591,7 @@ netconf_application_rpc(clicon_handle h,
/* 1. Check xn arguments with input statement. */
if ((yinput = yang_find(yrpc, Y_INPUT, NULL)) != NULL){
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
- if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(xn, yspec, NULL) < 0)
goto done;
if ((ret = xml_yang_validate_all_top(h, xn, &xerr)) < 0)
goto done;
@@ -618,9 +623,8 @@ netconf_application_rpc(clicon_handle h,
if ((youtput = yang_find(yrpc, Y_OUTPUT, NULL)) != NULL){
xoutput=xpath_first(*xret, NULL, "/");
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
- if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(xoutput, yspec, NULL) < 0)
goto done;
-
if ((ret = xml_yang_validate_all_top(h, xoutput, &xerr)) < 0)
goto done;
if (ret > 0 && (ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
diff --git a/apps/restconf/README.md b/apps/restconf/README.md
index 35afad43..78c7a67b 100644
--- a/apps/restconf/README.md
+++ b/apps/restconf/README.md
@@ -217,7 +217,7 @@ You can also run restconf in a debugger.
sudo gdb /www-data/clixon_restconf
(gdb) run -D 1 -f /usr/local/etc/example.xml
```
-but you need to ensure /www-data/fastcgi_restconf.sock has the following access:
+but you need to ensure /www-data/fastcgi_restconf.sock has the following access (may need to be done after restconf has started)
```
rwxr-xr-x 1 www-data www-data 0 sep 22 11:46 /www-data/fastcgi_restconf.sock
```
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index ad3fd10e..297a09fa 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -3,6 +3,7 @@
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -233,7 +234,7 @@ api_root(clicon_handle h,
if (xml_parse_string("2016-06-21", NULL, &xt) < 0)
goto done;
- if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(xt, yspec, NULL) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
@@ -548,15 +549,15 @@ usage(clicon_handle h,
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
- "\t-h \t\tHelp\n"
- "\t-D \tDebug level\n"
- "\t-f \tConfiguration file (mandatory)\n"
- "\t-l > \tLog on (s)yslog, (f)ile (syslog is default)\n"
- "\t-p \tYang directory path (see CLICON_YANG_DIR)\n"
- "\t-d \tSpecify restconf plugin directory dir (default: %s)\n"
- "\t-y \tLoad yang spec file (override yang main module)\n"
+ "\t-h \t\t Help\n"
+ "\t-D \t Debug level\n"
+ "\t-f \t Configuration file (mandatory)\n"
+ "\t-l > \t Log on (s)yslog, (f)ile (syslog is default)\n"
+ "\t-p \t Yang directory path (see CLICON_YANG_DIR)\n"
+ "\t-d \t Specify restconf plugin directory dir (default: %s)\n"
+ "\t-y \t Load yang spec file (override yang main module)\n"
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
- "\t-u \tInternal socket domain path or IP addr (see -a)\n"
+ "\t-u \t Internal socket domain path or IP addr (see -a)\n"
"\t-o \"]]>]]>" "^]]>]]>$"
-
#new "cli not defined extension"
#new "netconf not defined extension"
#expecteof "$clixon_netconf -qf $cfg -l o" 0 "$YANG" "Extension ex:not-defined not found"
@@ -179,7 +178,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^<
#expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found"
new "netconf schema resource, RFC 7895"
-expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' 'ietf-yang-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-yang-typesimplement'
+expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' 'ietf-yang-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-yang-typesimplement'
new "netconf edit config"
expecteof "$clixon_netconf -qf $cfg" 0 '125one]]>]]>' "^]]>]]>$"
@@ -272,7 +271,7 @@ new "netconfig config submodule"
expecteof "$clixon_netconf -qf $cfg" 0 'afoo]]>]]>' "^]]>]]>$"
new "netconf submodule get config"
-expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^afoo]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg -D 1 -l s" 0 ']]>]]>' '^afoo]]>]]>$'
new "netconf submodule validate"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
diff --git a/test/test_yang_bind.sh b/test/test_yang_bind.sh
new file mode 100755
index 00000000..64719662
--- /dev/null
+++ b/test/test_yang_bind.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+# Check how XML/JSON associates to YANG spec in different situations
+# In more detail, xml/json usually goes through these steps:
+# 1. Parse syntax, eg map JSON/XML concrete syntax to cxobj trees
+# 2. Populate/match cxobj tree X with yang statements, ie bind each cxobj node to yang_stmt nodes
+# a. X is a top-level node (XML and JSON)
+# b. X is a not a top-level node (XML and JSON)
+# 3. Sort children
+# 4. Validation (optional)
+# These tests are for cases 2a and 2b primarily. They occur somewhat differently in XML and JSON.
+
+# Magic line must be first in script (see README.md)
+s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
+
+: ${clixon_util_xml:="clixon_util_xml"}
+: ${clixon_util_json:="clixon_util_json"}
+
+APPNAME=example
+
+cfg=$dir/conf_match.xml
+fyang=$dir/match.yang
+fxml=$dir/x.xml
+fjson=$dir/x.json
+ftop=$dir/top.xml
+
+cat < $cfg
+
+ $cfg
+ /usr/local/share/clixon
+ $dir
+ $IETFRFC
+ $fyang
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/cli
+ /usr/local/lib/$APPNAME/netconf
+ /usr/local/lib/$APPNAME/backend
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ $dir
+ true
+
+EOF
+
+cat < $fyang
+module match{
+ yang-version 1.1;
+ prefix m;
+
+ namespace "urn:example:match";
+ container a {
+ description "Top level";
+ presence true;
+ list a {
+ description "Note same as parent to catch false positives 2a/2b";
+ key k;
+ leaf k{
+ type uint32;
+ }
+ }
+ anyxml any;
+ }
+}
+EOF
+
+new "test params: -f $cfg"
+
+cat < $ftop
+ 0
+EOF
+
+cat < $fxml
+ 43
+EOF
+
+new "2a XML Add a/a/k on top"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml)" 0 '^$'
+
+# Subtree without namespace (maybe shouldnt work?)
+cat < $fxml
+ 42
+EOF
+new "2b XML Add a/k under a without namespace"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml -t $ftop -T m:a)" 0 '^$'
+
+# Subtree with namespace
+cat < $fxml
+ 42
+EOF
+
+new "2b XML Add a/k under a"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml -t $ftop -T m:a)" 0 '^$'
+
+new "XML Add a/k on top, should fail"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml 2> /dev/null)" 255 '^$'
+
+cat < $fxml
+ 43
+EOF
+new "2b XML Add a/a/k under a should fail"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml -t $ftop -T m:a 2> /dev/null)" 255 '^$'
+
+# Anyxml
+cat < $fxml
+ hej
+EOF
+new "XML Add any under a"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml -t $ftop -T m:a)" 0 '^$'
+
+cat < $fxml
+ hej
+EOF
+
+new "XML Add any on top"
+expectpart "$($clixon_util_xml -vy $fyang -f $fxml)" 0 '^$'
+
+# OK, same thing with JSON!
+
+cat < $fjson
+ {"match:a":{"a":{"k":43}}}
+EOF
+
+new "2a JSON Add a/a/k on top"
+expectpart "$($clixon_util_xml -Jvy $fyang -f $fjson)" 0 '^$'
+
+
+# Subtree with namespace
+cat < $fjson
+ {"match:a":{"k":43}}
+EOF
+
+new "2b JSON Add a/k under a"
+expectpart "$($clixon_util_xml -Jvy $fyang -f $fjson -t $ftop -T m:a)" 0 '^$'
+
+new "JSON Add a/k on top, should fail"
+expectpart "$($clixon_util_xml -Jvy $fyang -f $fjson 2> /dev/null)" 255 '^$'
+
+cat < $fjson
+ {"match:a":{"a":{"k":43}}}
+EOF
+new "2b JSON Add a/a/k under a should fail"
+expectpart "$($clixon_util_xml -Jvy $fyang -f $fjson -t $ftop -T m:a 2> /dev/null)" 255 '^$'
+
+# Anyxml
+cat < $fjson
+ {"match:any":{"kalle":"hej"}}
+EOF
+new "JSON Add any under a"
+expectpart "$($clixon_util_xml -Jvy $fyang -f $fjson -t $ftop -T m:a)" 0 '^$'
+
+cat < $fjson
+ {"match:a":{"any":{"kalle":"hej"}}}
+EOF
+
+new "JSON Add any on top"
+expectpart "$($clixon_util_xml -Jvy $fyang -f $fjson)" 0 '^$'
+
+
+#rm -rf $dir
diff --git a/util/clixon_util_datastore.c b/util/clixon_util_datastore.c
index 51063a07..ff4122ba 100644
--- a/util/clixon_util_datastore.c
+++ b/util/clixon_util_datastore.c
@@ -2,7 +2,9 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -252,7 +254,7 @@ main(int argc, char **argv)
clicon_err(OE_UNIX, errno, "open(%s)", xmlfilename);
goto done;
}
- if (xml_parse_file(fd, "", yspec, &xt) < 0)
+ if (xml_parse_file(fd, yspec, &xt) < 0)
goto done;
close(fd);
}
diff --git a/util/clixon_util_grpc.c b/util/clixon_util_grpc.c
index 4df16d1e..00a09493 100644
--- a/util/clixon_util_grpc.c
+++ b/util/clixon_util_grpc.c
@@ -2,7 +2,9 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -65,7 +67,7 @@
/* clixon */
#include "clixon/clixon.h"
-#define UTIL_SSL_OPTS "hD:H:"
+#define UTIL_GRPC_OPTS "hD:H:"
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
@@ -485,7 +487,7 @@ main(int argc,
if ((h = clicon_handle_init()) == NULL)
goto done;
- while ((c = getopt(argc, argv, UTIL_SSL_OPTS)) != -1)
+ while ((c = getopt(argc, argv, UTIL_GRPC_OPTS)) != -1)
switch (c) {
case 'h':
usage(argv[0]);
diff --git a/util/clixon_util_insert.c b/util/clixon_util_insert.c
index 037fe585..fc528724 100644
--- a/util/clixon_util_insert.c
+++ b/util/clixon_util_insert.c
@@ -2,7 +2,9 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -150,7 +152,7 @@ main(int argc, char **argv)
clicon_err(OE_XML, 0, "Parsing base xml: %s", x0str);
goto done;
}
- if (xml_apply(x0, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(x0, yspec, NULL) < 0)
goto done;
if ((xb = xpath_first(x0, NULL, "%s", xpath)) == NULL){
clicon_err(OE_XML, 0, "xpath: %s not found in x0", xpath);
@@ -165,7 +167,7 @@ main(int argc, char **argv)
clicon_err(OE_XML, 0, "Parsing insert xml: %s", xistr);
goto done;
}
- if (xml_apply(xi, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(xi, yspec, NULL) < 0)
goto done;
if ((xi = xpath_first(xi, NULL, "%s", xpath)) == NULL){
clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath);
diff --git a/util/clixon_util_path.c b/util/clixon_util_path.c
index 084b9d94..dbba53ff 100644
--- a/util/clixon_util_path.c
+++ b/util/clixon_util_path.c
@@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2020 Olof Hagsand
+ Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -104,7 +105,8 @@ main(int argc,
clicon_handle h;
struct stat st;
cxobj *xcfg = NULL;
-
+ cxobj *xerr = NULL; /* malloced must be freed */
+
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init("api-path", LOG_DEBUG, CLICON_LOG_STDERR);
/* Initialize clixon handle */
@@ -204,38 +206,32 @@ main(int argc,
* If fd=0, then continue reading from stdin (after CR)
* If fd>0, reading from file opened as argv[1]
*/
- if (xml_parse_file(fd, "", NULL, &x) < 0){
+ if (xml_parse_file(fd, NULL, &x) < 0){
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
return -1;
}
/* Validate XML as well */
if (yang_file_dir){
- cxobj *x1;
- cxobj *xerr = NULL; /* malloced must be freed */
-
- x1 = NULL;
- while ((x1 = xml_child_each(x, x1, CX_ELMNT)) != NULL) {
- /* Populate */
- if (xml_apply0(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
- goto done;
- /* Add default values */
- if (xml_apply(x1, CX_ELMNT, xml_default, h) < 0)
- goto done;
- if ((ret = xml_yang_validate_all_top(h, x1, &xerr)) < 0)
- goto done;
- if (ret > 0 && (ret = xml_yang_validate_add(h, x1, &xerr)) < 0)
- goto done;
- if (ret == 0){
- if ((cb = cbuf_new()) ==NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
- goto done;
- }
- if (netconf_err2cb(xerr, cb) < 0)
- goto done;
- fprintf(stderr, "xml validation error: %s\n", cbuf_get(cb));
+ /* Populate */
+ if (xml_spec_populate(x, yspec, NULL) < 0)
+ goto done;
+ /* Add default values */
+ if (xml_apply(x, CX_ELMNT, xml_default, h) < 0)
+ goto done;
+ if ((ret = xml_yang_validate_all_top(h, x, &xerr)) < 0)
+ goto done;
+ if (ret > 0 && (ret = xml_yang_validate_add(h, x, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ if ((cb = cbuf_new()) ==NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
+ if (netconf_err2cb(xerr, cb) < 0)
+ goto done;
+ fprintf(stderr, "xml validation error: %s\n", cbuf_get(cb));
+ goto done;
}
if (xml_apply0(x, CX_ELMNT, xml_sort, h) < 0)
goto done;
diff --git a/util/clixon_util_socket.c b/util/clixon_util_socket.c
index 81e6621d..17a48bd9 100644
--- a/util/clixon_util_socket.c
+++ b/util/clixon_util_socket.c
@@ -150,7 +150,7 @@ main(int argc,
}
}
else{
- if (xml_parse_file(fd, NULL, NULL, &xt) < 0){
+ if (xml_parse_file(fd, NULL, &xt) < 0){
fprintf(stderr, "xml parse error: %s\n", clicon_err_reason);
goto done;
}
diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c
index b6b84201..e6241eb8 100644
--- a/util/clixon_util_xml.c
+++ b/util/clixon_util_xml.c
@@ -2,7 +2,9 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2017-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -63,6 +65,48 @@
/* clixon */
#include "clixon/clixon.h"
+/* Command line options to be passed to getopt(3) */
+#define UTIL_XML_OPTS "hD:f:Jjl:pvoy:Y:t:T:"
+
+static int
+validate_tree(clicon_handle h,
+ cxobj *xt,
+ yang_stmt *yspec)
+{
+ int retval = -1;
+ int ret;
+ cxobj *xerr = NULL; /* malloced must be freed */
+ cbuf *cbret = NULL;
+
+ /* should already be populated */
+ /* Add default values */
+ if (xml_apply(xt, CX_ELMNT, xml_default, h) < 0)
+ goto done;
+ if (xml_apply(xt, -1, xml_sort_verify, h) < 0)
+ clicon_log(LOG_NOTICE, "%s: sort verify failed", __FUNCTION__);
+ if ((ret = xml_yang_validate_all_top(h, xt, &xerr)) < 0)
+ goto done;
+ if (ret > 0 && (ret = xml_yang_validate_add(h, xt, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ if ((cbret = cbuf_new()) ==NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ if (netconf_err2cb(xerr, cbret) < 0)
+ goto done;
+ fprintf(stderr, "xml validation error: %s\n", cbuf_get(cbret));
+ goto done;
+ }
+ retval = 0;
+ done:
+ if (cbret)
+ cbuf_free(cbret);
+ if (xerr)
+ xml_free(xerr);
+ return retval;
+}
+
static int
usage(char *argv0)
{
@@ -79,6 +123,8 @@ usage(char *argv0)
"\t-p \t\tPretty-print output\n"
"\t-y \tYang filename or dir (load all files)\n"
"\t-Y \tYang dirs (can be several)\n"
+ "\t-t \tXML top input file (where base tree is pasted to)\n"
+ "\t-T \tXPath to where in top input file base should be pasted\n"
,
argv0);
exit(0);
@@ -89,7 +135,8 @@ main(int argc,
char **argv)
{
int retval = -1;
- cxobj *xt = NULL;
+ int ret;
+ cxobj *xt = NULL; /* Base cxobj tree parsed from xml or json */
cxobj *xc;
cbuf *cb = cbuf_new();
int c;
@@ -97,18 +144,23 @@ main(int argc,
int jsonin = 0;
int jsonout = 0;
char *input_filename = NULL;
+ char *top_input_filename = NULL;
char *yang_file_dir = NULL;
yang_stmt *yspec = NULL;
cxobj *xerr = NULL; /* malloced must be freed */
- int ret;
int pretty = 0;
int validate = 0;
int output = 0;
clicon_handle h;
struct stat st;
- int fd = 0; /* stdin */
+ int fd = 0; /* base file, stdin */
+ int tfd = -1; /* top file */
cxobj *xcfg = NULL;
cbuf *cbret = NULL;
+ cxobj *xtop = NULL; /* Top tree if any */
+ char *top_path = NULL;
+ cxobj *xbot; /* Place in xtop where base cxobj is parsed */
+ cvec *nsc = NULL;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
@@ -124,7 +176,7 @@ main(int argc,
clicon_conf_xml_set(h, xcfg);
optind = 1;
opterr = 0;
- while ((c = getopt(argc, argv, "hD:f:Jjl:pvoy:Y:")) != -1)
+ while ((c = getopt(argc, argv, UTIL_XML_OPTS)) != -1)
switch (c) {
case 'h':
usage(argv[0]);
@@ -162,6 +214,12 @@ main(int argc,
if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
goto done;
break;
+ case 't':
+ top_input_filename = optarg;
+ break;
+ case 'T': /* top file xpath */
+ top_path = optarg;
+ break;
default:
usage(argv[0]);
break;
@@ -170,6 +228,10 @@ main(int argc,
fprintf(stderr, "-v requires -y\n");
usage(argv[0]);
}
+ if (top_input_filename && top_path == NULL){
+ fprintf(stderr, "-t requires -T\n");
+ usage(argv[0]);
+ }
clicon_log_init(__FILE__, debug?LOG_DEBUG:LOG_INFO, logdst);
/* 1. Parse yang */
if (yang_file_dir){
@@ -188,6 +250,31 @@ main(int argc,
goto done;
}
}
+ /* If top file is declared, the base XML/JSON is pasted as child to the top-file.
+ * This is to emulate sub-tress, not just top-level parsing.
+ * Always validated
+ */
+ if (top_input_filename){
+ if ((tfd = open(top_input_filename, O_RDONLY)) < 0){
+ clicon_err(OE_YANG, errno, "open(%s)", top_input_filename);
+ goto done;
+ }
+ if (xml_parse_file(tfd, yspec, &xtop) < 0){
+ fprintf(stderr, "xml parse error: %s\n", clicon_err_reason);
+ goto done;
+ }
+ if (validate_tree(h, xtop, yspec) < 0)
+ goto done;
+
+ /* Compute canonical namespace context */
+ if (xml_nsctx_yangspec(yspec, &nsc) < 0)
+ goto done;
+ if ((xbot = xpath_first(xtop, nsc, "%s", top_path)) == NULL){
+ fprintf(stderr, "Path not found in top tree: %s\n", top_path);
+ goto done;
+ }
+ xt = xbot;
+ }
if (input_filename){
if ((fd = open(input_filename, O_RDONLY)) < 0){
clicon_err(OE_YANG, errno, "open(%s)", input_filename);
@@ -203,11 +290,15 @@ main(int argc,
goto done;
}
}
- else{
- if (xml_parse_file(fd, "", NULL, &xt) < 0){
+ else{ /* XML */
+ if ((ret = xml_parse_file2(fd, (xt==NULL)?YB_TOP:YB_PARENT, yspec, NULL, &xt, &xerr)) < 0){
fprintf(stderr, "xml parse error: %s\n", clicon_err_reason);
goto done;
}
+ if (ret == 0){
+ clicon_rpc_generate_error(xerr, "util_xml", NULL);
+ goto done;
+ }
}
/* Dump data structures (for debug) */
@@ -220,32 +311,8 @@ main(int argc,
/* 3. Validate data (if yspec) */
if (validate){
- xc = xml_child_i(xt, 0);
- /* Populate */
- if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (validate_tree(h, xt, yspec) < 0)
goto done;
- /* Sort */
- if (xml_apply0(xc, CX_ELMNT, xml_sort, h) < 0)
- goto done;
- /* Add default values */
- if (xml_apply(xc, CX_ELMNT, xml_default, h) < 0)
- goto done;
- if (xml_apply0(xc, -1, xml_sort_verify, h) < 0)
- clicon_log(LOG_NOTICE, "%s: sort verify failed", __FUNCTION__);
- if ((ret = xml_yang_validate_all_top(h, xc, &xerr)) < 0)
- goto done;
- if (ret > 0 && (ret = xml_yang_validate_add(h, xc, &xerr)) < 0)
- goto done;
- if (ret == 0){
- if ((cbret = cbuf_new()) ==NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
- goto done;
- }
- if (netconf_err2cb(xerr, cbret) < 0)
- goto done;
- fprintf(stderr, "xml validation error: %s\n", cbuf_get(cbret));
- goto done;
- }
}
/* 4. Output data (xml/json) */
if (output){
@@ -260,11 +327,17 @@ main(int argc,
}
retval = 0;
done:
+ if (nsc)
+ cvec_free(nsc);
if (cbret)
cbuf_free(cbret);
if (xcfg)
xml_free(xcfg);
- if (xt)
+ if (xerr)
+ xml_free(xerr);
+ if (xtop)
+ xml_free(xtop);
+ else if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c
index 4c1bdd9d..7d8f3599 100644
--- a/util/clixon_util_xpath.c
+++ b/util/clixon_util_xpath.c
@@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2020 Olof Hagsand
+ Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
This file is part of CLIXON.
@@ -137,6 +138,8 @@ main(int argc,
cvec *nsc = NULL;
int canonical = 0;
cxobj *xcfg = NULL;
+ cbuf *cbret = NULL;
+ cxobj *xerr = NULL; /* malloced must be freed */
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
@@ -277,32 +280,28 @@ main(int argc,
* If fd=0, then continue reading from stdin (after CR)
* If fd>0, reading from file opened as argv[1]
*/
- if (xml_parse_file(fd, "", NULL, &x0) < 0){
+ if (xml_parse_file(fd, NULL, &x0) < 0){
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
return -1;
}
/* Validate XML as well */
if (yang_file_dir){
- cbuf *cbret = NULL;
- cxobj *x1;
- cxobj *xerr = NULL; /* malloced must be freed */
-
- x1 = xml_child_i(x0, 0);
/* Populate */
- if (xml_apply0(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ if (xml_spec_populate(x0, yspec, NULL) < 0)
goto done;
/* Sort */
- if (xml_apply0(x1, CX_ELMNT, xml_sort, h) < 0)
+ if (xml_apply(x0, CX_ELMNT, xml_sort, h) < 0)
goto done;
+
/* Add default values */
- if (xml_apply(x1, CX_ELMNT, xml_default, h) < 0)
+ if (xml_apply(x0, CX_ELMNT, xml_default, h) < 0)
goto done;
- if (xml_apply0(x1, -1, xml_sort_verify, h) < 0)
+ if (xml_apply0(x0, -1, xml_sort_verify, h) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed", __FUNCTION__);
- if ((ret = xml_yang_validate_all_top(h, x1, &xerr)) < 0)
+ if ((ret = xml_yang_validate_all_top(h, x0, &xerr)) < 0)
goto done;
- if (ret > 0 && (ret = xml_yang_validate_add(h, x1, &xerr)) < 0)
+ if (ret > 0 && (ret = xml_yang_validate_add(h, x0, &xerr)) < 0)
goto done;
if (ret == 0){
if ((cbret = cbuf_new()) ==NULL){