- Rewrote process control to simpler state model: stopped/running/exiting

- Stricter CLICON_BACKEND_RESTCONF_PROCESS :
 - if set, restconf daemon queries backend for its config
 - if not set, restconf daemon reads its config from main config file
This commit is contained in:
Olof hagsand 2021-04-11 17:36:22 +02:00
parent 9f5176adf5
commit 953326d39f
11 changed files with 517 additions and 310 deletions

View file

@ -37,8 +37,10 @@ Expected: April
### API changes on existing protocol/config features ### API changes on existing protocol/config features
* Native RESTCONF mode * Native RESTCONF mode
* Restconf "evhtp" mode MUST use libevhtp from https://github.com/clixon/clixon-libevhtp.git instead from criticalstack * Renamed restconf "evhtp" mode to "native" mode
* To configure native mode use: `configure --with-restconf=native`, changed from: `configure --with-restconf=evhtp` * To configure native mode use: `configure --with-restconf=native`, changed from: `configure --with-restconf=evhtp`
* Native mode MUST use libevhtp from https://github.com/clixon/clixon-libevhtp.git instead from criticalstack
* NETCONF Hello message semantics has been made stricter according to RFC 6241 Sec 8.1, for example: * NETCONF Hello message semantics has been made stricter according to RFC 6241 Sec 8.1, for example:
* A client MUST send a <hello> element. * A client MUST send a <hello> element.
* Each peer MUST send at least the base NETCONF capability, "urn:ietf:params:netconf:base:1.1" (or 1.0 for RFC 4741) * Each peer MUST send at least the base NETCONF capability, "urn:ietf:params:netconf:base:1.1" (or 1.0 for RFC 4741)
@ -46,6 +48,7 @@ Expected: April
* You can set `CLICON_NETCONF_HELLO_OPTIONAL` to true to use the old behavior of essentially ignoring hellos. * You can set `CLICON_NETCONF_HELLO_OPTIONAL` to true to use the old behavior of essentially ignoring hellos.
* New clixon-lib@2020-03-08.yang revision * New clixon-lib@2020-03-08.yang revision
* Changed: RPC process-control output to choice with status fields * Changed: RPC process-control output to choice with status fields
* The fields are: active, description, command, status, starttime, pid (or just ok).
* New clixon-config@2020-03-08.yang revision * New clixon-config@2020-03-08.yang revision
* Added: `CLICON_NETCONF_HELLO_OPTIONAL` * Added: `CLICON_NETCONF_HELLO_OPTIONAL`
* Added: `CLICON_CLI_AUTOCLI_EXCLUDE` * Added: `CLICON_CLI_AUTOCLI_EXCLUDE`

View file

@ -238,12 +238,14 @@ restconf_pseudo_process_commit(clicon_handle h,
{ {
int retval = -1; int retval = -1;
cxobj *xtarget; cxobj *xtarget;
cxobj *xsource;
cxobj *cx; cxobj *cx;
int enabled = 0; int enabled = 0;
cxobj *xb; cxobj *xb;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td); xtarget = transaction_target(td);
xsource = transaction_src(td);
if (xpath_first(xtarget, NULL, "/restconf[enable='true']") != NULL) if (xpath_first(xtarget, NULL, "/restconf[enable='true']") != NULL)
enabled++; enabled++;
/* Get debug flag of restconf config, set the restconf start -D daemon flag according /* Get debug flag of restconf config, set the restconf start -D daemon flag according
@ -271,10 +273,18 @@ restconf_pseudo_process_commit(clicon_handle h,
/* A restart can terminate a restconf connection (cut the tree limb you are sitting on) /* A restart can terminate a restconf connection (cut the tree limb you are sitting on)
* Specifically, the socket is terminated where the reply is sent, which will * Specifically, the socket is terminated where the reply is sent, which will
* cause the curl to fail. * cause the curl to fail.
* Note that it should really be a START if the process is stopped, but the
* commit code need not know any of that
*/ */
if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0) < 0) if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0) < 0)
goto done; goto done;
} }
else if ((cx = xpath_first(xsource, NULL, "/restconf")) != NULL &&
xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_DEL)){
/* Or something deleted */
if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0) < 0)
goto done;
}
} }
} }
retval = 0; retval = 0;

View file

