diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c37aa27..8e04eb36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -250,6 +250,7 @@
example backend_main() and others if you need details.
### Minor changes
+* Added cache for modules-state RFC7895 to avoid building new XML every get call
* Renamed test/test_auth*.sh tests to test/test_nacm*.sh
* YANG keywords "action" and "belongs-to" implemented by syntactically by parser (but not proper semantics).
* clixon-config YAML file has new revision: 2018-10-21.
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index c2c370fe..04e993d0 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -335,7 +335,7 @@ client_statedata(clicon_handle h,
if ((retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
goto done;
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895"))
- if ((retval = yang_modules_state_get(h, yspec, xret)) != 0)
+ if ((retval = yang_modules_state_get(h, yspec, xpath, xret)) != 0)
goto done;
if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0)
goto done;
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 31f7eafe..457f6ff4 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -95,6 +95,7 @@ backend_terminate(clicon_handle h)
clicon_debug(1, "%s", __FUNCTION__);
if ((ss = clicon_socket_get(h)) != -1)
close(ss);
+ modules_state_cache_set(h, NULL);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
diff --git a/datastore/clixon_xmldb_text.c b/datastore/clixon_xmldb_text.c
index 277e0373..5daa6ded 100644
--- a/datastore/clixon_xmldb_text.c
+++ b/datastore/clixon_xmldb_text.c
@@ -573,7 +573,7 @@ text_get(xmldb_handle xh,
/* Add default values (if not set) */
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
-#if 1 /* debug */
+#if 0 /* debug */
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
#endif
diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h
index cefc0f6b..15bc53f4 100644
--- a/lib/clixon/clixon_options.h
+++ b/lib/clixon/clixon_options.h
@@ -202,4 +202,8 @@ int clicon_username_set(clicon_handle h, void *username);
int clicon_socket_get(clicon_handle h);
int clicon_socket_set(clicon_handle h, int s);
+/*! Set and set module state cache */
+cxobj *clicon_module_state_get(clicon_handle h);
+int clicon_module_state_set(clicon_handle h, cxobj *xms);
+
#endif /* _CLIXON_OPTIONS_H_ */
diff --git a/lib/clixon/clixon_yang_module.h b/lib/clixon/clixon_yang_module.h
index 2a26b7ba..0904d7c2 100644
--- a/lib/clixon/clixon_yang_module.h
+++ b/lib/clixon/clixon_yang_module.h
@@ -50,8 +50,10 @@
/*
* Prototypes
*/
+int modules_state_cache_set(clicon_handle h, cxobj *msx);
int yang_modules_init(clicon_handle h);
char *yang_modules_revision(clicon_handle h);
-int yang_modules_state_get(clicon_handle h, yang_spec *yspec, cxobj **xret);
+int yang_modules_state_get(clicon_handle h, yang_spec *yspec, char *xpath,
+ cxobj **xret);
#endif /* _CLIXON_YANG_MODULE_H_ */
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 24b5c309..0b016e7f 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -922,3 +922,36 @@ clicon_socket_set(clicon_handle h,
return hash_del(cdat, "socket");
return hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0;
}
+
+/*! Get module state cache
+ * @param[in] h Clicon handle
+ * @retval xms Module state cache XML tree
+ */
+cxobj *
+clicon_module_state_get(clicon_handle h)
+{
+ clicon_hash_t *cdat = clicon_data(h);
+
+ void *p;
+
+ if ((p = hash_value(cdat, "module_state_cache", NULL)) != NULL)
+ return *(cxobj **)p;
+ return NULL;
+}
+
+/*! Set module state cache
+ * @param[in] h Clicon handle
+ * @param[in] s Open socket (or -1 to close)
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+int
+clicon_module_state_set(clicon_handle h,
+ cxobj *xms)
+{
+ clicon_hash_t *cdat = clicon_data(h);
+
+ if (hash_add(cdat, "module_state_cache", &xms, sizeof(xms))==NULL)
+ return -1;
+ return 0;
+}
diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c
index 500aaf89..20dc3fad 100644
--- a/lib/src/clixon_yang_module.c
+++ b/lib/src/clixon_yang_module.c
@@ -69,6 +69,8 @@
#include "clixon_file.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
+#include "clixon_xpath_ctx.h"
+#include "clixon_xpath.h"
#include "clixon_options.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
@@ -129,9 +131,64 @@ yang_modules_revision(clicon_handle h)
return revision;
}
+
+/*! Get modules state cache associated with module_set_id, or NULL if none
+ * @param[in] h Clixon handle
+ * @param[in] msid Module set id. Cache stored per id
+ * @param[out] xms XML tree for module state cache. Note need to xml_dup it.
+ * @retval 0 OK, x is either NULL or set
+ * @retval -1 Error
+ */
+static int
+modules_state_cache_get(clicon_handle h,
+ char *msid,
+ cxobj **xms)
+{
+ cxobj *x; /* module state cache XML */
+ cxobj *xmsid; /* module state id of cache XML */
+
+ if ((x = clicon_module_state_get(h)) == NULL)
+ return 0;
+ if ((xmsid = xpath_first(x, "modules-state/module-set-id")) == NULL)
+ return 0;
+ if (strcmp(xml_body(xmsid), msid) == 0) /* return cache */
+ *xms = x;
+ return 0;
+}
+
+/*! Set modules state cache associated with msid, or NULL if none
+ * @param[in] h Clixon handle
+ * @param[in] msid Module set id. Cache stored per id
+ * @param[out] xms XML tree for module state cache. Note need to xml_dup it.
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+int
+modules_state_cache_set(clicon_handle h,
+ cxobj *msx)
+{
+ int retval = -1;
+ cxobj *x; /* module state cache XML */
+
+ if ((x = clicon_module_state_get(h)) != NULL)
+ xml_free(x);
+ clicon_module_state_set(h, NULL);
+ if (msx == NULL)
+ goto ok;
+ /* Duplicate XML tree from original. */
+ if ((x = xml_dup(msx)) == NULL)
+ goto done;
+ clicon_module_state_set(h, x);
+ ok:
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! Get modules state according to RFC 7895
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
+ * @param[in] xpath XML Xpath
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 OK
@@ -158,6 +215,7 @@ x +--ro namespace inet:uri
int
yang_modules_state_get(clicon_handle h,
yang_spec *yspec,
+ char *xpath,
cxobj **xret)
{
int retval = -1;
@@ -168,85 +226,103 @@ yang_modules_state_get(clicon_handle h,
yang_stmt *ymod; /* generic module */
yang_stmt *ys;
yang_stmt *yc;
- char *module_set_id;
+ char *msid; /* modules-set-id */
char *module = "ietf-yang-library";
-
- module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID");
- if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL &&
- (ylib = yang_find((yang_node*)yspec, Y_SUBMODULE, module)) == NULL){
- clicon_err(OE_YANG, 0, "%s not found", module);
- goto done;
- }
- if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){
- clicon_err(OE_YANG, 0, "%s yang namespace not found", module);
- goto done;
- }
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, 0, "clicon buffer");
- goto done;
- }
- cprintf(cb,"", yns->ys_argument);
- cprintf(cb,"%s", module_set_id);
+ cxobj *x1;
- ymod = NULL;
- while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
- if (ymod->ys_keyword != Y_MODULE &&
- ymod->ys_keyword != Y_SUBMODULE)
- continue;
- cprintf(cb,"");
- cprintf(cb,"%s", ymod->ys_argument);
- if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
- cprintf(cb,"%s", ys->ys_argument);
- else
- cprintf(cb,"");
- if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL)
- cprintf(cb,"%s", ys->ys_argument);
- else
- cprintf(cb,"");
- /* This follows order in rfc 7895: feature, conformance-type, submodules */
- yc = NULL;
- while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
- switch(yc->ys_keyword){
- case Y_FEATURE:
- if (yc->ys_cv && cv_bool_get(yc->ys_cv))
- cprintf(cb,"%s", yc->ys_argument);
- break;
- default:
- break;
- }
- }
- cprintf(cb, "implement");
- yc = NULL;
- while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
- switch(yc->ys_keyword){
- case Y_SUBMODULE:
- cprintf(cb,"");
- cprintf(cb,"%s", yc->ys_argument);
- if ((ys = yang_find((yang_node*)yc, Y_REVISION, NULL)) != NULL)
- cprintf(cb,"%s", ys->ys_argument);
- else
- cprintf(cb,"");
- cprintf(cb,"");
- break;
- default:
- break;
- }
- }
- cprintf(cb,"");
- }
- cprintf(cb,"");
-
- if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
- if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
- goto done;
- retval = 1;
+ msid = clicon_option_str(h, "CLICON_MODULE_SET_ID");
+ if (modules_state_cache_get(h, msid, &x) < 0)
goto done;
+ if (x != NULL){ /* Yes a cache (but no duplicate) */
+ if (xpath_first(x, "%s", xpath)){
+ if ((x1 = xml_dup(x)) == NULL)
+ goto done;
+ x = x1;
+ }
+ else
+ x = NULL;
}
- retval = netconf_trymerge(x, yspec, xret);
+ else { /* No cache -> build the tree */
+ if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL &&
+ (ylib = yang_find((yang_node*)yspec, Y_SUBMODULE, module)) == NULL){
+ clicon_err(OE_YANG, 0, "%s not found", module);
+ goto done;
+ }
+ if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){
+ clicon_err(OE_YANG, 0, "%s yang namespace not found", module);
+ goto done;
+ }
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "clicon buffer");
+ goto done;
+ }
+ cprintf(cb,"", yns->ys_argument);
+ cprintf(cb,"%s", msid);
+
+ ymod = NULL;
+ while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
+ if (ymod->ys_keyword != Y_MODULE &&
+ ymod->ys_keyword != Y_SUBMODULE)
+ continue;
+ cprintf(cb,"");
+ cprintf(cb,"%s", ymod->ys_argument);
+ if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
+ cprintf(cb,"%s", ys->ys_argument);
+ else
+ cprintf(cb,"");
+ if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL)
+ cprintf(cb,"%s", ys->ys_argument);
+ else
+ cprintf(cb,"");
+ /* This follows order in rfc 7895: feature, conformance-type, submodules */
+ yc = NULL;
+ while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
+ switch(yc->ys_keyword){
+ case Y_FEATURE:
+ if (yc->ys_cv && cv_bool_get(yc->ys_cv))
+ cprintf(cb,"%s", yc->ys_argument);
+ break;
+ default:
+ break;
+ }
+ }
+ cprintf(cb, "implement");
+ yc = NULL;
+ while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
+ switch(yc->ys_keyword){
+ case Y_SUBMODULE:
+ cprintf(cb,"");
+ cprintf(cb,"%s", yc->ys_argument);
+ if ((ys = yang_find((yang_node*)yc, Y_REVISION, NULL)) != NULL)
+ cprintf(cb,"%s", ys->ys_argument);
+ else
+ cprintf(cb,"");
+ cprintf(cb,"");
+ break;
+ default:
+ break;
+ }
+ }
+ cprintf(cb,"");
+ }
+ cprintf(cb,"");
+
+ if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
+ if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
+ goto done;
+ retval = 1;
+ goto done;
+ }
+ if (modules_state_cache_set(h, x) < 0)
+ goto done;
+ }
+ if (x && netconf_trymerge(x, yspec, xret) < 0)
+ goto done;
+ retval = 0;
done:
- if (cb)
- cbuf_free(cb);
if (x)
xml_free(x);
+ if (cb)
+ cbuf_free(cb);
return retval;
}