* xml-stats moved from clixon-config.yang as state data to an rpc datastatsin clixon-lib.yang

* Two new plugin callbacks added
  * ca_daemon: Called just after a server has "daemonized", ie put in background.
  * ca_trans_commit_done: Called when all plugin commits have been done.
This commit is contained in:
Olof hagsand 2020-04-23 22:58:57 +02:00
parent 65806f1ef2
commit 1c99bd6a9b
20 changed files with 290 additions and 112 deletions

View file

@ -31,10 +31,14 @@ Expected: May 2020
### API changes on existing protocol/config features (You may have have to change how you use Clixon) ### API changes on existing protocol/config features (You may have have to change how you use Clixon)
* xml-stats moved from clixon-config.yang as state data to an rpc `datastats`in clixon-lib.yang
* Stricter incoming RPC sanity checking, error messages may have changed. * Stricter incoming RPC sanity checking, error messages may have changed.
### C-API changes on existing features (you may need to change your plugin C-code) ### C-API changes on existing features (you may need to change your plugin C-code)
* Two new plugin callbacks added
* ca_daemon: Called just after a server has "daemonized", ie put in background.
* ca_trans_commit_done: Called when all plugin commits have been done.
* Length of xml vector in many structs changed from `size_t` to `int`since it is a vector size, not byte size. This includes `transaction_data_t` * Length of xml vector in many structs changed from `size_t` to `int`since it is a vector size, not byte size. This includes `transaction_data_t`
* `xml_merge()` changed to use 3-value return: 1:OK, 0:Yang failed, -1: Error * `xml_merge()` changed to use 3-value return: 1:OK, 0:Yang failed, -1: Error
* `clixon_netconf_error(category, xerr, msg, arg)` removed first argument -> `clixon_netconf_error(xerr, msg, arg)` * `clixon_netconf_error(category, xerr, msg, arg)` removed first argument -> `clixon_netconf_error(xerr, msg, arg)`

View file