@ -431,15 +431,17 @@ main(int argc,
if (clixon_plugin_start_all(h) < 0) if (clixon_plugin_start_all(h) < 0)
goto done; goto done;
/* First try to get restconf config from local config-file */ if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){
if ((xrestconf1 = clicon_conf_restconf(h)) != NULL){ /* If not read from backend, try to get restconf config from local config-file */
if ((ret = restconf_config_init(h, xrestconf1)) < 0) if ((xrestconf1 = clicon_conf_restconf(h)) != NULL){
goto done; if ((ret = restconf_config_init(h, xrestconf1)) < 0)
if (ret == 1) goto done;
configure_done = 1; if (ret == 1)
configure_done = 1;
}
} }
/* If no local config, or it is disabled, try to query backend of config. */ /* If no local config, or it is disabled, try to query backend of config. */
if (!configure_done){ else {
/* Loop to wait for backend starting, try again if not done */ /* Loop to wait for backend starting, try again if not done */
while (1){ while (1){
if (clicon_hello_req(h, &id) < 0){ if (clicon_hello_req(h, &id) < 0){

View file

@ -1610,24 +1610,25 @@ restconf_clixon_init(clicon_handle h,
goto done; goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0) if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done; goto done;
/* First try to get restconf config from local config-file */
ret = 0; ret = 0;
if ((xrestconf = clicon_conf_restconf(h)) != NULL){ if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){
/*! Basic config init, set auth-type, pretty, etc ret 0 means disabled */ /* If not read from backend, try to get restconf config from local config-file */
if ((ret = restconf_config_init(h, xrestconf)) < 0) if ((xrestconf = clicon_conf_restconf(h)) != NULL){
goto done; /*! Basic config init, set auth-type, pretty, etc ret 0 means disabled */
/* ret == 1 means this config is OK */ if ((ret = restconf_config_init(h, xrestconf)) < 0)
if (ret == 0){
xrestconf = NULL; /* Dont free since it is part of conf tree */
}
else
if ((*xrestconfp = xml_dup(xrestconf)) == NULL)
goto done; goto done;
/* ret == 1 means this config is OK */
if (ret == 0){
xrestconf = NULL; /* Dont free since it is part of conf tree */
}
else
if ((*xrestconfp = xml_dup(xrestconf)) == NULL)
goto done;
}
} }
/* If no local config, or it is disabled, try to query backend of config. */ /* If no local config, or it is disabled, try to query backend of config.
if (ret == 0){ */
assert(*xrestconfp == NULL); else {
if ((ret = restconf_clixon_backend(h, xrestconfp)) < 0) if ((ret = restconf_clixon_backend(h, xrestconfp)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)

View file

@ -34,20 +34,48 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* Processes daemons * Processes daemons
* States of processes:
A description of process states. A description of process states.
It starts in a STOPPED state. On operation "start" or "restart" it gets a pid and goes into RUNNING: An entity is a process_entry_t with a unique name. pids are created for "active" processes.
STOPPED --(re)start--> RUNNING States:
In RUNNING several things can happen: STOPPED: pid=0, No process running
- It is killed externally: the process then gets a SIGCHLD which triggers a wait and it goes into STOPPED: RUNNING: pid set, Process started and believed to be running
RUNNING --sigchld/wait--> STOPPED EXITING: pid set, Process is killed by parent but not waited for
It is stopped due to an rpc or by config commit removing the config. In that case the parent
process kills the process and enters into EXITING waiting for a SIGCHLD that triggers a wait: Operations:
RUNNING --stop--> EXITING --sigchld/wait--> STOPPED start, stop, restart
It is restarted due to an rpc or config change (eg a server is added, a key modified, etc). Then
a new process is started which enters RUNNING, while the old process (the dying clone) enters EXITING: Transitions:
STOPPED --restart--> RUNNING(newpid) Process struct created by calling clixon_process_register() with static info such as name,
RUNNING --stop--> EXITING --sigchld/wait--> REMOVED (oldpid/clone) description, namespace, start arguments, etc. Starts in STOPPED state:
--> STOPPED
On operation "start" or "restart" it gets a pid and goes into RUNNING state:
STOPPED -- (re)start --> RUNNING(pid)
When running, several things may happen:
1. It is killed externally: the process gets a SIGCHLD triggers a wait and it goes to STOPPED:
RUNNING --sigchld/wait--> STOPPED
2. It is stopped due to a rpc or configuration remove:
The parent kills the process and enters EXITING waiting for a SIGCHLD that triggers a wait,
therafter it goes to STOPPED
RUNNING --stop--> EXITING --sigchld/wait--> STOPPED
3. It is restarted due to rpc or config change (eg a server is added, a key modified, etc).
The parent kills the process and enters EXITING waiting for a SIGCHLD that triggers a wait,
therafter a new process is started and it goes to RUNNING with a new pid
RUNNING --restart--> EXITING --sigchld/wait + restart --> RUNNING(pid)
A complete state diagram is:
STOPPED --(re)start--> RUNNING(pid)
^ <--1.wait(kill)--- | ^
| stop/| |
| restart| | restart
| v |
wait(stop) ------- EXITING(dying pid)
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -98,6 +126,14 @@
/* /*
* Types * Types
*/ */
/* Process state
*/
enum proc_state {
PROC_STATE_STOPPED,
PROC_STATE_RUNNING,
PROC_STATE_EXITING
};
typedef enum proc_state proc_state_t;
/* Process entry list */ /* Process entry list */
struct process_entry_t { struct process_entry_t {
@ -108,10 +144,9 @@ struct process_entry_t {
char **pe_argv; /* argv with command as element 0 and NULL-terminated */ char **pe_argv; /* argv with command as element 0 and NULL-terminated */
int pe_argc; /* Length of argc */ int pe_argc; /* Length of argc */
pid_t pe_pid; /* Running process id (state) or 0 if dead (pid is set if exiting=1) */ pid_t pe_pid; /* Running process id (state) or 0 if dead (pid is set if exiting=1) */
int pe_exiting; /* If set process is in the process of dying needs reaping */ proc_operation pe_operation;/* Pending operation: stop/start/restart */
int pe_clone; /* Duplicate when restarting, delete when reaped */ proc_state_t pe_state; /* stopped, running, exiting */
pid_t pe_status; /* Status on exit as defined in waitpid */ pid_t pe_exit_status;/* Status on exit as defined in waitpid */
proc_operation pe_op; /* Operation pending? */
struct timeval pe_starttime; /* Start time */ struct timeval pe_starttime; /* Start time */
proc_cb_t *pe_callback; /* Wrapper function, may be called from process_operation */ proc_cb_t *pe_callback; /* Wrapper function, may be called from process_operation */
}; };
@ -307,14 +342,21 @@ clixon_proc_background(char **argv,
* Process management: start/stop registered processes for internal use * Process management: start/stop registered processes for internal use
*/ */
static const map_str2int proc_state_map[] = {
{"stopped", PROC_STATE_STOPPED},
{"running", PROC_STATE_RUNNING},
{"exiting", PROC_STATE_EXITING},
{NULL, -1}
};
/* Process operations /* Process operations
*/ */
static const map_str2int proc_operation_map[] = { static const map_str2int proc_operation_map[] = {
{"none", PROC_OP_NONE}, {"none", PROC_OP_NONE}, /* Not state transition operator */
{"start", PROC_OP_START}, {"start", PROC_OP_START}, /* State transition operator */
{"stop", PROC_OP_STOP}, {"stop", PROC_OP_STOP}, /* State transition operator */
{"restart", PROC_OP_RESTART}, {"restart", PROC_OP_RESTART},/* State transition operator */
{"status", PROC_OP_STATUS}, {"status", PROC_OP_STATUS}, /* Not state transition operator */
{NULL, -1} {NULL, -1}
}; };
@ -356,6 +398,7 @@ clixon_process_argv_get(clicon_handle h,
return 0; return 0;
} }
#ifdef NYI
/*! Make a copy of process-entry struct /*! Make a copy of process-entry struct
* *
* @param[in] pe0 Original process-entry * @param[in] pe0 Original process-entry
@ -415,6 +458,7 @@ clixon_process_register_dup(process_entry_t *pe0,
/* dealloc pe1 on error */ /* dealloc pe1 on error */
return retval; return retval;
} }
#endif
/*! Register an internal process /*! Register an internal process
* *
@ -480,6 +524,11 @@ clixon_process_register(clicon_handle h,
} }
} }
pe->pe_callback = callback; pe->pe_callback = callback;
clicon_debug(1, "%s %s ----> %s", __FUNCTION__,
pe->pe_name,
clicon_int2str(proc_state_map, PROC_STATE_STOPPED)
);
pe->pe_state = PROC_STATE_STOPPED;
ADDQ(pe, _proc_entry_list); ADDQ(pe, _proc_entry_list);
retval = 0; retval = 0;
done: done:
@ -557,7 +606,7 @@ proc_op_run(pid_t pid0,
* *
* @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, restart, status * @param[in] op0 start, stop, restart, status
* @param[in] wrapit If set, call potential callback, if false, dont call it * @param[in] wrapit If set, call potential callback, if false, dont call it
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
@ -570,11 +619,12 @@ proc_op_run(pid_t pid0,
int int
clixon_process_operation(clicon_handle h, clixon_process_operation(clicon_handle h,
const char *name, const char *name,
proc_operation op, proc_operation op0,
int wrapit) int wrapit)
{ {
int retval = -1; int retval = -1;
process_entry_t *pe; process_entry_t *pe;
proc_operation op;
int sched = 0; /* If set, process action should be scheduled, register a timeout */ int sched = 0; /* If set, process action should be scheduled, register a timeout */
clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, clicon_int2str(proc_operation_map, op)); clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, clicon_int2str(proc_operation_map, op));
@ -583,17 +633,22 @@ 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){
/* Call wrapper function that eg changes op based on config */ /* Call wrapper function that eg changes op1 based on config */
op = op0;
if (wrapit && pe->pe_callback != NULL) if (wrapit && pe->pe_callback != NULL)
if (pe->pe_callback(h, pe, &op) < 0) if (pe->pe_callback(h, pe, &op) < 0)
goto done; goto done;
clicon_debug(1, "%s name: %s pid:%d op: %s", __FUNCTION__,
name, pe->pe_pid, clicon_int2str(proc_operation_map, op));
if (op == PROC_OP_START || op == PROC_OP_STOP || op == PROC_OP_RESTART){ if (op == PROC_OP_START || op == PROC_OP_STOP || op == PROC_OP_RESTART){
pe->pe_op = op; pe->pe_operation = op;
clicon_debug(1, "%s scheduling %s pid:%d", __FUNCTION__, name, pe->pe_pid); clicon_debug(1, "%s scheduling name: %s pid:%d op: %s", __FUNCTION__,
name, pe->pe_pid,
clicon_int2str(proc_operation_map, pe->pe_operation));
sched++; sched++;
} }
else{
clicon_debug(1, "%s name:%s op %s cancelled by wrwap", __FUNCTION__, name, clicon_int2str(proc_operation_map, op0));
}
break; /* hit break here */ break; /* hit break here */
} }
pe = NEXTQ(process_entry_t *, pe); pe = NEXTQ(process_entry_t *, pe);
@ -632,8 +687,6 @@ clixon_process_status(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){
if (pe->pe_clone)
continue; /* this may be a dying duplicate */
/* Check if running */ /* Check if running */
run = 0; run = 0;
if (pe->pe_pid && proc_op_run(pe->pe_pid, &run) < 0) if (pe->pe_pid && proc_op_run(pe->pe_pid, &run) < 0)
@ -642,8 +695,6 @@ clixon_process_status(clicon_handle h,
NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, run?"true":"false"); NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, run?"true":"false");
if (pe->pe_description) if (pe->pe_description)
cprintf(cbret, "<description xmlns=\"%s\">%s</description>", CLIXON_LIB_NS, pe->pe_description); cprintf(cbret, "<description xmlns=\"%s\">%s</description>", CLIXON_LIB_NS, pe->pe_description);
if (pe->pe_pid)
cprintf(cbret, "<pid xmlns=\"%s\">%u</pid>", CLIXON_LIB_NS, pe->pe_pid);
cprintf(cbret, "<command xmlns=\"%s\">", CLIXON_LIB_NS); cprintf(cbret, "<command xmlns=\"%s\">", CLIXON_LIB_NS);
for (i=0; i<pe->pe_argc-1; i++){ for (i=0; i<pe->pe_argc-1; i++){
if (i) if (i)
@ -651,13 +702,17 @@ clixon_process_status(clicon_handle h,
cprintf(cbret, "%s", pe->pe_argv[i]); cprintf(cbret, "%s", pe->pe_argv[i]);
} }
cprintf(cbret, "</command>"); cprintf(cbret, "</command>");
if (run && timerisset(&pe->pe_starttime)){ cprintf(cbret, "<status xmlns=\"%s\">%s</status>", CLIXON_LIB_NS,
clicon_int2str(proc_state_map, pe->pe_state));
if (timerisset(&pe->pe_starttime)){
if (time2str(pe->pe_starttime, timestr, sizeof(timestr)) < 0){ if (time2str(pe->pe_starttime, timestr, sizeof(timestr)) < 0){
clicon_err(OE_UNIX, errno, "time2str"); clicon_err(OE_UNIX, errno, "time2str");
goto done; goto done;
} }
cprintf(cbret, "<starttime xmlns=\"%s\">%s</starttime>", CLIXON_LIB_NS, timestr); cprintf(cbret, "<starttime xmlns=\"%s\">%s</starttime>", CLIXON_LIB_NS, timestr);
} }
if (pe->pe_pid)
cprintf(cbret, "<pid xmlns=\"%s\">%u</pid>", CLIXON_LIB_NS, pe->pe_pid);
cprintf(cbret, "</rpc-reply>"); cprintf(cbret, "</rpc-reply>");
break; /* hit break here */ break; /* hit break here */
} }
@ -694,7 +749,7 @@ clixon_process_start_all(clicon_handle h)
if (pe->pe_callback(h, pe, &op) < 0) if (pe->pe_callback(h, pe, &op) < 0)
goto done; goto done;
if (op == PROC_OP_START){ if (op == PROC_OP_START){
pe->pe_op = op; pe->pe_operation = op;
sched++; sched++;
} }
pe = NEXTQ(process_entry_t *, pe); pe = NEXTQ(process_entry_t *, pe);
@ -723,73 +778,90 @@ clixon_process_sched(int fd,
{ {
int retval = -1; int retval = -1;
process_entry_t *pe; process_entry_t *pe;
process_entry_t *pe1; int isrunning; /* Process is actually running */
proc_operation op;
pid_t newpid;
int run;
clicon_debug(1, "%s",__FUNCTION__); clicon_debug(1, "%s",__FUNCTION__);
if (_proc_entry_list == NULL) if (_proc_entry_list == NULL)
goto ok; goto ok;
pe = _proc_entry_list; pe = _proc_entry_list;
do { do {
clicon_debug(1, "%s name: %s pid:%d op: %s", __FUNCTION__, clicon_debug(1, "%s name: %s pid:%d %s --op:%s-->", __FUNCTION__,
pe->pe_name, pe->pe_pid, clicon_int2str(proc_operation_map, pe->pe_op)); pe->pe_name, pe->pe_pid, clicon_int2str(proc_state_map, pe->pe_state), clicon_int2str(proc_operation_map, pe->pe_operation));
/* Execute pending operations and not already exiting */ /* Execute pending operations and not already exiting */
if ((op = pe->pe_op) != PROC_OP_NONE && if (pe->pe_operation != PROC_OP_NONE){
pe->pe_exiting == 0){ switch (pe->pe_state){
/* Check if running */ case PROC_STATE_EXITING:
run = 0; break; /* only clixon_process_waitpid can change state in exiting */
if (proc_op_run(pe->pe_pid, &run) < 0) case PROC_STATE_STOPPED:
goto done; switch (pe->pe_operation){
switch (op){ case PROC_OP_RESTART: /* stopped -> restart can happen if its externall stopped */
case PROC_OP_STOP: case PROC_OP_START:
clicon_debug(1, "%s stop pid:%d", __FUNCTION__, pe->pe_pid); /* Check if actual running using kill(0) */
case PROC_OP_RESTART: isrunning = 0;
if (run){ if (proc_op_run(pe->pe_pid, &isrunning) < 0)
clicon_log(LOG_NOTICE, "Killing old process %s with pid: %d", pe->pe_name, pe->pe_pid); goto done;
kill(pe->pe_pid, SIGTERM); if (!isrunning)
/* Cant wait here because it would block the backend and terminating may involve if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0)
* some protocol handling, instead SIGCHLD is receoved and goto done;
* clixon_process_waitpid is called that for waits/reaps the dead process */ clicon_debug(1, "%s %s(%d) %s --%s--> %s", __FUNCTION__,
pe->pe_exiting = 1; pe->pe_name, pe->pe_pid,
} clicon_int2str(proc_state_map, pe->pe_state),
if (op == PROC_OP_STOP) clicon_int2str(proc_operation_map, pe->pe_operation),
clicon_int2str(proc_state_map, PROC_STATE_RUNNING)
);
pe->pe_state = PROC_STATE_RUNNING;
gettimeofday(&pe->pe_starttime, NULL);
pe->pe_operation = PROC_OP_NONE;
break; break;
if (!run){ default:
break;
}
break;
case PROC_STATE_RUNNING:
/* Check if actual running using kill(0) */
isrunning = 0;
if (proc_op_run(pe->pe_pid, &isrunning) < 0)
goto done;
switch (pe->pe_operation){
case PROC_OP_STOP:
clicon_debug(1, "%s stop pid:%d", __FUNCTION__, pe->pe_pid);
case PROC_OP_RESTART:
if (isrunning){
clicon_log(LOG_NOTICE, "Killing old process %s with pid: %d", pe->pe_name, pe->pe_pid);
kill(pe->pe_pid, SIGTERM);
/* Cant wait here because it would block the backend and terminating may involve
* some protocol handling, instead SIGCHLD is receoved and
* clixon_process_waitpid is called that for waits/reaps the dead process */
}
clicon_debug(1, "%s %s(%d) %s --%s--> %s", __FUNCTION__,
pe->pe_name, pe->pe_pid,
clicon_int2str(proc_state_map, pe->pe_state),
clicon_int2str(proc_operation_map, pe->pe_operation),
clicon_int2str(proc_state_map, PROC_STATE_EXITING)
);
pe->pe_state = PROC_STATE_EXITING; /* Keep operation stop/restart */
break;
case PROC_OP_START:
if (isrunning) /* Already runs */
break;
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0) if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0)
goto done; goto done;
clicon_debug(1, "%s %s(%d) %s --%s--> %s", __FUNCTION__,
pe->pe_name, pe->pe_pid,
clicon_int2str(proc_state_map, pe->pe_state),
clicon_int2str(proc_operation_map, pe->pe_operation),
clicon_int2str(proc_state_map, PROC_STATE_RUNNING)
);
gettimeofday(&pe->pe_starttime, NULL); gettimeofday(&pe->pe_starttime, NULL);
clicon_debug(1, "%s started pid:%d", __FUNCTION__, pe->pe_pid); pe->pe_operation = PROC_OP_NONE;
}
else {
/* This is the case where there is an existing process running.
* it was killed above but still runs and needs to be reaped */
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &newpid) < 0)
goto done;
gettimeofday(&pe->pe_starttime, NULL);
clicon_debug(1, "%s restart pid:%d -> %d", __FUNCTION__, pe->pe_pid, newpid);
/* Create a new pe */
if (clixon_process_register_dup(pe, &pe1) < 0)
goto done;
pe->pe_clone = 1; /* Delete when reaped */
pe1->pe_op = PROC_OP_NONE; /* Dont restart again */
pe1->pe_pid = newpid;
}
break;
case PROC_OP_START:
if (run) /* Already runs */
break; break;
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0) default:
goto done; break;
gettimeofday(&pe->pe_starttime, NULL); }/* switch pe_state */
clicon_debug(1, "%s started pid:%d", __FUNCTION__, pe->pe_pid);
break;
default: default:
break; break;
} } /* switch pe_state */
} }
pe->pe_op = PROC_OP_NONE;
pe = NEXTQ(process_entry_t *, pe); pe = NEXTQ(process_entry_t *, pe);
} while (pe != _proc_entry_list); } while (pe != _proc_entry_list);
ok: ok:
@ -841,18 +913,50 @@ clixon_process_waitpid(clicon_handle h)
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
pe = _proc_entry_list; pe = _proc_entry_list;
do { do {
if (pe->pe_pid != 0){ clicon_debug(1, "%s %s(%d) %s op:%s", __FUNCTION__,
clicon_debug(1, "%s waitpid(%d)", __FUNCTION__, pe->pe_pid); pe->pe_name, pe->pe_pid,
clicon_int2str(proc_state_map, pe->pe_state),
clicon_int2str(proc_operation_map, pe->pe_operation));
if (pe->pe_pid != 0
&& (pe->pe_state == PROC_STATE_RUNNING || pe->pe_state == PROC_STATE_EXITING)
// && (pe->pe_operation == PROC_OP_STOP || pe->pe_operation == PROC_OP_RESTART)
){
clicon_debug(1, "%s %s waitpid(%d)", __FUNCTION__, pe->pe_name, pe->pe_pid);
if ((wpid = waitpid(pe->pe_pid, &status, WNOHANG)) == pe->pe_pid){ if ((wpid = waitpid(pe->pe_pid, &status, WNOHANG)) == pe->pe_pid){
clicon_debug(1, "%s waitpid(%d) waited", __FUNCTION__, pe->pe_pid); clicon_debug(1, "%s waitpid(%d) waited", __FUNCTION__, pe->pe_pid);
pe->pe_exiting = 0; pe->pe_exit_status = status;
pe->pe_pid = 0; /* mark as dead */ switch (pe->pe_operation){
pe->pe_status = status; case PROC_OP_NONE: /* Spontaneous / External termination */
if (pe->pe_clone){ case PROC_OP_STOP:
/* Delete it */ clicon_debug(1, "%s %s(%d) %s --%s--> %s", __FUNCTION__,
DELQ(pe, _proc_entry_list, process_entry_t *); pe->pe_name, pe->pe_pid,
clixon_process_delete_only(pe); clicon_int2str(proc_state_map, pe->pe_state),
clicon_int2str(proc_operation_map, pe->pe_operation),
clicon_int2str(proc_state_map, PROC_STATE_STOPPED)
);
pe->pe_state = PROC_STATE_STOPPED;
pe->pe_pid = 0;
timerclear(&pe->pe_starttime);
break;
case PROC_OP_RESTART:
/* This is the case where there is an existing process running.
* it was killed above but still runs and needs to be reaped */
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0)
goto done;
gettimeofday(&pe->pe_starttime, NULL);
clicon_debug(1, "%s %s(%d) %s --%s--> %s", __FUNCTION__,
pe->pe_name, pe->pe_pid,
clicon_int2str(proc_state_map, pe->pe_state),
clicon_int2str(proc_operation_map, pe->pe_operation),
clicon_int2str(proc_state_map, PROC_STATE_RUNNING)
);
pe->pe_state = PROC_STATE_RUNNING;
gettimeofday(&pe->pe_starttime, NULL);
break;
default:
break;
} }
pe->pe_operation = PROC_OP_NONE;
break; /* pid is unique */ break; /* pid is unique */
} }
else else
@ -861,7 +965,7 @@ clixon_process_waitpid(clicon_handle h)
pe = NEXTQ(process_entry_t *, pe); pe = NEXTQ(process_entry_t *, pe);
} while (pe != _proc_entry_list); } while (pe != _proc_entry_list);
retval = 0; retval = 0;
// done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval; return retval;
} }

