From 7412bb7b3d1cf5e32199e93f017065bb5ab56819 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 6 Apr 2021 22:36:46 +0200 Subject: [PATCH] Fixed [changing interface name not support with openconfig module #195](https://github.com/clicon/clixon/issues/195) --- CHANGELOG.md | 1 + apps/cli/cli_show.c | 168 ++++++++++++++++++++++------- apps/netconf/README.md | 9 ++ lib/src/clixon_xpath.c | 1 + test/test_openconfig_interfaces.sh | 2 +- 5 files changed, 140 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c43207c2..1df40d5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,7 @@ Developers may need to change their code ### Corrected Bugs +* Fixed [changing interface name not support with openconfig module #195](https://github.com/clicon/clixon/issues/195) * Fixed [making cli_show_options's output more human readable #199](https://github.com/clicon/clixon/issues/199) * Fixed Yang parsing of comments in (extension) unknown statements, to allow multiple white space * this also caused spaces to be printed to stdout after clixon-restconf was terminated diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index f0a20904..0f68fc35 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -72,6 +72,74 @@ #include "clixon_cli_api.h" #include "cli_common.h" /* internal functions */ +/*! Given an xpath encoded in a cbuf, append a second xpath into the first + * The method reuses prefixes from xpath1 if they exist, otherwise the module prefix + * from y is used. Unless the element is .., . + * XXX: Predicates not handled + * The algorithm is not fool-proof, there are many cases it may not work + * To make it more complete, maybe parse the xpath to a tree and put it + * back to an xpath after modifcations, something like: + if (xpath_parse(yang_argument_get(ypath), &xpt) < 0) + goto done; + if (xpath_tree2cbuf(xpt, xcb) < 0) + goto done; +and +traverse_canonical + */ +static int +xpath_myappend(cbuf *xpath0, + char *xpath1, + yang_stmt *y, + cvec *nsc) +{ + int retval = -1; + char **vec = NULL; + char *v; + int nvec; + int i; + char *myprefix; + char *id = NULL; + char *prefix = NULL; + + if (xpath0 == NULL){ + clicon_err(OE_XML, EINVAL, "xpath0 is NULL"); + goto done; + } + if ((myprefix = yang_find_myprefix(y)) == NULL) + goto done; + if ((vec = clicon_strsep(xpath1, "/", &nvec)) == NULL) + goto done; + if (xpath1 && xpath1[0] == '/') + cbuf_reset(xpath0); + for (i=0; i "); @@ -153,18 +220,6 @@ expand_dbvar(void *h, */ if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0) goto done; - if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) - goto done; - - /* Get configuration */ - if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */ - goto done; - if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clixon_netconf_error(xe, "Get configuration", NULL); - goto ok; - } - xcur = xt; /* default top-of-tree */ - xpathcur = xpath; /* Create config top-of-tree */ if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) goto done; @@ -184,34 +239,65 @@ expand_dbvar(void *h, if (y==NULL) goto ok; + /* Transform api-path to xpath for netconf */ + if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) + goto done; + if (nsc != NULL){ + cvec_free(nsc); + nsc = NULL; + } + if (xml_nsctx_yang(y, &nsc) < 0) + goto done; + if ((cbxpath = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + cprintf(cbxpath, "%s", xpath); + if ((ytype = yang_find(y, Y_TYPE, NULL)) != NULL && + strcmp(yang_argument_get(ytype), "leafref") == 0){ + /* Special case for leafref. Detect leafref via Yang-type, + * Get Yang path element, tentatively add the new syntax to the whole + * tree and apply the path to that. + * Last, the reference point for the xpath code below is changed to + * the point of the tentative new xml. + * Here the whole syntax tree is loaded, and it would be better to offload + * such operations to the datastore by a generic xpath function. + */ - /* Special case for leafref. Detect leafref via Yang-type, - * Get Yang path element, tentatively add the new syntax to the whole - * tree and apply the path to that. - * Last, the reference point for the xpath code below is changed to - * the point of the tentative new xml. - * Here the whole syntax tree is loaded, and it would be better to offload - * such operations to the datastore by a generic xpath function. - */ - if ((ytype = yang_find(y, Y_TYPE, NULL)) != NULL) - if (strcmp(yang_argument_get(ytype), "leafref")==0){ - if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){ - clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype)); - goto done; - } - xpathcur = yang_argument_get(ypath); - if (xml_merge(xt, xtop, yspec, &reason) < 0) /* Merge xtop into xt */ - goto done; - if (reason){ - fprintf(stderr, "%s\n", reason); - goto done; - } - if ((xcur = xpath_first(xt, nsc, "%s", xpath)) == NULL){ - clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath); - goto done; - } + /* + * The syntax for a path argument is a subset of the XPath abbreviated + * syntax. Predicates are used only for constraining the values for the + * key nodes for list entries. Each predicate consists of exactly one + * equality test per key, and multiple adjacent predicates MAY be + * present if a list has multiple keys. The syntax is formally defined + * by the rule "path-arg" in Section 14. + * The "path" XPath expression is conceptually evaluated in the + * following context, in addition to the definition in Section 6.4.1: + * + * - If the "path" statement is defined within a typedef, the context + * node is the leaf or leaf-list node in the data tree that + * references the typedef. + * - Otherwise, the context node is the node in the data tree for which + * the "path" statement is defined. + */ + if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){ + clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype)); + goto done; } - if (xpath_vec(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 0) + /* */ + /* Extend xpath with leafref path: Append yang_argument_get(ypath) to xpath + */ + if (xpath_myappend(cbxpath, yang_argument_get(ypath), y, nsc) < 0) + goto done; + } + /* Get configuration based on cbxpath */ + if (clicon_rpc_get_config(h, NULL, dbstr, cbuf_get(cbxpath), nsc, &xt) < 0) + goto done; + if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){ + clixon_netconf_error(xe, "Get configuration", NULL); + goto ok; + } + if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0) goto done; /* Loop for inserting into commands cvec. * Detect duplicates: for ordered-by system assume list is ordered, so you need @@ -252,6 +338,8 @@ expand_dbvar(void *h, ok: retval = 0; done: + if (cbxpath) + cbuf_free(cbxpath); if (xerr) xml_free(xerr); if (nsc) diff --git a/apps/netconf/README.md b/apps/netconf/README.md index 02b4b80b..d4acf478 100644 --- a/apps/netconf/README.md +++ b/apps/netconf/README.md @@ -2,4 +2,13 @@ Clixon implements NETCONF as external access, and also as an internal protocol between backend and frontent clients. +You can expose ``clixon_netconf`` as an SSH subsystem according to `RFC 6242`. Register the subsystem in ``/etc/sshd_config``:: + + Subsystem netconf /usr/local/bin/clixon_netconf + +and then invoke it from a client using:: + + ssh -s netconf + + For more defails see [Clixon docs netconf](https://clixon-docs.readthedocs.io/en/latest/standards.html#netconf) \ No newline at end of file diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 9ae69d82..04eb34a1 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -480,6 +480,7 @@ xpath_tree_free(xpath_tree *xs) * xpath_tree_free(xpt); * @endcode * @see xpath_tree_free + * @see xpath_tree2cbuf for unparsing, ie producing an original xpath string */ int xpath_parse(const char *xpath, diff --git a/test/test_openconfig_interfaces.sh b/test/test_openconfig_interfaces.sh index deb3cb5a..94a16a34 100755 --- a/test/test_openconfig_interfaces.sh +++ b/test/test_openconfig_interfaces.sh @@ -75,7 +75,7 @@ cat < $cfg /usr/local/lib/$APPNAME/cli $APPNAME $APPNAME - + clixon-restconf ietf-interfaces /usr/local/var/$APPNAME/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile $dir