* Restconf: added inline configuration using -R <xml> command line as an alternative to making advanced restconf configuration

This commit is contained in:
Olof hagsand 2021-05-25 15:21:45 +02:00
parent 1ef7a280d7
commit 164aa1cb4c
9 changed files with 161 additions and 41 deletions

View file

@ -72,8 +72,9 @@ Users may have to change how they access the system
### Minor features
* Restconf: added inline configuration using `-R <xml>` command line as an alternative to making advanced restconf configuration
* [Need an option to disable restconf mandatory action of overwriting startup_db #230](https://github.com/clicon/clixon/issues/230)
* Disable RFC 8040 mandatory copy of running to startup after commit
* Configure option `CLICON_RESTCONF_STARTUP_DONTUPDATE` added to disable RFC 8040 mandatory copy of running to startup after commit
* Add default network namespace constant: `RESTCONF_NETNS_DEFAULT` with default value "default".
* CLI: Two new hide variables added (thanks: shmuelnatan)
* hide-database : specifies that a command is not visible in database. This can be useful for setting passwords and not exposing them to users.

View file

@ -61,10 +61,11 @@
#define RESTCONF_PROCESS "restconf"
/*! Set current debug flag when starting process using -D <dbg>
/*! Set current debug and log flags when starting process using -D <dbg> -l <logdst>
*
* process argv list including -D is set on start. But the debug flags may change and this is a way
* to set it dynamically, ie at the time the process is started, not when the backend is started.
* process argv list including -D and -l are set on start. But the debug/log flags may change and
* this is a way to set it dynamically, ie at the time the process is started, not when the backend
* is started.
* @param[in] h Clixon backend
* @param[in] xt XML target
*/
@ -129,6 +130,57 @@ restconf_pseudo_set_log(clicon_handle h,
return retval;
}
/*! Set current restconf inline config when starting process using -R <config>
*
* process argv list including -R is set on start. Get the running restconfig config, stringify it
* and insert it as a optimization to reading it from the backend.
* @param[in] h Clixon backend
* @param[in] xt XML target
*/
static int
restconf_pseudo_set_inline(clicon_handle h,
cxobj *xt)
{
int retval = -1;
char **argv;
int argc;
int i;
char *str = NULL;
cxobj *xrestconf;
cbuf *cb = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (clixon_process_argv_get(h, RESTCONF_PROCESS, &argv, &argc) < 0)
goto done;
if ((xrestconf = xpath_first(xt, NULL, "restconf")) != NULL)
for (i=0; i<argc; i++){
if (argv[i] == NULL)
break;
if (strcmp(argv[i], "-R") == 0 && argc > i+1 && argv[i+1]){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xrestconf, 0, 0, -1) < 0)
goto done;
if ((str = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_XML, errno, "stdup");
goto done;
}
clicon_debug(1, "%s str:%s", __FUNCTION__, str);
if (argv[i+1])
free(argv[i+1]);
argv[i+1] = str;
break;
}
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Process rpc callback function
* - if RPC op is start, if enable is true, start the service, if false, error or ignore it
* - if RPC op is stop, stop the service
@ -164,6 +216,8 @@ restconf_rpc_wrapper(clicon_handle h,
*/
if (restconf_pseudo_set_log(h, xt) < 0)
goto done;
if (restconf_pseudo_set_inline(h, xt) < 0)
goto done;
}
break;
default:
@ -192,7 +246,7 @@ restconf_pseudo_process_control(clicon_handle h)
int nr;
cbuf *cb = NULL;
nr = 8;
nr = 10;
if ((argv = calloc(nr, sizeof(char *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
@ -216,9 +270,11 @@ restconf_pseudo_process_control(clicon_handle h)
* see restconf_pseudo_set_log which sets flag when process starts
*/
argv[i++] = "-D";
argv[i++] = "0";
argv[i++] = strdup("0");
argv[i++] = "-l";
argv[i++] = "s"; /* There is also log-destination in clixon-restconf.yang */
argv[i++] = strdup("s"); /* There is also log-destination in clixon-restconf.yang */
argv[i++] = "-R";
argv[i++] = strdup("");
argv[i++] = NULL;
assert(i==nr);
if (clixon_process_register(h, RESTCONF_PROCESS,
@ -289,6 +345,8 @@ restconf_pseudo_process_commit(clicon_handle h,
*/
if (restconf_pseudo_set_log(h, xtarget) < 0)
goto done;
if (restconf_pseudo_set_inline(h, xtarget) < 0)
goto done;
/* Toggle start/stop if enable flag changed */
if ((cx = xpath_first(xtarget, NULL, "/restconf/enable")) != NULL &&
xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_ADD)){

View file

@ -88,7 +88,7 @@
#include "restconf_stream.h"
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:rW:o:"
#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:rW:R:o:"
/*! Convert FCGI parameters to clixon runtime data
* @param[in] h Clixon handle
@ -177,7 +177,6 @@ restconf_sig_child(int arg)
static void
usage(clicon_handle h,
char *argv0)
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
@ -192,7 +191,8 @@ usage(clicon_handle h,
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
"\t-r \t\t Do not drop privileges if run as root\n"
"\t-W <user>\tRun restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
"\t-W <user>\t Run restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
"\t-R <xml> \t Restconf configuration in-line overriding config file\n"
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n",
argv0
);
@ -227,14 +227,16 @@ main(int argc,
size_t cligen_bufthreshold;
int dbg = 0;
int ret;
cxobj *xrestconf1 = NULL; /* Local config file */
cxobj *xconfig2 = NULL;
cxobj *xrestconf2 = NULL; /* Config from backend */
cxobj *xrestconf1 = NULL; /* Inline */
cxobj *xrestconf2 = NULL; /* Local config file */
cxobj *xconfig3 = NULL;
cxobj *xrestconf3 = NULL; /* Config from backend */
int configure_done = 0; /* First try local then backend */
cvec *nsc = NULL;
cxobj *xerr = NULL;
struct passwd *pw;
char *wwwuser;
char *inline_config = NULL;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -330,6 +332,9 @@ main(int argc,
goto done;
break;
}
case 'R': /* Restconf on-line config */
inline_config = optarg;
break;
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
@ -446,16 +451,33 @@ main(int argc,
if (clixon_plugin_start_all(h) < 0)
goto done;
if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){
/* If not read from backend, try to get restconf config from local config-file */
if ((xrestconf1 = clicon_conf_restconf(h)) != NULL){
if ((ret = restconf_config_init(h, xrestconf1)) < 0)
/* 1. try inline configure option */
if (inline_config != NULL && strlen(inline_config)){
clicon_debug(1, "restconf_main_fcgi using restconf inline config");
if ((ret = clixon_xml_parse_string(inline_config, YB_MODULE, yspec, &xrestconf1, &xerr)) < 0)
goto done;
if (ret == 0){
clixon_netconf_error(xerr, "Inline restconf config", NULL);
goto done;
}
/* Replace parent w first child */
if (xml_rootchild(xrestconf1, 0, &xrestconf1) < 0)
goto done;
if ((ret = restconf_config_init(h, xrestconf1)) < 0)
goto done;
if (ret == 1)
configure_done = 1;
}
else if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){
/* 2. If not read from backend, try to get restconf config from local config-file */
if ((xrestconf2 = clicon_conf_restconf(h)) != NULL){
if ((ret = restconf_config_init(h, xrestconf2)) < 0)
goto done;
if (ret == 1)
configure_done = 1;
}
}
/* If no local config, or it is disabled, try to query backend of config. */
/* 3. If no local config, or it is disabled, try to query backend of config. */
else {
/* Loop to wait for backend starting, try again if not done */
while (1){
@ -477,15 +499,15 @@ main(int argc,
clicon_err(OE_UNIX, errno, "getpwuid");
goto done;
}
if (clicon_rpc_get_config(h, pw->pw_name, "running", "/restconf", nsc, &xconfig2) < 0)
if (clicon_rpc_get_config(h, pw->pw_name, "running", "/restconf", nsc, &xconfig3) < 0)
goto done;
if ((xerr = xpath_first(xconfig2, NULL, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xconfig3, NULL, "/rpc-error")) != NULL){
clixon_netconf_error(xerr, "Get backend restconf config", NULL);
goto done;
}
/* Extract restconf configuration */
if ((xrestconf2 = xpath_first(xconfig2, nsc, "restconf")) != NULL){
if ((ret = restconf_config_init(h, xrestconf2)) < 0)
if ((xrestconf3 = xpath_first(xconfig3, nsc, "restconf")) != NULL){
if ((ret = restconf_config_init(h, xrestconf3)) < 0)
goto done;
if (ret == 1)
configure_done = 1;
@ -615,8 +637,10 @@ main(int argc,
} /* while */
retval = 0;
done:
if (xconfig2)
xml_free(xconfig2);
if (xrestconf1)
xml_free(xrestconf1);
if (xconfig3)
xml_free(xconfig3);
if (nsc)
cvec_free(nsc);
stream_child_freeall(h);

View file

@ -168,7 +168,7 @@
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:o:"
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:R:o:"
/* If set, open outwards socket non-blocking, as opposed to blocking
* Should work both ways, but in the ninblocking case,
@ -1727,14 +1727,16 @@ restconf_openssl_init(clicon_handle h,
* - if local config found, open sockets accordingly and exit function
* - If no local config found, query backend for config and open sockets.
* That is, EITHER local config OR read config from backend once
* @param[in] h Clicon handle
* @param[out] xrestconf XML restconf config, malloced (if retval = 1)
* @retval 1 OK (and xrestconf set)
* @retval 0 Fail - no config
* @retval -1 Error
* @param[in] h Clicon handle
* @param[in] inline_config XML restconf config, malloced (if retval = 1)
* @param[out] xrestconf XML restconf config, malloced (if retval = 1)
* @retval 1 OK (and xrestconf set)
* @retval 0 Fail - no config
* @retval -1 Error
*/
int
restconf_clixon_init(clicon_handle h,
char *inline_config,
cxobj **xrestconfp)
{
int retval = -1;
@ -1746,6 +1748,7 @@ restconf_clixon_init(clicon_handle h,
char *str;
cvec *nsctx_global = NULL; /* Global namespace context */
cxobj *xrestconf;
cxobj *xerr = NULL;
int ret;
/* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
@ -1827,8 +1830,29 @@ restconf_clixon_init(clicon_handle h,
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
ret = 0;
if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){
if (inline_config != NULL && strlen(inline_config)){
clicon_debug(1, "%s using restconf inline config", __FUNCTION__);
if ((ret = clixon_xml_parse_string(inline_config, YB_MODULE, yspec, &xrestconf, &xerr)) < 0)
goto done;
if (ret == 0){
clixon_netconf_error(xerr, "Inline restconf config", NULL);
goto done;
}
/* Replace parent w first child */
if (xml_rootchild(xrestconf, 0, &xrestconf) < 0)
goto done;
if ((ret = restconf_config_init(h, xrestconf)) < 0)
goto done;
/* ret == 1 means this config is OK */
if (ret == 0){
xml_free(xrestconf);
xrestconf = NULL;
}
else
if ((*xrestconfp = xrestconf) == NULL)
goto done;
}
else if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){
/* If not read from backend, try to get restconf config from local config-file */
if ((xrestconf = clicon_conf_restconf(h)) != NULL){
/*! Basic config init, set auth-type, pretty, etc ret 0 means disabled */
@ -1853,6 +1877,8 @@ restconf_clixon_init(clicon_handle h,
}
retval = 1;
done:
if (xerr)
xml_free(xerr);
return retval;
fail:
retval = 0;
@ -1885,7 +1911,6 @@ restconf_sig_term(int arg)
static void
usage(clicon_handle h,
char *argv0)
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
@ -1899,7 +1924,8 @@ usage(clicon_handle h,
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
"\t-r \t\t Do not drop privileges if run as root\n"
"\t-W <user>\tRun restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
"\t-W <user>\t Run restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
"\t-R <xml> \t Restconf configuration in-line overriding config file\n"
"\t-o <option>=<value> Set configuration option overriding config file (see clixon-config.yang)\n"
,
argv0
@ -1920,6 +1946,7 @@ main(int argc,
restconf_native_handle *rh = NULL;
int ret;
cxobj *xrestconf = NULL;
char *inline_config = NULL;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -2023,6 +2050,9 @@ main(int argc,
if (clicon_option_add(h, "CLICON_RESTCONF_USER", optarg) < 0)
goto done;
break;
case 'R': /* Restconf on-line config */
inline_config = optarg;
break;
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
@ -2057,7 +2087,7 @@ main(int argc,
goto done;
/* Clixon inits / configs */
if ((ret = restconf_clixon_init(h, &xrestconf)) < 0)
if ((ret = restconf_clixon_init(h, inline_config, &xrestconf)) < 0)
goto done;
if (ret == 0){ /* restconf disabled */
clicon_debug(1, "restconf configuration not found or disabled");

View file

@ -272,7 +272,6 @@ int xml_search_child_insert(cxobj *xp, cxobj *x);
int xml_search_child_rm(cxobj *xp, cxobj *x);
cxobj *xml_child_index_each(cxobj *xparent, char *name, cxobj *xprev, enum cxobj_type type);
#endif
#endif /* _CLIXON_XML_H */

View file

@ -697,11 +697,15 @@ clixon_process_status(clicon_handle h,
if (pe->pe_description)
cprintf(cbret, "<description xmlns=\"%s\">%s</description>", CLIXON_LIB_NS, pe->pe_description);
cprintf(cbret, "<command xmlns=\"%s\">", CLIXON_LIB_NS);
/* the command may include any data, including XML (such as restconf -R command) and
therefore needs CDATA encoding */
cprintf(cbret, "<![CDATA[");
for (i=0; i<pe->pe_argc-1; i++){
if (i)
cprintf(cbret, " ");
cprintf(cbret, "%s", pe->pe_argv[i]);
}
cprintf(cbret, "]]>");
cprintf(cbret, "</command>");
cprintf(cbret, "<status xmlns=\"%s\">%s</status>", CLIXON_LIB_NS,
clicon_int2str(proc_state_map, pe->pe_state));

View file

@ -157,7 +157,11 @@ function testrun()
stop_restconf_pre
new "start restconf daemon"
start_restconf -f $cfg
# inline of start_restconf, cant make quotes to work
sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG -f $cfg -R "$RESTCONFIG1" &
if [ $? -ne 0 ]; then
err1 "expected 0" "$?"
fi
fi
new "wait restconf"

View file

@ -115,10 +115,10 @@ EOF
err "No pid return value" "$retx"
fi
if $active; then
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/.*/clixon_restconf -f $cfg -D [0-9] -l ${LOGDST_CMD}</command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS><!\[CDATA\[/.*/.*/clixon_restconf -f $cfg -D [0-9] .*\]\]></command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
else
# inactive, no startime or pid
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/.*/clixon_restconf -f $cfg -D [0-9] -l ${LOGDST_CMD}</command><status $LIBNS>$status</status></rpc-reply>]]>]]>$"
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS><!\[CDATA\[/.*/clixon_restconf -f $cfg -D [0-9] .*\]\]></command><status $LIBNS>$status</status></rpc-reply>]]>]]>$"
fi
match=$(echo "$retx" | grep --null -Go "$expect")
if [ -z "$match" ]; then

View file

@ -131,10 +131,10 @@ EOF
fi
echo "retx:$retx" # XXX
if $active; then
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/.*/clixon_restconf -f $cfg -D [0-9] -l ${LOGDST_CMD}</command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS><!\[CDATA\[/.*/.*/clixon_restconf -f $cfg -D [0-9] .*\]\]></command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
else
# inactive, no startime or pid
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/.*/clixon_restconf -f $cfg -D [0-9] -l ${LOGDST_CMD}</command><status $LIBNS>$status</status></rpc-reply>]]>]]>$"
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS><!\[CDATA\[/.*/clixon_restconf -f $cfg -D [0-9] .*\]\]></command><status $LIBNS>$status</status></rpc-reply>]]>]]>$"
fi
match=$(echo "$retx" | grep --null -Go "$expect")
if [ -z "$match" ]; then