* New clixon-lib@2020-12-30.yang revision
* Added callback to process-control RPC feature in clixon-lib.yang to manage processes
* Changed behavior of starting restconf internally using `CLICON_BACKEND_RESTCONF_PROCESS` monitoring changes in enable flag, not only the RPC.
* Changed: RPC process-control output parameter status to pid
This commit is contained in:
Olof hagsand 2021-01-01 17:26:49 +01:00
parent 7459925bd0
commit cf63d0f761
21 changed files with 741 additions and 280 deletions

View file

@ -39,11 +39,23 @@ Developers may need to change their code
Users may have to change how they access the system Users may have to change how they access the system
* New clixon-lib@2020-12-30.yang revision
* Changed: RPC process-control output parameter status to pid
* New clixon-config@2020-12-30.yang revision
* Removed obsolete RESTCONF and SSL options
* Changed namespace of clixon-restconf@2020-10-30.yang from https://clicon.org/restconf ->http://clicon.org/restconf ->
* CLIspec dbxml API: Ability to specify deletion of _any_ vs _specific_ entry. * CLIspec dbxml API: Ability to specify deletion of _any_ vs _specific_ entry.
* In a cli_del() call, the cvv arg list either exactly matches the api-format-path in which case _any_ deletion is specified, otherwise, if there is an extra element in the cvv list, that is used for a specific delete. * In a cli_del() call, the cvv arg list either exactly matches the api-format-path in which case _any_ deletion is specified, otherwise, if there is an extra element in the cvv list, that is used for a specific delete.
### Minor changes ### Minor changes
* Added callback to process-control RPC feature in clixon-lib.yang to manage processes
* WHen an RPC comes in, be able to look at configuration
* Changed behavior of starting restconf internally using `CLICON_BACKEND_RESTCONF_PROCESS` monitoring changes in enable flag, not only the RPC. The semantics is as follows:
* on RPC start, if enable is true, start the service, if false, error or ignore it
* on RPC stop, stop the service
* on backend start make the state as configured
* on enable change, make the state as configured
* Limited fuzz by AFL committed, * Limited fuzz by AFL committed,
* see [fuzz/README.md](fuzz/README.md) for details * see [fuzz/README.md](fuzz/README.md) for details

View file

@ -1,6 +1,6 @@
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC (Netgate) Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC (Netgate)
CLIXON is dual license. CLIXON is dual license.

View file

