diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bbbac90..850eb04d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ Expected: April * Each peer MUST send at least the base NETCONF capability, "urn:ietf:params:netconf:base:1.1" (or 1.0 for RFC 4741) * 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 * New clixon-config@2020-03-08.yang revision * Added: `CLICON_NETCONF_HELLO_OPTIONAL` diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 41326a24..9a7d4fa3 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1596,8 +1596,14 @@ from_client_process_control(clicon_handle h, /* Make the actual process operation (with wrap function enabled) */ if (clixon_process_operation(h, name, op, 1, &pid) < 0) goto done; - cprintf(cbret, "%u", - NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, pid); + if (op == PROC_OP_STATUS) + cprintf(cbret, "%s%u", + NETCONF_BASE_NAMESPACE, + CLIXON_LIB_NS, pid?"true":"false", + CLIXON_LIB_NS, pid); + else + cprintf(cbret, "", + NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS); retval = 0; done: return retval; diff --git a/doc/DEVELOP.md b/doc/DEVELOP.md index 97bc60d0..21867f0f 100644 --- a/doc/DEVELOP.md +++ b/doc/DEVELOP.md @@ -68,10 +68,19 @@ How to debug CFLAGS="-g -Wall" INSTALLFLAGS="" ./configure ``` -### Set backend debug flag using curl +### Set backend debug using curl -curl -Ssik -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/operations/clixon-lib:debug -d '{"clixon-lib:input":{"level":1}}' +Set backend debug using rpc +curl -Ssik -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/operations/clixon-lib:debug -d '{"clixon-lib:input":{"level":1}}' +### Set restconf debug using curl + +Only if using clixon-restconf.yang + +curl -Ssik -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-rstconf:restconf/debug -d '{"clixon-restconf:debug":{"level":1}}' + +Get restconf daemon status +curl -Ssik -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}' ### Make your own simplified yang and configuration file. ``` diff --git a/lib/src/clixon_proc.c b/lib/src/clixon_proc.c index 96273f41..455ddd11 100644 --- a/lib/src/clixon_proc.c +++ b/lib/src/clixon_proc.c @@ -494,10 +494,14 @@ proc_op_run(pid_t pid0, * @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] status true if process is running / false if not running on entry + * @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 + * @note operations are not made directly but postponed by a scheduling the actions. + * This is not really necessary for all operations (like start) but made for all + * for reducing complexity of code. + * @see clixon_process_sched where operations are actually executed */ int clixon_process_operation(clicon_handle h, @@ -516,19 +520,26 @@ clixon_process_operation(clicon_handle h, pe = _proc_entry_list; do { if (strcmp(pe->pe_name, name) == 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; - 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){ - pe->pe_op = op; - clicon_debug(1, "%s scheduling %s pid:%d", __FUNCTION__, name, pe->pe_pid); - sched++; + 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) + 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){ + pe->pe_op = op; + clicon_debug(1, "%s scheduling %s pid:%d", __FUNCTION__, name, pe->pe_pid); + sched++; + } + } - if (pid) - *pid = pe->pe_pid; break; /* hit break here */ } pe = NEXTQ(process_entry_t *, pe); @@ -587,6 +598,7 @@ clixon_process_start_all(clicon_handle h) * (2) edit changes or rpc restart especially of restconf where you may saw of your arm and terminate * return socket. * A special complexity is restarting processes, where the old is killed, but state must be kept until it is reaped + * @see clixon_process_waitpid where killed/restarted processes are "reaped" */ static int clixon_process_sched(int fd, diff --git a/test/config.sh.in b/test/config.sh.in index 53b7b3e5..bd213d7f 100755 --- a/test/config.sh.in +++ b/test/config.sh.in @@ -66,3 +66,8 @@ CLIXON_VERSION=@CLIXON_VERSION@ # see also DATASTORE_TOP_SYMBOL DATASTORE_TOP="config" +# clixon yang revisions occuring in tests +CLIXON_LIB_REV="2021-03-08" +CLIXON_CONFIG_REV="2021-03-08" +CLIXON_EXAMPLE_REV="2020-12-01" + diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 99382cdd..5f09b10e 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -25,7 +25,8 @@ cfg=$dir/conf.xml # clixon-example and clixon-restconf is used in the test, need local copy # This is a kludge: look in src otherwise assume it is installed in /usr/local/share # Note that revisions may change and may need to be updated -y=clixon-example@2020-12-01.yang +y="clixon-example@${CLIXON_EXAMPLE_REV}.yang" + if [ -d ${TOP_SRCDIR}/example/main/$y ]; then cp ${TOP_SRCDIR}/example/main/$y $dir/ else @@ -193,7 +194,7 @@ function 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-30","' + 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\":\"${CLIXON_EXAMPLE_REV}\",\"namespace\":\"urn:example:clixon\",\"conformance-type\":\"implement\"},{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\"" 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" diff --git a/test/test_restconf_jukebox.sh b/test/test_restconf_jukebox.sh index 898c9629..fccff480 100755 --- a/test/test_restconf_jukebox.sh +++ b/test/test_restconf_jukebox.sh @@ -104,7 +104,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-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"}' +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\":\"${CLIXON_LIB_REV}\",\"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 e3dcd849..5a7718ba 100755 --- a/test/test_restconf_rpc.sh +++ b/test/test_restconf_rpc.sh @@ -141,8 +141,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\"}}'" - +>&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 # Get pid of running process and check return xml new "1. Get rpc status" pid0=$(testrpc status 1) # Save pid0 diff --git a/test/test_upgrade_quit.sh b/test/test_upgrade_quit.sh index 95524531..3e6c5c7e 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-30http://clicon.org/lib' +MODSTATE1="0clixon-lib${CLIXON_LIB_REV}http://clicon.org/lib" MODSTATE2='interfaces2018-02-20urn:example:interfaces' diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index 448ddecb..6578514a 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -42,7 +42,7 @@ datarootdir = @datarootdir@ YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANGSPECS = clixon-config@2021-03-08.yang -YANGSPECS += clixon-lib@2020-12-30.yang +YANGSPECS += clixon-lib@2021-03-08.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-restconf@2020-12-30.yang diff --git a/yang/clixon/clixon-lib@2021-03-08.yang b/yang/clixon/clixon-lib@2021-03-08.yang new file mode 100644 index 00000000..9bdd90b0 --- /dev/null +++ b/yang/clixon/clixon-lib@2021-03-08.yang @@ -0,0 +1,201 @@ +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-2021 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 2021-03-08 { + description + "Changed: RPC process-control output to choice dependent on operation"; + } + 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 process 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 { + choice result { + case status { + description + "Output from status rpc"; + leaf status { + description "True if process is running, false if not"; + type boolean; + } + leaf pid { + description "Process-id of running process or 0 if not running + Value is only valid for operation status"; + type uint32; + } + } + case other { + description + "Output from start/stop/restart rpc"; + leaf ok { + type empty; + } + } + } + } + } +}