1943 lines
64 KiB
C
1943 lines
64 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
|
Copyright (C) 2017-2019 Olof Hagsand
|
|
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
|
|
|
This file is part of CLIXON.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
the GNU General Public License Version 3 or later (the "GPL"),
|
|
in which case the provisions of the GPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of the GPL, and not to allow others to
|
|
use your version of this file under the terms of Apache License version 2,
|
|
indicate your decision by deleting the provisions above and replace them with
|
|
the notice and other provisions required by the GPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the Apache License version 2 or the GPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <syslog.h>
|
|
#include <pwd.h>
|
|
#include <inttypes.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clixon */
|
|
#include <clixon/clixon.h>
|
|
|
|
/* Exported functions in this file are in clixon_cli_api.h */
|
|
#include "clixon_cli_api.h"
|
|
#include "cli_autocli.h"
|
|
#include "cli_common.h" /* internal functions */
|
|
|
|
/*! Given an xpath encoded in a cbuf, append a second xpath into the first (unless absolute path)
|
|
*
|
|
* The method reuses prefixes from xpath1 if they exist, otherwise the module prefix
|
|
* from y is used. Unless the element is .., .
|
|
* @param[in,out] cb0 Result XPath as cbuf
|
|
* @param[in] xpath1 Input XPath
|
|
* @param[in] y Yang of xpath1
|
|
* @param[in,out] nsc Namespace
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*
|
|
* 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;
|
|
*/
|
|
static int
|
|
xpath_append(cbuf *cb0,
|
|
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;
|
|
int initialups = 1; /* If starts with ../../.. */
|
|
char *xpath0;
|
|
char *ns;
|
|
int ret;
|
|
int j;
|
|
|
|
if (cb0 == NULL){
|
|
clixon_err(OE_XML, EINVAL, "cb0 is NULL");
|
|
goto done;
|
|
}
|
|
if (xpath1 == NULL || strlen(xpath1)==0)
|
|
goto ok;
|
|
if ((myprefix = yang_find_myprefix(y)) == NULL)
|
|
goto done;
|
|
if ((vec = clicon_strsep(xpath1, "/", &nvec)) == NULL)
|
|
goto done;
|
|
if (xpath1[0] == '/')
|
|
cbuf_reset(cb0);
|
|
xpath0 = cbuf_get(cb0);
|
|
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)
|
|
initialups = 0;
|
|
else if (strcmp(id, "..") == 0){
|
|
if (initialups){
|
|
/* Subtract from xpath0 */
|
|
for (j=cbuf_len(cb0); j >= 0; j--){
|
|
if (xpath0[j] != '/')
|
|
continue;
|
|
cbuf_trunc(cb0, j);
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
initialups = 0;
|
|
cprintf(cb0, "/%s", id);
|
|
}
|
|
}
|
|
else{
|
|
initialups = 0;
|
|
/* If prefix is not in nsc, it needs to be added */
|
|
if (prefix && cvec_find(nsc, prefix) == NULL){
|
|
ns = NULL;
|
|
if ((ret = yang_find_namespace_by_prefix(y, prefix, &ns)) < 0)
|
|
goto done;
|
|
if (ret == 0){
|
|
clixon_err(OE_DB, 0, "Prefix %s does not have an associated namespace", prefix);
|
|
goto done;
|
|
}
|
|
if (xml_nsctx_add(nsc, prefix, ns) < 0)
|
|
goto done;
|
|
}
|
|
cprintf(cb0, "/%s:%s", prefix?prefix:myprefix, id);
|
|
}
|
|
if (prefix){
|
|
free(prefix);
|
|
prefix = NULL;
|
|
}
|
|
if (id){
|
|
free(id);
|
|
id = NULL;
|
|
}
|
|
}
|
|
ok:
|
|
retval = 0;
|
|
done:
|
|
if (prefix)
|
|
free(prefix);
|
|
if (id)
|
|
free(id);
|
|
free(vec);
|
|
return retval;
|
|
}
|
|
|
|
/*! Completion callback of variable for configured data and automatically generated data model
|
|
*
|
|
* Returns an expand-type list of commands as used by cligen 'expand'
|
|
* functionality.
|
|
*
|
|
* Assume callback given in a cligen spec: a <x:int expand_dbvar("db" "<xmlkeyfmt>")
|
|
* @param[in] h clicon handle
|
|
* @param[in] name Name of this function (eg "expand_dbvar")
|
|
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
|
* @param[in] argv Arguments given at the callback:
|
|
* <db> Name of datastore, such as "running"
|
|
* <api_path_fmt> Generated API PATH (this is sometimes added implicitly)
|
|
* [<mt-point>] Optional YANG path-arg/xpath from mount-point
|
|
* @param[out] commands vector of function pointers to callback functions
|
|
* @param[out] helptxt vector of pointers to helptexts
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see cli_expand_var_generate where api_path_fmt + mt-point are generated
|
|
* The syntax of <api_path_fmt> is of RFC8040 api-path with the following extension:
|
|
* %s Represents the values of cvv in order starting from element 1
|
|
* %k Represents the (first) key of the (previous) list
|
|
*/
|
|
int
|
|
expand_dbvar(void *h,
|
|
char *name,
|
|
cvec *cvv,
|
|
cvec *argv,
|
|
cvec *commands,
|
|
cvec *helptexts)
|
|
{
|
|
int retval = -1;
|
|
cbuf *api_path_fmt_cb = NULL;
|
|
char *api_path_fmt;
|
|
char *api_path = NULL;
|
|
char *api_path_fmt01 = NULL;
|
|
char *dbstr;
|
|
cxobj *xt = NULL;
|
|
char *xpath = NULL;
|
|
cxobj **xvec = NULL;
|
|
cxobj *xe; /* direct ptr */
|
|
cxobj *xerr = NULL; /* free */
|
|
size_t xlen = 0;
|
|
cxobj *x;
|
|
char *bodystr;
|
|
int i;
|
|
char *bodystr0 = NULL; /* previous */
|
|
cg_var *cv;
|
|
cxobj *xtop = NULL; /* xpath root */
|
|
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
|
yang_stmt *y = NULL; /* yang spec of xpath */
|
|
yang_stmt *yp;
|
|
cvec *nsc = NULL;
|
|
int ret;
|
|
int cvvi = 0;
|
|
cbuf *cbxpath = NULL;
|
|
yang_stmt *ypath;
|
|
yang_stmt *ytype;
|
|
char *mtpoint = NULL;
|
|
yang_stmt *yspec0 = NULL;
|
|
cvec *nsc0 = NULL;
|
|
char *str;
|
|
int grouping_treeref;
|
|
cvec *callback_cvv;
|
|
|
|
if (argv == NULL || (cvec_len(argv) != 2 && cvec_len(argv) != 3)){
|
|
clixon_err(OE_PLUGIN, EINVAL, "requires arguments: <db> <apipathfmt> [<mountpt>]");
|
|
goto done;
|
|
}
|
|
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
|
|
clixon_err(OE_FATAL, 0, "No DB_SPEC");
|
|
goto done;
|
|
}
|
|
if ((cv = cvec_i(argv, 0)) == NULL){
|
|
clixon_err(OE_PLUGIN, 0, "Error when accessing argument <db>");
|
|
goto done;
|
|
}
|
|
dbstr = cv_string_get(cv);
|
|
if (strcmp(dbstr, "running") != 0 &&
|
|
strcmp(dbstr, "candidate") != 0 &&
|
|
strcmp(dbstr, "startup") != 0){
|
|
clixon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
|
|
goto done;
|
|
}
|
|
if ((cv = cvec_i(argv, 1)) == NULL){
|
|
clixon_err(OE_PLUGIN, 0, "Error when accessing argument <api_path>");
|
|
goto done;
|
|
}
|
|
if (autocli_grouping_treeref(h, &grouping_treeref) < 0)
|
|
goto done;
|
|
if ((api_path_fmt_cb = cbuf_new()) == NULL){
|
|
clixon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if (grouping_treeref &&
|
|
(callback_cvv = cligen_callback_arguments_get(cli_cligen(h))) != NULL){
|
|
/* Concatenate callback arguments to a single prepend string */
|
|
if (cvec_concat_cb(callback_cvv, api_path_fmt_cb) < 0)
|
|
goto done;
|
|
}
|
|
cprintf(api_path_fmt_cb, "%s", cv_string_get(cv));
|
|
api_path_fmt = cbuf_get(api_path_fmt_cb);
|
|
if (cvec_len(argv) > 2){ /* mountpoint */
|
|
/* api_path_fmt is without top-level */
|
|
cv = cvec_i(argv, 2);
|
|
str = cv_string_get(cv);
|
|
if (strncmp(str, "mtpoint:", strlen("mtpoint:")) != 0){
|
|
clixon_err(OE_PLUGIN, 0, "mtpoint does not begin with 'mtpoint:'");
|
|
goto done;
|
|
}
|
|
mtpoint = str + strlen("mtpoint:");
|
|
/* Get and combined api-path01 */
|
|
if (mtpoint_paths(yspec0, mtpoint, api_path_fmt, &api_path_fmt01) < 0)
|
|
goto done;
|
|
if (api_path_fmt2api_path(api_path_fmt01, cvv, yspec0, &api_path, &cvvi) < 0)
|
|
goto done;
|
|
}
|
|
else{
|
|
if (api_path_fmt2api_path(api_path_fmt, cvv, yspec0, &api_path, &cvvi) < 0)
|
|
goto done;
|
|
}
|
|
if (api_path == NULL)
|
|
goto ok;
|
|
/* Create config top-of-tree */
|
|
if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
xbot = xtop;
|
|
/* This is primarily to get "y",
|
|
* xpath2xml would have worked!!
|
|
* XXX: but y is just the first in this list, there could be other y:s?
|
|
*/
|
|
if ((ret = api_path2xml(api_path, yspec0, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0)
|
|
goto done;
|
|
if (ret == 0){
|
|
clixon_err_netconf(h, OE_NETCONF, 0, xerr, "Expand datastore symbol");
|
|
goto done;
|
|
}
|
|
if (y == NULL){
|
|
clixon_err(OE_YANG, 0, "y is NULL");
|
|
goto done;
|
|
}
|
|
/* Transform api-path to xpath for netconf */
|
|
if ((ret = api_path2xpath(api_path, yspec0, &xpath, &nsc, &xerr)) < 0)
|
|
goto done;
|
|
if (ret == 0){
|
|
clixon_err_netconf(h, OE_NETCONF, 0, xerr, "Expand datastore symbol");
|
|
goto done;
|
|
}
|
|
if ((cbxpath = cbuf_new()) == NULL){
|
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if (mtpoint){
|
|
if (xml_nsctx_yangspec(yspec0, &nsc0) < 0)
|
|
goto done;
|
|
cv = NULL; /* Append nsc0 to nsc */
|
|
while ((cv = cvec_each(nsc0, cv)) != NULL)
|
|
cvec_append_var(nsc, cv);
|
|
}
|
|
cprintf(cbxpath, "%s", xpath);
|
|
if (clicon_option_bool(h, "CLICON_CLI_EXPAND_LEAFREF") &&
|
|
(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.
|
|
*/
|
|
|
|
/*
|
|
* 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){
|
|
clixon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype));
|
|
goto done;
|
|
}
|
|
/* Extend xpath with leafref path: Append yang_argument_get(ypath) to xpath
|
|
*/
|
|
if (xpath_append(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, NULL, &xt) < 0)
|
|
goto done;
|
|
if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
|
clixon_err_netconf(h, OE_NETCONF, 0, xe, "Get configuration");
|
|
goto ok;
|
|
}
|
|
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, cbuf_get(cbxpath)) < 0)
|
|
goto done;
|
|
/* Loop for inserting into commands cvec.
|
|
* Detect duplicates: for ordered-by system assume list is ordered, so you need
|
|
* just remember previous
|
|
* but for ordered-by system, check the whole list
|
|
*/
|
|
bodystr0 = NULL;
|
|
for (i = 0; i < xlen; i++) {
|
|
x = xvec[i];
|
|
if (xml_type(x) == CX_BODY)
|
|
bodystr = xml_value(x);
|
|
else
|
|
bodystr = xml_body(x);
|
|
if (bodystr == NULL)
|
|
continue; /* no body, cornercase */
|
|
if ((y = xml_spec(x)) != NULL &&
|
|
(yp = yang_parent_get(y)) != NULL &&
|
|
yang_keyword_get(yp) == Y_LIST &&
|
|
yang_find(yp, Y_ORDERED_BY, "user") != NULL){
|
|
/* Detect duplicates linearly in existing values */
|
|
{
|
|
cg_var *cv = NULL;
|
|
while ((cv = cvec_each(commands, cv)) != NULL)
|
|
if (strcmp(cv_string_get(cv), bodystr) == 0)
|
|
break;
|
|
if (cv == NULL)
|
|
cvec_add_string(commands, NULL, bodystr);
|
|
}
|
|
}
|
|
else{
|
|
if (bodystr0 && strcmp(bodystr, bodystr0) == 0)
|
|
continue; /* duplicate, assume sorted */
|
|
bodystr0 = bodystr;
|
|
/* RFC3986 decode */
|
|
cvec_add_string(commands, NULL, bodystr);
|
|
}
|
|
}
|
|
ok:
|
|
retval = 0;
|
|
done:
|
|
if (nsc0)
|
|
cvec_free(nsc0);
|
|
if (api_path_fmt_cb)
|
|
cbuf_free(api_path_fmt_cb);
|
|
if (api_path_fmt01)
|
|
free(api_path_fmt01);
|
|
if (cbxpath)
|
|
cbuf_free(cbxpath);
|
|
if (xerr)
|
|
xml_free(xerr);
|
|
if (nsc)
|
|
xml_nsctx_free(nsc);
|
|
if (api_path)
|
|
free(api_path);
|
|
if (xvec)
|
|
free(xvec);
|
|
if (xtop)
|
|
xml_free(xtop);
|
|
if (xt)
|
|
xml_free(xt);
|
|
if (xpath)
|
|
free(xpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! Completion callback of variable for yang schema list nodes
|
|
*
|
|
* Typical yang:
|
|
* container foo { list bar; }
|
|
* modA:
|
|
* augment foo bar;
|
|
* modB:
|
|
* augment foo fie;
|
|
* This function expands foo to: bar, fie...
|
|
* Or (if <module> is true): modA:bar, modB:fie...
|
|
* @param[in] h clicon handle
|
|
* @param[in] name Name of this function
|
|
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
|
* @param[in] argv Arguments given at the callback:
|
|
* <schemanode> Absolute YANG schema-node (eg: /ctrl:services)
|
|
* <modname> true|false: Show with api-path module-name, eg moda:foo, modb:fie
|
|
* @param[out] commands vector of function pointers to callback functions
|
|
* @param[out] helptxt vector of pointers to helptexts
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
expand_yang_list(void *h,
|
|
char *name,
|
|
cvec *cvv,
|
|
cvec *argv,
|
|
cvec *commands,
|
|
cvec *helptexts)
|
|
{
|
|
int retval = -1;
|
|
int argc = 0;
|
|
cg_var *cv;
|
|
char *schema_nodeid;
|
|
yang_stmt *yspec0 = NULL;
|
|
yang_stmt *yres = NULL;
|
|
yang_stmt *yn = NULL;
|
|
yang_stmt *ydesc;
|
|
yang_stmt *ymod;
|
|
int modname = 0;
|
|
cbuf *cb = NULL;
|
|
|
|
if (argv == NULL || cvec_len(argv) < 1 || cvec_len(argv) > 2){
|
|
clixon_err(OE_PLUGIN, EINVAL, "requires arguments: <schemanode> [<modname>]");
|
|
goto done;
|
|
}
|
|
if ((cv = cvec_i(argv, argc++)) == NULL){
|
|
clixon_err(OE_PLUGIN, 0, "Error when accessing argument <schemanode>");
|
|
goto done;
|
|
}
|
|
schema_nodeid = cv_string_get(cv);
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &modname) < 0)
|
|
goto done;
|
|
}
|
|
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
|
|
clixon_err(OE_FATAL, 0, "No DB_SPEC");
|
|
goto done;
|
|
}
|
|
if (yang_abs_schema_nodeid(yspec0, schema_nodeid, &yres) < 0)
|
|
goto done;
|
|
if ((cb = cbuf_new()) == NULL){
|
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
yn = NULL;
|
|
while ((yn = yn_each(yres, yn)) != NULL) {
|
|
if (yang_keyword_get(yn) != Y_LIST)
|
|
continue;
|
|
cbuf_reset(cb);
|
|
if (modname){
|
|
if (ys_real_module(yn, &ymod) < 0)
|
|
goto done;
|
|
cprintf(cb, "%s:", yang_argument_get(ymod));
|
|
}
|
|
cprintf(cb, "%s", yang_argument_get(yn));
|
|
cvec_add_string(commands, NULL, cbuf_get(cb));
|
|
if ((ydesc = yang_find(yn, Y_DESCRIPTION, NULL)) != NULL)
|
|
cvec_add_string(helptexts, NULL, yang_argument_get(ydesc));
|
|
else
|
|
cvec_add_string(helptexts, NULL, "Service");
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*! CLI callback show yang spec. If arg given matches yang argument string
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of command variables
|
|
* @param[in] argv
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
show_yang(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
yang_stmt *yn;
|
|
char *str = NULL;
|
|
yang_stmt *yspec;
|
|
|
|
yspec = clicon_dbspec_yang(h);
|
|
if (cvec_len(argv) > 0){
|
|
if ((str = cv_string_get(cvec_i(argv, 0))) != NULL &&
|
|
(yn = yang_find(yspec, 0, str)) != NULL)
|
|
if (yang_print_cb(stdout, yn, cligen_output) < 0)
|
|
goto done;
|
|
}
|
|
else{
|
|
yn = NULL;
|
|
while ((yn = yn_each(yspec, yn)) != NULL) {
|
|
if (yang_print_cb(stdout, yn, cligen_output) < 0)
|
|
goto done;
|
|
}
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Common internal show routine for several show cli callbacks
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] db Datastore
|
|
* @param[in] format Output format
|
|
* @param[in] pretty
|
|
* @param[in] state
|
|
* @param[in] withdefault RFC 6243 with-default modes
|
|
* @param[in] extdefault with-defaults with propriatary extensions
|
|
* @param[in] prepend CLI prefix to prepend cli syntax, eg "set "
|
|
* @param[in] xpath XPath
|
|
* @param[in] fromroot If 0, display config from node of XPATH, if 1 display from root
|
|
* @param[in] nsc Namespace mapping for xpath
|
|
* @param[in] skiptop If set, do not show object itself, only its children
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
cli_show_common(clixon_handle h,
|
|
char *db,
|
|
enum format_enum format,
|
|
int pretty,
|
|
int state,
|
|
char *withdefault,
|
|
char *extdefault,
|
|
char *prepend,
|
|
char *xpath,
|
|
int fromroot,
|
|
cvec *nsc,
|
|
int skiptop
|
|
)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xt = NULL;
|
|
cxobj *xerr;
|
|
cxobj **vec = NULL;
|
|
size_t veclen;
|
|
cxobj *xp;
|
|
int i;
|
|
|
|
if (state && strcmp(db, "running") != 0){
|
|
clixon_err(OE_FATAL, 0, "Show state only for running database, not %s", db);
|
|
goto done;
|
|
}
|
|
if (state == 0){ /* Get configuration-only from a database */
|
|
if (clicon_rpc_get_config(h, NULL, db, xpath, nsc, withdefault, &xt) < 0)
|
|
goto done;
|
|
}
|
|
else { /* Get configuration and state from running */
|
|
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, withdefault, &xt) < 0)
|
|
goto done;
|
|
}
|
|
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
|
clixon_err_netconf(h, OE_NETCONF, 0, xerr, "Get configuration");
|
|
goto done;
|
|
}
|
|
/* Special tagged modes: strip wd:default=true attribute and (optionally) nodes associated with it */
|
|
if (extdefault &&
|
|
(strcmp(extdefault, "report-all-tagged-strip") == 0 ||
|
|
strcmp(extdefault, "report-all-tagged-default") == 0)){
|
|
if (purge_tagged_nodes(xt, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE, "default", "true",
|
|
strcmp(extdefault, "report-all-tagged-strip")
|
|
) < 0)
|
|
goto done;
|
|
/* Remove empty containers */
|
|
if (xml_default_nopresence(xt, 2, 0) < 0)
|
|
goto done;
|
|
}
|
|
if (fromroot)
|
|
xpath="/";
|
|
if (xpath_vec(xt, nsc, "%s", &vec, &veclen, xpath) < 0)
|
|
goto done;
|
|
if (veclen){
|
|
/* Special case LIST */
|
|
if (format == FORMAT_JSON){
|
|
switch (format){
|
|
case FORMAT_JSON:
|
|
if (xml2json_vec(stdout, vec, veclen, pretty, cligen_output, skiptop) < 0)
|
|
goto done;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else /* Default */
|
|
for (i=0; i<veclen; i++){
|
|
xp = vec[i];
|
|
/* Print configuration according to format */
|
|
switch (format){
|
|
case FORMAT_XML:
|
|
if (clixon_xml2file(stdout, xp, 0, pretty, NULL, cligen_output, skiptop, 1) < 0)
|
|
goto done;
|
|
if (!pretty && i == veclen-1)
|
|
cligen_output(stdout, "\n");
|
|
break;
|
|
case FORMAT_TEXT: /* XXX does not handle multiple leaf-list */
|
|
if (clixon_text2file(stdout, xp, 0, cligen_output, skiptop, 1) < 0)
|
|
goto done;
|
|
break;
|
|
case FORMAT_CLI:
|
|
/* If xp is not bound, cli prints are skipped */
|
|
if (clixon_cli2file(h, stdout, xp, prepend, cligen_output, skiptop) < 0) /* cli syntax */
|
|
goto done;
|
|
break;
|
|
case FORMAT_NETCONF:
|
|
if (i==0){
|
|
cligen_output(stdout, "<rpc xmlns=\"%s\" %s><edit-config><target><candidate/></target><config>",
|
|
NETCONF_BASE_NAMESPACE, NETCONF_MESSAGE_ID_ATTR);
|
|
if (pretty)
|
|
cligen_output(stdout, "\n");
|
|
}
|
|
if (clixon_xml2file(stdout, xp, 2, pretty, NULL, cligen_output, skiptop, 1) < 0)
|
|
goto done;
|
|
if (i == veclen-1)
|
|
cligen_output(stdout, "</config></edit-config></rpc>]]>]]>\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (format == FORMAT_JSON)
|
|
cligen_output(stdout, "{}\n");
|
|
retval = 0;
|
|
done:
|
|
if (vec)
|
|
free(vec);
|
|
if (xt)
|
|
xml_free(xt);
|
|
return retval;
|
|
}
|
|
|
|
/*! Common internal parse cli show format option
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] argv String vector: <dbname> <format> <xpath> [<varname>]
|
|
* @param[in] argc Index into argv
|
|
* @param[out] format Output format
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
cli_show_option_format(clixon_handle h,
|
|
cvec *argv,
|
|
int argc,
|
|
enum format_enum *formatp)
|
|
{
|
|
int retval = -1;
|
|
enum format_enum format = FORMAT_XML;
|
|
char *formatstr;
|
|
|
|
formatstr = cv_string_get(cvec_i(argv, argc));
|
|
if ((int)(format = format_str2int(formatstr)) < 0){
|
|
clixon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
|
|
goto done;
|
|
}
|
|
/* Special default format handling */
|
|
if (format == FORMAT_DEFAULT){
|
|
formatstr = clicon_option_str(h, "CLICON_CLI_OUTPUT_FORMAT");
|
|
if ((int)(format = format_str2int(formatstr)) < 0){
|
|
clixon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
|
|
goto done;
|
|
}
|
|
}
|
|
*formatp = format;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Common internal parse cli show boolean option
|
|
*
|
|
* @param[in] argv String vector: <dbname> <format> <xpath> [<varname>]
|
|
* @param[in] argc Index into argv
|
|
* @param[out] result result boolean: 0 or 1
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
cli_show_option_bool(cvec *argv,
|
|
int argc,
|
|
int *result
|
|
)
|
|
{
|
|
int retval = -1;
|
|
char *boolstr;
|
|
cg_var *boolcv = NULL;
|
|
|
|
boolstr = cv_string_get(cvec_i(argv, argc));
|
|
if ((boolcv = cv_new(CGV_BOOL)) == NULL){
|
|
clixon_err(OE_UNIX, errno, "cv_new");
|
|
goto done;
|
|
}
|
|
if (cv_parse(boolstr, boolcv) < 0){
|
|
clixon_err(OE_UNIX, errno, "Parse boolean %s", boolstr);
|
|
goto done;
|
|
}
|
|
*result = cv_bool_get(boolcv);
|
|
retval = 0;
|
|
done:
|
|
if (boolcv)
|
|
cv_free(boolcv);
|
|
return retval;
|
|
}
|
|
|
|
/*! Common internal parse cli show with-default option
|
|
*
|
|
* Default modes accorsing to RFC6243 + three extra modes based on report-all-tagged:
|
|
* 1) NULL
|
|
* 2) report-all-tagged-default Strip "default" attribute (=report-all)
|
|
* 3) report-all-tagged-strip Strip "default" attribute and all nodes tagged with it (=trim)
|
|
* @param[in] argv String vector: <dbname> <format> <xpath> [<varname>]
|
|
* @param[in] argc Index into argv
|
|
* @param[in] withdefault RFC 6243 with-default modes
|
|
* @param[in] extdefault with-defaults with propriatary extensions
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
cli_show_option_withdefault(cvec *argv,
|
|
int argc,
|
|
char **withdefault,
|
|
char **extdefault)
|
|
{
|
|
int retval = -1;
|
|
char *e;
|
|
|
|
e = cv_string_get(cvec_i(argv, argc));
|
|
/* From extended to RFC6243 withdefault modes */
|
|
if (strcmp(e, "report-all-tagged-strip") == 0)
|
|
*withdefault = "report-all-tagged";
|
|
else if (strcmp(e, "report-all-tagged-default") == 0)
|
|
*withdefault = "report-all-tagged";
|
|
else if (strcmp(e, "NULL") == 0){
|
|
e = NULL;
|
|
*withdefault = NULL;
|
|
}
|
|
else if (strcmp(e, "report-all") != 0 &&
|
|
strcmp(e, "trim") != 0 &&
|
|
strcmp(e, "explicit") != 0 &&
|
|
strcmp(e, "report-all-tagged") != 0){
|
|
clixon_err(OE_YANG, EINVAL, "Unexpected with-default option: %s", e);
|
|
goto done;
|
|
}
|
|
else
|
|
*withdefault = e;
|
|
*extdefault = e;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Generic show configuration callback
|
|
*
|
|
* Does not need to be used with the autocli as cli_show_auto does
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of variables from CLIgen command-line
|
|
* @param[in] argv String vector of show options, format:
|
|
* <dbname> Name of datastore, such as "running"
|
|
* -- from here optional:
|
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum), default: xml
|
|
* <xpath> xpath expression, that may contain one %, eg "/sender[name='foo']"
|
|
* <namespace> xpath default namespace (or NULL) not needed for xpath=NULL
|
|
* <pretty> true|false: pretty-print or not
|
|
* <state> true|false: also print state
|
|
* <default> Retrieval mode: report-all, trim, explicit, report-all-tagged,
|
|
* NULL, report-all-tagged-default, report-all-tagged-strip (extended)
|
|
* <prepend> CLI prefix: prepend before cli syntax output
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @code
|
|
* clispec:
|
|
* show config, cli_show_config("running","xml");
|
|
* cli run:
|
|
* > set table parameter a value x
|
|
* > show config
|
|
* <table xmlns="urn:example:clixon">
|
|
* <parameter>
|
|
* <name>a</name>
|
|
* <value>x</value>
|
|
* </parameter>
|
|
* </table>
|
|
* @endcode
|
|
* @see cli_show_auto autocli with expansion
|
|
* @see cli_show_auto_mode autocli with edit menu support
|
|
*/
|
|
int
|
|
cli_show_config(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
char *dbname;
|
|
enum format_enum format = FORMAT_XML;
|
|
cvec *nsc = NULL;
|
|
int pretty = 1;
|
|
char *prepend = NULL;
|
|
int state = 0;
|
|
char *withdefault = NULL; /* RFC 6243 modes */
|
|
char *extdefault = NULL; /* with extended tagged modes */
|
|
int argc = 0;
|
|
char *xpath = "/";
|
|
char *namespace = NULL;
|
|
int fromroot = 0;
|
|
|
|
if (cvec_len(argv) < 2 || cvec_len(argv) > 8){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: <dbname> [<format><xpath> <namespace> <pretty> <state> <default> <prepend>]", cvec_len(argv));
|
|
goto done;
|
|
}
|
|
dbname = cv_string_get(cvec_i(argv, argc++));
|
|
if (cvec_len(argv) > argc)
|
|
if (cli_show_option_format(h, argv, argc++, &format) < 0)
|
|
goto done;
|
|
if (cvec_len(argv) > argc)
|
|
xpath = cv_string_get(cvec_i(argv, argc++));
|
|
if (cvec_len(argv) > argc){
|
|
namespace = cv_string_get(cvec_i(argv, argc++));
|
|
/* Special symbol NULL means no namespace */
|
|
if (strcmp(namespace, "NULL") != 0)
|
|
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &pretty) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &state) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_withdefault(argv, argc++,
|
|
&withdefault,
|
|
&extdefault) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
prepend = cv_string_get(cvec_i(argv, argc++));
|
|
}
|
|
if (cli_show_common(h, dbname, format, pretty, state,
|
|
withdefault, extdefault,
|
|
prepend, xpath, fromroot, nsc, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (nsc)
|
|
xml_nsctx_free(nsc);
|
|
return retval;
|
|
}
|
|
|
|
/*! Show configuration xpath
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of variables from CLIgen command-line
|
|
* @param[in] argv String vector of show options, format:
|
|
* <dbname> "running"|"candidate"|"startup"
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
show_conf_xpath(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
char *dbname;
|
|
char *xpath;
|
|
cg_var *cv;
|
|
cvec *nsc = NULL;
|
|
yang_stmt *yspec;
|
|
int fromroot = 0;
|
|
|
|
if (cvec_len(argv) != 1){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Requires one element to be <dbname>");
|
|
goto done;
|
|
}
|
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
|
clixon_err(OE_FATAL, 0, "No DB_SPEC");
|
|
goto done;
|
|
}
|
|
dbname = cv_string_get(cvec_i(argv, 0));
|
|
/* Look for xpath in command (kludge: cv must be called "xpath") */
|
|
if ((cv = cvec_find(cvv, "xpath")) == NULL){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Requires one variable to be <xpath>");
|
|
goto done;
|
|
}
|
|
xpath = cv_string_get(cv);
|
|
/* Create canonical namespace */
|
|
if (xml_nsctx_yangspec(yspec, &nsc) < 0)
|
|
goto done;
|
|
/* Look for and add default namespace variable in command */
|
|
if ((cv = cvec_find(cvv, "ns")) != NULL){
|
|
if (xml_nsctx_add(nsc, NULL, cv_string_get(cv)) < 0)
|
|
goto done;
|
|
}
|
|
if (cli_show_common(h, dbname, FORMAT_XML, 1, 0,
|
|
NULL, NULL,
|
|
NULL, xpath, fromroot, nsc, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (nsc)
|
|
xml_nsctx_free(nsc);
|
|
return retval;
|
|
}
|
|
|
|
/*! Show clixon and CLIgen versions
|
|
*/
|
|
int
|
|
cli_show_version(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
cligen_output(stdout, "Clixon: %s\n", CLIXON_GITHASH);
|
|
cligen_output(stdout, "CLIgen: %s\n", CLIGEN_VERSION);
|
|
return 0;
|
|
}
|
|
|
|
/*! Show configuration callback using auto CLI syntax with expansion
|
|
*
|
|
* Can be used only in context of an autocli generated syntax tree, such as:
|
|
* show @datamodel, cli_show_auto();
|
|
* This show command can use expansion to "TAB" inside the syntax tree to show
|
|
* portions of the syntax.
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of variables from CLIgen command-line
|
|
* @param[in] argv String vector of show options, format:
|
|
* <api_path_fmt> Generated API PATH (this is added implicitly, not actually given in argv)
|
|
* [<mt-point>] Optional YANG path-arg/xpath from mount-point
|
|
* <dbname> Name of datastore, such as "running"
|
|
* -- from here optional:
|
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum), default: xml
|
|
* <pretty> true|false: pretty-print or not
|
|
* <state> true|false: also print state
|
|
* <default> Retrieval mode: report-all, trim, explicit, report-all-tagged,
|
|
* NULL, report-all-tagged-default, report-all-tagged-strip (extended)
|
|
* <prepend> CLI prefix: prepend before cli syntax output
|
|
* <fromroot> true|false: Show from root
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @code
|
|
* clispec:
|
|
* show config @datamodelshow, cli_show_auto("candidate", "xml");
|
|
* cli run:
|
|
* > set table parameter a value x
|
|
* > show config table parameter a
|
|
* <parameter>
|
|
* <name>a</name>
|
|
* <value>x</value>
|
|
* </parameter>
|
|
* @endcode
|
|
* @see cli_show_config with no autocli coupling
|
|
* @see cli_show_auto_mode autocli with edit menu support
|
|
*
|
|
* XXX merge cli_show_auto and cli_show_auto_mode
|
|
* @see cli_callback_generate where api_path_fmt + mt-point are generated
|
|
*/
|
|
int
|
|
cli_show_auto(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
char *dbname;
|
|
enum format_enum format = FORMAT_XML;
|
|
cvec *nsc = NULL;
|
|
int pretty = 1;
|
|
char *prepend = NULL;
|
|
int state = 0;
|
|
char *withdefault = NULL; /* RFC 6243 modes */
|
|
char *extdefault = NULL; /* with extended tagged modes */
|
|
int argc = 0;
|
|
char *xpath = NULL;
|
|
yang_stmt *yspec0;
|
|
char *api_path = NULL;
|
|
int cvvi = 0;
|
|
char *api_path_fmt; /* xml key format */
|
|
char *api_path_fmt01 = NULL;
|
|
char *str;
|
|
char *mtpoint = NULL;
|
|
int fromroot = 0;
|
|
|
|
if (cvec_len(argv) < 2 || cvec_len(argv) > 9){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected:: <api-path-fmt>* <database> [<format> <pretty> <state> <default> <prepend> <fromroot>]", cvec_len(argv));
|
|
goto done;
|
|
}
|
|
api_path_fmt = cv_string_get(cvec_i(argv, argc++));
|
|
str = cv_string_get(cvec_i(argv, argc++));
|
|
if (str && strncmp(str, "mtpoint:", strlen("mtpoint:")) == 0){
|
|
mtpoint = str + strlen("mtpoint:");
|
|
dbname = cv_string_get(cvec_i(argv, argc++));
|
|
}
|
|
else
|
|
dbname = str;
|
|
if (cvec_len(argv) > argc)
|
|
if (cli_show_option_format(h, argv, argc++, &format) < 0)
|
|
goto done;
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &pretty) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &state) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_withdefault(argv, argc++,
|
|
&withdefault,
|
|
&extdefault) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
prepend = cv_string_get(cvec_i(argv, argc++));
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &fromroot) < 0)
|
|
goto done;
|
|
}
|
|
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
|
|
clixon_err(OE_FATAL, 0, "No DB_SPEC");
|
|
goto done;
|
|
}
|
|
if (mtpoint){
|
|
/* Get and combined api-path01 */
|
|
if (mtpoint_paths(yspec0, mtpoint, api_path_fmt, &api_path_fmt01) < 0)
|
|
goto done;
|
|
if (api_path_fmt2api_path(api_path_fmt01, cvv, yspec0, &api_path, &cvvi) < 0)
|
|
goto done;
|
|
}
|
|
else{
|
|
if (api_path_fmt2api_path(api_path_fmt, cvv, yspec0, &api_path, &cvvi) < 0)
|
|
goto done;
|
|
}
|
|
if (api_path2xpath(api_path, yspec0, &xpath, &nsc, NULL) < 0)
|
|
goto done;
|
|
if (xpath == NULL){
|
|
clixon_err(OE_FATAL, 0, "Invalid api-path: %s", api_path);
|
|
goto done;
|
|
}
|
|
if (cli_show_common(h, dbname, format, pretty, state,
|
|
withdefault, extdefault,
|
|
prepend, xpath, fromroot, nsc, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (api_path_fmt01)
|
|
free(api_path_fmt01);
|
|
if (nsc)
|
|
xml_nsctx_free(nsc);
|
|
if (xpath)
|
|
free(xpath);
|
|
if (api_path)
|
|
free(api_path);
|
|
return retval;
|
|
}
|
|
|
|
/*! Show configuration callback for autocli edit modes using tree working point
|
|
*
|
|
* Can be used together with "edit modes". The xpath is derived from
|
|
* the current "cli-edit-mode" as described here:
|
|
* https://clixon-docs.readthedocs.io/en/latest/cli.html#edit-modes
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of variables from CLIgen command-line
|
|
* @param[in] argv String vector of show options, format:
|
|
* <dbname> Name of datastore, such as "running"
|
|
* -- from here optional:
|
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum), default: xml
|
|
* <pretty> true|false: pretty-print or not
|
|
* <state> true|false: also print state
|
|
* <default> Retrieval mode: report-all, trim, explicit, report-all-tagged,
|
|
* NULL, report-all-tagged-default, report-all-tagged-strip (extended)
|
|
* <prepend> CLI prefix: prepend before cli syntax output
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @cli_show_auto_ctrl
|
|
code
|
|
* clispec:
|
|
* show config, cli_show_auto_mode("candidate");
|
|
* cli run:
|
|
* > set table parameter a value x
|
|
* > edit table
|
|
* > show config
|
|
* <parameter>
|
|
* <name>a</name>
|
|
* <value>x</value>
|
|
* </parameter>
|
|
* @endcode
|
|
* @see cli_show_auto autocli with expansion
|
|
* @see cli_show_config with no autocli coupling
|
|
*/
|
|
int
|
|
cli_show_auto_mode(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
char *dbname;
|
|
enum format_enum format = FORMAT_XML;
|
|
cvec *nsc = NULL;
|
|
int pretty = 1;
|
|
char *prepend = NULL;
|
|
int state = 0;
|
|
char *withdefault = NULL; /* RFC 6243 modes */
|
|
char *extdefault = NULL; /* with extended tagged modes */
|
|
int argc = 0;
|
|
int skiptop = 0;
|
|
char *xpath = NULL;
|
|
yang_stmt *yspec0;
|
|
yang_stmt *yspec;
|
|
char *api_path = NULL;
|
|
char *mtpoint = NULL;
|
|
yang_stmt *yu;
|
|
cbuf *cbxpath = NULL;
|
|
cvec *nsc0 = NULL;
|
|
cg_var *cv;
|
|
int fromroot = 0;
|
|
|
|
if (cvec_len(argv) < 2 || cvec_len(argv) > 7){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: <database> [ <format> <pretty> <state> <default> <cli-prefix>]", cvec_len(argv));
|
|
goto done;
|
|
}
|
|
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
|
|
clixon_err(OE_FATAL, 0, "No DB_SPEC");
|
|
goto done;
|
|
}
|
|
dbname = cv_string_get(cvec_i(argv, argc++));
|
|
if (cvec_len(argv) > argc)
|
|
if (cli_show_option_format(h, argv, argc++, &format) < 0)
|
|
goto done;
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &pretty) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_bool(argv, argc++, &state) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
if (cli_show_option_withdefault(argv, argc++,
|
|
&withdefault,
|
|
&extdefault) < 0)
|
|
goto done;
|
|
}
|
|
if (cvec_len(argv) > argc){
|
|
prepend = cv_string_get(cvec_i(argv, argc++));
|
|
}
|
|
/* Store this as edit-mode */
|
|
if (clicon_data_get(h, "cli-edit-mode", &api_path) == 0 && strlen(api_path))
|
|
;
|
|
else
|
|
api_path = "/";
|
|
if (clicon_data_get(h, "cli-edit-mtpoint", &mtpoint) == 0 && strlen(mtpoint)){
|
|
if (yang_path_arg(yspec0, mtpoint, &yu) < 0)
|
|
goto done;
|
|
if (yang_mount_get(yu, mtpoint, &yspec) < 0)
|
|
goto done;
|
|
}
|
|
yspec = yspec0;
|
|
if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
|
|
goto done;
|
|
if (xpath == NULL){
|
|
clixon_err(OE_FATAL, 0, "Invalid api-path: %s", api_path);
|
|
goto done;
|
|
}
|
|
if ((cbxpath = cbuf_new()) == NULL){
|
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if (mtpoint){
|
|
/*
|
|
* XXX disabled the line below, because otherwise the path up to the
|
|
* mount point would be added twice to cbxpath. This is because the
|
|
* api_path and thus also the xpath already include the path up to the
|
|
* mount point. (at least since cli_auto_edit() was changed)
|
|
*/
|
|
//cprintf(cbxpath, "%s", mtpoint);
|
|
if (xml_nsctx_yangspec(yspec0, &nsc0) < 0)
|
|
goto done;
|
|
cv = NULL; /* Append cvv1 to cvv2 */
|
|
while ((cv = cvec_each(nsc0, cv)) != NULL)
|
|
cvec_append_var(nsc, cv);
|
|
}
|
|
cprintf(cbxpath, "%s", xpath);
|
|
skiptop = (strcmp(xpath,"/") != 0);
|
|
if (cli_show_common(h, dbname, format, pretty, state,
|
|
withdefault, extdefault,
|
|
prepend, cbuf_get(cbxpath), fromroot, nsc, skiptop) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (nsc0)
|
|
cvec_free(nsc0);
|
|
if (cbxpath)
|
|
cbuf_free(cbxpath);
|
|
if (nsc)
|
|
xml_nsctx_free(nsc);
|
|
if (xpath)
|
|
free(xpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! Show clixon configuration options as loaded
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of command variables
|
|
* @param[in] argv
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
'* @see clicon_option_dump clicon_option_dump1
|
|
*/
|
|
int
|
|
cli_show_options(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
clicon_hash_t *hash = clicon_options(h);
|
|
int i;
|
|
char **keys = NULL;
|
|
void *val;
|
|
size_t klen;
|
|
size_t vlen;
|
|
cxobj *x = NULL;
|
|
|
|
if (clicon_hash_keys(hash, &keys, &klen) < 0)
|
|
goto done;
|
|
for(i = 0; i < klen; i++) {
|
|
val = clicon_hash_value(hash, keys[i], &vlen);
|
|
if (vlen){
|
|
if (((char*)val)[vlen-1]=='\0') /* assume string */
|
|
fprintf(stdout, "%s: \"%s\"\n", keys[i], (char*)val);
|
|
else
|
|
fprintf(stdout, "%s: 0x%p , length %zu\n", keys[i], val, vlen);
|
|
}
|
|
else
|
|
fprintf(stdout, "%s: NULL\n", keys[i]);
|
|
}
|
|
/* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB from config tree
|
|
* Since they are lists they are placed in the config tree.
|
|
*/
|
|
x = NULL;
|
|
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
|
|
if (strcmp(xml_name(x), "CLICON_YANG_DIR") != 0)
|
|
continue;
|
|
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
|
|
}
|
|
x = NULL;
|
|
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
|
|
if (strcmp(xml_name(x), "CLICON_FEATURE") != 0)
|
|
continue;
|
|
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
|
|
}
|
|
x = NULL;
|
|
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
|
|
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
|
|
continue;
|
|
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (keys)
|
|
free(keys);
|
|
return retval;
|
|
}
|
|
|
|
/*! Show pagination
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of cli string and instantiated variables
|
|
* @param[in] argv Vector. Format: <xpath> <prefix> <namespace> <format> <limit>
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* Also, if there is a cligen variable called "xpath" it will override argv xpath arg
|
|
*/
|
|
int
|
|
cli_pagination(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
cbuf *cb = NULL;
|
|
char *xpath = NULL;
|
|
char *prefix = NULL;
|
|
char *namespace = NULL;
|
|
cxobj *xret = NULL;
|
|
cxobj *xerr;
|
|
cvec *nsc = NULL;
|
|
char *str;
|
|
enum format_enum format;
|
|
cxobj *xc;
|
|
cg_var *cv;
|
|
int i;
|
|
int j;
|
|
uint32_t limit = 0;
|
|
cxobj **xvec = NULL;
|
|
size_t xlen;
|
|
int locked = 0;
|
|
int argc = 0;
|
|
|
|
if (cvec_len(argv) != 5){
|
|
clixon_err(OE_PLUGIN, 0, "Expected usage: <xpath> <prefix> <namespace> <format> <limit>");
|
|
goto done;
|
|
}
|
|
/* prefix:variable overrides argv */
|
|
if ((cv = cvec_find(cvv, "xpath")) != NULL)
|
|
xpath = cv_string_get(cv);
|
|
else
|
|
xpath = cvec_i_str(argv, argc);
|
|
argc++;
|
|
prefix = cvec_i_str(argv, argc++);
|
|
namespace = cvec_i_str(argv, argc++);
|
|
if (cli_show_option_format(h, argv, argc++, &format) < 0)
|
|
goto done;
|
|
if ((str = cv_string_get(cvec_i(argv, 4))) != NULL){
|
|
if (parse_uint32(str, &limit, NULL) < 1){
|
|
clixon_err(OE_UNIX, errno, "error parsing limit:%s", str);
|
|
goto done;
|
|
}
|
|
}
|
|
if (limit == 0){
|
|
clixon_err(OE_UNIX, EINVAL, "limit is 0");
|
|
goto done;
|
|
}
|
|
if ((nsc = xml_nsctx_init(prefix, namespace)) == NULL)
|
|
goto done;
|
|
if (clicon_rpc_lock(h, "running") < 0)
|
|
goto done;
|
|
locked++;
|
|
for (i = 0;; i++){
|
|
if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc,
|
|
CONTENT_ALL,
|
|
-1, /* depth */
|
|
NULL, /* with-default */
|
|
limit*i, /* offset */
|
|
limit, /* limit */
|
|
NULL, NULL, NULL, /* nyi */
|
|
&xret) < 0){
|
|
goto done;
|
|
}
|
|
if ((xerr = xpath_first(xret, NULL, "/rpc-error")) != NULL){
|
|
clixon_err_netconf(h, OE_NETCONF, 0, xerr, "Get configuration");
|
|
goto done;
|
|
}
|
|
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
|
goto done;
|
|
for (j = 0; j<xlen; j++){
|
|
xc = xvec[j];
|
|
switch (format){
|
|
case FORMAT_XML:
|
|
if (clixon_xml2file(stdout, xc, 0, 1, NULL, cligen_output, 0, 1) < 0)
|
|
goto done;
|
|
break;
|
|
case FORMAT_JSON:
|
|
if (clixon_json2file(stdout, xc, 1, cligen_output, 0, 1) < 0)
|
|
goto done;
|
|
break;
|
|
case FORMAT_TEXT:
|
|
if (clixon_text2file(stdout, xc, 0, cligen_output, 0, 1) < 0)
|
|
goto done;
|
|
break;
|
|
case FORMAT_CLI:
|
|
/* hardcoded to compress and list-keyword = nokey */
|
|
if (clixon_cli2file(h, stdout, xc, NULL, cligen_output, 0) < 0)
|
|
goto done;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (cli_output_status() < 0)
|
|
break;
|
|
} /* for j */
|
|
if (cli_output_status() < 0)
|
|
break;
|
|
if (xlen != limit) /* Break if fewer elements than requested */
|
|
break;
|
|
if (xret){
|
|
xml_free(xret);
|
|
xret = NULL;
|
|
}
|
|
if (xvec){
|
|
free(xvec);
|
|
xvec = NULL;
|
|
}
|
|
} /* for i */
|
|
retval = 0;
|
|
done:
|
|
if (locked)
|
|
clicon_rpc_unlock(h, "running");
|
|
if (xvec)
|
|
free(xvec);
|
|
if (xret)
|
|
xml_free(xret);
|
|
if (nsc)
|
|
cvec_free(nsc);
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*! Translate to CLI commands in cbuf
|
|
*
|
|
* Howto: join strings and pass them down.
|
|
* Identify unique/index keywords for correct set syntax.
|
|
* @param[in] h Clixon handle
|
|
* @param[in,out] cb Cligen buffer to write to
|
|
* @param[in] xn XML Parse-tree (to translate)
|
|
* @param[in] prepend Print this text in front of all commands.
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see clixon_cli2file
|
|
*/
|
|
static int
|
|
cli2cbuf(clixon_handle h,
|
|
cbuf *cb,
|
|
cxobj *xn,
|
|
char *prepend)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xe = NULL;
|
|
cbuf *cbpre = NULL;
|
|
yang_stmt *ys;
|
|
int match;
|
|
char *body;
|
|
int compress = 0;
|
|
autocli_listkw_t listkw;
|
|
int exist = 0;
|
|
char *name;
|
|
|
|
if (autocli_list_keyword(h, &listkw) < 0)
|
|
goto done;
|
|
if (xml_type(xn)==CX_ATTR)
|
|
goto ok;
|
|
if ((ys = xml_spec(xn)) == NULL)
|
|
goto ok;
|
|
if (yang_extension_value(ys, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
|
|
goto done;
|
|
if (exist)
|
|
goto ok;
|
|
exist = 0;
|
|
if (yang_extension_value(ys, "alias", CLIXON_AUTOCLI_NS, &exist, &name) < 0)
|
|
goto done;
|
|
if (!exist)
|
|
name = xml_name(xn);
|
|
/* If leaf/leaf-list or presence container, then print line */
|
|
if (yang_keyword_get(ys) == Y_LEAF ||
|
|
yang_keyword_get(ys) == Y_LEAF_LIST){
|
|
if (prepend)
|
|
cprintf(cb, "%s", prepend);
|
|
if (listkw != AUTOCLI_LISTKW_NONE)
|
|
cprintf(cb, "%s ", name);
|
|
if ((body = xml_body(xn)) != NULL){
|
|
if (index(body, ' '))
|
|
cprintf(cb, "\"%s\"", body);
|
|
else
|
|
cprintf(cb, "%s", body);
|
|
}
|
|
cprintf(cb, "\n");
|
|
goto ok;
|
|
}
|
|
/* Create prepend variable string */
|
|
if ((cbpre = cbuf_new()) == NULL){
|
|
clixon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if (prepend)
|
|
cprintf(cbpre, "%s", prepend);
|
|
|
|
/* If non-presence container && HIDE mode && only child is
|
|
* a list, then skip container keyword
|
|
* See also yang2cli_container */
|
|
if (autocli_compress(h, ys, &compress) < 0)
|
|
goto done;
|
|
if (!compress)
|
|
cprintf(cbpre, "%s ", xml_name(xn));
|
|
|
|
/* If list then first loop through keys */
|
|
if (yang_keyword_get(ys) == Y_LIST){
|
|
xe = NULL;
|
|
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
|
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
|
goto done;
|
|
if (!match)
|
|
continue;
|
|
if (listkw == AUTOCLI_LISTKW_ALL)
|
|
cprintf(cbpre, "%s ", xml_name(xe));
|
|
cprintf(cbpre, "%s ", xml_body(xe));
|
|
}
|
|
}
|
|
else if ((yang_keyword_get(ys) == Y_CONTAINER) &&
|
|
yang_find(ys, Y_PRESENCE, NULL) != NULL){
|
|
/* If presence container, then print as leaf (but continue to children) */
|
|
if (prepend)
|
|
cprintf(cb, "%s", prepend);
|
|
if (listkw != AUTOCLI_LISTKW_NONE)
|
|
cprintf(cb, "%s ", xml_name(xn));
|
|
if ((body = xml_body(xn)) != NULL){
|
|
if (index(body, ' '))
|
|
cprintf(cb, "\"%s\"", body);
|
|
else
|
|
cprintf(cb, "%s", body);
|
|
}
|
|
cprintf(cb, "\n");
|
|
}
|
|
|
|
/* For lists, print cbpre before its elements */
|
|
if (yang_keyword_get(ys) == Y_LIST)
|
|
cprintf(cb, "%s\n", cbuf_get(cbpre));
|
|
/* Then loop through all other (non-keys) */
|
|
xe = NULL;
|
|
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
|
if (yang_keyword_get(ys) == Y_LIST){
|
|
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
|
goto done;
|
|
if (match)
|
|
continue; /* Not key itself */
|
|
}
|
|
if (cli2cbuf(h, cb, xe, cbuf_get(cbpre)) < 0)
|
|
goto done;
|
|
}
|
|
ok:
|
|
retval = 0;
|
|
done:
|
|
if (cbpre)
|
|
cbuf_free(cbpre);
|
|
return retval;
|
|
}
|
|
|
|
/*! Translate from XML to CLI commands, internal
|
|
*
|
|
* Howto: join strings and pass them down.
|
|
* Identify unique/index keywords for correct set syntax.
|
|
* @param[in] h Clixon handle
|
|
* @param[in] f Output FILE (eg stdout)
|
|
* @param[in] xn XML Parse-tree (to translate)
|
|
* @param[in] prepend Print this text in front of all commands.
|
|
* @param[in] fn Callback to make print function
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
cli2file(clixon_handle h,
|
|
FILE *f,
|
|
cxobj *xn,
|
|
char *prepend,
|
|
clicon_output_cb *fn)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xe = NULL;
|
|
cbuf *cbpre = NULL;
|
|
yang_stmt *ys;
|
|
int match;
|
|
char *body;
|
|
int compress = 0;
|
|
autocli_listkw_t listkw;
|
|
int exist = 0;
|
|
char *name;
|
|
|
|
if (autocli_list_keyword(h, &listkw) < 0)
|
|
goto done;
|
|
if (xml_type(xn)==CX_ATTR)
|
|
goto ok;
|
|
if ((ys = xml_spec(xn)) == NULL)
|
|
goto ok;
|
|
if (yang_extension_value(ys, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
|
|
goto done;
|
|
if (exist)
|
|
goto ok;
|
|
exist = 0;
|
|
if (yang_extension_value(ys, "alias", CLIXON_AUTOCLI_NS, &exist, &name) < 0)
|
|
goto done;
|
|
if (!exist)
|
|
name = xml_name(xn);
|
|
/* If leaf/leaf-list or presence container, then print line */
|
|
if (yang_keyword_get(ys) == Y_LEAF ||
|
|
yang_keyword_get(ys) == Y_LEAF_LIST){
|
|
if (prepend)
|
|
(*fn)(f, "%s", prepend);
|
|
if (listkw != AUTOCLI_LISTKW_NONE)
|
|
(*fn)(f, "%s ", name);
|
|
if ((body = xml_body(xn)) != NULL){
|
|
if (index(body, ' '))
|
|
(*fn)(f, "\"%s\"", body);
|
|
else
|
|
(*fn)(f, "%s", body);
|
|
}
|
|
(*fn)(f, "\n");
|
|
goto ok;
|
|
}
|
|
/* Create prepend variable string */
|
|
if ((cbpre = cbuf_new()) == NULL){
|
|
clixon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if (prepend)
|
|
cprintf(cbpre, "%s", prepend);
|
|
|
|
/* If non-presence container && HIDE mode && only child is
|
|
* a list, then skip container keyword
|
|
* See also yang2cli_container */
|
|
if (autocli_compress(h, ys, &compress) < 0)
|
|
goto done;
|
|
if (!compress)
|
|
cprintf(cbpre, "%s ", xml_name(xn));
|
|
|
|
/* If list then first loop through keys */
|
|
if (yang_keyword_get(ys) == Y_LIST){
|
|
xe = NULL;
|
|
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
|
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
|
goto done;
|
|
if (!match)
|
|
continue;
|
|
if (listkw == AUTOCLI_LISTKW_ALL)
|
|
cprintf(cbpre, "%s ", xml_name(xe));
|
|
cprintf(cbpre, "%s ", xml_body(xe));
|
|
}
|
|
}
|
|
else if ((yang_keyword_get(ys) == Y_CONTAINER) &&
|
|
yang_find(ys, Y_PRESENCE, NULL) != NULL){
|
|
/* If presence container, then print as leaf (but continue to children) */
|
|
if (prepend)
|
|
(*fn)(f, "%s", prepend);
|
|
if (listkw != AUTOCLI_LISTKW_NONE)
|
|
(*fn)(f, "%s ", xml_name(xn));
|
|
if ((body = xml_body(xn)) != NULL){
|
|
if (index(body, ' '))
|
|
(*fn)(f, "\"%s\"", body);
|
|
else
|
|
(*fn)(f, "%s", body);
|
|
}
|
|
(*fn)(f, "\n");
|
|
}
|
|
|
|
/* For lists, print cbpre before its elements */
|
|
if (yang_keyword_get(ys) == Y_LIST)
|
|
(*fn)(f, "%s\n", cbuf_get(cbpre));
|
|
/* Then loop through all other (non-keys) */
|
|
xe = NULL;
|
|
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
|
if (yang_keyword_get(ys) == Y_LIST){
|
|
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
|
goto done;
|
|
if (match)
|
|
continue; /* Not key itself */
|
|
}
|
|
if (cli2file(h, f, xe, cbuf_get(cbpre), fn) < 0)
|
|
goto done;
|
|
}
|
|
ok:
|
|
retval = 0;
|
|
done:
|
|
if (cbpre)
|
|
cbuf_free(cbpre);
|
|
return retval;
|
|
}
|
|
|
|
/*! Translate from XML to CLI commands
|
|
*
|
|
* Howto: join strings and pass them down.
|
|
* Identify unique/index keywords for correct set syntax.
|
|
* @param[in] h Clixon handle
|
|
* @param[in] f Output FILE (eg stdout)
|
|
* @param[in] xn XML Parse-tree (to translate)
|
|
* @param[in] prepend Print this text in front of all commands.
|
|
* @param[in] fn File print function (if NULL, use fprintf)
|
|
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see clixon_cli2cbuf
|
|
*/
|
|
int
|
|
clixon_cli2file(clixon_handle h,
|
|
FILE *f,
|
|
cxobj *xn,
|
|
char *prepend,
|
|
clicon_output_cb *fn,
|
|
int skiptop)
|
|
{
|
|
int retval = 1;
|
|
cxobj *xc;
|
|
|
|
if (fn == NULL)
|
|
fn = fprintf;
|
|
if (skiptop){
|
|
xc = NULL;
|
|
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
|
|
if (cli2file(h, f, xc, prepend, fn) < 0)
|
|
goto done;
|
|
}
|
|
else {
|
|
if (cli2file(h, f, xn, prepend, fn) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Translate from XML to CLI commands
|
|
*
|
|
* Howto: join strings and pass them down.
|
|
* Identify unique/index keywords for correct set syntax.
|
|
* @param[in] h Clixon handle
|
|
* @param[in] f Output FILE (eg stdout)
|
|
* @param[in] xn XML Parse-tree (to translate)
|
|
* @param[in] prepend Print this text in front of all commands.
|
|
* @param[in] fn File print function (if NULL, use fprintf)
|
|
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see clixon_cli2file
|
|
*/
|
|
int
|
|
clixon_cli2cbuf(clixon_handle h,
|
|
cbuf *cb,
|
|
cxobj *xn,
|
|
char *prepend,
|
|
int skiptop)
|
|
{
|
|
int retval = 1;
|
|
cxobj *xc;
|
|
|
|
if (skiptop){
|
|
xc = NULL;
|
|
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
|
|
if (cli2cbuf(h, cb, xc, prepend) < 0)
|
|
goto done;
|
|
}
|
|
else {
|
|
if (cli2cbuf(h, cb, xn, prepend) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! CLI callback show statistics
|
|
*/
|
|
int
|
|
cli_show_statistics(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
cbuf *cb = NULL;
|
|
cxobj *xret = NULL;
|
|
cxobj *xerr;
|
|
cg_var *cv;
|
|
int modules = 0;
|
|
pt_head *ph;
|
|
parse_tree *pt;
|
|
uint64_t nr = 0;
|
|
size_t sz = 0;
|
|
|
|
if (argv != NULL && cvec_len(argv) != 1){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Expected arguments: [modules]");
|
|
goto done;
|
|
}
|
|
if (argv){
|
|
cv = cvec_i(argv, 0);
|
|
modules = (strcmp(cv_string_get(cv), "modules") == 0);
|
|
}
|
|
if ((cb = cbuf_new()) == NULL){
|
|
clixon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
/* CLI */
|
|
cligen_output(stdout, "CLI:\n");
|
|
ph = NULL;
|
|
while ((ph = cligen_ph_each(cli_cligen(h), ph)) != NULL) {
|
|
if ((pt = cligen_ph_parsetree_get(ph)) == NULL)
|
|
continue;
|
|
nr = 0; sz = 0;
|
|
pt_stats(pt, &nr, &sz);
|
|
cligen_output(stdout, "%s: nr=%" PRIu64 " size:%zu\n",
|
|
cligen_ph_name_get(ph), nr, sz);
|
|
}
|
|
/* Backend */
|
|
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
|
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
|
|
cprintf(cb, ">");
|
|
cprintf(cb, "<stats xmlns=\"%s\">", CLIXON_LIB_NS);
|
|
if (modules)
|
|
cprintf(cb, "<modules>true</modules>");
|
|
cprintf(cb, "</stats>");
|
|
cprintf(cb, "</rpc>");
|
|
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, NULL) < 0)
|
|
goto done;
|
|
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
|
clixon_err_netconf(h, OE_NETCONF, 0, xerr, "Get configuration");
|
|
goto done;
|
|
}
|
|
fprintf(stdout, "Backend:\n");
|
|
if (clixon_xml2file(stdout, xml_child_i(xret, 0), 0, 1, NULL, cligen_output, 0, 1) < 0)
|
|
goto done;
|
|
fprintf(stdout, "CLI:\n");
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*! CLI set default output format
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of cli string and instantiated variables, expected: 1: format
|
|
* @param[in] argv Vector, expected NULL
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* Format of argv:
|
|
* <api-path-fmt> Generated
|
|
*/
|
|
int
|
|
cli_format_set(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
int retval = -1;
|
|
cg_var *cv;
|
|
char *str;
|
|
enum format_enum fmt = FORMAT_XML;
|
|
|
|
if ((cv = cvec_find(cvv, "fmt")) == NULL){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Requires one variable to be <format>");
|
|
goto done;
|
|
}
|
|
str = cv_string_get(cv);
|
|
if ((fmt = format_str2int(str)) < 0){
|
|
clixon_err(OE_PLUGIN, EINVAL, "Invalid format: %s", str);
|
|
goto done;
|
|
}
|
|
/* Alt make a int option/data */
|
|
retval = clicon_option_str_set(h, "CLICON_CLI_OUTPUT_FORMAT", str);
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! CLI set default output format
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cvv Vector of cli string and instantiated variables, expected: 1: format
|
|
* @param[in] argv Vector, expected NULL
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* Format of argv:
|
|
* <api-path-fmt> Generated
|
|
*/
|
|
int
|
|
cli_format_show(clixon_handle h,
|
|
cvec *cvv,
|
|
cvec *argv)
|
|
{
|
|
char *str;
|
|
|
|
str = clicon_option_str(h, "CLICON_CLI_OUTPUT_FORMAT");
|
|
cligen_output(stderr, "%s\n", str);
|
|
return 0;
|
|
}
|