Fixed [changing interface name not support with openconfig module #195](https://github.com/clicon/clixon/issues/195)

This commit is contained in:
Olof hagsand 2021-04-06 22:36:46 +02:00
parent d8be601606
commit 7412bb7b3d
5 changed files with 140 additions and 41 deletions

View file

@ -85,6 +85,7 @@ Developers may need to change their code
### Corrected Bugs ### 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 [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 * 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 * this also caused spaces to be printed to stdout after clixon-restconf was terminated

View file

@ -72,6 +72,74 @@
#include "clixon_cli_api.h" #include "clixon_cli_api.h"
#include "cli_common.h" /* internal functions */ #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<nvec; i++){
v = vec[i];
if (strlen(v) == 0)
continue;
if (nodeid_split(v, &prefix, &id) < 0)
goto done;
if (strcmp(id, "..") == 0 || strcmp(id, ".") == 0)
cprintf(xpath0, "/%s", id);
else
cprintf(xpath0, "/%s:%s", prefix?prefix:myprefix, id);
if (prefix){
free(prefix);
prefix = NULL;
}
if (id){
free(id);
id = NULL;
}
}
retval = 0;
done:
if (prefix)
free(prefix);
if (id)
free(id);
free(vec);
return retval;
}
/*! Completion callback intended for automatically generated data model /*! Completion callback intended for automatically generated data model
* *
* Returns an expand-type list of commands as used by cligen 'expand' * Returns an expand-type list of commands as used by cligen 'expand'
@ -116,12 +184,11 @@ expand_dbvar(void *h,
yang_stmt *yp; yang_stmt *yp;
yang_stmt *ytype; yang_stmt *ytype;
yang_stmt *ypath; yang_stmt *ypath;
cxobj *xcur;
char *xpathcur;
char *reason = NULL; char *reason = NULL;
cvec *nsc = NULL; cvec *nsc = NULL;
int ret; int ret;
int cvvi = 0; int cvvi = 0;
cbuf *cbxpath = NULL;
if (argv == NULL || cvec_len(argv) != 2){ if (argv == NULL || cvec_len(argv) != 2){
clicon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <xmlkeyfmt>"); clicon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <xmlkeyfmt>");
@ -153,18 +220,6 @@ expand_dbvar(void *h,
*/ */
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0) if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0)
goto done; 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 */ /* Create config top-of-tree */
if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
goto done; goto done;
@ -184,34 +239,65 @@ expand_dbvar(void *h,
if (y==NULL) if (y==NULL)
goto ok; 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 * The syntax for a path argument is a subset of the XPath abbreviated
* tree and apply the path to that. * syntax. Predicates are used only for constraining the values for the
* Last, the reference point for the xpath code below is changed to * key nodes for list entries. Each predicate consists of exactly one
* the point of the tentative new xml. * equality test per key, and multiple adjacent predicates MAY be
* Here the whole syntax tree is loaded, and it would be better to offload * present if a list has multiple keys. The syntax is formally defined
* such operations to the datastore by a generic xpath function. * by the rule "path-arg" in Section 14.
*/ * The "path" XPath expression is conceptually evaluated in the
if ((ytype = yang_find(y, Y_TYPE, NULL)) != NULL) * following context, in addition to the definition in Section 6.4.1:
if (strcmp(yang_argument_get(ytype), "leafref")==0){ *
if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){ * - If the "path" statement is defined within a typedef, the context
clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype)); * node is the leaf or leaf-list node in the data tree that
goto done; * references the typedef.
} * - Otherwise, the context node is the node in the data tree for which
xpathcur = yang_argument_get(ypath); * the "path" statement is defined.
if (xml_merge(xt, xtop, yspec, &reason) < 0) /* Merge xtop into xt */ */
goto done; if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
if (reason){ clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype));
fprintf(stderr, "%s\n", reason); goto done;
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;
}
} }
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; goto done;
/* Loop for inserting into commands cvec. /* Loop for inserting into commands cvec.
* Detect duplicates: for ordered-by system assume list is ordered, so you need * Detect duplicates: for ordered-by system assume list is ordered, so you need
@ -252,6 +338,8 @@ expand_dbvar(void *h,
ok: ok:
retval = 0; retval = 0;
done: done:
if (cbxpath)
cbuf_free(cbxpath);
if (xerr) if (xerr)
xml_free(xerr); xml_free(xerr);
if (nsc) if (nsc)

View file

@ -2,4 +2,13 @@
Clixon implements NETCONF as external access, and also as an internal protocol between backend and frontent clients. 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 <host> netconf
For more defails see [Clixon docs netconf](https://clixon-docs.readthedocs.io/en/latest/standards.html#netconf) For more defails see [Clixon docs netconf](https://clixon-docs.readthedocs.io/en/latest/standards.html#netconf)

View file

@ -480,6 +480,7 @@ xpath_tree_free(xpath_tree *xs)
* xpath_tree_free(xpt); * xpath_tree_free(xpt);
* @endcode * @endcode
* @see xpath_tree_free * @see xpath_tree_free
* @see xpath_tree2cbuf for unparsing, ie producing an original xpath string
*/ */
int int
xpath_parse(const char *xpath, xpath_parse(const char *xpath,

View file

@ -75,7 +75,7 @@ cat <<EOF > $cfg
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR> <CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE> <CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE> <CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<!--CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE--> <CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf ietf-interfaces</CLICON_CLI_AUTOCLI_EXCLUDE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK> <CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE> <CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>