diff --git a/CHANGELOG.md b/CHANGELOG.md index 687ace20..16c9f027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,15 @@ ## 6.1.0 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 Users may have to change how they access the system diff --git a/lib/clixon/clixon_data.h b/lib/clixon/clixon_data.h index d0ae7dcb..e7678a02 100644 --- a/lib/clixon/clixon_data.h +++ b/lib/clixon/clixon_data.h @@ -52,7 +52,8 @@ * modified, and these changes have not been committed or rolled back. */ 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 */ 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 */ diff --git a/lib/clixon/clixon_datastore.h b/lib/clixon/clixon_datastore.h index 04c79048..bb843364 100644 --- a/lib/clixon/clixon_datastore.h +++ b/lib/clixon/clixon_datastore.h @@ -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_all(clicon_handle h, uint32_t id); 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_clear(clicon_handle h, const char *db); int xmldb_delete(clicon_handle h, const char *db); diff --git a/lib/src/clixon_datastore.c b/lib/src/clixon_datastore.c index 65bc6f57..b78cff7e 100644 --- a/lib/src/clixon_datastore.c +++ b/lib/src/clixon_datastore.c @@ -274,6 +274,7 @@ xmldb_lock(clicon_handle h, if ((de = clicon_db_elmnt_get(h, db)) != NULL) de0 = *de; de0.de_id = id; + gettimeofday(&de0.de_tv, NULL); clicon_db_elmnt_set(h, db, &de0); clicon_debug(1, "%s: locked by %u", db, id); return 0; @@ -294,6 +295,7 @@ xmldb_unlock(clicon_handle h, if ((de = clicon_db_elmnt_get(h, db)) != NULL){ de->de_id = 0; + memset(&de->de_tv, 0, sizeof(struct timeval)); clicon_db_elmnt_set(h, db, de); } return 0; @@ -323,6 +325,7 @@ xmldb_unlock_all(clicon_handle h, if ((de = clicon_db_elmnt_get(h, keys[i])) != NULL && de->de_id == id){ de->de_id = 0; + memset(&de->de_tv, 0, sizeof(struct timeval)); clicon_db_elmnt_set(h, keys[i], de); } } @@ -351,6 +354,26 @@ xmldb_islocked(clicon_handle h, 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 * @param[in] h Clicon handle * @param[in] db Database diff --git a/lib/src/clixon_netconf_monitoring.c b/lib/src/clixon_netconf_monitoring.c index 138a7dc4..34ec467d 100644 --- a/lib/src/clixon_netconf_monitoring.c +++ b/lib/src/clixon_netconf_monitoring.c @@ -56,23 +56,86 @@ #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_xml.h" +#include "clixon_yang_module.h" #include "clixon_xml_io.h" #include "clixon_netconf_lib.h" #include "clixon_options.h" #include "clixon_err.h" +#include "clixon_datastore.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, "%s", db); + if ((sid = xmldb_islocked(h, db)) > 0){ + cprintf(cb, ""); + cprintf(cb, "%u", sid); + xmldb_lock_timestamp(h, db, &tv); + if (time2str(tv, timestr, sizeof(timestr)) < 0){ + clicon_err(OE_UNIX, errno, "time2str"); + goto done; + } + cprintf(cb, "%s", timestr); + cprintf(cb, ""); + } + cprintf(cb, ""); + retval = 0; + done: + return retval; +} + +/*! Get netconf monitoring datastore 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.2 */ static int -yang_modules(clicon_handle h, - yang_stmt *yspec, - cbuf *cb) +netconf_monitoring_datastores(clicon_handle h, + yang_stmt *yspec, + cbuf *cb) +{ + int retval = -1; + + cprintf(cb, ""); + 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, ""); + 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, + cbuf *cb) { int retval = -1; yang_stmt *ym = NULL; @@ -111,7 +174,54 @@ yang_modules(clicon_handle h, 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] yspec Yang spec * @param[in] xpath XML Xpath @@ -121,13 +231,7 @@ yang_modules(clicon_handle h, * @retval -1 Error (fatal) * @retval 0 Statedata callback failed * @retval 1 OK - * 2.1 - * netconf-state - * /capabilities - * /datastores - * /schemas - * /sessions - * /statistics + * @see RFC 6022 */ int 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"); goto done; } - /* capabilities 2.1.1 */ cprintf(cb, "", NETCONF_MONITORING_NAMESPACE); if (netconf_capabilites(h, cb) < 0) goto done; - - /* datastores 2.1.2 */ - // XXX - - /* schemas 2.1.3 */ - if (yang_modules(h, yspec, cb) < 0) + if (netconf_monitoring_datastores(h, yspec, cb) < 0) + goto done; + if (netconf_monitoring_schemas(h, yspec, cb) < 0) + goto done; + if (netconf_monitoring_sessions(h, yspec, cb) < 0) + goto done; + if (netconf_monitoring_statistics(h, yspec, cb) < 0) goto done; - - /* sessions 2.1.4 */ - // XXX - - /* statistics 2.1.5 */ - // XXX - cprintf(cb, ""); if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0) goto done; diff --git a/lib/src/clixon_xml_default.c b/lib/src/clixon_xml_default.c index 42b150f6..a0e08029 100644 --- a/lib/src/clixon_xml_default.c +++ b/lib/src/clixon_xml_default.c @@ -675,7 +675,7 @@ xml_flag_state_default_value(cxobj *x, */ int xml_flag_default_value(cxobj *x, - uint16_t flag) + uint16_t flag) { yang_stmt *y; cg_var *cv; diff --git a/test/test_netconf_monitoring.sh b/test/test_netconf_monitoring.sh index 350d3535..2455ce40 100755 --- a/test/test_netconf_monitoring.sh +++ b/test/test_netconf_monitoring.sh @@ -17,7 +17,6 @@ fyangsub=$dir/clixon-sub@2022-01-01.yang cat < $cfg $cfg - ${YANG_INSTALLDIR} ${dir} $fyang @@ -69,7 +68,32 @@ new "wait backend" wait_backend new "Retrieving all state via operation" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "urn:ietf:params:netconf:base:1.0urn:ietf:params:netconf:base:1.1.*urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring.*.*" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "urn:ietf:params:netconf:base:1.0urn:ietf:params:netconf:base:1.1.*urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring.*candidaterunning.*" + +# send multiple frames +rpc=$(chunked_framing "") +rpc="${rpc} +$(chunked_framing "candidate")" + +reply=$(chunked_framing "/rpc-reply") +reply=${reply}$(chunked_framing "candidate32022-12-23T13:18:57.112204Z" "candidate[0-9]+202.*Z Operation # match bith module and sub-module