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