diff --git a/CHANGELOG.md b/CHANGELOG.md index dafd1c16..e435e252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,11 +39,23 @@ Developers may need to change their code 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. * 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 +* 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, * see [fuzz/README.md](fuzz/README.md) for details diff --git a/LICENSE.md b/LICENSE.md index ae31e385..455bac51 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren 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. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 9cef1b8f..f1926d54 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1580,23 +1580,18 @@ from_client_process_control(clicon_handle h, cxobj *x; char *name = NULL; char *operation = NULL; - int status = 0; + uint32_t pid = 0; clicon_debug(1, "%s", __FUNCTION__); if ((x = xml_find_type(xe, NULL, "name", CX_ELMNT)) != NULL) name = xml_body(x); if ((x = xml_find_type(xe, NULL, "operation", CX_ELMNT)) != NULL) operation = xml_body(x); - /* Make the actual process operation */ - if (clixon_process_operation(h, name, operation, &status) < 0) + /* Make the actual process operation (with wrap function enabled) */ + if (clixon_process_operation(h, name, operation, 1, &pid) < 0) goto done; - if (strcmp(operation, "status") == 0) - cprintf(cbret, "%s", - NETCONF_BASE_NAMESPACE, - CLIXON_LIB_NS, - status?"true":"false"); - else - cprintf(cbret, "", NETCONF_BASE_NAMESPACE); + cprintf(cbret, "%u", + NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, pid); retval = 0; done: return retval; @@ -1633,7 +1628,6 @@ from_client_hello(clicon_handle h, return retval; } - /*! An internal clicon message has arrived from a client. Receive and dispatch. * @param[in] h Clicon handle * @param[in] s Socket where message arrived. read from this. diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 025e803f..1b3c24ac 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -60,6 +60,7 @@ #include #include #include +#include /* cligen */ #include @@ -67,7 +68,7 @@ /* clicon */ #include -#include "clixon_backend_handle.h" +#include "clixon_backend_transaction.h" #include "backend_socket.h" #include "backend_client.h" #include "backend_plugin.h" @@ -178,50 +179,6 @@ backend_server_socket(clicon_handle h) 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 */ static int @@ -431,6 +388,140 @@ ret2status(int ret, 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 */ static void @@ -789,12 +880,6 @@ main(int argc, clicon_option_str(h, "CLICON_BACKEND_REGEXP")) < 0) 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 * 1. Load a yang module as a specific absolute filename */ if ((str = clicon_yang_main_file(h)) != NULL) @@ -815,7 +900,7 @@ main(int argc, /* Load yang module library, RFC7895 */ if (yang_modules_init(h) < 0) 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) goto done; @@ -834,6 +919,11 @@ main(int argc, if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE") && yang_spec_parse_module(h, "ietf-yang-library", NULL, yspec)< 0) 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 * Compute and set canonical namespace context */ diff --git a/apps/restconf/restconf_main_evhtp.c b/apps/restconf/restconf_main_evhtp.c index 18538f53..fdc05501 100644 --- a/apps/restconf/restconf_main_evhtp.c +++ b/apps/restconf/restconf_main_evhtp.c @@ -1121,9 +1121,6 @@ restconf_config(clicon_handle h, /* Add netconf yang spec, used as internal protocol */ if (netconf_module_load(h) < 0) goto done; - /* Clixon restconf daemon config */ - if (yang_spec_parse_module(h, "clixon-restconf", NULL, yspec)< 0) - goto done; /* Add system modules */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && @@ -1171,7 +1168,7 @@ restconf_config(clicon_handle h, clicon_session_id_set(h, id); break; } - if ((nsc = xml_nsctx_init(NULL, "https://clicon.org/restconf")) == NULL) + if ((nsc = xml_nsctx_init(NULL, CLIXON_RESTCONF_NS)) == NULL) goto done; if ((pw = getpwuid(getuid())) == NULL){ clicon_err(OE_UNIX, errno, "getpwuid"); diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 1f20937c..ba9fc4f3 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -50,8 +50,9 @@ * @see clixon-config.yang * @see clixon-lib.yang */ -#define CLIXON_CONF_NS "http://clicon.org/config" -#define CLIXON_LIB_NS "http://clicon.org/lib" +#define CLIXON_CONF_NS "http://clicon.org/config" +#define CLIXON_LIB_NS "http://clicon.org/lib" +#define CLIXON_RESTCONF_NS "http://clicon.org/restconf" /* * Types diff --git a/lib/clixon/clixon_proc.h b/lib/clixon/clixon_proc.h index ba1a65b8..cdb2b9fa 100644 --- a/lib/clixon/clixon_proc.h +++ b/lib/clixon/clixon_proc.h @@ -38,14 +38,22 @@ #ifndef _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 */ 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_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_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_ */ diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c index 144a9fee..b2984f5c 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -902,7 +902,7 @@ xmldb_get(clicon_handle h, * @note Use of 1 for OK * @code * 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; * ... * xmldb_get0_clear(h, xt); # Clear tree from default values and flags @@ -910,6 +910,19 @@ xmldb_get(clicon_handle h, * @endcode * @see xml_nsctx_node to get a XML namespace context from XML tree * @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: + * 1 + * 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: + * 0 */ int xmldb_get0(clicon_handle h, diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 966515b9..878c1b60 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -1356,7 +1356,7 @@ netconf_module_features(clicon_handle h) 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 * @retval 0 OK * @retval -1 Error @@ -1379,6 +1379,9 @@ netconf_module_load(clicon_handle h) if (clicon_option_bool(h, "CLICON_XML_CHANGELOG")) if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0) 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; done: return retval; diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 6c5e0ff8..9ea9f170 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -317,6 +317,12 @@ done: * @param[out] cpp Clixon plugin structure (direct pointer) * @retval 0 OK, with cpp set * @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 clixon_pseudo_plugin(clicon_handle h, diff --git a/lib/src/clixon_proc.c b/lib/src/clixon_proc.c index dd1c28a7..1448deee 100644 --- a/lib/src/clixon_proc.c +++ b/lib/src/clixon_proc.c @@ -76,6 +76,19 @@ #include "clixon_queue.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 * 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? */ int -clixon_proc_background(char **argv, - char *netns, - pid_t *pid0) +clixon_proc_background(char **argv, + const char *netns, + pid_t *pid0) { int retval = -1; pid_t child; @@ -273,7 +286,6 @@ clixon_proc_background(char **argv, } } #endif /* HAVE_SETNS */ - clicon_debug(1, "%s argv0:%s", __FUNCTION__, argv[0]); if (execv(argv[0], argv) < 0) { clicon_err(OE_UNIX, errno, "execv"); exit(1); @@ -289,7 +301,7 @@ clixon_proc_background(char **argv, *pid0 = child; retval = 0; quit: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + clicon_debug(1, "%s retval:%d child:%u", __FUNCTION__, retval, child); return retval; } @@ -373,13 +385,6 @@ clixon_proc_daemon(char **argv, /* * 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 */ static process_entry_t *proc_entry_list = NULL; @@ -398,6 +403,7 @@ int clixon_process_register(clicon_handle h, const char *name, const char *netns, + proc_cb_t *callback, char **argv, int argc) { @@ -436,6 +442,7 @@ clixon_process_register(clicon_handle h, clicon_err(OE_UNIX, errno, "strdup"); } } + pe->pe_callback = callback; ADDQ(pe, proc_entry_list); retval = 0; done: @@ -469,15 +476,15 @@ clixon_process_delete_all(clicon_handle h) } static int -proc_op_run(process_entry_t *pe, - int *runp) +proc_op_run(pid_t pid0, + int *runp) { int retval = -1; int run; pid_t pid; run = 0; - if ((pid = pe->pe_pid) != 0){ /* if 0 stopped */ + if ((pid = pid0) != 0){ /* if 0 stopped */ /* Check if lives */ run = 1; if ((kill(pid, 0)) < 0){ @@ -497,11 +504,53 @@ proc_op_run(process_entry_t *pe, 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] name Name of process * @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 * @retval -1 Error * @retval 0 OK @@ -509,13 +558,13 @@ proc_op_run(process_entry_t *pe, */ int clixon_process_operation(clicon_handle h, - char *name, + const char *name, char *op, - int *status) + int wrapit, + uint32_t *pid) { int retval = -1; process_entry_t *pe; - int run; clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, op); if (proc_entry_list == NULL) @@ -523,31 +572,14 @@ clixon_process_operation(clicon_handle h, pe = proc_entry_list; do { if (strcmp(pe->pe_name, name) == 0){ - /* Check if running */ - if (proc_op_run(pe, &run) < 0) + /* Call wrapper function that eg changes op based on config */ + 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; - if (status) /* Store as output parameter */ - *status = run; - 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 */ - } + if (pid) + *pid = pe->pe_pid; break; /* hit break here */ } pe = NEXTQ(process_entry_t *, pe); diff --git a/test/mem.sh b/test/mem.sh index 36660d52..338e6798 100755 --- a/test/mem.sh +++ b/test/mem.sh @@ -28,8 +28,8 @@ memonce(){ 'backend') valgrindtest=2 # This means backend valgrind test : ${DEMWAIT:=10} # valgrind backend needs some time to get up - - 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" + # 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=no --log-file=$valgrindfile clixon_backend" ;; 'restconf') valgrindtest=3 # This means backend valgrind test diff --git a/test/test_restconf.sh b/test/test_restconf.sh index c8645f91..ca421f9e 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -74,7 +74,7 @@ state='{"clixon-example:state":{"op":\["41","42","43"\]}' if $IPv6; then # For backend config, create 4 sockets, all combinations IPv4/IPv6 + http/https RESTCONFIG=$(cat < + true password $srvcert @@ -90,7 +90,7 @@ EOF else # For backend config, create 4 sockets, all combinations IPv4/IPv6 + http/https RESTCONFIG=$(cat < + true password $srvcert @@ -142,10 +142,12 @@ testrun() new "kill old restconf daemon" stop_restconf_pre - new "start restconf daemon ZZZ" + new "start restconf daemon" echo "cfg:$cfg" start_restconf -f $cfg + fi + new "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"}\]}' 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" 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\]" fi for addr in $addrs; do - new "restconf test: proto:$proto addr:$addr config:$config" + new "restconf test: proto:$proto addr:$addr" testrun $proto $addr done done diff --git a/test/test_restconf_jukebox.sh b/test/test_restconf_jukebox.sh index 089ee1d7..903945f4 100755 --- a/test/test_restconf_jukebox.sh +++ b/test/test_restconf_jukebox.sh @@ -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 # seems wrong to recreate 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" 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' 'urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=expliciturn:ietf:params:restconf:capability:depth diff --git a/test/test_restconf_rpc.sh b/test/test_restconf_rpc.sh index d89aa876..8d6e59a9 100755 --- a/test/test_restconf_rpc.sh +++ b/test/test_restconf_rpc.sh @@ -1,6 +1,12 @@ #!/usr/bin/env bash -# Restconf direct start/stop using RPC (as alternative to systemd or other) -# Also try ip netns +# Restconf direct start/stop using RPC and config enable flag (as alternative to systemd or other) +# 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) 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 cfg=$dir/conf.xml - -# 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 +startupdb=$dir/startup_db cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $IETFRFC $dir @@ -31,7 +32,7 @@ cat < $cfg $APPNAME /usr/local/var/$APPNAME/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile - /usr/local/var/$APPNAME + $dir true true @@ -39,6 +40,72 @@ cat < $cfg 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< + + restconf + $operation + +]]>]]> +EOF +) + + expect1="" + match=$(echo "$ret" | grep --null -Go "$expect1") + if [ -z "$match" ]; then + err "$expect1" "$ret" + fi + + expect2="]]>]]>" + 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< $startupdb + + + true + + +EOF + +new "kill old restconf" +stop_restconf_pre + new "test params: -f $cfg" if [ $BE -ne 0 ]; then new "kill old backend" @@ -46,92 +113,82 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err 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 fi -new "kill old restconf" -stop_restconf_pre +# Get pid of running process and check return xml +new "Get rpc status" +pid0=$(testrpc status 1) +if [ $? -ne 0 ]; then exit -1; fi -new "1)check no restconf" -ps=$(ps aux|grep "$WWWDIR/clixon_restconf" | grep -v grep) -if [ -n "$ps" ]; then - err "No restconf running" "$ps" -fi +new "check restconf process running using ps pid0:$pid0" +ps=$(ps -hp $pid0) -new "2)check status off" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfstatus]]>]]>" "false]]>]]>" - -new "start restconf" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfstart]]>]]>" "]]>]]>" - -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 "restconfstatus]]>]]>" "true]]>]]>" - -new "stop restconf" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfstop]]>]]>" "]]>]]>" - -new "start restconf again" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfstart]]>]]>" "]]>]]>" - -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 err "A restconf running" 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" stop_restconf_pre -new "6)check no restconf" -ps=$(ps aux|grep "$WWWDIR/clixon_restconf" | grep -v grep) -if [ -n "$ps" ]; then - err "No restconf running" "$ps" +new "start restconf RPC" +pid4=$(testrpc start 1) +if [ $? -ne 0 ]; then exit -1; fi + +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 -new "restart restconf" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfrestart]]>]]>" "]]>]]>" - -new "7)check status on" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfstatus]]>]]>" "true]]>]]>" - -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 -pid0=$(pgrep clixon_restconf) - -new "restart restconf" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfrestart]]>]]>" "]]>]]>" - -new "8)check status on" -expecteof "$clixon_netconf -qf $cfg" 0 "restconfstatus]]>]]>" "true]]>]]>" - -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 [ $valgrindtest -eq 0 ]; then # Cant get pgrep to work properly +# new "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 new "Kill backend" @@ -142,19 +199,70 @@ if [ $BE -ne 0 ]; then fi # kill backend stop_backend -f $cfg + fi - # XXX Cant get this to work in docker/alpine - if false; then - new "10)check no restconf" - sleep $DEMWAIT - ps=$(ps aux|grep "$WWWDIR/clixon_restconf" | grep -v grep) - if [ -n "$ps" ]; then - err "No restconf running" "$ps" - fi +# So far, no restconf config enable flag has been true. Now change enable flag. + +new "ENABLE false" +# Second basic operation with restconf enable is false +cat< $startupdb + + + false + + +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 + new "start backend -s startup -f $cfg" + start_backend -s startup -f $cfg + + new "waiting" + wait_backend 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 "mergetrue]]>]]>" "^]]>]]>$" + +new "netconf commit" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + +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 "mergefalse]]>]]>" "^]]>]]>$" + +new "netconf commit" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + +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 new "restconf rpc in network namespace" @@ -207,3 +315,4 @@ sudo ip netns delete $netns fi # namespaces rm -rf $dir + diff --git a/test/test_upgrade_quit.sh b/test/test_upgrade_quit.sh index 1965e1a6..b157f2a0 100755 --- a/test/test_upgrade_quit.sh +++ b/test/test_upgrade_quit.sh @@ -301,7 +301,7 @@ cat < $dir/startup_db EOF -MODSTATE1='0clixon-lib2020-12-08http://clicon.org/lib' +MODSTATE1='0clixon-lib2020-12-30http://clicon.org/lib' MODSTATE2='interfaces2018-02-20urn:example:interfaces' diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index 75fd9347..40d2d9ee 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -42,8 +42,8 @@ datarootdir = @datarootdir@ # See also OPT_YANG_INSTALLDIR for the standard yang files YANG_INSTALLDIR = @YANG_INSTALLDIR@ -YANGSPECS = clixon-config@2020-11-03.yang -YANGSPECS += clixon-lib@2020-12-08.yang +YANGSPECS = clixon-config@2020-12-30.yang +YANGSPECS += clixon-lib@2020-12-30.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-restconf@2020-10-30.yang diff --git a/yang/clixon/clixon-config.yang b/yang/clixon/clixon-config.yang index 5756b75f..a76a1706 120000 --- a/yang/clixon/clixon-config.yang +++ b/yang/clixon/clixon-config.yang @@ -1 +1 @@ -clixon-config@2020-10-01.yang \ No newline at end of file +clixon-config@2020-12-30.yang \ No newline at end of file diff --git a/yang/clixon/clixon-config@2020-10-01.yang b/yang/clixon/clixon-config@2020-12-30.yang similarity index 93% rename from yang/clixon/clixon-config@2020-10-01.yang rename to yang/clixon/clixon-config@2020-12-30.yang index 4ef7a4ca..0c839363 100644 --- a/yang/clixon/clixon-config@2020-10-01.yang +++ b/yang/clixon/clixon-config@2020-12-30.yang @@ -3,6 +3,9 @@ module clixon-config { namespace "http://clicon.org/config"; prefix cc; + import clixon-restconf { + prefix clrc; + } organization "Clicon / Clixon"; @@ -40,9 +43,33 @@ module clixon-config { ***** 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 { description - "Added: CLICON_CONFIGDIR"; + "Added: CLICON_CONFIGDIR."; } revision 2020-08-17 { 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 restconf { + uses clrc:clixon-restconf; + } leaf-list CLICON_FEATURE { description "Supported features as used by YANG feature/if-feature @@ -392,7 +433,8 @@ module clixon-config { default "/www-data/fastcgi_restconf.sock"; description "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 { type boolean; @@ -407,58 +449,6 @@ module clixon-config { Setting this value to false makes restconf return not pretty-printed 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 - 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 - 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 { type string; description @@ -624,15 +614,15 @@ module clixon-config { from a spec, such as in the autocli."; } leaf CLICON_SOCK_FAMILY { - type string; - default "UNIX"; + type socket_address_family; + default UNIX; description - "Address family for communicating with clixon_backend - (UNIX|IPv4). IPv6 not yet implemented. + "Address family for communicating with clixon_backend with one of: + Note IPv6 not implemented. Note that UNIX socket makes credential check as follows: (1) client needs rw access to the socket (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 { @@ -681,6 +671,20 @@ module clixon-config { mandatory true; 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 { type int32; default 0; diff --git a/yang/clixon/clixon-lib@2020-12-30.yang b/yang/clixon/clixon-lib@2020-12-30.yang new file mode 100644 index 00000000..2886b28a --- /dev/null +++ b/yang/clixon/clixon-lib@2020-12-30.yang @@ -0,0 +1,183 @@ +module clixon-lib { + yang-version 1.1; + namespace "http://clicon.org/lib"; + prefix cl; + + organization + "Clicon / Clixon"; + + contact + "Olof Hagsand "; + + 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; + } + } + } +} diff --git a/yang/clixon/clixon-restconf@2020-10-30.yang b/yang/clixon/clixon-restconf@2020-10-30.yang index 05b17204..9344d855 100644 --- a/yang/clixon/clixon-restconf@2020-10-30.yang +++ b/yang/clixon/clixon-restconf@2020-10-30.yang @@ -1,6 +1,6 @@ module clixon-restconf { yang-version 1.1; - namespace "https://clicon.org/restconf"; + namespace "http://clicon.org/restconf"; prefix "clrc"; import ietf-inet-types { @@ -124,6 +124,13 @@ module clixon-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"; uses clixon-restconf; }