View file

@ -72,8 +72,12 @@ testname=
# eg logging to a file: RCLOG="-l f/www-data/restconf.log" # eg logging to a file: RCLOG="-l f/www-data/restconf.log"
: ${RCLOG:=} : ${RCLOG:=}
# Namespace: netconf base
BASENS='urn:ietf:params:xml:ns:netconf:base:1.0' BASENS='urn:ietf:params:xml:ns:netconf:base:1.0'
# Namespace: Clixon lib
LIBNS='xmlns="http://clicon.org/lib"'
# Default netconf namespace statement, typically as placed on top-level <rpc xmlns="" # Default netconf namespace statement, typically as placed on top-level <rpc xmlns=""
DEFAULTONLY="xmlns=\"$BASENS\"" DEFAULTONLY="xmlns=\"$BASENS\""
@ -230,6 +234,7 @@ fi
# error and exit, # error and exit,
# arg1: expected # arg1: expected
# arg2: errmsg[optional] # arg2: errmsg[optional]
# Assumes: $dir and $expect are set
function err(){ function err(){
echo -e "\e[31m\nError in Test$testnr [$testname]:" echo -e "\e[31m\nError in Test$testnr [$testname]:"
if [ $# -gt 0 ]; then if [ $# -gt 0 ]; then
@ -248,6 +253,20 @@ function err(){
exit -1 #$testnr exit -1 #$testnr
} }
# Dont print diffs
function err1(){
echo -e "\e[31m\nError in Test$testnr [$testname]:"
if [ $# -gt 0 ]; then
echo "Expected: $1"
echo
fi
if [ $# -gt 1 ]; then
echo "Received: $2"
fi
echo -e "\e[0m"
exit -1 #$testnr
}
# Test is previous test had valgrind errors if so quit # Test is previous test had valgrind errors if so quit
function checkvalgrind(){ function checkvalgrind(){
if [ -f $valgrindfile ]; then if [ -f $valgrindfile ]; then
@ -318,7 +337,7 @@ function start_restconf(){
echo "sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG $*" echo "sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG $*"
sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG $* & sudo -u $wwwstartuser -s $clixon_restconf $RCLOG -D $DBG $* &
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err1 "expected 0" "$?"
fi fi
} }
@ -352,7 +371,7 @@ function wait_restconf(){
while [[ $hdr != *"200 OK"* ]]; do while [[ $hdr != *"200 OK"* ]]; do
# echo "wait_restconf $i" # echo "wait_restconf $i"
if [ $i -ge $DEMLOOP ]; then if [ $i -ge $DEMLOOP ]; then
err "restconf timeout $DEMWAIT seconds" err1 "restconf timeout $DEMWAIT seconds"
fi fi
sleep $DEMSLEEP sleep $DEMSLEEP
hdr=$(curl $CURLOPTS $* $RCPROTO://localhost/restconf 2> /dev/null) hdr=$(curl $CURLOPTS $* $RCPROTO://localhost/restconf 2> /dev/null)

View file

@ -51,9 +51,11 @@ if [ "${WITH_RESTCONF}" = "native" ]; then
# Create server certs and CA # Create server certs and CA
cacerts $cakey $cacert cacerts $cakey $cacert
servercerts $cakey $cacert $srvkey $srvcert servercerts $cakey $cacert $srvkey $srvcert
USEBACKEND=true
else else
# Define default restconfig config: RESTCONFIG # Define default restconfig config: RESTCONFIG
RESTCONFIG=$(restconf_config none false) RESTCONFIG=$(restconf_config none false)
USEBACKEND=false
fi fi
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there # This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
@ -111,6 +113,7 @@ cat <<EOF > $cfg
<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>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895> <CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
<CLICON_BACKEND_RESTCONF_PROCESS>$USEBACKEND</CLICON_BACKEND_RESTCONF_PROCESS>
$RESTCONFIG <!-- only fcgi --> $RESTCONFIG <!-- only fcgi -->
</clixon-config> </clixon-config>
EOF EOF

View file

@ -7,9 +7,8 @@
# - on enable change, make the state as configured # - on enable change, make the state as configured
# - No restconf config means enable: false (extra rule) # - No restconf config means enable: false (extra rule)
# See test_restconf_netns for network namespaces # See test_restconf_netns for network namespaces
# See test_restconf_internal_cases for some special use-cases
# XXX Lots of sleeps to remove race conditions. I am sure there are others way to fix this # XXX Lots of sleeps to remove race conditions. I am sure there are others way to fix this
# XXX It is wrong to use $RESTCONF in clixon-config when using CLICON_BACKEND_RESTCONF_PROCESS
# XXX the tests should be rewritten to use running datastore
# 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
@ -19,14 +18,21 @@ APPNAME=example
cfg=$dir/conf.xml cfg=$dir/conf.xml
startupdb=$dir/startup_db startupdb=$dir/startup_db
# Define default restconfig config: RESTCONFIG # Restconf debug
RESTCONFIG=$(restconf_config none false) RESTCONFDBG=$DBG
RCPROTO=http # no ssl here
if [ "${WITH_RESTCONF}" = "fcgi" ]; then
EXTRACONF="<CLICON_FEATURE>clixon-restconf:fcgi</CLICON_FEATURE>"
else
EXTRACONF=""
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_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none --> <CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
$EXTRACONF
<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>
@ -42,7 +48,6 @@ cat <<EOF > $cfg
<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>
$RESTCONFIG
</clixon-config> </clixon-config>
EOF EOF
@ -58,65 +63,89 @@ module example {
EOF EOF
# Subroutine send a process control RPC and tricks to echo process-id returned # Subroutine send a process control RPC and tricks to echo process-id returned
# Args: # Args, expected values of:
# 1: operation # 0: ACTIVE: true or false
# 2: expectret 0: means expect pi 0 as return, else something else # 1: STATUS: stopped/running/exiting
function testrpc() # retvalue:
# $pid
function rpcstatus()
{ {
operation=$1 if [ $# -ne 2 ]; then
expectret=$2 err1 "rpcstatus: # arguments: 2" "$#"
fi
active=$1
status=$2
sleep $DEMSLEEP sleep $DEMSLEEP
new "send rpc $operation" new "send rpc status"
ret=$($clixon_netconf -qf $cfg<<EOF ret=$($clixon_netconf -qf $cfg<<EOF
$DEFAULTHELLO $DEFAULTHELLO
<rpc $DEFAULTNS> <rpc $DEFAULTNS>
<process-control xmlns="http://clicon.org/lib"> <process-control xmlns="http://clicon.org/lib">
<name>restconf</name> <name>restconf</name>
<operation>$operation</operation> <operation>status</operation>
</process-control> </process-control>
</rpc>]]>]]> </rpc>]]>]]>
EOF EOF
) )
# Check pid
# >&2 echo "ret:$ret" # debug expect="<pid xmlns=\"http://clicon.org/lib\">[0-9]*</pid>"
match=$(echo "$ret" | grep --null -Go "$expect")
expect1="<pid xmlns=\"http://clicon.org/lib\">[0-9]*</pid>"
match=$(echo "$ret" | grep --null -Go "$expect1")
# >&2 echo "match:$match" # debug
if [ -z "$match" ]; then if [ -z "$match" ]; then
pid=0 pid=0
else else
pid=$(echo "$match" | awk -F'[<>]' '{print $3}') pid=$(echo "$match" | awk -F'[<>]' '{print $3}')
fi fi
>&2 echo "pid:$pid" # debug
if [ -z "$pid" ]; then if [ -z "$pid" ]; then
err "Running process" "$ret" err "No pid return value" "$ret"
fi fi
if $active; then
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/www-data/clixon_restconf -f $cfg -D [0-9]</command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
else
# inactive, no startime or pid
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/www-data/clixon_restconf -f $cfg -D [0-9]</command><status $LIBNS>$status</status></rpc-reply>]]>]]>$"
fi
match=$(echo "$ret" | grep --null -Go "$expect")
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
}
# Subroutine send a process control RPC and tricks to echo process-id returned
# Args:
# 1: operation One of stop/start/restart
function rpcoperation()
{
operation=$1
sleep $DEMSLEEP
new "send rpc $operation"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>$operation</operation></process-control></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok xmlns=\"http://clicon.org/lib\"/></rpc-reply>]]>]]>$"
new "check restconf retvalue"
if [ $operation = "status" ]; then
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
echo "$pid" # cant use return that only uses 0-255
fi
sleep $DEMSLEEP sleep $DEMSLEEP
} }
# This test is confusing:
# The whole restconf config is in clixon-config wich binds 0.0.0.0:80 which will be the only
# config the restconf daemon ever reads.
# However, enable (and debug) flag is stored in running db but only backend will ever read that.
# It just controls how restconf is started, but thereafter the restconf daemon reads the static db in clixon-config file
new "ENABLE true" new "ENABLE true"
# First basic operation with restconf enable is true # First basic operation with restconf enable is true
cat<<EOF > $startupdb cat<<EOF > $startupdb
<${DATASTORE_TOP}> <${DATASTORE_TOP}>
<restconf xmlns="http://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
<enable>true</enable> <enable>true</enable>
<auth-type>none</auth-type>
<pretty>false</pretty>
<debug>$RESTCONFDBG</debug>
<socket>
<namespace>default</namespace>
<address>0.0.0.0</address>
<port>80</port>
<ssl>false</ssl>
</socket>
</restconf> </restconf>
</${DATASTORE_TOP}> </${DATASTORE_TOP}>
EOF EOF
@ -134,18 +163,19 @@ if [ $BE -ne 0 ]; then
new "start backend -s startup -f $cfg" new "start backend -s startup -f $cfg"
start_backend -s startup -f $cfg start_backend -s startup -f $cfg
new "wait backend"
wait_backend
fi fi
new "wait backend"
wait_backend
# For debug # For debug
#>&2 echo "curl $CURLOPTS -X POST -H \"Content-Type: application/yang-data+json\" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{\"clixon-lib:input\":{\"name\":\"restconf\",\"operation\":\"status\"}}'" #>&2 echo "curl $CURLOPTS -X POST -H \"Content-Type: application/yang-data+json\" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{\"clixon-lib:input\":{\"name\":\"restconf\",\"operation\":\"status\"}}'"
# Get pid of running process and check return xml # Get pid of running process and check return xml
new "1. Get rpc status" new "1. Get rpc status"
pid0=$(testrpc status 1) # Save pid0 rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid0";exit -1; fi pid0=$pid # Save pid0
if [ $pid0 -eq 0 ]; then err "Pid" 0; fi
new "check restconf process runnng using ps pid:$pid0" new "check restconf process runnng using ps pid:$pid0"
ps=$(ps -hp $pid0) ps=$(ps -hp $pid0)
@ -167,8 +197,9 @@ new "try restconf rpc status"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}')" 0 "HTTP/1.1 200 OK" '{"clixon-lib:output":' '"active":' '"pid":' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}')" 0 "HTTP/1.1 200 OK" '{"clixon-lib:output":' '"active":' '"pid":'
new "2. Get status" new "2. Get status"
pid1=$(testrpc status 1) rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid1";exit -1; fi pid1=$pid
if [ $pid1 -eq 0 ]; then err "pid" 0; fi
new "Check same pid" new "Check same pid"
if [ "$pid0" -ne "$pid1" ]; then if [ "$pid0" -ne "$pid1" ]; then
@ -179,29 +210,31 @@ new "try restconf rpc restart"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"restart"}}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"restart"}}')" 0 "HTTP/1.1 204 No Content"
new "3. Get status" new "3. Get status"
pid1=$(testrpc status 1) rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid1";exit -1; fi pid1=$pid
if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
new "check different pids" new "check different pids"
if [ "$pid0" -eq "$pid1" ]; then if [ "$pid0" -eq "$pid1" ]; then
err "not $pid0" err1 "not $pid0" "$pid1"
fi fi
new "4. stop restconf RPC" new "4. stop restconf RPC"
testrpc stop 0 rpcoperation stop
if [ $? -ne 0 ]; then exit -1; fi if [ $? -ne 0 ]; then exit -1; fi
new "5. Get rpc status stopped" new "5. Get rpc status stopped"
pid=$(testrpc status 0) rpcstatus false stopped
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
new "6. Start rpc again" new "6. Start rpc again"
testrpc start 0 rpcoperation start
if [ $? -ne 0 ]; then exit -1; fi if [ $? -ne 0 ]; then exit -1; fi
new "7. Get rpc status" new "7. Get rpc status"
pid3=$(testrpc status 1) rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid3";exit -1; fi pid3=$pid
if [ $pid3 -eq 0 ]; then err "Pid" 0; fi
new "check restconf process running using ps" new "check restconf process running using ps"
ps=$(ps -hp $pid3) ps=$(ps -hp $pid3)
@ -210,30 +243,32 @@ if [ -z "$ps" ]; then
fi fi
if [ $pid0 -eq $pid3 ]; then if [ $pid0 -eq $pid3 ]; then
err "A different pid" "same pid: $pid3" err1 "A different pid" "same pid: $pid3"
fi fi
new "kill restconf" new "kill restconf"
stop_restconf_pre stop_restconf_pre
new "8. start restconf RPC" new "8. start restconf RPC"
testrpc start 0 rpcoperation start
if [ $? -ne 0 ]; then exit -1; fi if [ $? -ne 0 ]; then exit -1; fi
new "9. check status RPC on" new "9. check status RPC on"
pid5=$(testrpc status 1) # Save pid5 rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid5";exit -1; fi pid5=$pid
if [ $pid5 -eq 0 ]; then err "Pid" 0; fi
new "10. restart restconf RPC" new "10. restart restconf RPC"
testrpc restart 0 rpcoperation restart
if [ $? -ne 0 ]; then exit -1; fi if [ $? -ne 0 ]; then exit -1; fi
new "11. Get restconf status rpc" new "11. Get restconf status rpc"
pid7=$(testrpc status 1) # Save pid7 rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid7";exit -1; fi pid7=$pid
if [ $pid7 -eq 0 ]; then err "Pid" 0; fi
if [ $pid5 -eq $pid7 ]; then if [ $pid5 -eq $pid7 ]; then
err "A different pid" "samepid: $pid7" err1 "A different pid" "samepid: $pid7"
fi fi
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
@ -261,14 +296,17 @@ if [ $BE -ne 0 ]; then
new "start backend -s none -f $cfg" new "start backend -s none -f $cfg"
start_backend -s none -f $cfg start_backend -s none -f $cfg
new "waiting" new "wait backend"
wait_backend wait_backend
fi fi
new "12. Get restconf (running) after restart" new "wait restconf"
pid=$(testrpc status 1) wait_restconf
if [ valgrindtest -ne 2 ]; then # XXX does not work w backend valgrind test
if [ $? -ne 0 ]; then echo "$pid"; exit -1; fi if [ $valgrindtest -ne 2 ]; then # Restart with same restconf pid does not work w backend valgrind test
new "12. Get restconf (running) after restart"
rpcstatus true running
if [ $pid -eq 0 ]; then err "Pid" 0; fi
fi fi
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
@ -283,14 +321,23 @@ if [ $BE -ne 0 ]; then
fi fi
#-------------------------- #--------------------------
# So far, restconf config enable flag has been true. Now change enable flag. # Now start with enable=false
new "ENABLE false" new "enable false"
# Second basic operation with restconf enable is false # Second basic operation with restconf enable is false
cat<<EOF > $startupdb cat<<EOF > $startupdb
<${DATASTORE_TOP}> <${DATASTORE_TOP}>
<restconf xmlns="http://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
<enable>false</enable> <enable>false</enable>
<auth-type>none</auth-type>
<pretty>false</pretty>
<debug>$RESTCONFDBG</debug>
<socket>
<namespace>default</namespace>
<address>0.0.0.0</address>
<port>80</port>
<ssl>false</ssl>
</socket>
</restconf> </restconf>
</${DATASTORE_TOP}> </${DATASTORE_TOP}>
EOF EOF
@ -307,68 +354,74 @@ if [ $BE -ne 0 ]; then
fi fi
new "start backend -s startup -f $cfg" new "start backend -s startup -f $cfg"
start_backend -s startup -f $cfg start_backend -s startup -f $cfg
new "waiting"
wait_backend
fi fi
new "wait backend"
wait_backend
new "13. check status RPC off" new "13. check status RPC off"
pid=$(testrpc status 0) rpcstatus false stopped
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
new "14. start restconf RPC" new "14. start restconf RPC (but disabled)"
testrpc start 0 rpcoperation start
if [ $? -ne 0 ]; then exit -1; fi if [ $? -ne 0 ]; then exit -1; fi
new "15. check status RPC off" new "15. check status RPC still off"
pid=$(testrpc status 0) rpcstatus false stopped
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
new "Enable restconf" new "Enable restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<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>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><default-operation>merge</default-operation><target><candidate/></target><config><restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><debug>$RESTCONFDBG</debug></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "commit enable"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
sleep $DEMSLEEP
new "16. check status RPC on" new "16. check status RPC on"
pid=$(testrpc status 1) rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi pid1=$pid
if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
# Edit a field, eg debug new "wait restconf"
new "Edit a restconf field via restconf" wait_restconf
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-restconf:restconf/debug -d '{"clixon-restconf:debug":1}' )" 0 "HTTP/1.1 201 Created"
# Edit a field, eg pretty to trigger a restart
new "Edit a restconf field via restconf" # XXX fcgi fails here
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-restconf:restconf/pretty -d '{"clixon-restconf:pretty":true}' )" 0 "HTTP/1.1 204 No Content"
new "check status RPC new pid" new "check status RPC new pid"
pid1=$(testrpc status 1) rpcstatus true running
pid2=$pid
if [ $pid2 -eq 0 ]; then err "Pid" 0; fi
if [ $? -ne 0 ]; then echo "$pid1";exit -1; fi if [ $pid1 -eq $pid2 ]; then
if [ $pid -eq $pid1 ]; then err1 "A different pid" "$pid1"
err "A different pid" "Same pid: $pid"
fi fi
sleep $DEMSLEEP new "wait restconf"
wait_restconf
new "Edit a non-restconf field via restconf" new "Edit a non-restconf field via restconf"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:val":"xyz"}' )" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:val":"xyz"}' )" 0 "HTTP/1.1 201 Created"
new "check status RPC same pid" new "17. check status RPC same pid"
pid2=$(testrpc status 1) rpcstatus true running
if [ $? -ne 0 ]; then echo "$pid2";exit -1; fi pid3=$pid
if [ $pid1 -ne $pid2 ]; then if [ $pid3 -eq 0 ]; then err "Pid" 0; fi
err "Same pid $pid1" "$pid2"
if [ $pid2 -ne $pid3 ]; then
err1 "Same pid $pid2" "$pid3"
fi fi
new "Disable restconf" new "Disable restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<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>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<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" new "commit disable"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "17. check status RPC off" new "17. check status RPC off"
pid=$(testrpc status 0) rpcstatus false stopped
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
# Negative validation checks of clixon-restconf / socket # Negative validation checks of clixon-restconf / socket
@ -400,6 +453,8 @@ endtest
# Set by restconf_config # Set by restconf_config
unset RESTCONFIG unset RESTCONFIG
unset RESTCONFDBG
unset RCPROTO
rm -rf $dir rm -rf $dir

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Send restconf rpc:s when starting from backend # Send restconf rpc:s when starting from backend
# Two specific usecases that have been problematic are tested here # Two specific usecases that have been problematic are tested here
# In comparison test_restconf_rpc.sh: # In comparison test_restconf_internal.sh:
# - uses externally started restconf, here started by backend # - uses externally started restconf, here started by backend
# - generic tests, here specific # - generic tests, here specific
# The first usecases is: empty status message # The first usecases is: empty status message
@ -26,14 +26,21 @@ cfg=$dir/conf.xml
startupdb=$dir/startup_db startupdb=$dir/startup_db
# Restconf debug # Restconf debug
RESTCONFDBG=0 RESTCONFDBG=$DBG
RCPROTO=http # no ssl here RCPROTO=http # no ssl here
if [ "${WITH_RESTCONF}" = "fcgi" ]; then
EXTRACONF="<CLICON_FEATURE>clixon-restconf:fcgi</CLICON_FEATURE>"
else
EXTRACONF=""
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_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none --> <CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
$EXTRACONF
<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>
@ -63,54 +70,54 @@ module example {
} }
EOF EOF
function testrpc() # Subroutine send a process control RPC and tricks to echo process-id returned
# Args, expected values of:
# 0: ACTIVE: true or false
# 1: STATUS: stopped/running/exiting
# retvalue:
# $pid
# See also in test_restconf_internal.sh
function rpcstatus()
{ {
operation=$1 if [ $# -ne 2 ]; then
expectret=$2 err1 "rpcstatus: # arguments: 2" "$#"
fi
active=$1
status=$2
sleep $DEMSLEEP sleep $DEMSLEEP
new "send rpc $operation" new "send rpc status"
ret=$($clixon_netconf -qf $cfg<<EOF ret=$($clixon_netconf -qf $cfg<<EOF
$DEFAULTHELLO $DEFAULTHELLO
<rpc $DEFAULTNS> <rpc $DEFAULTNS>
<process-control xmlns="http://clicon.org/lib"> <process-control xmlns="http://clicon.org/lib">
<name>restconf</name> <name>restconf</name>
<operation>$operation</operation> <operation>status</operation>
</process-control> </process-control>
</rpc>]]>]]> </rpc>]]>]]>
EOF EOF
) )
# Check pid
# >&2 echo "ret:$ret" # debug expect="<pid xmlns=\"http://clicon.org/lib\">[0-9]*</pid>"
match=$(echo "$ret" | grep --null -Go "$expect")
expect1="<pid xmlns=\"http://clicon.org/lib\">[0-9]*</pid>"
match=$(echo "$ret" | grep --null -Go "$expect1")
# >&2 echo "match:$match" # debug
if [ -z "$match" ]; then if [ -z "$match" ]; then
pid=0 pid=0
else else
pid=$(echo "$match" | awk -F'[<>]' '{print $3}') pid=$(echo "$match" | awk -F'[<>]' '{print $3}')
fi fi
# >&2 echo "pid:$pid" # debug
if [ -z "$pid" ]; then if [ -z "$pid" ]; then
err "Running process" "$ret" err "No pid return value" "$ret"
fi fi
if $active; then
new "check restconf retvalue" expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/www-data/clixon_restconf -f $cfg -D [0-9]</command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
if [ $operation = "status" ]; then else
if [ $expectret -eq 0 ]; then # inactive, no startime or pid
if [ $pid -ne 0 ]; then expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/www-data/clixon_restconf -f $cfg -D [0-9]</command><status $LIBNS>$status</status></rpc-reply>]]>]]>$"
err "No process" "$pid" fi
fi match=$(echo "$ret" | grep --null -Go "$expect")
else if [ -z "$match" ]; then
if [ $pid -eq 0 ]; then err "$expect" "$ret"
err "Running process"
fi
fi
echo "$pid" # cant use return that only uses 0-255
fi fi
sleep $DEMSLEEP
} }
# FIRST usecase # FIRST usecase
@ -127,7 +134,6 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend -s init -f $cfg" new "start backend -s init -f $cfg"
start_backend -s init -f $cfg start_backend -s init -f $cfg
fi fi
@ -142,53 +148,33 @@ RESTCONFIG1=$(cat <<EOF
EOF EOF
) )
LIBNS='xmlns="http://clicon.org/lib"' new "1. get status"
rpcstatus false stopped
new "get status 1" if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>status</operation></process-control></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><active $LIBNS>false</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS>/www-data/clixon_restconf -f $cfg -D $RESTCONFDBG</command></rpc-reply>]]>]]>$"
new "enable minimal restconf, no server" new "enable minimal restconf, no server"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RESTCONFIG1</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RESTCONFIG1</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "commit minimal server"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
# Get pid2 new "2. get status, get pid1"
new "get pid" rpcstatus true running
pid2=$(testrpc status 1) pid1=$pid
echo "pid2:$pid2" if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
new "get status 2" new "kill $pid1 externally"
ret=$($clixon_netconf -qf $cfg<<EOF sudo kill $pid1
$DEFAULTHELLO
<rpc $DEFAULTNS>
<process-control xmlns="http://clicon.org/lib">
<name>restconf</name>
<operation>status</operation>
</process-control>
</rpc>]]>]]>
EOF
)
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>true</active><description $LIBNS>Clixon RESTCONF process</description><pid $LIBNS>$pid2</pid><command $LIBNS>/www-data/clixon_restconf -f $cfg -D $RESTCONFDBG</command><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime>"
match=$(echo "$ret" | grep --null -Go "$expect")
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
# Kill it
new "kill $pid2"
sudo kill $pid2
sleep $DEMSLEEP sleep $DEMSLEEP
# Why kill it twice? # Why kill it twice? it happens in docker somethimes but is not the aim of the test
# I should really debug this,... it happens in docker somethimes but its not the aim of the test new "kill $pid1 again"
new "kill $pid2 again" sudo kill $pid1 2> /dev/null
sudo kill $pid2
sleep $DEMSLEEP sleep $DEMSLEEP
new "Check killed" new "3. get status: Check killed"
# Ensure no pid rpcstatus false stopped
testrpc status 0 > /dev/null if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
RESTCONFIG2=$(cat <<EOF RESTCONFIG2=$(cat <<EOF
<restconf xmlns="http://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
@ -196,16 +182,15 @@ RESTCONFIG2=$(cat <<EOF
</restconf> </restconf>
EOF EOF
) )
new "create a server" new "create server"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RESTCONFIG2</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RESTCONFIG2</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "commit create server"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
# 3. get status new "4. get status"
rpcstatus true running
new "get status 3" if [ $pid -eq 0 ]; then err "Pid" 0; fi
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><process-control xmlns=\"http://clicon.org/lib\"><name>restconf</name><operation>status</operation></process-control></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><active $LIBNS>true</active><description $LIBNS>Clixon RESTCONF process</description><pid $LIBNS>"
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"
@ -238,8 +223,9 @@ fi
new "wait backend" new "wait backend"
wait_backend wait_backend
new "get status 1" new "5. get status not started"
testrpc status 0 > /dev/null rpcstatus false stopped
if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
RESTCONFIG1=$(cat <<EOF RESTCONFIG1=$(cat <<EOF
<restconf xmlns="http://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
@ -255,7 +241,7 @@ EOF
new "Create server with invalid address" new "Create server with invalid address"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RESTCONFIG1</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RESTCONFIG1</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "commit invalid server"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
sleep $DEMSLEEP sleep $DEMSLEEP
@ -285,6 +271,9 @@ if [ -n "$ret" ]; then
err "No zombie process" "$ret" err "No zombie process" "$ret"
fi fi
# THIRD usecase
# NOTE this does not apply for fcgi where servers cant be "removed"
if [ "${WITH_RESTCONF}" != "fcgi" ]; then
new "3. restconf not removed" new "3. restconf not removed"
new "kill old restconf" new "kill old restconf"
@ -304,8 +293,9 @@ fi
new "wait backend" new "wait backend"
wait_backend wait_backend
new "get status 1" new "6. get status stopped"
testrpc status 0 > /dev/null rpcstatus false stopped
if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
RESTCONFIG1=$(cat <<EOF RESTCONFIG1=$(cat <<EOF
<restconf xmlns="http://clicon.org/restconf"> <restconf xmlns="http://clicon.org/restconf">
@ -325,12 +315,14 @@ new "commit create"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
# pid # pid
pid1=$(testrpc status 1) new "7. get status, get pid1"
rpcstatus true running
pid1=$pid
if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
sleep $DEMSLEEP sleep $DEMSLEEP
new "Get restconf config 1" new "Get restconf config 1"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/1.1 200 OK" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>0</debug><pretty>false</pretty><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket></restconf>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/1.1 200 OK" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>$RESTCONFDBG</debug><pretty>false</pretty><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket></restconf>"
# remove it # remove it
new "Delete server" new "Delete server"
@ -339,15 +331,21 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS xmlns:nc=\"
new "commit delete" new "commit delete"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "Get restconf config 2" # Here restconf should have been restarted with no listener, the process is up but does not
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/1.1 200 OK" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>0</debug><pretty>false</pretty></restconf>" # reply on restconf
pid2=$(testrpc status 1) new "8. get status, get different pid2"
rpcstatus true running
pid2=$pid
if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
if [ $pid1 -ne $pid2 ]; then if [ $pid1 -eq $pid2 ]; then
err "Same pid:$pid1" "$pid2" err1 "A different pid" "same pid: $pid1"
fi fi
new "Get restconf config 2: no server"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf 2>&1)" 7 "Failed to connect" "Connection refused"
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"
# Check if premature kill # Check if premature kill
@ -359,6 +357,8 @@ if [ $BE -ne 0 ]; then
stop_backend -f $cfg stop_backend -f $cfg
fi fi
fi # "${WITH_RESTCONF}" != "fcgi"
new "endtest" new "endtest"
endtest endtest

View file

@ -726,6 +726,8 @@ module clixon-config {
description description
"If set, enable process-control of restconf daemon, ie start/stop restconf "If set, enable process-control of restconf daemon, ie start/stop restconf
daemon internally from backend daemon. daemon internally from backend daemon.
Also, if set, restconf daemon queries backend for its config
if not set, restconf daemon reads its config from main config file
It uses clixon-restconf.yang for config and clixon-lib.yang for RPC It uses clixon-restconf.yang for config and clixon-lib.yang for RPC
Process control of restconf daemon is as follows: 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 start, if enable is true, start the service, if false, error or ignore it

View file

@ -184,7 +184,10 @@ module clixon-lib {
leaf active { leaf active {
description description
"True if process is running, false if not. "True if process is running, false if not.
More specifically, there is a process-id and it exists (in Linux: kill(pid,0)"; More specifically, there is a process-id and it exists (in Linux: kill(pid,0).
Note that this is actual state and status is administrative state,
which means that changing the administrative state, eg stopped->running
may not immediately switch active to true.";
type boolean; type boolean;
} }
leaf description { leaf description {
@ -196,8 +199,13 @@ module clixon-lib {
description "Start command with arguments"; description "Start command with arguments";
} }
leaf status { leaf status {
description "Exit Status as defined by waitpid() - if exited"; description
type int32; "Administrative status (except on external kill where it enters stopped
directly from running):
stopped: pid=0, No process running
running: pid set, Process started and believed to be running
exiting: pid set, Process is killed by parent but not waited for";
type string;
} }
leaf starttime { leaf starttime {
description "Time of starting process UTC"; description "Time of starting process UTC";