From 3c332b689fd1fcea117278a1d03b9ed6db1c12e4 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 21 Feb 2019 15:06:20 +0100 Subject: [PATCH] Added cache for modules-state RFC7895 to avoid building new XML every get call --- CHANGELOG.md | 1 + apps/backend/backend_client.c | 2 +- apps/backend/backend_main.c | 1 + datastore/clixon_xmldb_text.c | 2 +- lib/clixon/clixon_options.h | 4 + lib/clixon/clixon_yang_module.h | 4 +- lib/src/clixon_options.c | 33 +++++ lib/src/clixon_yang_module.c | 222 +++++++++++++++++++++----------- 8 files changed, 193 insertions(+), 76 deletions(-) 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; }