Netconf monitoring RFC 6022: datastores
This commit is contained in:
parent
1eefadfc74
commit
62b40a9397
7 changed files with 185 additions and 30 deletions
|
|
@ -41,6 +41,15 @@
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
Expected: beginning of 2023
|
Expected: beginning of 2023
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
* Netconf monitoring, part 2
|
||||||
|
* Datastores
|
||||||
|
* Remaining: sessions and statistics state
|
||||||
|
* Standards
|
||||||
|
* RFC 6022 "YANG Module for NETCONF Monitoring"
|
||||||
|
* See [Feature Request: Support RFC 6022 (NETCONF Monitoring)](https://github.com/clicon/clixon/issues/370)
|
||||||
|
|
||||||
### API changes on existing protocol/config features
|
### API changes on existing protocol/config features
|
||||||
|
|
||||||
Users may have to change how they access the system
|
Users may have to change how they access the system
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@
|
||||||
* modified, and these changes have not been committed or rolled back.
|
* modified, and these changes have not been committed or rolled back.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t de_id; /* If set, lockaed by this client/session id */
|
uint32_t de_id; /* If set, locked by this client/session id */
|
||||||
|
struct timeval de_tv; /* Timevalue */
|
||||||
cxobj *de_xml; /* cache */
|
cxobj *de_xml; /* cache */
|
||||||
int de_modified; /* Dirty since loaded/copied/committed/etc XXX:nocache? */
|
int de_modified; /* Dirty since loaded/copied/committed/etc XXX:nocache? */
|
||||||
int de_empty; /* Empty on read from file, xmldb_readfile and xmldb_put sets it */
|
int de_empty; /* Empty on read from file, xmldb_readfile and xmldb_put sets it */
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ int xmldb_lock(clicon_handle h, const char *db, uint32_t id);
|
||||||
int xmldb_unlock(clicon_handle h, const char *db);
|
int xmldb_unlock(clicon_handle h, const char *db);
|
||||||
int xmldb_unlock_all(clicon_handle h, uint32_t id);
|
int xmldb_unlock_all(clicon_handle h, uint32_t id);
|
||||||
uint32_t xmldb_islocked(clicon_handle h, const char *db);
|
uint32_t xmldb_islocked(clicon_handle h, const char *db);
|
||||||
|
int xmldb_lock_timestamp(clicon_handle h, const char *db, struct timeval *tv);
|
||||||
int xmldb_exists(clicon_handle h, const char *db);
|
int xmldb_exists(clicon_handle h, const char *db);
|
||||||
int xmldb_clear(clicon_handle h, const char *db);
|
int xmldb_clear(clicon_handle h, const char *db);
|
||||||
int xmldb_delete(clicon_handle h, const char *db);
|
int xmldb_delete(clicon_handle h, const char *db);
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,7 @@ xmldb_lock(clicon_handle h,
|
||||||
if ((de = clicon_db_elmnt_get(h, db)) != NULL)
|
if ((de = clicon_db_elmnt_get(h, db)) != NULL)
|
||||||
de0 = *de;
|
de0 = *de;
|
||||||
de0.de_id = id;
|
de0.de_id = id;
|
||||||
|
gettimeofday(&de0.de_tv, NULL);
|
||||||
clicon_db_elmnt_set(h, db, &de0);
|
clicon_db_elmnt_set(h, db, &de0);
|
||||||
clicon_debug(1, "%s: locked by %u", db, id);
|
clicon_debug(1, "%s: locked by %u", db, id);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -294,6 +295,7 @@ xmldb_unlock(clicon_handle h,
|
||||||
|
|
||||||
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
|
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
|
||||||
de->de_id = 0;
|
de->de_id = 0;
|
||||||
|
memset(&de->de_tv, 0, sizeof(struct timeval));
|
||||||
clicon_db_elmnt_set(h, db, de);
|
clicon_db_elmnt_set(h, db, de);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -323,6 +325,7 @@ xmldb_unlock_all(clicon_handle h,
|
||||||
if ((de = clicon_db_elmnt_get(h, keys[i])) != NULL &&
|
if ((de = clicon_db_elmnt_get(h, keys[i])) != NULL &&
|
||||||
de->de_id == id){
|
de->de_id == id){
|
||||||
de->de_id = 0;
|
de->de_id = 0;
|
||||||
|
memset(&de->de_tv, 0, sizeof(struct timeval));
|
||||||
clicon_db_elmnt_set(h, keys[i], de);
|
clicon_db_elmnt_set(h, keys[i], de);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -351,6 +354,26 @@ xmldb_islocked(clicon_handle h,
|
||||||
return de->de_id;
|
return de->de_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get timestamp of when database was locked
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] db Database
|
||||||
|
* @param[out] tv Timestamp
|
||||||
|
* @retval -1 No timestamp / not locked
|
||||||
|
* @retval 0 OK
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xmldb_lock_timestamp(clicon_handle h,
|
||||||
|
const char *db,
|
||||||
|
struct timeval *tv)
|
||||||
|
{
|
||||||
|
db_elmnt *de;
|
||||||
|
|
||||||
|
if ((de = clicon_db_elmnt_get(h, db)) == NULL)
|
||||||
|
return -1;
|
||||||
|
memcpy(tv, &de->de_tv, sizeof(*tv));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Check if db exists or is empty
|
/*! Check if db exists or is empty
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] db Database
|
* @param[in] db Database
|
||||||
|
|
|
||||||
|
|
@ -56,21 +56,84 @@
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_xml_io.h"
|
#include "clixon_xml_io.h"
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
|
#include "clixon_datastore.h"
|
||||||
#include "clixon_netconf_monitoring.h"
|
#include "clixon_netconf_monitoring.h"
|
||||||
|
|
||||||
/*!
|
static int
|
||||||
|
per_datastore(clicon_handle h,
|
||||||
|
cbuf *cb,
|
||||||
|
const char *db)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
uint32_t sid;
|
||||||
|
struct timeval tv = {0,};
|
||||||
|
char timestr[28];
|
||||||
|
|
||||||
|
cprintf(cb, "<datastore><name>%s</name>", db);
|
||||||
|
if ((sid = xmldb_islocked(h, db)) > 0){
|
||||||
|
cprintf(cb, "<locks>");
|
||||||
|
cprintf(cb, "<locked-by-session>%u</locked-by-session>", sid);
|
||||||
|
xmldb_lock_timestamp(h, db, &tv);
|
||||||
|
if (time2str(tv, timestr, sizeof(timestr)) < 0){
|
||||||
|
clicon_err(OE_UNIX, errno, "time2str");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "<locked-time>%s</locked-time>", timestr);
|
||||||
|
cprintf(cb, "</locks>");
|
||||||
|
}
|
||||||
|
cprintf(cb, "</datastore>");
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get netconf monitoring datastore state
|
||||||
|
*
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in,out] cb CLIgen buffer
|
* @param[in,out] cb CLIgen buffer
|
||||||
* @retval -1 Error (fatal)
|
* @retval -1 Error (fatal)
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
* @see RFC 6022 Section 2.1.2
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
yang_modules(clicon_handle h,
|
netconf_monitoring_datastores(clicon_handle h,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
cbuf *cb)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
cprintf(cb, "<datastores>");
|
||||||
|
if (per_datastore(h, cb, "running") < 0)
|
||||||
|
goto done;
|
||||||
|
if (per_datastore(h, cb, "candidate") < 0)
|
||||||
|
goto done;
|
||||||
|
if (if_feature(yspec, "ietf-netconf", "startup")){
|
||||||
|
if (per_datastore(h, cb, "startup") < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "</datastores>");
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get netconf monitoring schema state
|
||||||
|
*
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] yspec Yang spec
|
||||||
|
* @param[in,out] cb CLIgen buffer
|
||||||
|
* @retval -1 Error (fatal)
|
||||||
|
* @retval 0 OK
|
||||||
|
* @see RFC 6022 Section 2.1.3
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
netconf_monitoring_schemas(clicon_handle h,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
|
|
@ -111,7 +174,54 @@ yang_modules(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Get modules state according to RFC 7895
|
/*! Get netconf monitoring sessions state
|
||||||
|
*
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] yspec Yang spec
|
||||||
|
* @param[in,out] cb CLIgen buffer
|
||||||
|
* @retval -1 Error (fatal)
|
||||||
|
* @retval 0 OK
|
||||||
|
* @see RFC 6022 Section 2.1.4
|
||||||
|
* XXX: NYI
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
netconf_monitoring_sessions(clicon_handle h,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
cbuf *cb)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
retval = 0;
|
||||||
|
//done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get netconf monitoring statistics state
|
||||||
|
*
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] yspec Yang spec
|
||||||
|
* @param[in,out] cb CLIgen buffer
|
||||||
|
* @retval -1 Error (fatal)
|
||||||
|
* @retval 0 OK
|
||||||
|
* @see RFC 6022 Section 2.1.5
|
||||||
|
* XXX: NYI
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
netconf_monitoring_statistics(clicon_handle h,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
cbuf *cb)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
retval = 0;
|
||||||
|
//done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get netconf monitoring state
|
||||||
|
*
|
||||||
|
* Netconf monitoring state is:
|
||||||
|
* capabilities, datastores, schemas, sessions, statistics
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in] xpath XML Xpath
|
* @param[in] xpath XML Xpath
|
||||||
|
|
@ -121,13 +231,7 @@ yang_modules(clicon_handle h,
|
||||||
* @retval -1 Error (fatal)
|
* @retval -1 Error (fatal)
|
||||||
* @retval 0 Statedata callback failed
|
* @retval 0 Statedata callback failed
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* 2.1
|
* @see RFC 6022
|
||||||
* netconf-state
|
|
||||||
* /capabilities
|
|
||||||
* /datastores
|
|
||||||
* /schemas
|
|
||||||
* /sessions
|
|
||||||
* /statistics
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
netconf_monitoring_state_get(clicon_handle h,
|
netconf_monitoring_state_get(clicon_handle h,
|
||||||
|
|
@ -144,24 +248,17 @@ netconf_monitoring_state_get(clicon_handle h,
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* capabilities 2.1.1 */
|
|
||||||
cprintf(cb, "<netconf-state xmlns=\"%s\">", NETCONF_MONITORING_NAMESPACE);
|
cprintf(cb, "<netconf-state xmlns=\"%s\">", NETCONF_MONITORING_NAMESPACE);
|
||||||
if (netconf_capabilites(h, cb) < 0)
|
if (netconf_capabilites(h, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (netconf_monitoring_datastores(h, yspec, cb) < 0)
|
||||||
/* datastores 2.1.2 */
|
goto done;
|
||||||
// XXX
|
if (netconf_monitoring_schemas(h, yspec, cb) < 0)
|
||||||
|
goto done;
|
||||||
/* schemas 2.1.3 */
|
if (netconf_monitoring_sessions(h, yspec, cb) < 0)
|
||||||
if (yang_modules(h, yspec, cb) < 0)
|
goto done;
|
||||||
|
if (netconf_monitoring_statistics(h, yspec, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* sessions 2.1.4 */
|
|
||||||
// XXX
|
|
||||||
|
|
||||||
/* statistics 2.1.5 */
|
|
||||||
// XXX
|
|
||||||
|
|
||||||
cprintf(cb, "</netconf-state>");
|
cprintf(cb, "</netconf-state>");
|
||||||
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
|
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ fyangsub=$dir/clixon-sub@2022-01-01.yang
|
||||||
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>
|
||||||
<!-- The following are errors in ietf-l3vpn-ntw@2022-02-14.yang -->
|
|
||||||
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_DIR>${dir}</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>${dir}</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
|
|
@ -69,7 +68,32 @@ new "wait backend"
|
||||||
wait_backend
|
wait_backend
|
||||||
|
|
||||||
new "Retrieving all state via <get> operation"
|
new "Retrieving all state via <get> operation"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get/></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability>.*<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</capability>.*</capabilities><schemas>.*</schemas></netconf-state></data></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get/></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability>.*<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</capability>.*</capabilities><datastores><datastore><name>candidate</name></datastore><datastore><name>running</name></datastore></datastores><schemas>.*</schemas></netconf-state></data></rpc-reply>"
|
||||||
|
|
||||||
|
# send multiple frames
|
||||||
|
rpc=$(chunked_framing "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>")
|
||||||
|
rpc="${rpc}
|
||||||
|
$(chunked_framing "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><datastores><datastore><name>candidate</name></datastore></datastores></netconf-state></filter></get></rpc>")"
|
||||||
|
|
||||||
|
reply=$(chunked_framing "<rpc-reply $DEFAULTNS><ok/>/rpc-reply")
|
||||||
|
reply=${reply}$(chunked_framing "<rpc-reply $DEFAULTNS><data><netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"><datastores><datastore><name>candidate</name><locks><locked-by-session>3</locked-by-session><locked-time>2022-12-23T13:18:57.112204Z</locked-time></locks></datastore></datastores></netconf-state></data></rpc-reply")
|
||||||
|
|
||||||
|
new "Get databases with lock"
|
||||||
|
ret=$($clixon_netconf -q -f $cfg <<EOF
|
||||||
|
$DEFAULTHELLO$rpc
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
r=$?
|
||||||
|
if [ $r -ne 0 ]; then
|
||||||
|
err "0" "$r"
|
||||||
|
fi
|
||||||
|
for expect in "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><datastores><datastore><name>candidate</name><locks><locked-by-session>[0-9]+</locked-by-session><locked-time>202.*Z</locked-time></locks></datastore></datastores></netconf-state></data></rpc-reply"; do
|
||||||
|
new "expect:$expect"
|
||||||
|
match=`echo $ret | grep --null -Eo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# 4.1. Retrieving Schema List via <get> Operation
|
# 4.1. Retrieving Schema List via <get> Operation
|
||||||
# match bith module and sub-module
|
# match bith module and sub-module
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue