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,7 +239,22 @@ 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, /* Special case for leafref. Detect leafref via Yang-type,
* Get Yang path element, tentatively add the new syntax to the whole * Get Yang path element, tentatively add the new syntax to the whole
* tree and apply the path to that. * tree and apply the path to that.
@ -193,25 +263,41 @@ expand_dbvar(void *h,
* Here the whole syntax tree is loaded, and it would be better to offload * Here the whole syntax tree is loaded, and it would be better to offload
* such operations to the datastore by a generic xpath function. * 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){ /*
* 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){ if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype)); clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype));
goto done; goto done;
} }
xpathcur = yang_argument_get(ypath); /* */
if (xml_merge(xt, xtop, yspec, &reason) < 0) /* Merge xtop into xt */ /* Extend xpath with leafref path: Append yang_argument_get(ypath) to xpath
goto done; */
if (reason){ if (xpath_myappend(cbxpath, yang_argument_get(ypath), y, nsc) < 0)
fprintf(stderr, "%s\n", reason);
goto done; goto done;
} }
if ((xcur = xpath_first(xt, nsc, "%s", xpath)) == NULL){ /* Get configuration based on cbxpath */
clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath); if (clicon_rpc_get_config(h, NULL, dbstr, cbuf_get(cbxpath), nsc, &xt) < 0)
goto done; 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)
if (xpath_vec(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 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>