@ -276,60 +276,19 @@ clixon_stats_get_db(clicon_handle h,
db_elmnt *de = NULL; db_elmnt *de = NULL;
/* This is the db cache */ /* This is the db cache */
if ((de = clicon_db_elmnt_get(h, dbname)) != NULL) if ((de = clicon_db_elmnt_get(h, dbname)) == NULL ||
xt = de->de_xml; (xt = de->de_xml) == NULL){
xml_stats(xt, &nr, &sz); cprintf(cb, "<datastore><name>%s</name><nr>0</nr><size>0</size></datastore>", dbname);
}
else{
if (xml_stats(xt, &nr, &sz) < 0)
goto done;
cprintf(cb, "<datastore><name>%s</name><nr>%" PRIu64 "</nr>" cprintf(cb, "<datastore><name>%s</name><nr>%" PRIu64 "</nr>"
"<size>%" PRIu64 "</size></datastore>", "<size>%" PRIu64 "</size></datastore>",
dbname, nr, sz); dbname, nr, sz);
retval = 0;
return retval;
}
/*! Get clixon stats
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath
* @param[in,out] xret Existing XML tree, merge x into this
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_stats_get(clicon_handle h,
yang_stmt *yspec,
char *xpath,
cvec *nsc,
cxobj **xret)
{
int retval = -1;
cbuf *cb = NULL;
uint64_t nr;
int ret;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "<clixon-stats xmlns=\"%s\">", CLIXON_CONF_NS);
nr=0;
xml_stats_global(&nr);
cprintf(cb, "<global><xmlnr>%" PRIu64 "</xmlnr></global>", nr);
clixon_stats_get_db(h, "running", cb);
clixon_stats_get_db(h, "candidate", cb);
clixon_stats_get_db(h, "startup", cb);
cprintf(cb, "</clixon-stats>");
if ((ret = clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL)) < 0)
goto done;
if (ret == 0){
clicon_err(OE_XML, EINVAL, "Internal error");
goto done;
} }
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (cb)
cbuf_free(cb);
return retval; return retval;
} }
@ -408,11 +367,6 @@ client_statedata(clicon_handle h,
if (ret == 0) if (ret == 0)
goto fail; goto fail;
} }
/* Clixon-config has a state data, if yang is present */
if (yang_find(yspec, Y_MODULE, "clixon-config") != NULL){
if (clixon_stats_get(h, yspec, xpath, nsc, xret) < 0)
goto done;
}
if ((ret = clixon_plugin_statedata(h, yspec, nsc, xpath, xret)) < 0) if ((ret = clixon_plugin_statedata(h, yspec, nsc, xpath, xret)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
@ -1441,6 +1395,41 @@ from_client_ping(clicon_handle h,
return 0; return 0;
} }
/*! Check liveness of backend daemon, just send a reply
* @param[in] h Clicon handle
* @param[in] xe Request: <rpc><xn></rpc>
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[in] arg client-entry
* @param[in] regarg User argument given at rpc_callback_register()
* @retval 0 OK
* @retval -1 Error
*/
static int
from_client_datastats(clicon_handle h,
cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
{
int retval = -1;
uint64_t nr;
cprintf(cbret, "<rpc-reply>");
nr=0;
xml_stats_global(&nr);
cprintf(cbret, "<global><xmlnr>%" PRIu64 "</xmlnr></global>", nr);
if (clixon_stats_get_db(h, "running", cbret) < 0)
goto done;
if (clixon_stats_get_db(h, "candidate", cbret) < 0)
goto done;
if (clixon_stats_get_db(h, "startup", cbret) < 0)
goto done;
cprintf(cbret, "</rpc-reply>");
retval = 0;
done:
return retval;
}
/*! Verify nacm user with peer uid credentials /*! Verify nacm user with peer uid credentials
* @param[in] mode Peer credential mode: none, exact or except * @param[in] mode Peer credential mode: none, exact or except
* @param[in] peername Peer username if any * @param[in] peername Peer username if any
@ -1797,10 +1786,13 @@ backend_rpc_init(clicon_handle h)
goto done; goto done;
/* Clixon RPC */ /* Clixon RPC */
if (rpc_callback_register(h, from_client_debug, NULL, if (rpc_callback_register(h, from_client_debug, NULL,
"http://clicon.org/lib", "debug") < 0) CLIXON_LIB_NS, "debug") < 0)
goto done; goto done;
if (rpc_callback_register(h, from_client_ping, NULL, if (rpc_callback_register(h, from_client_ping, NULL,
"http://clicon.org/lib", "ping") < 0) CLIXON_LIB_NS, "ping") < 0)
goto done;
if (rpc_callback_register(h, from_client_datastats, NULL,
CLIXON_LIB_NS, "datastats") < 0)
goto done; goto done;
retval =0; retval =0;
done: done:

View file

@ -349,6 +349,9 @@ startup_commit(clicon_handle h,
/* 8. Call plugin transaction commit callbacks */ /* 8. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0) if (plugin_transaction_commit(h, td) < 0)
goto done; goto done;
/* After commit, make a post-commit call (sure that all plugins have committed) */
if (plugin_transaction_commit_done(h, td) < 0)
goto done;
/* Clear cached trees from default values and marking */ /* Clear cached trees from default values and marking */
if (xmldb_get0_clear(h, td->td_target) < 0) if (xmldb_get0_clear(h, td->td_target) < 0)
goto done; goto done;
@ -538,6 +541,9 @@ candidate_commit(clicon_handle h,
/* 7. Call plugin transaction commit callbacks */ /* 7. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0) if (plugin_transaction_commit(h, td) < 0)
goto done; goto done;
/* After commit, make a post-commit call (sure that all plugins have committed) */
if (plugin_transaction_commit_done(h, td) < 0)
goto done;
/* Clear cached trees from default values and marking */ /* Clear cached trees from default values and marking */
if (xmldb_get0_clear(h, td->td_target) < 0) if (xmldb_get0_clear(h, td->td_target) < 0)

View file

@ -885,6 +885,10 @@ main(int argc,
} }
} }
/* Call plugin callbacks when in background and before dropped privileges */
if (clixon_plugin_daemon(h) < 0)
goto done;
/* Write pid-file */ /* Write pid-file */
if ((pid = pidfile_write(pidfile)) < 0) if ((pid = pidfile_write(pidfile)) < 0)
goto done; goto done;

View file

@ -92,6 +92,32 @@ clixon_plugin_reset(clicon_handle h,
return retval; return retval;
} }
/*! Request plugins to reset system state
* The system 'state' should be the same as the contents of running_db
* @param[in] h Clicon handle
* @param[in] db Name of database
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugin_daemon(clicon_handle h)
{
clixon_plugin *cp = NULL;
plgdaemon_t *daemonfn; /* Plugin auth */
int retval = 1;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((daemonfn = cp->cp_api.ca_daemon) == NULL)
continue;
if ((retval = daemonfn(h)) < 0) {
clicon_debug(1, "plugin_daemon() failed");
return -1;
}
break;
}
return retval;
}
/*! Go through all backend statedata callbacks and collect state data /*! Go through all backend statedata callbacks and collect state data
* This is internal system call, plugin is invoked (does not call) this function * This is internal system call, plugin is invoked (does not call) this function
* Backend plugins can register * Backend plugins can register
@ -345,6 +371,36 @@ plugin_transaction_commit(clicon_handle h,
return retval; return retval;
} }
/*! Call transaction_commit_done callbacks in all backend plugins
* @param[in] h Clicon handle
* @param[in] td Transaction data
* @retval 0 OK
* @retval -1 Error: one of the plugin callbacks returned error
* @note no revert is done
*/
int
plugin_transaction_commit_done(clicon_handle h,
transaction_data_t *td)
{
int retval = 0;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
int i=0;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
i++;
if ((fn = cp->cp_api.ca_trans_commit_done) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit_done callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
break;
}
}
return retval;
}
/*! Call transaction_end() in all plugins after a successful commit. /*! Call transaction_end() in all plugins after a successful commit.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] td Transaction data * @param[in] td Transaction data

View file

@ -2,7 +2,9 @@
* *
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.
@ -69,6 +71,8 @@ typedef struct {
*/ */
int clixon_plugin_reset(clicon_handle h, char *db); int clixon_plugin_reset(clicon_handle h, char *db);
int clixon_plugin_daemon(clicon_handle h);
int clixon_plugin_statedata(clicon_handle h, yang_stmt *yspec, cvec *nsc, int clixon_plugin_statedata(clicon_handle h, yang_stmt *yspec, cvec *nsc,
char *xpath, cxobj **xtop); char *xpath, cxobj **xtop);
transaction_data_t * transaction_new(void); transaction_data_t * transaction_new(void);
@ -78,6 +82,7 @@ int plugin_transaction_begin(clicon_handle h, transaction_data_t *td);
int plugin_transaction_validate(clicon_handle h, transaction_data_t *td); int plugin_transaction_validate(clicon_handle h, transaction_data_t *td);
int plugin_transaction_complete(clicon_handle h, transaction_data_t *td); int plugin_transaction_complete(clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit(clicon_handle h, transaction_data_t *td); int plugin_transaction_commit(clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit_done(clicon_handle h, transaction_data_t *td);
int plugin_transaction_end(clicon_handle h, transaction_data_t *td); int plugin_transaction_end(clicon_handle h, transaction_data_t *td);
int plugin_transaction_abort(clicon_handle h, transaction_data_t *td); int plugin_transaction_abort(clicon_handle h, transaction_data_t *td);

View file

@ -26,6 +26,7 @@ module clixon-example {
import iana-if-type { import iana-if-type {
prefix ianaift; prefix ianaift;
} }
/* Example interface type for tests, local callbacks, etc */ /* Example interface type for tests, local callbacks, etc */
identity eth { identity eth {
base if:interface-type; base if:interface-type;
@ -33,7 +34,20 @@ module clixon-example {
identity loopback { identity loopback {
base if:interface-type; base if:interface-type;
} }
/* Translation function example - See also example_cli */ /* Generic config data */
container table{
list parameter{
key name;
leaf name{
type string;
}
leaf value{
type string;
}
}
}
/* Translation function example - See also example_cli
* XXX use table ^instead */
container translate{ container translate{
description "dont have lists directly under top since restconf cant address list directly"; description "dont have lists directly under top since restconf cant address list directly";
list translate{ list translate{

View file

@ -46,8 +46,10 @@
/*! Clixon configuration namespace /*! Clixon configuration namespace
* Probably should be defined somewhere else or extracted from yang * Probably should be defined somewhere else or extracted from yang
* @see clixon-config.yang * @see clixon-config.yang
* @see clixon-lib.yang
*/ */
#define CLIXON_CONF_NS "http://clicon.org/config" #define CLIXON_CONF_NS "http://clicon.org/config"
#define CLIXON_LIB_NS "http://clicon.org/lib"
/* /*
* Types * Types

View file

@ -105,6 +105,12 @@ typedef int (*clicon_upgrade_cb)(
*/ */
typedef int (plgstart_t)(clicon_handle); /* Plugin start */ typedef int (plgstart_t)(clicon_handle); /* Plugin start */
/* Called just after a server has "daemonized", ie put in background.
* Backend: If daemon privileges are dropped this callback is called *before* privileges are dropped.
* If daemon is started in foreground (-F) it is still called.
*/
typedef int (plgdaemon_t)(clicon_handle); /* Plugin daemonized */
/* Called just before plugin unloaded. /* Called just before plugin unloaded.
*/ */
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
@ -211,12 +217,14 @@ struct clixon_plugin_api{
struct { /* netconf-specific */ struct { /* netconf-specific */
} cau_netconf; } cau_netconf;
struct { /* backend-specific */ struct { /* backend-specific */
plgdaemon_t *cb_daemon; /* Plugin daemonized */
plgreset_t *cb_reset; /* Reset system status */ plgreset_t *cb_reset; /* Reset system status */
plgstatedata_t *cb_statedata; /* Get state data from plugin (backend only) */ plgstatedata_t *cb_statedata; /* Get state data from plugin (backend only) */
trans_cb_t *cb_trans_begin; /* Transaction start */ trans_cb_t *cb_trans_begin; /* Transaction start */
trans_cb_t *cb_trans_validate; /* Transaction validation */ trans_cb_t *cb_trans_validate; /* Transaction validation */
trans_cb_t *cb_trans_complete; /* Transaction validation complete */ trans_cb_t *cb_trans_complete; /* Transaction validation complete */
trans_cb_t *cb_trans_commit; /* Transaction commit */ trans_cb_t *cb_trans_commit; /* Transaction commit */
trans_cb_t *cb_trans_commit_done; /* Transaction when commit done */
trans_cb_t *cb_trans_revert; /* Transaction revert */ trans_cb_t *cb_trans_revert; /* Transaction revert */
trans_cb_t *cb_trans_end; /* Transaction completed */ trans_cb_t *cb_trans_end; /* Transaction completed */
trans_cb_t *cb_trans_abort; /* Transaction aborted */ trans_cb_t *cb_trans_abort; /* Transaction aborted */
@ -229,12 +237,14 @@ struct clixon_plugin_api{
#define ca_suspend u.cau_cli.ci_suspend #define ca_suspend u.cau_cli.ci_suspend
#define ca_interrupt u.cau_cli.ci_interrupt #define ca_interrupt u.cau_cli.ci_interrupt
#define ca_auth u.cau_restconf.cr_auth #define ca_auth u.cau_restconf.cr_auth
#define ca_daemon u.cau_backend.cb_daemon
#define ca_reset u.cau_backend.cb_reset #define ca_reset u.cau_backend.cb_reset
#define ca_statedata u.cau_backend.cb_statedata #define ca_statedata u.cau_backend.cb_statedata
#define ca_trans_begin u.cau_backend.cb_trans_begin #define ca_trans_begin u.cau_backend.cb_trans_begin
#define ca_trans_validate u.cau_backend.cb_trans_validate #define ca_trans_validate u.cau_backend.cb_trans_validate
#define ca_trans_complete u.cau_backend.cb_trans_complete #define ca_trans_complete u.cau_backend.cb_trans_complete
#define ca_trans_commit u.cau_backend.cb_trans_commit #define ca_trans_commit u.cau_backend.cb_trans_commit
#define ca_trans_commit_done u.cau_backend.cb_trans_commit_done
#define ca_trans_revert u.cau_backend.cb_trans_revert #define ca_trans_revert u.cau_backend.cb_trans_revert
#define ca_trans_end u.cau_backend.cb_trans_end #define ca_trans_end u.cau_backend.cb_trans_end
#define ca_trans_abort u.cau_backend.cb_trans_abort #define ca_trans_abort u.cau_backend.cb_trans_abort

View file

@ -154,7 +154,7 @@ typedef struct clixon_xml_vec clixon_xvec; /* struct defined in clicon_xml_vec.c
*/ */
char *xml_type2str(enum cxobj_type type); char *xml_type2str(enum cxobj_type type);
int xml_stats_global(uint64_t *nr); int xml_stats_global(uint64_t *nr);
size_t xml_stats(cxobj *xt, uint64_t *nrp, size_t *szp); int xml_stats(cxobj *xt, uint64_t *nrp, size_t *szp);
char *xml_name(cxobj *xn); char *xml_name(cxobj *xn);
int xml_name_set(cxobj *xn, char *name); int xml_name_set(cxobj *xn, char *name);
char *xml_prefix(cxobj *xn); char *xml_prefix(cxobj *xn);

View file

@ -471,7 +471,7 @@ clixon_plugin_extension(clicon_handle h,
return retval; return retval;
} }
/*! Call plugingeneral-purpose datastore upgrade in all plugins /*! Call plugin general-purpose datastore upgrade in all plugins
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of datastore, eg "running", "startup" or "tmp" * @param[in] db Name of datastore, eg "running", "startup" or "tmp"

View file

@ -1011,7 +1011,10 @@ clicon_rpc_debug(clicon_handle h,
goto done; goto done;
username = clicon_username_get(h); username = clicon_username_get(h);
if ((msg = clicon_msg_encode(session_id, if ((msg = clicon_msg_encode(session_id,
"<rpc username=\"%s\"><debug xmlns=\"http://clicon.org/lib\"><level>%d</level></debug></rpc>", username?username:"", level)) == NULL) "<rpc username=\"%s\"><debug xmlns=\"%s\"><level>%d</level></debug></rpc>",
username?username:"",
CLIXON_LIB_NS,
level)) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done; goto done;

View file

@ -284,18 +284,24 @@ xml_stats_one(cxobj *x,
} }
/*! Return statistics of an XML tree recursively /*! Return statistics of an XML tree recursively
* @param[in] x XML object * @param[in] xt XML object
* @param[out] szp Size of this XML obj recursively * @param[out] szp Size of this XML obj recursively
* @retval 0 OK * @retval 0 OK
* @retval -1 Error
*/ */
size_t int
xml_stats(cxobj *xt, xml_stats(cxobj *xt,
uint64_t *nrp, uint64_t *nrp,
size_t *szp) size_t *szp)
{ {
int retval = -1;
size_t sz = 0; size_t sz = 0;
cxobj *xc; cxobj *xc;
if (xt == NULL){
clicon_err(OE_XML, EINVAL, "xml node is NULL");
goto done;
}
*nrp += 1; *nrp += 1;
xml_stats_one(xt, &sz); xml_stats_one(xt, &sz);
if (szp) if (szp)
@ -308,7 +314,9 @@ xml_stats(cxobj *xt,
*szp += sz; *szp += sz;
} }
clicon_debug(1, "%s %zu", __FUNCTION__, *szp); clicon_debug(1, "%s %zu", __FUNCTION__, *szp);
return 0; retval = 0;
done:
return retval;
} }
/* /*

View file

@ -157,8 +157,7 @@ if [ $RC -ne 0 ]; then
fi fi
new "auth get" new "auth get"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:state":{"op":["41","42","43"]}}} expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data)" 0 'HTTP/1.1 200 OK' '{"data":{"clixon-example:state":{"op":\["41","42","43"\]}'
'
new "Set x to 0" new "Set x to 0"
expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 0}' http://localhost/restconf/data/nacm-example:x)" 0 "" expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 0}' http://localhost/restconf/data/nacm-example:x)" 0 ""

View file

@ -77,12 +77,11 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r
# Should be alphabetically ordered # Should be alphabetically ordered
new "restconf get restconf/operations. RFC8040 3.3.2 (json)" new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations":{"clixon-example:client-rpc":[null],"clixon-example:empty":[null],"clixon-example:optional":[null],"clixon-example:example":[null],"clixon-lib:debug":[null],"clixon-lib:ping":[null],"ietf-netconf:get-config":[null],"ietf-netconf:edit-config":[null],"ietf-netconf:copy-config":[null],"ietf-netconf:delete-config":[null],"ietf-netconf:lock":[null],"ietf-netconf:unlock":[null],"ietf-netconf:get":[null],"ietf-netconf:close-session":[null],"ietf-netconf:kill-session":[null],"ietf-netconf:commit":[null],"ietf-netconf:discard-changes":[null],"ietf-netconf:validate":[null],"clixon-rfc5277:create-subscription":[null]}} expectpart "$(curl -siG http://localhost/restconf/operations)" 0 'HTTP/1.1 200 OK' '{"operations":{"clixon-example:client-rpc":\[null\],"clixon-example:empty":\[null\],"clixon-example:optional":\[null\],"clixon-example:example":\[null\],"clixon-lib:debug":\[null\],"clixon-lib:ping":\[null\],"clixon-lib:datastats":\[null\],"ietf-netconf:get-config":\[null\],"ietf-netconf:edit-config":\[null\],"ietf-netconf:copy-config":\[null\],"ietf-netconf:delete-config":\[null\],"ietf-netconf:lock":\[null\],"ietf-netconf:unlock":\[null\],"ietf-netconf:get":\[null\],"ietf-netconf:close-session":\[null\],"ietf-netconf:kill-session":\[null\],"ietf-netconf:commit":\[null\],"ietf-netconf:discard-changes":\[null\],"ietf-netconf:validate":\[null\],"clixon-rfc5277:create-subscription":\[null\]}}'
'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
expect='<operations><client-rpc xmlns="urn:example:clixon"/><empty xmlns="urn:example:clixon"/><optional xmlns="urn:example:clixon"/><example xmlns="urn:example:clixon"/><debug xmlns="http://clicon.org/lib"/><ping xmlns="http://clicon.org/lib"/><get-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><copy-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><delete-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><lock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><unlock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><kill-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><commit xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><discard-changes xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><validate xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"/></operations>' expect='<operations><client-rpc xmlns="urn:example:clixon"/><empty xmlns="urn:example:clixon"/><optional xmlns="urn:example:clixon"/><example xmlns="urn:example:clixon"/><debug xmlns="http://clicon.org/lib"/><ping xmlns="http://clicon.org/lib"/><datastats xmlns="http://clicon.org/lib"/><get-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><copy-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><delete-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><lock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><unlock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><kill-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><commit xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><discard-changes xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><validate xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"/></operations>'
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"

View file

@ -101,7 +101,7 @@ expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://loca
# This just catches the header and the jukebox module, the RFC has foo and bar which # This just catches the header and the jukebox module, the RFC has foo and bar which
# seems wrong to recreate # seems wrong to recreate
new "B.1.2. Retrieve the Server Module Information" new "B.1.2. Retrieve the Server Module Information"
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://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":"2019-08-13","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"clixon-rfc5277","revision":"2008-07-01","namespace":"urn:ietf:params:xml:ns:netmod:notification","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"},{"name":"ietf-inet-types","revision":"2013-07-15","namespace":"urn:ietf:params:xml:ns:yang:ietf-inet-types","conformance-type":"implement"},' expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://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-04-23","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"clixon-rfc5277","revision":"2008-07-01","namespace":"urn:ietf:params:xml:ns:netmod:notification","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"},{"name":"ietf-inet-types","revision":"2013-07-15","namespace":"urn:ietf:params:xml:ns:yang:ietf-inet-types","conformance-type":"implement"},'
new "B.1.3. Retrieve the Server Capability Information" new "B.1.3. Retrieve the Server Capability Information"
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability> expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>

View file

@ -205,16 +205,16 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<
# Now same with restconf # Now same with restconf
new "restconf edit main" new "restconf edit main"
expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"main:main":{"x":"foo","ext":"foo"}}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"main:main":{"x":"foo","ext":"foo"}}')" 0 'HTTP/1.1 201 Created'
new "restconf edit sub1" new "restconf edit sub1"
expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"main:sub1":{"x":"foo","ext1":"foo"}}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"main:sub1":{"x":"foo","ext1":"foo"}}')" 0 'HTTP/1.1 201 Created'
new "restconf edit sub2" new "restconf edit sub2"
expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"main:sub2":{"x":"foo","ext2":"foo"}}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"main:sub2":{"x":"foo","ext2":"foo"}}')" 0 'HTTP/1.1 201 Created'
new "restconf check main/sub1/sub2 contents" new "restconf check main/sub1/sub2 contents"
expectpart "$(curl -s -X GET http://localhost/restconf/data)" 0 '{"data":{"main:main":{"ext":"foo","x":"foo"},"main:sub1":{"ext1":"foo","x":"foo"},"main:sub2":{"ext2":"foo","x":"foo"}}}' expectpart "$(curl -si -X GET http://localhost/restconf/data?content=config)" 0 'HTTP/1.1 200 OK' '{"data":{"main:main":{"ext":"foo","x":"foo"},"main:sub1":{"ext1":"foo","x":"foo"},"main:sub2":{"ext2":"foo","x":"foo"}}}'
new "Kill restconf daemon" new "Kill restconf daemon"
stop_restconf stop_restconf

View file

@ -1,8 +1,9 @@
# #
# ***** BEGIN LICENSE BLOCK ***** # ***** BEGIN LICENSE BLOCK *****
# #
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren # Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# # Copyright (C) 2017-2019 Olof Hagsand
# Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)#
# This file is part of CLIXON # This file is part of CLIXON
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@ -42,7 +43,7 @@ datarootdir = @datarootdir@
YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
YANGSPECS = clixon-config@2020-02-22.yang YANGSPECS = clixon-config@2020-02-22.yang
YANGSPECS += clixon-lib@2019-08-13.yang YANGSPECS += clixon-lib@2020-04-23.yang
YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang

View file

@ -40,6 +40,8 @@ module clixon-config {
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
/* Deleted: clixon-stats state for clixon XML and memory statistics. (moved to clixon-lib)
*/
revision 2020-02-22 { revision 2020-02-22 {
description description
"Added: search index extension, "Added: search index extension,
@ -718,33 +720,4 @@ module clixon-config {
} }
} }
container clixon-stats{
config false;
description "Clixon backend statistics.";
container global{
description "Clixon global statistics";
leaf xmlnr{
description "Number of XML objects. That is 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;
}
}
}
} }

View file

@ -0,0 +1,102 @@
module clixon-lib {
yang-version 1.1;
namespace "http://clicon.org/lib";
prefix cl;
organization
"Clicon / Clixon";
contact
"Olof Hagsand <olof@hagsand.se>";
description
"Clixon Netconf extensions for communication between clients and backend.
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON
Licensed under the Apache License, Version 2.0 (the \"License\");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an \"AS IS\" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the \"GPL\"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****";
revision 2020-04-23 {
description
"Added: clixon-stats state for clixon XML and memory statistics.";
}
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";
}
rpc debug {
description "Set debug level of backend.";
input {
leaf level {
type uint32;
}
}
}
rpc ping {
description "Check aliveness of backend daemon.";
}
rpc datastats {
description "Clixon XML statistics.";
output {
container global{
description "Clixon global statistics";
leaf xmlnr{
description "Number of XML objects. That is 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;
}
}
}
}
}