@ -1580,23 +1580,18 @@ from_client_process_control(clicon_handle h,
cxobj *x; cxobj *x;
char *name = NULL; char *name = NULL;
char *operation = NULL; char *operation = NULL;
int status = 0; uint32_t pid = 0;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((x = xml_find_type(xe, NULL, "name", CX_ELMNT)) != NULL) if ((x = xml_find_type(xe, NULL, "name", CX_ELMNT)) != NULL)
name = xml_body(x); name = xml_body(x);
if ((x = xml_find_type(xe, NULL, "operation", CX_ELMNT)) != NULL) if ((x = xml_find_type(xe, NULL, "operation", CX_ELMNT)) != NULL)
operation = xml_body(x); operation = xml_body(x);
/* Make the actual process operation */ /* Make the actual process operation (with wrap function enabled) */
if (clixon_process_operation(h, name, operation, &status) < 0) if (clixon_process_operation(h, name, operation, 1, &pid) < 0)
goto done; goto done;
if (strcmp(operation, "status") == 0) cprintf(cbret, "<rpc-reply xmlns=\"%s\"><pid xmlns=\"%s\">%u</pid></rpc-reply>",
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><status xmlns=\"%s\">%s</status></rpc-reply>", NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, pid);
NETCONF_BASE_NAMESPACE,
CLIXON_LIB_NS,
status?"true":"false");
else
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
retval = 0; retval = 0;
done: done:
return retval; return retval;
@ -1633,7 +1628,6 @@ from_client_hello(clicon_handle h,
return retval; return retval;
} }
/*! An internal clicon message has arrived from a client. Receive and dispatch. /*! An internal clicon message has arrived from a client. Receive and dispatch.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] s Socket where message arrived. read from this. * @param[in] s Socket where message arrived. read from this.

View file

@ -60,6 +60,7 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <libgen.h> #include <libgen.h>
#include <assert.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -67,7 +68,7 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#include "clixon_backend_handle.h" #include "clixon_backend_transaction.h"
#include "backend_socket.h" #include "backend_socket.h"
#include "backend_client.h" #include "backend_client.h"
#include "backend_plugin.h" #include "backend_plugin.h"
@ -178,50 +179,6 @@ backend_server_socket(clicon_handle h)
return ss; return ss;
} }
/*! Enable process-control of restconf daemon, ie start/stop restconf
* @param[in] h Clicon handle
* @note Could also look in clixon-restconf and start process if enable is true, but that needs to
* be in start callback using a pseudo plugin.
*/
static int
backend_restconf_process_control(clicon_handle h)
{
int retval = -1;
char **argv = NULL;
int i;
int nr;
char dbgstr[8];
char wwwstr[64];
nr = 4;
if (clicon_debug_get() != 0)
nr += 2;
if ((argv = calloc(nr, sizeof(char *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
i = 0;
snprintf(wwwstr, sizeof(wwwstr)-1, "%s/clixon_restconf", clicon_option_str(h, "CLICON_WWWDIR"));
argv[i++] = wwwstr;
argv[i++] = "-f";
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
if (clicon_debug_get() != 0){
argv[i++] = "-D";
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
argv[i++] = dbgstr;
}
argv[i++] = NULL;
if (clixon_process_register(h, "restconf",
NULL /* XXX network namespace */,
argv, nr) < 0)
goto done;
if (argv != NULL)
free(argv);
retval = 0;
done:
return retval;
}
/*! Load external NACM file /*! Load external NACM file
*/ */
static int static int
@ -431,6 +388,140 @@ ret2status(int ret,
return retval; return retval;
} }
/*---------------------------------------------------------------------
* Restconf process pseudo plugin
*/
#define RESTCONF_PROCESS "restconf"
/*! 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
* These rules give that if RPC op is start and enable is false -> change op to none
*/
int
restconf_rpc_wrapper(clicon_handle h,
process_entry_t *pe,
char **operation)
{
int retval = -1;
cxobj *xt = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (strcmp(*operation, "stop") == 0){
/* if RPC op is stop, stop the service */
}
else if (strcmp(*operation, "start") == 0){
/* RPC op is start & enable is true, then start the service,
& enable is false, error or ignore it */
if (xmldb_get(h, "running", NULL, "/restconf", &xt) < 0)
goto done;
if (xt != NULL &&
xpath_first(xt, NULL, "/restconf[enable='false']") != NULL) {
*operation = "none";
}
}
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Enable process-control of restconf daemon, ie start/stop restconf by registering restconf process
* @param[in] h Clicon handle
* @note Could also look in clixon-restconf and start process if enable is true, but that needs to
* be in start callback using a pseudo plugin.
*/
static int
restconf_pseudo_process_control(clicon_handle h)
{
int retval = -1;
char **argv = NULL;
int i;
int nr;
char dbgstr[8];
char wwwstr[64];
nr = 4;
if (clicon_debug_get() != 0)
nr += 2;
if ((argv = calloc(nr, sizeof(char *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
i = 0;
snprintf(wwwstr, sizeof(wwwstr)-1, "%s/clixon_restconf", clicon_option_str(h, "CLICON_WWWDIR"));
argv[i++] = wwwstr;
argv[i++] = "-f";
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
if (clicon_debug_get() != 0){
argv[i++] = "-D";
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
argv[i++] = dbgstr;
}
argv[i++] = NULL;
assert(i==nr);
if (clixon_process_register(h, RESTCONF_PROCESS,
NULL /* XXX network namespace */,
restconf_rpc_wrapper,
argv, nr) < 0)
goto done;
if (argv != NULL)
free(argv);
retval = 0;
done:
return retval;
}
/*! Restconf pseduo-plugin process commit
*/
static int
restconf_pseudo_process_commit(clicon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *xtarget;
cxobj *cx;
int enabled = 0;
clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td);
if (xpath_first(xtarget, NULL, "/restconf[enable='true']") != NULL)
enabled++;
if ((cx = xpath_first(xtarget, NULL, "/restconf/enable")) != NULL &&
xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_ADD)){
if (clixon_process_operation(h, RESTCONF_PROCESS,
enabled?"start":"stop", 0, NULL) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Register start/stop restconf RPC and create pseudo-plugin to monitor enable flag
* @param[in] h Clixon handle
*/
static int
restconf_pseudo_process_reg(clicon_handle h,
yang_stmt *yspec)
{
int retval = -1;
clixon_plugin *cp = NULL;
if (clixon_pseudo_plugin(h, "restconf pseudo plugin", &cp) < 0)
goto done;
cp->cp_api.ca_trans_commit = restconf_pseudo_process_commit;
/* Register generic process-control of restconf daemon, ie start/stop restconf */
if (restconf_pseudo_process_control(h) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! usage /*! usage
*/ */
static void static void
@ -789,12 +880,6 @@ main(int argc,
clicon_option_str(h, "CLICON_BACKEND_REGEXP")) < 0) clicon_option_str(h, "CLICON_BACKEND_REGEXP")) < 0)
goto done; goto done;
/* Enable process-control of restconf daemon, ie start/stop restconf */
if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS")){
if (backend_restconf_process_control(h) < 0)
goto done;
}
/* Load Yang modules /* Load Yang modules
* 1. Load a yang module as a specific absolute filename */ * 1. Load a yang module as a specific absolute filename */
if ((str = clicon_yang_main_file(h)) != NULL) if ((str = clicon_yang_main_file(h)) != NULL)
@ -815,7 +900,7 @@ main(int argc,
/* Load yang module library, RFC7895 */ /* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0) if (yang_modules_init(h) < 0)
goto done; goto done;
/* Add netconf yang spec, used by netconf client and as internal protocol /* Add generic yang specs, used by netconf client and as internal protocol
*/ */
if (netconf_module_load(h) < 0) if (netconf_module_load(h) < 0)
goto done; goto done;
@ -834,6 +919,11 @@ main(int argc,
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE") && if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE") &&
yang_spec_parse_module(h, "ietf-yang-library", NULL, yspec)< 0) yang_spec_parse_module(h, "ietf-yang-library", NULL, yspec)< 0)
goto done; goto done;
/* Check restconf start/stop from backend */
if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS")){
if (restconf_pseudo_process_reg(h, yspec) < 0)
goto done;
}
/* Here all modules are loaded /* Here all modules are loaded
* Compute and set canonical namespace context * Compute and set canonical namespace context
*/ */

View file

@ -1121,9 +1121,6 @@ restconf_config(clicon_handle h,
/* Add netconf yang spec, used as internal protocol */ /* Add netconf yang spec, used as internal protocol */
if (netconf_module_load(h) < 0) if (netconf_module_load(h) < 0)
goto done; goto done;
/* Clixon restconf daemon config */
if (yang_spec_parse_module(h, "clixon-restconf", NULL, yspec)< 0)
goto done;
/* Add system modules */ /* Add system modules */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
@ -1171,7 +1168,7 @@ restconf_config(clicon_handle h,
clicon_session_id_set(h, id); clicon_session_id_set(h, id);
break; break;
} }
if ((nsc = xml_nsctx_init(NULL, "https://clicon.org/restconf")) == NULL) if ((nsc = xml_nsctx_init(NULL, CLIXON_RESTCONF_NS)) == NULL)
goto done; goto done;
if ((pw = getpwuid(getuid())) == NULL){ if ((pw = getpwuid(getuid())) == NULL){
clicon_err(OE_UNIX, errno, "getpwuid"); clicon_err(OE_UNIX, errno, "getpwuid");

View file

@ -50,8 +50,9 @@
* @see clixon-config.yang * @see clixon-config.yang
* @see clixon-lib.yang * @see clixon-lib.yang
*/ */
#define CLIXON_CONF_NS "http://clicon.org/config" #define CLIXON_CONF_NS "http://clicon.org/config"
#define CLIXON_LIB_NS "http://clicon.org/lib" #define CLIXON_LIB_NS "http://clicon.org/lib"
#define CLIXON_RESTCONF_NS "http://clicon.org/restconf"
/* /*
* Types * Types

View file

@ -38,14 +38,22 @@
#ifndef _CLIXON_PROC_H_ #ifndef _CLIXON_PROC_H_
#define _CLIXON_PROC_H_ #define _CLIXON_PROC_H_
/*
* Types
*/
typedef struct process_entry_t process_entry_t;
/* Process RPC callback function */
typedef int (proc_cb_t)(clicon_handle h, process_entry_t *pe, char **operation);
/* /*
* Prototypes * Prototypes
*/ */
int clixon_proc_run(char **argv, void (outcb)(char *), int doerr); int clixon_proc_run(char **argv, void (outcb)(char *), int doerr);
int clixon_proc_background(char **argv, char *netns, pid_t *pid); int clixon_proc_background(char **argv, const char *netns, pid_t *pid);
int clixon_proc_daemon(char **argv, pid_t *pid); int clixon_proc_daemon(char **argv, pid_t *pid);
int clixon_process_register(clicon_handle h, const char *name, const char *netns, char **argv, int argc); int clixon_process_register(clicon_handle h, const char *name, const char *netns, proc_cb_t *callback, char **argv, int argc);
int clixon_process_delete_all(clicon_handle h); int clixon_process_delete_all(clicon_handle h);
int clixon_process_operation(clicon_handle h, char *name, char *op, int *status); int clixon_process_operation(clicon_handle h, const char *name, char *op, const int wrapit, uint32_t *pid);
#endif /* _CLIXON_PROC_H_ */ #endif /* _CLIXON_PROC_H_ */

View file

@ -902,7 +902,7 @@ xmldb_get(clicon_handle h,
* @note Use of 1 for OK * @note Use of 1 for OK
* @code * @code
* cxobj *xt; * cxobj *xt;
* if (xmldb_get0(xh, "running", YB_MODULE, nsc, "/interface[name="eth"]", 0, &xt, NULL) < 0) * if (xmldb_get0(h, "running", YB_MODULE, nsc, "/interface[name="eth"]", 0, &xt, NULL) < 0)
* err; * err;
* ... * ...
* xmldb_get0_clear(h, xt); # Clear tree from default values and flags * xmldb_get0_clear(h, xt); # Clear tree from default values and flags
@ -910,6 +910,19 @@ xmldb_get(clicon_handle h,
* @endcode * @endcode
* @see xml_nsctx_node to get a XML namespace context from XML tree * @see xml_nsctx_node to get a XML namespace context from XML tree
* @see xmldb_get for a copy version (old-style) * @see xmldb_get for a copy version (old-style)
* @note An annoying issue is one with default values and xpath miss:
* Assume a yang spec:
* module m {
* container c {
* leaf x {
* default 0;
* And a db content:
* <c><x>1</x></c>
* With the following call:
* xmldb_get0(h, "running", NULL, NULL, "/c[x=0]", 1, &xt, NULL)
* which result in a miss (there is no c with x=0), but when the returned xt is printed
* (the existing tree is discarded), the default (empty) xml tree is:
* <c><x>0</x></c>
*/ */
int int
xmldb_get0(clicon_handle h, xmldb_get0(clicon_handle h,

View file

@ -1356,7 +1356,7 @@ netconf_module_features(clicon_handle h)
return retval; return retval;
} }
/*! Load ietf netconf yang module and set enabled features /*! Load generic yang specs, ie ietf netconf yang module and set enabled features
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
@ -1379,6 +1379,9 @@ netconf_module_load(clicon_handle h)
if (clicon_option_bool(h, "CLICON_XML_CHANGELOG")) if (clicon_option_bool(h, "CLICON_XML_CHANGELOG"))
if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0) if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0)
goto done; goto done;
/* Load restconf yang. Note this is also a part of clixon-config */
if (yang_spec_parse_module(h, "clixon-restconf", NULL, yspec)< 0)
goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;

View file

@ -317,6 +317,12 @@ done:
* @param[out] cpp Clixon plugin structure (direct pointer) * @param[out] cpp Clixon plugin structure (direct pointer)
* @retval 0 OK, with cpp set * @retval 0 OK, with cpp set
* @retval -1 Error * @retval -1 Error
* @code
* clixon_plugin *cp = NULL;
* if (clixon_pseudo_plugin(h, "pseudo plugin", &cp) < 0)
* err;
* cp->cp_api.ca_extension = my_ext_cb;
* @endcode
*/ */
int int
clixon_pseudo_plugin(clicon_handle h, clixon_pseudo_plugin(clicon_handle h,

View file

@ -76,6 +76,19 @@
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_proc.h" #include "clixon_proc.h"
/*
* Types
*/
/* Process entry list */
struct process_entry_t {
qelem_t pe_qelem; /* List header */
char *pe_name; /* Name of process used for internal use */
char *pe_netns; /* Network namespace */
char **pe_argv; /* argv with command as element 0 and NULL-terminated */
pid_t pe_pid; /* Running process id (state) or 0 if dead */
proc_cb_t *pe_callback; /* Wrapper function */
};
/* /*
* Child process ID * Child process ID
* XXX Really shouldn't be a global variable * XXX Really shouldn't be a global variable
@ -208,9 +221,9 @@ clixon_proc_run(char **argv,
* @note SIGCHLD is set to IGN here. Maybe it should be done in main? * @note SIGCHLD is set to IGN here. Maybe it should be done in main?
*/ */
int int
clixon_proc_background(char **argv, clixon_proc_background(char **argv,
char *netns, const char *netns,
pid_t *pid0) pid_t *pid0)
{ {
int retval = -1; int retval = -1;
pid_t child; pid_t child;
@ -273,7 +286,6 @@ clixon_proc_background(char **argv,
} }
} }
#endif /* HAVE_SETNS */ #endif /* HAVE_SETNS */
clicon_debug(1, "%s argv0:%s", __FUNCTION__, argv[0]);
if (execv(argv[0], argv) < 0) { if (execv(argv[0], argv) < 0) {
clicon_err(OE_UNIX, errno, "execv"); clicon_err(OE_UNIX, errno, "execv");
exit(1); exit(1);
@ -289,7 +301,7 @@ clixon_proc_background(char **argv,
*pid0 = child; *pid0 = child;
retval = 0; retval = 0;
quit: quit:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d child:%u", __FUNCTION__, retval, child);
return retval; return retval;
} }
@ -373,13 +385,6 @@ clixon_proc_daemon(char **argv,
/* /*
* Types * Types
*/ */
typedef struct {
qelem_t pe_qelem; /* List header */
char *pe_name; /* Name of process used for internal use */
char *pe_netns; /* Network namespace */
char **pe_argv; /* argv with command as element 0 and NULL-terminated */
pid_t pe_pid;
} process_entry_t;
/* List of process callback entries */ /* List of process callback entries */
static process_entry_t *proc_entry_list = NULL; static process_entry_t *proc_entry_list = NULL;
@ -398,6 +403,7 @@ int
clixon_process_register(clicon_handle h, clixon_process_register(clicon_handle h,
const char *name, const char *name,
const char *netns, const char *netns,
proc_cb_t *callback,
char **argv, char **argv,
int argc) int argc)
{ {
@ -436,6 +442,7 @@ clixon_process_register(clicon_handle h,
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
} }
} }
pe->pe_callback = callback;
ADDQ(pe, proc_entry_list); ADDQ(pe, proc_entry_list);
retval = 0; retval = 0;
done: done:
@ -469,15 +476,15 @@ clixon_process_delete_all(clicon_handle h)
} }
static int static int
proc_op_run(process_entry_t *pe, proc_op_run(pid_t pid0,
int *runp) int *runp)
{ {
int retval = -1; int retval = -1;
int run; int run;
pid_t pid; pid_t pid;
run = 0; run = 0;
if ((pid = pe->pe_pid) != 0){ /* if 0 stopped */ if ((pid = pid0) != 0){ /* if 0 stopped */
/* Check if lives */ /* Check if lives */
run = 1; run = 1;
if ((kill(pid, 0)) < 0){ if ((kill(pid, 0)) < 0){
@ -497,11 +504,53 @@ proc_op_run(process_entry_t *pe,
return retval; return retval;
} }
/*! Upgrade specific module identified by namespace, search matching callbacks /*! Perform process operation
*
*/
static int
clixon_process_operation_one(const char *op,
const char *netns,
char **argv,
pid_t *pidp)
{
int retval = -1;
int run = 0;
/* Check if running */
if (proc_op_run(*pidp, &run) < 0)
goto done;
if (strcmp(op, "stop") == 0 ||
strcmp(op, "restart") == 0){
if (run)
pidfile_zapold(*pidp); /* Ensures its dead */
*pidp = 0; /* mark as dead */
run = 0;
}
if (strcmp(op, "start") == 0 ||
strcmp(op, "restart") == 0){
if (run == 1){
; /* Already runs */
}
else{
if (clixon_proc_background(argv, netns, pidp) < 0)
goto done;
}
}
else if (strcmp(op, "status") == 0){
; /* status already set */
}
retval = 0;
done:
return retval;
}
/*! Find process operation entry given name and op and perform operation if found
* *
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] name Name of process * @param[in] name Name of process
* @param[in] op start, stop. * @param[in] op start, stop.
* @param[in] wrapit If set, call potential callback, if false, dont call it
* @param[out] status true if process is running / false if not running on entry * @param[out] status true if process is running / false if not running on entry
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
@ -509,13 +558,13 @@ proc_op_run(process_entry_t *pe,
*/ */
int int
clixon_process_operation(clicon_handle h, clixon_process_operation(clicon_handle h,
char *name, const char *name,
char *op, char *op,
int *status) int wrapit,
uint32_t *pid)
{ {
int retval = -1; int retval = -1;
process_entry_t *pe; process_entry_t *pe;
int run;
clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, op); clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, op);
if (proc_entry_list == NULL) if (proc_entry_list == NULL)
@ -523,31 +572,14 @@ clixon_process_operation(clicon_handle h,
pe = proc_entry_list; pe = proc_entry_list;
do { do {
if (strcmp(pe->pe_name, name) == 0){ if (strcmp(pe->pe_name, name) == 0){
/* Check if running */ /* Call wrapper function that eg changes op based on config */
if (proc_op_run(pe, &run) < 0) if (wrapit && pe->pe_callback != NULL)
if (pe->pe_callback(h, pe, &op) < 0)
goto done;
if (clixon_process_operation_one(op, pe->pe_netns, pe->pe_argv, &pe->pe_pid) < 0)
goto done; goto done;
if (status) /* Store as output parameter */ if (pid)
*status = run; *pid = pe->pe_pid;
if (strcmp(op, "stop") == 0 ||
strcmp(op, "restart") == 0){
if (run)
pidfile_zapold(pe->pe_pid); /* Ensures its dead */
pe->pe_pid = 0; /* mark as dead */
run = 0;
}
if (strcmp(op, "start") == 0 ||
strcmp(op, "restart") == 0){
if (run == 1){
; /* Already runs */
}
else{
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0)
goto done;
}
}
else if (strcmp(op, "status") == 0){
; /* status already set */
}
break; /* hit break here */ break; /* hit break here */
} }
pe = NEXTQ(process_entry_t *, pe); pe = NEXTQ(process_entry_t *, pe);

View file

@ -28,8 +28,8 @@ memonce(){
'backend') 'backend')
valgrindtest=2 # This means backend valgrind test valgrindtest=2 # This means backend valgrind test
: ${DEMWAIT:=10} # valgrind backend needs some time to get up : ${DEMWAIT:=10} # valgrind backend needs some time to get up
# trace-children=no for test_restconf_rpc.sh
clixon_backend="/usr/bin/valgrind --num-callers=50 --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=yes --log-file=$valgrindfile clixon_backend" clixon_backend="/usr/bin/valgrind --num-callers=50 --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --log-file=$valgrindfile clixon_backend"
;; ;;
'restconf') 'restconf')
valgrindtest=3 # This means backend valgrind test valgrindtest=3 # This means backend valgrind test

View file

@ -74,7 +74,7 @@ state='{"clixon-example:state":{"op":\["41","42","43"\]}'
if $IPv6; then if $IPv6; then
# For backend config, create 4 sockets, all combinations IPv4/IPv6 + http/https # For backend config, create 4 sockets, all combinations IPv4/IPv6 + http/https
RESTCONFIG=$(cat <<EOF RESTCONFIG=$(cat <<EOF
<restconf xmlns="https://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
<enable>true</enable> <enable>true</enable>
<auth-type>password</auth-type> <auth-type>password</auth-type>
<server-cert-path>$srvcert</server-cert-path> <server-cert-path>$srvcert</server-cert-path>
@ -90,7 +90,7 @@ EOF
else else
# For backend config, create 4 sockets, all combinations IPv4/IPv6 + http/https # For backend config, create 4 sockets, all combinations IPv4/IPv6 + http/https
RESTCONFIG=$(cat <<EOF RESTCONFIG=$(cat <<EOF
<restconf xmlns="https://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
<enable>true</enable> <enable>true</enable>
<auth-type>password</auth-type> <auth-type>password</auth-type>
<server-cert-path>$srvcert</server-cert-path> <server-cert-path>$srvcert</server-cert-path>
@ -142,10 +142,12 @@ testrun()
new "kill old restconf daemon" new "kill old restconf daemon"
stop_restconf_pre stop_restconf_pre
new "start restconf daemon ZZZ" new "start restconf daemon"
echo "cfg:$cfg" echo "cfg:$cfg"
start_restconf -f $cfg start_restconf -f $cfg
fi fi
new "wait restconf" new "wait restconf"
wait_restconf wait_restconf
@ -186,7 +188,7 @@ testrun()
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:module":\[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}\]}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:module":\[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}\]}'
new "restconf schema resource, mod-state top-level" new "restconf schema resource, mod-state top-level"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-example","revision":"2020-12-01","namespace":"urn:example:clixon","conformance-type":"implement"},{"name":"clixon-lib","revision":"2020-12-08","' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-example","revision":"2020-12-01","namespace":"urn:example:clixon","conformance-type":"implement"},{"name":"clixon-lib","revision":"2020-12-30","'
new "restconf options. RFC 8040 4.1" new "restconf options. RFC 8040 4.1"
expectpart "$(curl $CURLOPTS -X OPTIONS $proto://$addr/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" expectpart "$(curl $CURLOPTS -X OPTIONS $proto://$addr/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE"
@ -390,7 +392,7 @@ for proto in $protos; do
addrs="$addrs \[::1\]" addrs="$addrs \[::1\]"
fi fi
for addr in $addrs; do for addr in $addrs; do
new "restconf test: proto:$proto addr:$addr config:$config" new "restconf test: proto:$proto addr:$addr"
testrun $proto $addr testrun $proto $addr
done done
done done

View file

@ -102,7 +102,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPR
# This just catches the header and the jukebox module, the RFC has foo and bar which # This just catches the header and the jukebox module, the RFC has foo and bar which
# seems wrong to recreate # seems wrong to recreate
new "B.1.2. Retrieve the Server Module Information" new "B.1.2. Retrieve the Server Module Information"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2020-12-08","namespace":"http://clicon.org/lib","conformance-type":"implement"}' '{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"}' '{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}' '{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2020-12-30","namespace":"http://clicon.org/lib","conformance-type":"implement"}' '{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"}' '{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}' '{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"}'
new "B.1.3. Retrieve the Server Capability Information" new "B.1.3. Retrieve the Server Capability Information"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability> expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>

View file

@ -1,6 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Restconf direct start/stop using RPC (as alternative to systemd or other) # Restconf direct start/stop using RPC and config enable flag (as alternative to systemd or other)
# Also try ip netns # According tot he following behaviour:
# - on RPC start, if enable is true, start the service, if false, error or ignore it
# - on RPC stop, stop the service
# - on backend start make the state as configured
# - on enable change, make the state as configured
# - No restconf config means enable: false (extra rule)
# Also work-in-progress network namespaces, ip netns
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -8,17 +14,12 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example APPNAME=example
cfg=$dir/conf.xml cfg=$dir/conf.xml
startupdb=$dir/startup_db
# Cant get it to work in the general case, single tests work fine
# More specifically, if mem.sh background netconf, netconf crashes which is valgrindtest 1
if [ $valgrindtest -eq 1 ]; then
echo "...skipped "
return 0 # skip
fi
cat <<EOF > $cfg cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config"> <clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR> <CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR> <CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR> <CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
@ -31,7 +32,7 @@ cat <<EOF > $cfg
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE> <CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<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>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895> <CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
<!-- start restconf from backend --> <!-- start restconf from backend -->
<CLICON_BACKEND_RESTCONF_PROCESS>true</CLICON_BACKEND_RESTCONF_PROCESS> <CLICON_BACKEND_RESTCONF_PROCESS>true</CLICON_BACKEND_RESTCONF_PROCESS>
@ -39,6 +40,72 @@ cat <<EOF > $cfg
</clixon-config> </clixon-config>
EOF EOF
# Subroutine send a process control RPC and tricks to echo process-id returned
# Args:
# 1: operation
# 2: expectret 0: means expect pi 0 as return, else something else
testrpc()
{
operation=$1
expectret=$2
new "send rpc $operation"
ret=$($clixon_netconf -qf $cfg<<EOF
<rpc $DEFAULTNS>
<process-control xmlns="http://clicon.org/lib">
<name>restconf</name>
<operation>$operation</operation>
</process-control>
</rpc>]]>]]>
EOF
)
expect1="<rpc-reply $DEFAULTNS><pid xmlns=\"http://clicon.org/lib\">"
match=$(echo "$ret" | grep --null -Go "$expect1")
if [ -z "$match" ]; then
err "$expect1" "$ret"
fi
expect2="</pid></rpc-reply>]]>]]>"
match=$(echo "$ret" | grep --null -Go "$expect2")
if [ -z "$match" ]; then
err "$expect2" "$ret"
fi
new "check rpc $operation get pid"
pid=$(echo "$ret" | awk -F'[<>]' '{print $5}')
if [ -z "$pid" ]; then
err "Running process" "$ret"
fi
new "check restconf retvalue"
if [ $expectret -eq 0 ]; then
if [ $pid -ne 0 ]; then
err "No process" "$pid"
fi
else
if [ $pid -eq 0 ]; then
err "Running process"
fi
fi
>&2 echo "pid:$pid" # debug
echo $pid # cant use return that only uses 0-255
}
new "ENABLE true"
# First basic operation with restconf enable is true
cat<<EOF > $startupdb
<config>
<restconf xmlns="http://clicon.org/restconf">
<enable>true</enable>
</restconf>
</config>
EOF
new "kill old restconf"
stop_restconf_pre
new "test params: -f $cfg" new "test params: -f $cfg"
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "kill old backend" new "kill old backend"
@ -46,92 +113,82 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
new "waiting" new "start backend -s startup -f $cfg"
start_backend -s startup -f $cfg
new "wait backend"
wait_backend wait_backend
fi fi
new "kill old restconf" # Get pid of running process and check return xml
stop_restconf_pre new "Get rpc status"
pid0=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
new "1)check no restconf" new "check restconf process running using ps pid0:$pid0"
ps=$(ps aux|grep "$WWWDIR/clixon_restconf" | grep -v grep) ps=$(ps -hp $pid0)
if [ -n "$ps" ]; then
err "No restconf running" "$ps"
fi
new "2)check status off"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>status</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><status xmlns=\"http://clicon.org/lib\">false</status></rpc-reply>]]>]]>"
new "start restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>start</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
new "3)check restconf on"
if [ $valgrindtest -eq 0 ]; then # Cant get pgrep to work properly
sleep $DEMWAIT # Slows the tests down considerably, but needed in eg docker test
fi
ps=$(ps aux|grep "$WWWDIR/clixon_restconf -f $cfg" | grep -v grep)
if [ -z "$ps" ]; then
err "restconf running"
fi
new "4)check status on"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>status</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><status xmlns=\"http://clicon.org/lib\">true</status></rpc-reply>]]>]]>"
new "stop restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>stop</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
new "start restconf again"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>start</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
new "5)check restconf on"
if [ $valgrindtest -eq 0 ]; then # Cant get pgrep to work properly
sleep $DEMWAIT # Slows the tests down considerably, but needed in eg docker test
fi
ps=$(ps aux|grep "$WWWDIR/clixon_restconf -f $cfg" | grep -v grep)
if [ -z "$ps" ]; then if [ -z "$ps" ]; then
err "A restconf running" err "A restconf running"
fi fi
new "stop restconf RPC"
pid1=$(testrpc stop 0)
if [ $? -ne 0 ]; then exit -1; fi
new "Get rpc status stopped"
pid2=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
new "Start rpc again"
pid3=$(testrpc start 1)
if [ $? -ne 0 ]; then exit -1; fi
new "check restconf process running using ps"
ps=$(ps -hp $pid3)
if [ -z "$ps" ]; then
err "A restconf running"
fi
if [ $pid0 -eq $pid3 ]; then
err "A different pid" "$pid3"
fi
new "kill restconf" new "kill restconf"
stop_restconf_pre stop_restconf_pre
new "6)check no restconf" new "start restconf RPC"
ps=$(ps aux|grep "$WWWDIR/clixon_restconf" | grep -v grep) pid4=$(testrpc start 1)
if [ -n "$ps" ]; then if [ $? -ne 0 ]; then exit -1; fi
err "No restconf running" "$ps"
new "check status RPC on"
pid5=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
new "restart restconf RPC"
pid6=$(testrpc restart 1)
if [ $? -ne 0 ]; then exit -1; fi
new "Get restconf status rpc"
pid7=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
if [ $pid5 -eq $pid7 ]; then
err "A different pid" "$pid7"
fi fi
new "restart restconf" #if [ $valgrindtest -eq 0 ]; then # Cant get pgrep to work properly
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>restart</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>" # new "check new pid"
# sleep $DEMWAIT # Slows the tests down considerably, but needed in eg docker test
new "7)check status on" # pid1=$(pgrep clixon_restconf)
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>status</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><status xmlns=\"http://clicon.org/lib\">true</status></rpc-reply>]]>]]>" # if [ -z "$pid0" -o -z "$pid1" ]; then
# err "Pids expected" "pid0:$pid0 = pid1:$pid1"
if [ $valgrindtest -eq 0 ]; then # Cant get pgrep to work properly # fi
sleep $DEMWAIT # Slows the tests down considerably, but needed in eg docker test # if [ $pid0 -eq $pid1 ]; then#
fi # err "Different pids" "pid0:$pid0 = pid1:$pid1"
pid0=$(pgrep clixon_restconf) # fi
#fi
new "restart restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>restart</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
new "8)check status on"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>status</operation></process-control></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><status xmlns=\"http://clicon.org/lib\">true</status></rpc-reply>]]>]]>"
if [ $valgrindtest -eq 0 ]; then # Cant get pgrep to work properly
new "9)check new pid"
sleep $DEMWAIT # Slows the tests down considerably, but needed in eg docker test
pid1=$(pgrep clixon_restconf)
if [ -z "$pid0" -o -z "$pid1" ]; then
err "Pids expected" "pid0:$pid0 = pid1:$pid1"
fi
if [ $pid0 -eq $pid1 ]; then
err "Different pids" "pid0:$pid0 = pid1:$pid1"
fi
fi
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"
@ -142,19 +199,70 @@ if [ $BE -ne 0 ]; then
fi fi
# kill backend # kill backend
stop_backend -f $cfg stop_backend -f $cfg
fi
# XXX Cant get this to work in docker/alpine # So far, no restconf config enable flag has been true. Now change enable flag.
if false; then
new "10)check no restconf" new "ENABLE false"
sleep $DEMWAIT # Second basic operation with restconf enable is false
ps=$(ps aux|grep "$WWWDIR/clixon_restconf" | grep -v grep) cat<<EOF > $startupdb
if [ -n "$ps" ]; then <config>
err "No restconf running" "$ps" <restconf xmlns="http://clicon.org/restconf">
fi <enable>false</enable>
</restconf>
</config>
EOF
new "kill old restconf"
stop_restconf_pre
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -z -f $cfg
if [ $? -ne 0 ]; then
err
fi fi
new "start backend -s startup -f $cfg"
start_backend -s startup -f $cfg
new "waiting"
wait_backend
fi fi
if false; then # Work in progress new "check status RPC off"
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
new "start restconf RPC"
pid=$(testrpc start 0)
if [ $? -ne 0 ]; then exit -1; fi
new "check status RPC off"
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
new "Enable restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><default-operation>merge</default-operation><target><candidate/></target><config><restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "check status RPC on"
pid=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
new "Disable restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><default-operation>merge</default-operation><target><candidate/></target><config><restconf xmlns=\"http://clicon.org/restconf\"><enable>false</enable></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "check status RPC off"
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
if false; then # Work in progress - namespace
#------------------------------- #-------------------------------
# Now in a separate network namespace # Now in a separate network namespace
new "restconf rpc in network namespace" new "restconf rpc in network namespace"
@ -207,3 +315,4 @@ sudo ip netns delete $netns
fi # namespaces fi # namespaces
rm -rf $dir rm -rf $dir

View file

@ -301,7 +301,7 @@ cat <<EOF > $dir/startup_db
</config> </config>
EOF EOF
MODSTATE1='<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set-id>0</module-set-id><module><name>clixon-lib</name><revision>2020-12-08</revision><namespace>http://clicon.org/lib</namespace></module>' MODSTATE1='<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set-id>0</module-set-id><module><name>clixon-lib</name><revision>2020-12-30</revision><namespace>http://clicon.org/lib</namespace></module>'
MODSTATE2='<module><name>interfaces</name><revision>2018-02-20</revision><namespace>urn:example:interfaces</namespace></module>' MODSTATE2='<module><name>interfaces</name><revision>2018-02-20</revision><namespace>urn:example:interfaces</namespace></module>'

View file

@ -42,8 +42,8 @@ datarootdir = @datarootdir@
# See also OPT_YANG_INSTALLDIR for the standard yang files # See also OPT_YANG_INSTALLDIR for the standard yang files
YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
YANGSPECS = clixon-config@2020-11-03.yang YANGSPECS = clixon-config@2020-12-30.yang
YANGSPECS += clixon-lib@2020-12-08.yang YANGSPECS += clixon-lib@2020-12-30.yang
YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang
YANGSPECS += clixon-restconf@2020-10-30.yang YANGSPECS += clixon-restconf@2020-10-30.yang

View file

@ -1 +1 @@
clixon-config@2020-10-01.yang clixon-config@2020-12-30.yang

View file

@ -3,6 +3,9 @@ module clixon-config {
namespace "http://clicon.org/config"; namespace "http://clicon.org/config";
prefix cc; prefix cc;
import clixon-restconf {
prefix clrc;
}
organization organization
"Clicon / Clixon"; "Clicon / Clixon";
@ -40,9 +43,33 @@ module clixon-config {
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
revision 2020-12-30 {
description
"Removed obsolete options:
CLICON_RESTCONF_IPV4_ADDR
CLICON_RESTCONF_IPV6_ADDR
CLICON_RESTCONF_HTTP_PORT
CLICON_RESTCONF_HTTPS_PORT
CLICON_SSL_SERVER_CERT
CLICON_SSL_SERVER_KEY
CLICON_SSL_CA_CERT";
}
revision 2020-11-03 {
description
"Added CLICON_BACKEND_RESTCONF_PROCESS
Copied to clixon-restconf.yang and marked as obsolete:
CLICON_RESTCONF_IPV4_ADDR
CLICON_RESTCONF_IPV6_ADDR
CLICON_RESTCONF_HTTP_PORT
CLICON_RESTCONF_HTTPS_PORT
CLICON_SSL_SERVER_CERT
CLICON_SSL_SERVER_KEY
CLICON_SSL_CA_CERT
Removed obsolete option CLICON_TRANSACTION_MOD";
}
revision 2020-10-01 { revision 2020-10-01 {
description description
"Added: CLICON_CONFIGDIR"; "Added: CLICON_CONFIGDIR.";
} }
revision 2020-08-17 { revision 2020-08-17 {
description description
@ -264,8 +291,22 @@ module clixon-config {
} }
} }
} }
typedef socket_address_family {
description "Address family for internal socket";
type enumeration{
enum UNIX {
description "Unix domain socket";
}
enum IPv4 {
description "IPv4";
}
}
}
container clixon-config { container clixon-config {
container restconf {
uses clrc:clixon-restconf;
}
leaf-list CLICON_FEATURE { leaf-list CLICON_FEATURE {
description description
"Supported features as used by YANG feature/if-feature "Supported features as used by YANG feature/if-feature
@ -392,7 +433,8 @@ module clixon-config {
default "/www-data/fastcgi_restconf.sock"; default "/www-data/fastcgi_restconf.sock";
description description
"FastCGI unix socket. Should be specified in webserver "FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock"; Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock
Only if with-restconf=fcgi, NOT evhtp";
} }
leaf CLICON_RESTCONF_PRETTY { leaf CLICON_RESTCONF_PRETTY {
type boolean; type boolean;
@ -407,58 +449,6 @@ module clixon-config {
Setting this value to false makes restconf return not pretty-printed Setting this value to false makes restconf return not pretty-printed
which may be desirable for performance or tests"; which may be desirable for performance or tests";
} }
leaf CLICON_RESTCONF_IPV4_ADDR {
type string;
default "0.0.0.0";
description
"RESTCONF IPv4 socket binding address.
Applies to native http by config option --with-restconf=evhtp.";
}
leaf CLICON_RESTCONF_IPV6_ADDR {
type string;
default "::";
description
"RESTCONF IPv6 socket binding address.
Applies to native http by config option --with-restconf=evhtp.";
}
leaf CLICON_RESTCONF_HTTP_PORT {
type uint16;
default 80;
description
"RESTCONF socket binding port, non-ssl
In the restconf daemon, it can be overriden by -P <port>
Applies to native http only by config option --with-restconf=evhtp.";
}
leaf CLICON_RESTCONF_HTTPS_PORT {
type uint16;
default 443;
description
"RESTCONF socket binding port, ssl
In the restconf daemon, this is the port chosen if -s is given.
Note it can be overriden by -P <port>
Applies to native http by config option --with-restconf=evhtp.";
}
leaf CLICON_SSL_SERVER_CERT {
type string;
default "/etc/ssl/certs/clixon-server-crt.pem";
description
"SSL server cert for restconf https.
Applies to native http only by config option --with-restconf=evhtp.";
}
leaf CLICON_SSL_SERVER_KEY {
type string;
default "/etc/ssl/private/clixon-server-key.pem";
description
"SSL server private key for restconf https.
Applies to native http only by config option --with-restconf=evhtp.";
}
leaf CLICON_SSL_CA_CERT {
type string;
default "/etc/ssl/certs/clixon-ca_crt.pem";
description
"SSL CA cert for client authentication.
Applies to native http only by config option --with-restconf=evhtp.";
}
leaf CLICON_CLI_DIR { leaf CLICON_CLI_DIR {
type string; type string;
description description
@ -624,15 +614,15 @@ module clixon-config {
from a spec, such as in the autocli."; from a spec, such as in the autocli.";
} }
leaf CLICON_SOCK_FAMILY { leaf CLICON_SOCK_FAMILY {
type string; type socket_address_family;
default "UNIX"; default UNIX;
description description
"Address family for communicating with clixon_backend "Address family for communicating with clixon_backend with one of:
(UNIX|IPv4). IPv6 not yet implemented. Note IPv6 not implemented.
Note that UNIX socket makes credential check as follows: Note that UNIX socket makes credential check as follows:
(1) client needs rw access to the socket (1) client needs rw access to the socket
(2) NACM credentials can be checked according to CLICON_NACM_CREDENTIALS (2) NACM credentials can be checked according to CLICON_NACM_CREDENTIALS
Warning: IPv4 and IPv6 sockets have no credential mechanism. Warning: Only UNIX (not IPv4) sockets have credential mechanism.
"; ";
} }
leaf CLICON_SOCK { leaf CLICON_SOCK {
@ -681,6 +671,20 @@ module clixon-config {
mandatory true; mandatory true;
description "Process-id file of backend daemon"; description "Process-id file of backend daemon";
} }
leaf CLICON_BACKEND_RESTCONF_PROCESS {
type boolean;
default false;
description
"If set, enable process-control of restconf daemon, ie start/stop restconf
daemon internally from backend daemon.
It uses clixon-restconf.yang for config and clixon-lib.yang for RPC
Process control of restconf daemon is as follows:
- on RPC start, if enable is true, start the service, if false, error or ignore it
- on RPC stop, stop the service
- on backend start make the state as configured
- on enable change, make the state as configured
Disable if you start the restconf daemon by other means.";
}
leaf CLICON_AUTOCOMMIT { leaf CLICON_AUTOCOMMIT {
type int32; type int32;
default 0; default 0;

View file

@ -0,0 +1,183 @@
module clixon-lib {
yang-version 1.1;
namespace "http://clicon.org/lib";
prefix cl;
organization
"Clicon / Clixon";
contact
"Olof Hagsand <olof@hagsand.se>";
description
"Clixon Netconf extensions for communication between clients and backend.
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 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 *****";
revision 2020-12-30 {
description
"Changed: RPC process-control output parameter status to pid";
}
revision 2020-12-08 {
description
"Added: autocli-op extension.
rpc process-control for process/daemon management
Released in clixon 4.9";
}
revision 2020-04-23 {
description
"Added: stats RPC for clixon XML and memory statistics.
Added: restart-plugin RPC for restarting individual plugins without restarting backend.";
}
revision 2019-08-13 {
description
"No changes (reverted change)";
}
revision 2019-06-05 {
description
"ping rpc added for liveness";
}
revision 2019-01-02 {
description
"Released in Clixon 3.9";
}
typedef service-operation {
type enumeration {
enum start {
description
"Start if not already running";
}
enum stop {
description
"Stop if running";
}
enum restart {
description
"Stop if running, then start";
}
enum status {
description
"Check status";
}
}
description
"Common operations that can be performed on a service";
}
extension autocli-op {
description
"Takes an argument an operation defing how to modify the clispec at
this point in the YANG tree for the automated generated CLI.
Note that this extension is only used in clixon_cli.
Operations is expected to be extended, but the following operations are defined:
- hide This command is active but not shown by ? or TAB";
argument cliop;
}
rpc debug {
description "Set debug level of backend.";
input {
leaf level {
type uint32;
}
}
}
rpc ping {
description "Check aliveness of backend daemon.";
}
rpc stats {
description "Clixon XML statistics.";
output {
container global{
description "Clixon global statistics";
leaf xmlnr{
description "Number of XML objects: number of residing xml/json objects
in the internal 'cxobj' representation.";
type uint64;
}
}
list datastore{
description "Datastore statistics";
key "name";
leaf name{
description "name of datastore (eg running).";
type string;
}
leaf nr{
description "Number of XML objects. That is number of residing xml/json objects
in the internal 'cxobj' representation.";
type uint64;
}
leaf size{
description "Size in bytes of internal datastore cache of datastore tree.";
type uint64;
}
}
}
}
rpc restart-plugin {
description "Restart specific backend plugins.";
input {
leaf-list plugin {
description "Name of plugin to restart";
type string;
}
}
}
rpc process-control {
description
"Control a specific process or daemon: start/stop, etc.
This is for direct managing of a porcess by the backend.
Alternatively one can manage a daemon via systemd, containerd, kubernetes, etc.";
input {
leaf name {
description "Name of process";
type string;
mandatory true;
}
leaf operation {
type service-operation;
mandatory true;
description
"One of the strings 'start', 'stop', 'restart', or 'status'.";
}
}
output {
leaf pid {
description "Process-id of running process if operation is start,
restart or status. 0 if not running.
This is for two reasons:
- to check process running for status
- get the actual pid for targeting the actual process";
type uint32;
}
}
}
}

View file

@ -1,6 +1,6 @@
module clixon-restconf { module clixon-restconf {
yang-version 1.1; yang-version 1.1;
namespace "https://clicon.org/restconf"; namespace "http://clicon.org/restconf";
prefix "clrc"; prefix "clrc";
import ietf-inet-types { import ietf-inet-types {
@ -124,6 +124,13 @@ module clixon-restconf {
} }
} }
container restconf { container restconf {
description
"This presence is strictly not necessary since the enable flag
in clixon-restconf is the flag bearing the actual semantics.
However, removing the presence leads to default config in all
clixon installations, even those which do not use backend-started restconf.
One could see this as mostly cosmetically annoying.
Alternative would be to make the inclusion of this yang conditional.";
presence "Enables RESTCONF"; presence "Enables RESTCONF";
uses clixon-restconf; uses clixon-restconf;
} }