diff --git a/CHANGELOG.md b/CHANGELOG.md index 45eae3fd..011ea488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,20 +3,22 @@ ## 3.7.0 (Upcoming) ### Major changes: -* Much better support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex - * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` - * This includes all calls to `xpath_vec, xpath_first`, etc. - * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` - * The old API is stillenabled. To define the new, define XPATH_USE_NEW in include/clixon_custom.h and recompile -* Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. - * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 - * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 - * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. -* Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 +* Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5 + * XPATH checked at validation time +* Support for YANG identity, identityref, must and when according to RFC 7950 Sec 7.189. * Previous support did no validation of values. * Validation of types and CLI expansion * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. +* Much improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex + * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` + * This includes all calls to `xpath_vec, xpath_first`, etc. + * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` + * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. +* Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. + * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 + * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 + * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. ### Minor changes: * Added systemd example files under example/systemd @@ -33,7 +35,7 @@ * Added xmlns validation * for eg * Added yang identityref runtime validation -* Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. +* Removed cli callback vector functions. Set COMPAT_CLIV if you need to keep these functions in include/clixon_custom.h. * Replace functions as follows in CLI SPEC files: * cli_setv --> cli_set * cli_mergev --> cli_merge diff --git a/README.md b/README.md index bf4d82c5..74812d4f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ transaction support from a YANG specification. * [Support](#support) * [Dependencies](#dependencies) * [Extending](#extending) + * [XML and XPATH](#xml) * [Yang](#yang) * [Netconf](#netconf) * [Restconf](#restconf) @@ -89,9 +90,17 @@ are also available. Plugins are written in C and easiest is to look at [example](example/README.md) or consulting the [FAQ](doc/FAQ.md). +XML +=== +Clixon has its own implementation of XML and XPATH implementation. + +The standards covered include: +- [XML](https://www.w3.org/TR/2008/REC-xml-20081126) +- [Namespaces](https://www.w3.org/TR/2009/REC-xml-names-20091208) +- [XPATH](https://www.w3.org/TR/xpath-10) + Yang ==== - YANG and XML is at the heart of Clixon. Yang modules are used as a specification for handling XML configuration data. The YANG spec is used to generate an interactive CLI, netconf and restconf clients. It @@ -100,7 +109,7 @@ also manages an XML datastore. Clixon mainly follows [YANG 1.0 RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) with some exceptions: - conformance: feature, if-feature, deviation - list features: min/max-elements, unique -- when, must, action statements +- action statements - notifications The aim is also to cover new features in YANG 1.1 [YANG RFC 7950](https://www.rfc-editor.org/rfc/rfc7950.txt) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 19184c46..9528ac74 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -986,10 +986,10 @@ nacm_access(clicon_handle h, goto step10; /* User's group */ if (xpath_vec(xacm, -#ifdef XPATH_USE_NEW - "groups/group[user-name='%s']", -#else +#ifdef COMPAT_XSL "groups/group[user-name=%s]", +#else + "groups/group[user-name='%s']", #endif &gvec, &glen, username) < 0) goto done; @@ -1009,10 +1009,10 @@ nacm_access(clicon_handle h, char *gname; gname = xml_find_body(gvec[j], "name"); if (xpath_first(xrlist, -#ifdef XPATH_USE_NEW - ".[group='%s']", -#else +#ifdef COMPAT_XSL ".[group=%s]", +#else + ".[group='%s']", #endif gname)!=NULL) break; /* found */ diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 1d3c3e69..03ba734c 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -1249,7 +1249,7 @@ cli_copy_config(clicon_handle h, for (i=0; iys_keyword != Y_MUST) + continue; + xpath = yc->ys_argument; /* "must" has xpath argument */ + if ((b = xpath_vec_bool(xt, "%s", xpath)) < 0) + goto done; + if (!b){ + if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL) + clicon_err(OE_DB, 0, "%s", ye->ys_argument); + else + clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); + goto done; + } + } + /* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */ if ((yc = yang_find((yang_node*)ys, Y_WHEN, NULL)) != NULL){ xpath = yc->ys_argument; /* "when" has xpath argument */ - if (xpath_first(xt, "%s", xpath)) - ; - fprintf(stderr, "%s %s\n", __FUNCTION__, xpath); + if ((b = xpath_vec_bool(xt, "%s", xpath)) < 0) + goto done; + if (!b){ + clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); + goto done; + } } } retval = 0; @@ -1041,10 +1060,10 @@ api_path_fmt2xpath(char *api_path_fmt, goto done; } cprintf(cb, -#ifdef XPATH_USE_NEW - "[%s='%s']", -#else +#ifdef COMPAT_XSL "[%s=%s]", +#else + "[%s='%s']", #endif cv_name_get(cv), str); free(str); @@ -1464,10 +1483,10 @@ api_path2xpath_cvv(yang_spec *yspec, v = val; while ((cvi = cvec_each(cvk, cvi)) != NULL){ cprintf(xpath, -#ifdef XPATH_USE_NEW - "[%s='%s']", -#else +#ifdef COMPAT_XSL "[%s=%s]", +#else + "[%s='%s']", #endif cv_string_get(cvi), v); v += strlen(v)+1; diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 8e5d310a..34633e8a 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -776,6 +776,9 @@ xp_relop(xp_ctx *xc1, } /* switch type */ } ok: + /* Just ensure bool is 0 or 1 */ + if (xr->xc_type == XT_BOOL && xr->xc_bool != 0) + xr->xc_bool = 1; *xrp = xr; retval = 0; done: @@ -849,7 +852,6 @@ xp_eval(xp_ctx *xc, xp_ctx *xr2 = NULL; int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */ - assert(xc->xc_initial); if (debug){ cbuf *cb; if ((cb = cbuf_new()) == NULL){ diff --git a/test/lib.sh b/test/lib.sh index f361e6ec..35ab1c7a 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -5,8 +5,9 @@ testnr=0 testname= -# Set to 1 if new xpath. Set to nothing, or comment if old -#XPATH_USE_NEW=1 +# Set to 1 to enable old XSL implementation. Set to nothing, or comment if new. +# @see include/clixon_custom.h +#COMPAT_XSL=1 # For memcheck #clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli" diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 5496f69e..1a78f1cc 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -94,11 +94,11 @@ new "Add subtree eth/0/0 using none and create which should add eth/0/0" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" # Too many quotes, (single inside double inside single) need to fool bash -if [ -n "$XPATH_USE_NEW" ]; then +if [ -z "$COMPAT_XSL" ]; then cat < $tmp # new ]]>]]> EOF -else +else # old cat < $tmp ]]>]]> EOF @@ -122,12 +122,12 @@ new "netconf edit config" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" # Too many quotes -if [ -n "$XPATH_USE_NEW" ]; then +if [ -z "$COMPAT_XSL" ]; then cat < $tmp # new ]]>]]> EOF else -cat < $tmp # new +cat < $tmp # old ]]>]]> EOF fi @@ -136,7 +136,7 @@ new "netconf get config xpath" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth1true]]>]]>$" # Too many quotes -if [ -n "$XPATH_USE_NEW" ]; then +if [ -z "$COMPAT_XSL" ]; then cat < $tmp # new ]]>]]> EOF diff --git a/test/test_order.sh b/test/test_order.sh index d0536dbf..aa7f3413 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -121,7 +121,7 @@ fi new "verify running from start, should be: l,c,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if XML_SORT set to false" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhoppdbcaabcddbarabarcbarbbarabarbbarcbardbar]]>]]>$" -if [ -n "$XPATH_USE_NEW" ]; then #new +if [ -z "$COMPAT_XSL" ]; then #new new "get each ordered-by user leaf-list" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" diff --git a/test/test_when_must.sh b/test/test_when_must.sh new file mode 100755 index 00000000..dd8f2af8 --- /dev/null +++ b/test/test_when_must.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Yang when and must conditional xpath specification +# Testing of validation phase. + +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh + +cfg=$dir/conf_yang.xml +fyang=$dir/test.yang + +cat < $cfg + + $cfg + /usr/local/share/$APPNAME/yang + $APPNAME + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + +EOF + +cat < $fyang +module $APPNAME{ + prefix ex; + identity routing-protocol { + description + "Base identity from which routing protocol identities are + derived."; + } + identity direct { + base routing-protocol; + description + "Routing pseudo-protocol which provides routes to directly + connected networks."; + } + identity static { + base routing-protocol; + description + "Static routing pseudo-protocol."; + } + list whenex { + key "type name"; + leaf type { + type identityref { + base routing-protocol; + } + } + leaf name { + type string; + } + leaf route-preference { + type uint32; + } + container static-routes { + when "../type='static'" { + description + "This container is only valid for the 'static' + routing protocol."; + } + presence true; + } + } + container interface { + leaf ifType { + type enumeration { + enum ethernet; + enum atm; + } + } + leaf ifMTU { + type uint32; + } + must 'ifType != "ethernet" or ifMTU = 1500' { + error-message "An Ethernet MTU must be 1500"; + } + must 'ifType != "atm" or' + + ' (ifMTU <= 17966 and ifMTU >= 64)' { + error-message "An ATM MTU must be 64 .. 17966"; + } + } +} +EOF + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $cfg -y $fyang +if [ $? -ne 0 ]; then + err +fi + +new "start backend -s init -f $cfg -y $fyang" +# start new backend +sudo clixon_backend -s init -f $cfg -y $fyang +if [ $? -ne 0 ]; then + err +fi + +new "when: add static route" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "staticr1]]>]]>" "^]]>]]>$" + +new "when: validate ok" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "when: add direct route" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "directr2]]>]]>" "^]]>]]>$" + +new "when get config" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^directr2staticr1]]>]]>$" + +new "when: validate fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorxpath static-routes validation failed]]>]]>$" + +new "when: discard-changes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "must: add interface" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet1500]]>]]>" "^]]>]]>$" + +new "must: validate ok" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "must: add atm interface" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "atm32]]>]]>" "^]]>]]>$" + +new "must: atm validate fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorAn ATM MTU must be 64 .. 17966]]>]]>$" + +new "must: add eth interface" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet989]]>]]>" "^]]>]]>$" + +new "must: eth validate fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorAn Ethernet MTU must be 1500]]>]]>" + +# Check if still alive +pid=`pgrep clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi + +rm -rf $dir diff --git a/test/test_yang.sh b/test/test_yang.sh index 7b5a7af6..8b5ec20c 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -136,7 +136,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" -if [ -n "$XPATH_USE_NEW" ]; then # new +if [ -z "$COMPAT_XSL" ]; then # new new "netconf get leaf-list path" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" else # old