Added several fields to process-control status operation: active, description, command, status, starttime, pid

This commit is contained in:
Olof hagsand 2021-03-12 17:22:22 +01:00
parent 7e9a207ab2
commit 07d196dfd0
8 changed files with 144 additions and 58 deletions

View file

@ -39,7 +39,7 @@ Expected: April
* The netconf client will terminate (close the socket) if the client does not comply
* 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
* Changed: RPC process-control output to choice dependent on operation
* Changed: RPC process-control output to choice with status fields
* New clixon-config@2020-03-08.yang revision
* Added: `CLICON_NETCONF_HELLO_OPTIONAL`
@ -55,6 +55,7 @@ Developers may need to change their code
### Minor features
* Added several fields to process-control status operation: active, description, command, status, starttime, pid
* Changed signal handling
* Moved clixon-proc sigchild handling from handler to clixon_events
* The base capability has been changed to "urn:ietf:params:netconf:base:1.1" following RFC6241.

View file

@ -1584,7 +1584,6 @@ from_client_process_control(clicon_handle h,
cxobj *x;
char *name = NULL;
char *opstr = NULL;
uint32_t pid = 0;
proc_operation op = PROC_OP_NONE;
if ((x = xml_find_type(xe, NULL, "name", CX_ELMNT)) != NULL)
@ -1594,16 +1593,16 @@ from_client_process_control(clicon_handle h,
op = clixon_process_op_str2int(opstr);
}
/* Make the actual process operation (with wrap function enabled) */
if (clixon_process_operation(h, name, op, 1, &pid) < 0)
if (op == PROC_OP_STATUS){
if (clixon_process_status(h, name, cbret) < 0)
goto done;
}
else{
if (clixon_process_operation(h, name, op, 1) < 0)
goto done;
if (op == PROC_OP_STATUS)
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><status xmlns=\"%s\">%s</status><pid xmlns=\"%s\">%u</pid></rpc-reply>",
NETCONF_BASE_NAMESPACE,
CLIXON_LIB_NS, pid?"true":"false",
CLIXON_LIB_NS, pid);
else
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok xmlns=\"%s\"/></rpc-reply>",
NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS);
}
retval = 0;
done:
return retval;

View file

@ -137,6 +137,7 @@ restconf_pseudo_process_control(clicon_handle h)
argv[i++] = NULL;
assert(i==nr);
if (clixon_process_register(h, RESTCONF_PROCESS,
"Clixon RESTCONF process",
NULL /* XXX network namespace */,
restconf_rpc_wrapper,
argv, nr) < 0)
@ -196,7 +197,7 @@ restconf_pseudo_process_commit(clicon_handle h,
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?PROC_OP_START:PROC_OP_STOP, 0, NULL) < 0)
enabled?PROC_OP_START:PROC_OP_STOP, 0) < 0)
goto done;
}
else if (enabled){ /* If something changed and running, restart process */
@ -209,7 +210,7 @@ restconf_pseudo_process_commit(clicon_handle h,
* Specifically, the socket is terminated where the reply is sent, which will
* cause the curl to fail.
*/
if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0, NULL) < 0)
if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0) < 0)
goto done;
}
}

View file

@ -62,9 +62,10 @@ int clixon_proc_socket(char **argv, pid_t *pid, int *sock);
int clixon_proc_socket_close(pid_t pid, int sock);
int clixon_proc_background(char **argv, const char *netns, pid_t *pid);
proc_operation clixon_process_op_str2int(char *opstr);
int clixon_process_register(clicon_handle h, const char *name, const char *netns, proc_cb_t *callback, char **argv, int argc);
int clixon_process_register(clicon_handle h, const char *name, const char *descr, 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, const char *name, proc_operation op, const int wrapit, uint32_t *pid);
int clixon_process_operation(clicon_handle h, const char *name, proc_operation op, const int wrapit);
int clixon_process_status(clicon_handle h, const char *name, cbuf *cbret);
int clixon_process_start_all(clicon_handle h);
int clixon_process_sched_register(clicon_handle h);
int clixon_process_waitpid(clicon_handle h);

View file

@ -52,6 +52,8 @@
#include <sys/types.h>
#include <sys/time.h>
#include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_log.h"
#include "clixon_hash.h"

View file

@ -71,10 +71,14 @@
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_options.h"
#include "clixon_event.h"
#include "clixon_sig.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_netconf_lib.h"
#include "clixon_proc.h"
/*
@ -85,6 +89,7 @@
struct process_entry_t {
qelem_t pe_qelem; /* List header */
char *pe_name; /* Name of process used for internal use. Unique with exiting=0 */
char *pe_description; /* Description of service */
char *pe_netns; /* Network namespace */
char **pe_argv; /* argv with command as element 0 and NULL-terminated */
int pe_argc; /* Length of argc */
@ -93,6 +98,7 @@ struct process_entry_t {
int pe_clone; /* Duplicate when restarting, delete when reaped */
pid_t pe_status; /* Status on exit as defined in waitpid */
proc_operation pe_op; /* Operation pending? */
struct timeval pe_starttime; /* Start time */
proc_cb_t *pe_callback; /* Wrapper function, may be called from process_operation */
};
@ -336,6 +342,10 @@ clixon_process_register_dup(process_entry_t *pe0,
clicon_err(OE_DB, errno, "strdup name");
goto done;
}
if (pe0->pe_description && (pe1->pe_description = strdup(pe0->pe_description)) == NULL){
clicon_err(OE_DB, errno, "strdup name");
goto done;
}
if (pe0->pe_netns && (pe1->pe_netns = strdup(pe0->pe_netns)) == NULL){
clicon_err(OE_DB, errno, "strdup netns");
goto done;
@ -363,6 +373,7 @@ clixon_process_register_dup(process_entry_t *pe0,
*
* @param[in] h Clixon handle
* @param[in] name Process name
* @param[in] description Description of process
* @param[in] netns Namespace netspace (or NULL)
* @param[in] callback
* @param[in] argv NULL-terminated vector of vectors
@ -374,6 +385,7 @@ clixon_process_register_dup(process_entry_t *pe0,
int
clixon_process_register(clicon_handle h,
const char *name,
const char *description,
const char *netns,
proc_cb_t *callback,
char **argv,
@ -400,6 +412,10 @@ clixon_process_register(clicon_handle h,
clicon_err(OE_DB, errno, "strdup name");
goto done;
}
if (description && (pe->pe_description = strdup(description)) == NULL){
clicon_err(OE_DB, errno, "strdup description");
goto done;
}
if (netns && (pe->pe_netns = strdup(netns)) == NULL){
clicon_err(OE_DB, errno, "strdup netns");
goto done;
@ -430,6 +446,8 @@ clixon_process_delete_only(process_entry_t *pe)
if (pe->pe_name)
free(pe->pe_name);
if (pe->pe_description)
free(pe->pe_description);
if (pe->pe_netns)
free(pe->pe_netns);
if (pe->pe_argv){
@ -488,13 +506,12 @@ proc_op_run(pid_t pid0,
return retval;
}
/*! Find process operation entry given name and op and perform operation if found
/*! Find process entry given name and schedule operation
*
* @param[in] h clicon handle
* @param[in] name Name of process
* @param[in] op start, stop, restart, status
* @param[in] wrapit If set, call potential callback, if false, dont call it
* @param[out] pid >0 process# and is running / 0: not running
* @retval -1 Error
* @retval 0 OK
* @see upgrade_callback_reg_fn which registers the callbacks
@ -507,8 +524,7 @@ int
clixon_process_operation(clicon_handle h,
const char *name,
proc_operation op,
int wrapit,
uint32_t *pid)
int wrapit)
{
int retval = -1;
process_entry_t *pe;
@ -520,13 +536,6 @@ clixon_process_operation(clicon_handle h,
pe = _proc_entry_list;
do {
if (strcmp(pe->pe_name, name) == 0){
if (op == PROC_OP_STATUS){
if (pe->pe_clone)
continue; /* this may be a dying duplicate */
if (pid)
*pid = pe->pe_pid;
}
else {
/* Call wrapper function that eg changes op based on config */
if (wrapit && pe->pe_callback != NULL)
if (pe->pe_callback(h, pe, &op) < 0)
@ -538,8 +547,6 @@ clixon_process_operation(clicon_handle h,
clicon_debug(1, "%s scheduling %s pid:%d", __FUNCTION__, name, pe->pe_pid);
sched++;
}
}
break; /* hit break here */
}
pe = NEXTQ(process_entry_t *, pe);
@ -553,6 +560,66 @@ clixon_process_operation(clicon_handle h,
return retval;
}
/*! Get process status according to clixon-lib.yang
*
* @param[in] h clicon handle
* @param[in] name Name of process
* @param[out] cbret XML status string
* @retval -1 Error
* @retval 0 OK
*/
int
clixon_process_status(clicon_handle h,
const char *name,
cbuf *cbret)
{
int retval = -1;
process_entry_t *pe;
int run;
int i;
char timestr[28];
if (_proc_entry_list == NULL)
goto ok;
pe = _proc_entry_list;
do {
if (strcmp(pe->pe_name, name) == 0){
if (pe->pe_clone)
continue; /* this may be a dying duplicate */
/* Check if running */
run = 0;
if (pe->pe_pid && proc_op_run(pe->pe_pid, &run) < 0)
goto done;
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><active xmlns=\"%s\">%s</active>",
NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, run?"true":"false");
if (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);
for (i=0; i<pe->pe_argc-1; i++){
if (i)
cprintf(cbret, " ");
cprintf(cbret, "%s", pe->pe_argv[i]);
}
cprintf(cbret, "</command>");
cprintf(cbret, "<status xmlns=\"%s\">%u</status>", CLIXON_LIB_NS, pe->pe_status);
if (run && time2str(pe->pe_starttime, timestr, sizeof(timestr)) < 0)
goto done;
cprintf(cbret, "<starttime xmlns=\"%s\">%s</starttime>", CLIXON_LIB_NS, timestr);
cprintf(cbret, "</rpc-reply>");
break; /* hit break here */
}
pe = NEXTQ(process_entry_t *, pe);
} while (pe != _proc_entry_list);
ok:
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
}
/*! Go through process list and start all processes that are enabled via config wrap function
* @param[in] h Clixon handle
* Commit rules should have done this, but there are some cases such as backend -s none mode
@ -623,7 +690,6 @@ clixon_process_sched(int fd,
pe->pe_exiting == 0){
/* Check if running */
run = 0;
clicon_debug(1, "%s run: %d", __FUNCTION__, run);
if (proc_op_run(pe->pe_pid, &run) < 0)
goto done;
switch (op){
@ -642,6 +708,7 @@ clixon_process_sched(int fd,
break;
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)
@ -655,6 +722,7 @@ clixon_process_sched(int fd,
break;
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 started pid:%d", __FUNCTION__, pe->pe_pid);
break;
default:

View file

@ -76,22 +76,18 @@ $DEFAULTHELLO
EOF
)
expect1="<rpc-reply $DEFAULTNS><pid xmlns=\"http://clicon.org/lib\">"
match=$(echo "$ret" | grep --null -Go "$expect1")
if [ -z "$match" ]; then
err "$expect1" "$ret"
fi
# >&2 echo "ret:$ret" # debug
expect2="</pid></rpc-reply>]]>]]>"
match=$(echo "$ret" | grep --null -Go "$expect2")
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
err "$expect2" "$ret"
pid=0
else
pid=$(echo "$match" | awk -F'[<>]' '{print $3}')
fi
new "check rpc $operation get pid"
pid=$(echo "$ret" | awk -F'[<>]' '{print $5}')
>&2 echo "pid:$pid" # debug
if [ -z "$pid" ]; then
err "Running process" "$ret"
fi
@ -107,9 +103,9 @@ EOF
err "Running process"
fi
fi
echo "$pid" # cant use return that only uses 0-255
fi
sleep $DEMSLEEP
echo "$pid" # cant use return that only uses 0-255
}
new "ENABLE true"
@ -141,8 +137,8 @@ if [ $BE -ne 0 ]; then
fi
# 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\"}}'"
exit
#>&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
new "1. Get rpc status"
pid0=$(testrpc status 1) # Save pid0
@ -165,7 +161,7 @@ new "wait restconf"
wait_restconf
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":{"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"
pid1=$(testrpc status 1)
@ -177,7 +173,7 @@ if [ "$pid0" -ne "$pid1" ]; then
fi
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 200 OK" '{"clixon-lib:output":{"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":"restart"}}')" 0 "HTTP/1.1 204 No Content"
new "3. Get status"
pid1=$(testrpc status 1)

View file

@ -3,6 +3,9 @@ module clixon-lib {
namespace "http://clicon.org/lib";
prefix cl;
import ietf-yang-types {
prefix yang;
}
organization
"Clicon / Clixon";
@ -178,13 +181,28 @@ module clixon-lib {
case status {
description
"Output from status rpc";
leaf status {
leaf active {
description "True if process is running, false if not";
type boolean;
}
leaf description {
type string;
description "Description of process. This is a static string";
}
leaf command {
type string;
description "Start command with arguments";
}
leaf status {
description "Exit Status as defined by waitpid() - if exited";
type int32;
}
leaf starttime {
description "Time of starting process UTC";
type yang:date-and-time;
}
leaf pid {
description "Process-id of running process or 0 if not running
Value is only valid for operation status";
description "Process-id of main running process (if active)";
type uint32;
}
}