From a5220805b1de29d6a31d1653d10309f9ca30387f Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sun, 5 Feb 2023 11:09:06 +0100 Subject: [PATCH] New plugin callback: `ca_yang_patch` - for modifying existing YANG modules C-API: Added `spec` parameter to `xml2xpath()` --- CHANGELOG.md | 5 ++ apps/backend/backend_client.c | 5 +- lib/clixon/clixon_plugin.h | 20 ++++- lib/clixon/clixon_xml_map.h | 1 - lib/clixon/clixon_xpath.h | 1 + lib/clixon/clixon_yang_parse_lib.h | 2 +- lib/src/clixon_datastore_write.c | 15 +++- lib/src/clixon_netconf_lib.c | 6 +- lib/src/clixon_plugin.c | 59 ++++++++++++ lib/src/clixon_xml_bind.c | 67 ++++++++------ lib/src/clixon_xml_map.c | 134 --------------------------- lib/src/clixon_xpath.c | 140 +++++++++++++++++++++++++++++ lib/src/clixon_yang_parse_lib.c | 15 ++-- lib/src/clixon_yang_schema_mount.c | 37 ++++---- util/clixon_util_json.c | 2 +- util/clixon_util_xpath.c | 2 +- 16 files changed, 318 insertions(+), 193 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab6c3c3..750a42a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Expected: beginning of 2023 * Only schema-ref=inline, not shared-schema * Only presence containers can be mount-points * New plugin callback: `ca_yang_mount` + * To specify which YANG modules should be mounted * Standards: RFC 8528 * To enable configure with `--enable-yang-schema-mount` * Netconf monitoring RFC 6022 @@ -86,6 +87,7 @@ Developers may need to change their code * Added netconf ssh subsystem * Renamed from `clixon` built in `docker/base` * C-API + * Added `spec` parameter to `xml2xpath()`, default 0 * Added `clicon_handle` parameter to all `xml_bind_*` calls * All calls to `clicon_log_xml()` changed to new function `clicon_debug_xml()` * Changed type of `veclen` parameter to `size_t` in `xpath_vec_flag()` @@ -94,6 +96,9 @@ Developers may need to change their code ### Minor features +* New plugin callbacks + * `ca_yang_mount` - see the RFC 8528 support + * `ca_yang_patch` - for modifying existing YANG modules * Changed debug levels in `clicon_debug()` to be based on maskable flags: * Added flag names: `CLIXON_DBG_*` * Added maskable flags that can be combined when debugging: diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index b2867873..28bca705 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -485,7 +485,8 @@ from_client_edit_config(clicon_handle h, /* yang spec may be set to anyxml by ingress yang check,...*/ if (xml_spec(xc) != NULL) xml_spec_set(xc, NULL); - /* Populate XML with Yang spec (why not do this in parser?) + /* Populate XML with Yang spec. Binding is done in from_client_msg only frm an RPC perspective, + * where is ANYDATA */ if ((ret = xml_bind_yang(h, xc, YB_MODULE, yspec, &xret)) < 0) goto done; @@ -1560,7 +1561,7 @@ from_client_msg(clicon_handle h, * 2. The backend has restarted and the client uses an old op_id */ if (op_id != 0 && ce->ce_id != op_id){ - clicon_debug(1, "%s out-of-order sequence op-id:%u ce_id:%u", __FUNCTION__, op_id, ce->ce_id); + clicon_debug(1, "%s Warning: incoming session-id:%u does not match socket ce_id:%u", __FUNCTION__, op_id, ce->ce_id); } /* Check for empty frame (no mesaages), return empty message, not clear from RFC what to do */ if (xml_child_nr_type(xt, CX_ELMNT) == 0){ diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index 7f25161a..06214a26 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -289,6 +289,19 @@ typedef int (datastore_upgrade_t)(clicon_handle h, const char *db, cxobj *xt, mo */ typedef int (yang_mount_t)(clicon_handle h, cxobj *xt, cxobj **yanglib); +/*! YANG module patch + * + * Given a parsed YANG module, give the ability to patch it before import recursion, + * grouping/uses checks, augments, etc + * Can be useful if YANG in some way needs modification. + * Deviations could be used as alternative (probably better) + * @param[in] h Clixon handle + * @param[in] ymod YANG module + * @retval 0 OK + * @retval -1 Error + */ +typedef int (yang_patch_t)(clicon_handle h, yang_stmt *ymod); + /*! Startup status for use in startup-callback * Note that for STARTUP_ERR and STARTUP_INVALID, running runs in failsafe mode * and startup contains the erroneous or invalid database. @@ -315,6 +328,8 @@ struct clixon_plugin_api{ plgstart_t *ca_start; /* Plugin start */ plgexit_t *ca_exit; /* Plugin exit */ plgextension_t *ca_extension; /* Yang extension/unknown handler */ + yang_mount_t *ca_yang_mount; /* RFC 8528 schema mount */ + yang_patch_t *ca_yang_patch; /* Patch yang after parse */ union { struct { /* cli-specific */ cli_prompthook_t *ci_prompt; /* Prompt hook */ @@ -342,7 +357,6 @@ struct clixon_plugin_api{ trans_cb_t *cb_trans_end; /* Transaction completed */ trans_cb_t *cb_trans_abort; /* Transaction aborted */ datastore_upgrade_t *cb_datastore_upgrade; /* General-purpose datastore upgrade */ - yang_mount_t *cb_yang_mount; /* RFC 8528 schema mount */ } cau_backend; } u; }; @@ -365,7 +379,6 @@ struct clixon_plugin_api{ #define ca_trans_end u.cau_backend.cb_trans_end #define ca_trans_abort u.cau_backend.cb_trans_abort #define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade -#define ca_yang_mount u.cau_backend.cb_yang_mount /* * Macros @@ -444,6 +457,9 @@ int clixon_plugin_datastore_upgrade_all(clicon_handle h, const char *db, cxobj * int clixon_plugin_yang_mount_one(clixon_plugin_t *cp, clicon_handle h, cxobj *xt, cxobj **yanglib); int clixon_plugin_yang_mount_all(clicon_handle h, cxobj *xt, cxobj **yanglib); +int clixon_plugin_yang_patch_one(clixon_plugin_t *cp, clicon_handle h, yang_stmt *ymod); +int clixon_plugin_yang_patch_all(clicon_handle h, yang_stmt *ymod); + /* rpc callback API */ int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, const char *ns, const char *name); int rpc_callback_call(clicon_handle h, cxobj *xe, void *arg, int *nrp, cbuf *cbret); diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 95a0b22f..af6b3dbb 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -63,7 +63,6 @@ int xml_tree_prune_flags(cxobj *xt, int flags, int mask); int xml_namespace_change(cxobj *x, char *ns, char *prefix); int xml_sanity(cxobj *x, void *arg); int xml_non_config_data(cxobj *xt, cxobj **xerr); -int xml2xpath(cxobj *x, cvec *nsc, char **xpath); int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p); int assign_namespace_body(cxobj *x0, cxobj *x1); int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index b615e71f..5445af96 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -150,5 +150,6 @@ int xpath_vec(cxobj *xcur, cvec *nsc, const char *xpformat, cxobj ***vec, siz int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1, cbuf **cbreason); int xpath_count(cxobj *xcur, cvec *nsc, const char *xpath, uint32_t *count); +int xml2xpath(cxobj *x, cvec *nsc, int spec, char **xpath); #endif /* _CLIXON_XPATH_H */ diff --git a/lib/clixon/clixon_yang_parse_lib.h b/lib/clixon/clixon_yang_parse_lib.h index 9ffcabdc..724f71c0 100644 --- a/lib/clixon/clixon_yang_parse_lib.h +++ b/lib/clixon/clixon_yang_parse_lib.h @@ -53,7 +53,7 @@ */ yang_stmt *yang_parse_file(FILE *fp, const char *name, yang_stmt *ysp); int yang_file_find_match(clicon_handle h, const char *module, const char *revision, cbuf *fbuf); -yang_stmt *yang_parse_filename(const char *filename, yang_stmt *ysp); +yang_stmt *yang_parse_filename(clicon_handle h, const char *filename, yang_stmt *ysp); yang_stmt *yang_parse_module(clicon_handle h, const char *module, const char *revision, yang_stmt *yspec, char *origname); int yang_parse_post(clicon_handle h, yang_stmt *yspec, int modmin); int yang_spec_parse_module(clicon_handle h, const char *module, diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 80b36868..92745b09 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -487,7 +487,11 @@ text_modify(clicon_handle h, char *createstr = NULL; yang_stmt *yrestype = NULL; char *restype; - +#ifdef CLIXON_YANG_SCHEMA_MOUNT + int ismount = 0; + yang_stmt *mount_yspec = NULL; +#endif + if (x1 == NULL){ clicon_err(OE_XML, EINVAL, "x1 is missing"); goto done; @@ -908,6 +912,15 @@ text_modify(clicon_handle h, yc = xml_spec(x1c); #endif } +#ifdef CLIXON_YANG_SCHEMA_MOUNT + /* Check if xc is unresolved mountpoint, ie no yang mount binding yet */ + if ((ismount = xml_yang_mount_get(x1c, &mount_yspec)) < 0) + goto done; + if (ismount && mount_yspec == NULL){ + ret = 1; + } + else +#endif if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t, yc, op, username, xnacm, permit, cbret)) < 0) diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index d63a9410..8a426f8f 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -1045,7 +1045,7 @@ netconf_missing_choice_xml(cxobj **xret, if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; /* error-path: Path to the element with the missing choice. */ - if (xml2xpath(x, NULL, &path) < 0) + if (xml2xpath(x, NULL, 0, &path) < 0) goto done; if (xml_chardata_encode(&encpath, "%s", path) < 0) goto done; @@ -1407,7 +1407,7 @@ netconf_data_not_unique_xml(cxobj **xret, if (cvec_len(cvk)){ if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL) goto done; - if (xml2xpath(x, NULL, &path) < 0) + if (xml2xpath(x, NULL, 0, &path) < 0) goto done; if (xml_chardata_encode(&encpath, "%s", path) < 0) goto done; @@ -1465,7 +1465,7 @@ netconf_minmax_elements_xml(cxobj **xret, if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; if (xml_parent(xp)){ /* Dont include root, eg */ - if (xml2xpath(xp, NULL, &path) < 0) + if (xml2xpath(xp, NULL, 0, &path) < 0) goto done; if (xml_chardata_encode(&encpath, "%s", path) < 0) goto done; diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index a585c680..df93a3d2 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -1069,6 +1069,65 @@ clixon_plugin_yang_mount_all(clicon_handle h, return retval; } +/*! Call plugin YANG schema patch + * + + * @param[in] cp Plugin handle + * @param[in] h Clixon handle + * @param[in] ymod YANG module + * @retval 0 OK + * @retval -1 Error + */ +int +clixon_plugin_yang_patch_one(clixon_plugin_t *cp, + clicon_handle h, + yang_stmt *ymod) +{ + int retval = -1; + yang_patch_t *fn; + void *wh = NULL; + + if ((fn = cp->cp_api.ca_yang_patch) != NULL){ + wh = NULL; + if (plugin_context_check(h, &wh, cp->cp_name, __FUNCTION__) < 0) + goto done; + if (fn(h, ymod) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: Yang patch callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; + } + if (plugin_context_check(h, &wh, cp->cp_name, __FUNCTION__) < 0) + goto done; + } + retval = 0; + done: + return retval; +} + +/*! Call plugin YANG schema patch in all plugins. + * + * @param[in] h Clixon handle + * @param[in] ymod YANG module + * @retval 0 OK + * @retval -1 Error + */ +int +clixon_plugin_yang_patch_all(clicon_handle h, + yang_stmt *ymod) +{ + int retval = -1; + clixon_plugin_t *cp = NULL; + + while ((cp = clixon_plugin_each(h, cp)) != NULL) { + if (clixon_plugin_yang_patch_one(cp, h, ymod) < 0) + goto done; + } + retval = 0; + done: + return retval; +} + /*-------------------------------------------------------------------- * RPC callbacks for both client/frontend and backend plugins. */ diff --git a/lib/src/clixon_xml_bind.c b/lib/src/clixon_xml_bind.c index fc39afec..b4fa3453 100644 --- a/lib/src/clixon_xml_bind.c +++ b/lib/src/clixon_xml_bind.c @@ -375,10 +375,10 @@ populate_self_top(cxobj *xt, */ int xml_bind_yang(clicon_handle h, - cxobj *xt, - yang_bind yb, - yang_stmt *yspec, - cxobj **xerr) + cxobj *xt, + yang_bind yb, + yang_stmt *yspec, + cxobj **xerr) { int retval = -1; cxobj *xc; /* xml child */ @@ -414,11 +414,11 @@ xml_bind_yang(clicon_handle h, */ static int xml_bind_yang0_opt(clicon_handle h, - cxobj *xt, - yang_bind yb, - yang_stmt *yspec, - cxobj *xsibling, - cxobj **xerr) + cxobj *xt, + yang_bind yb, + yang_stmt *yspec, + cxobj *xsibling, + cxobj **xerr) { int retval = -1; cxobj *xc; /* xml child */ @@ -531,10 +531,10 @@ xml_bind_yang0_opt(clicon_handle h, */ int xml_bind_yang0(clicon_handle h, - cxobj *xt, - yang_bind yb, - yang_stmt *yspec, - cxobj **xerr) + cxobj *xt, + yang_bind yb, + yang_stmt *yspec, + cxobj **xerr) { int retval = -1; cxobj *xc; /* xml child */ @@ -579,14 +579,21 @@ xml_bind_yang0(clicon_handle h, } /*! RPC-specific - * @param[in] h Clixon handle (sometimes NULL) + * + * @param[in] h Clixon handle + * @param[in] xn XML action node + * @param[in] yspec Yang spec + * @param[out] xerr Reason for failure, or NULL + * @retval 1 OK yang assignment made + * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set + * @retval -1 Error */ static int xml_bind_yang_rpc_rpc(clicon_handle h, - cxobj *x, - yang_stmt *yrpc, - char *rpcname, - cxobj **xerr) + cxobj *x, + yang_stmt *yrpc, + char *rpcname, + cxobj **xerr) { int retval = -1; cbuf *cb = NULL; @@ -637,14 +644,20 @@ xml_bind_yang_rpc_rpc(clicon_handle h, * Find the innermost container or list containing an XML element that carries the name of the * defined action. * Only one action can be invoked in one rpc - * @param[in] h Clixon handle (sometimes NULL) + * @param[in] h Clixon handle + * @param[in] xn XML action node + * @param[in] yspec Yang spec + * @param[out] xerr Reason for failure, or NULL + * @retval 1 OK yang assignment made + * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set + * @retval -1 Error * XXX if not more action, consider folding into calling function */ static int xml_bind_yang_rpc_action(clicon_handle h, - cxobj *xn, - yang_stmt *yspec, - cxobj **xerr) + cxobj *xn, + yang_stmt *yspec, + cxobj **xerr) { int retval = -1; int ret; @@ -671,7 +684,7 @@ xml_bind_yang_rpc_action(clicon_handle h, /*! Find yang spec association of XML node for incoming RPC starting with * * Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang - * @param[in] h Clixon handle (sometimes NULL) + * @param[in] h Clixon handle * @param[in] xrpc XML rpc node * @param[in] yspec Yang spec * @param[out] xerr Reason for failure, or NULL @@ -818,10 +831,10 @@ xml_bind_yang_rpc(clicon_handle h, */ int xml_bind_yang_rpc_reply(clicon_handle h, - cxobj *xrpc, - char *name, - yang_stmt *yspec, - cxobj **xerr) + cxobj *xrpc, + char *name, + yang_stmt *yspec, + cxobj **xerr) { int retval = -1; yang_stmt *yrpc = NULL; /* yang node */ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 819a891e..646c7bfe 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -732,140 +732,6 @@ xml_non_config_data(cxobj *xt, return retval; } -/*! Given an XML node, build an xpath recursively to root, internal function - * @param[in] x XML object - * @param[in] nsc Namespace context - * @param[out] cb XPath string as cbuf. - * @retval 0 OK - * @retval -1 Error. eg XML malformed - */ -static int -xml2xpath1(cxobj *x, - cvec *nsc, - cbuf *cb) -{ - int retval = -1; - cxobj *xp; - yang_stmt *y = NULL; - cvec *cvk = NULL; /* vector of index keys */ - cg_var *cvi; - char *keyname; - cxobj *xkey; - cxobj *xb; - char *b; - enum rfc_6020 keyword; - char *prefix = NULL; - char *namespace; - - if ((xp = xml_parent(x)) == NULL) - goto ok; - if (xml2xpath1(xp, nsc, cb) < 0) - goto done; - if (nsc){ - if (xml2ns(x, xml_prefix(x), &namespace) < 0) - goto done; - if (namespace){ - if (xml_nsctx_get_prefix(nsc, namespace, &prefix) == 0) - ; /* maybe NULL? */ - } - else - prefix = xml_prefix(x); /* maybe NULL? */ - } - else - prefix = xml_prefix(x); - /* XXX: sometimes there should be a /, sometimes not */ - cprintf(cb, "/"); - if (prefix) - cprintf(cb, "%s:", prefix); - cprintf(cb, "%s", xml_name(x)); - if ((y = xml_spec(x)) != NULL){ - keyword = yang_keyword_get(y); - switch (keyword){ - case Y_LEAF_LIST: - if ((b = xml_body(x)) != NULL) - cprintf(cb, "[.=\"%s\"]", b); - else - cprintf(cb, "[.=\"\"]"); - break; - case Y_LIST: - cvk = yang_cvec_get(y); - cvi = NULL; - while ((cvi = cvec_each(cvk, cvi)) != NULL) { - keyname = cv_string_get(cvi); - if ((xkey = xml_find(x, keyname)) == NULL) - goto done; /* No key in xml */ - if ((xb = xml_find(x, keyname)) == NULL) - goto done; - b = xml_body(xb); - cprintf(cb, "["); - if (prefix) - cprintf(cb, "%s:", prefix); - cprintf(cb, "%s=\"%s\"]", keyname, b?b:""); - } - break; - default: - break; - } - } - ok: - retval = 0; - done: - return retval; -} - -/*! Given an XML node, build an xpath to root - * - * Creates an XPath from an XML node with some limitations, see notes below. - * The prefixes used are from the given namespace context if any, otherwise the native prefixes are used, if any. - * Note that this means that prefixes may be translated such as if the XML namespace mapping is different than the once used - * in the XML. - * Therefore, if nsc is "canonical", the returned xpath is also "canonical", even though the XML is not. - * @param[in] x XML object - * @param[in] nsc Namespace context - * @param[out] xpath Malloced xpath string. Need to free() after use - * @retval 0 OK - * @retval -1 Error. (eg XML malformed) - * @code - * char *xpath = NULL; - * cxobj *x; - * ... x is inside an xml tree ... - * if (xml2xpath(x, nsc, &xpath) < 0) - * err; - * free(xpath); - * @endcode - * @note x needs to be bound to YANG, see eg xml_bind_yang() - */ -int -xml2xpath(cxobj *x, - cvec *nsc, - char **xpathp) -{ - int retval = -1; - cbuf *cb; - char *xpath = NULL; - - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if (xml2xpath1(x, nsc, cb) < 0) - goto done; - /* XXX: see xpath in test statement,.. */ - xpath = cbuf_get(cb); - if (xpathp){ - if ((*xpathp = strdup(xpath)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; - } - xpath = NULL; - } - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - /*! Check if the module tree x is in is assigned right XML namespace, assign if not * @param[in] x XML node *(0. You should probably find the XML root and apply this function to that.) diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 1a122f56..d5598bf6 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -1164,3 +1164,143 @@ xpath_count(cxobj *xcur, ctx_free(xc); return retval; } + +/*! Given an XML node, build an xpath recursively to root, internal function + * @param[in] x XML object + * @param[in] nsc Namespace context + * @param[in] spec If set, recursively continue only to root without spec + * @param[out] cb XPath string as cbuf. + * @retval 0 OK + * @retval -1 Error. eg XML malformed + */ +static int +xml2xpath1(cxobj *x, + cvec *nsc, + int spec, + cbuf *cb) +{ + int retval = -1; + cxobj *xp; + yang_stmt *y = NULL; + cvec *cvk = NULL; /* vector of index keys */ + cg_var *cvi; + char *keyname; + cxobj *xkey; + cxobj *xb; + char *b; + enum rfc_6020 keyword; + char *prefix = NULL; + char *namespace; + + if ((xp = xml_parent(x)) == NULL) + goto ok; + if (spec && xml_spec(x) == NULL) + goto ok; + if (xml2xpath1(xp, nsc, spec, cb) < 0) + goto done; + if (nsc){ + if (xml2ns(x, xml_prefix(x), &namespace) < 0) + goto done; + if (namespace){ + if (xml_nsctx_get_prefix(nsc, namespace, &prefix) == 0) + ; /* maybe NULL? */ + } + else + prefix = xml_prefix(x); /* maybe NULL? */ + } + else + prefix = xml_prefix(x); + /* XXX: sometimes there should be a /, sometimes not */ + cprintf(cb, "/"); + if (prefix) + cprintf(cb, "%s:", prefix); + cprintf(cb, "%s", xml_name(x)); + if ((y = xml_spec(x)) != NULL){ + keyword = yang_keyword_get(y); + switch (keyword){ + case Y_LEAF_LIST: + if ((b = xml_body(x)) != NULL) + cprintf(cb, "[.=\"%s\"]", b); + else + cprintf(cb, "[.=\"\"]"); + break; + case Y_LIST: + cvk = yang_cvec_get(y); + cvi = NULL; + while ((cvi = cvec_each(cvk, cvi)) != NULL) { + keyname = cv_string_get(cvi); + if ((xkey = xml_find(x, keyname)) == NULL) + goto done; /* No key in xml */ + if ((xb = xml_find(x, keyname)) == NULL) + goto done; + b = xml_body(xb); + cprintf(cb, "["); + if (prefix) + cprintf(cb, "%s:", prefix); + cprintf(cb, "%s=\"%s\"]", keyname, b?b:""); + } + break; + default: + break; + } + } + ok: + retval = 0; + done: + return retval; +} + +/*! Given an XML node, build an xpath to root + * + * Creates an XPath from an XML node with some limitations, see notes below. + * The prefixes used are from the given namespace context if any, otherwise the native prefixes are used, if any. + * Note that this means that prefixes may be translated such as if the XML namespace mapping is different than the once used + * in the XML. + * Therefore, if nsc is "canonical", the returned xpath is also "canonical", even though the XML is not. + * @param[in] x XML object + * @param[in] nsc Namespace context + * @param[in] spec If set, recursively continue only to root without spec + * @param[out] xpath Malloced xpath string. Need to free() after use + * @retval 0 OK + * @retval -1 Error. (eg XML malformed) + * @code + * char *xpath = NULL; + * cxobj *x; + * ... x is inside an xml tree ... + * if (xml2xpath(x, nsc, &xpath) < 0) + * err; + * free(xpath); + * @endcode + * @note x needs to be bound to YANG, see eg xml_bind_yang() + */ +int +xml2xpath(cxobj *x, + cvec *nsc, + int spec, + char **xpathp) +{ + int retval = -1; + cbuf *cb; + char *xpath = NULL; + + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (xml2xpath1(x, nsc, spec, cb) < 0) + goto done; + /* XXX: see xpath in test statement,.. */ + xpath = cbuf_get(cb); + if (xpathp){ + if ((*xpathp = strdup(xpath)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + xpath = NULL; + } + retval = 0; + done: + if (cb) + cbuf_free(cb); + return retval; +} diff --git a/lib/src/clixon_yang_parse_lib.c b/lib/src/clixon_yang_parse_lib.c index 249d522b..f3942917 100644 --- a/lib/src/clixon_yang_parse_lib.c +++ b/lib/src/clixon_yang_parse_lib.c @@ -1013,6 +1013,7 @@ done: /*! Open a file, read into a string and invoke yang parsing * * Similar to clicon_yang_str(), just read a file first + * @param[in] h Clixon handle (can be NULL, but then no callbacks) * @param[in] filename Name of file * @param[in] yspec Yang specification. Should have been created by caller using yspec_new * @retval ymod Top-level yang (sub)module @@ -1022,8 +1023,9 @@ done: * See top of file for diagram of calling order */ yang_stmt * -yang_parse_filename(const char *filename, - yang_stmt *yspec) +yang_parse_filename(clicon_handle h, + const char *filename, + yang_stmt *yspec) { yang_stmt *ymod = NULL; FILE *fp = NULL; @@ -1040,6 +1042,9 @@ yang_parse_filename(const char *filename, } if ((ymod = yang_parse_file(fp, filename, yspec)) < 0) goto done; + /* YANG patch hook */ + if (h && clixon_plugin_yang_patch_all(h, ymod) < 0) + goto done; done: if (fp) fclose(fp); @@ -1097,7 +1102,7 @@ yang_parse_module(clicon_handle h, goto done; } filename = cbuf_get(fbuf); - if ((ymod = yang_parse_filename(filename, yspec)) == NULL) + if ((ymod = yang_parse_filename(h, filename, yspec)) == NULL) goto done; /* Sanity check that requested module name matches loaded module * If this does not match, the filename and containing module do not match @@ -1648,7 +1653,7 @@ yang_spec_parse_file(clicon_handle h, *index(base, '@') = '\0'; if (yang_find(yspec, Y_MODULE, base) != NULL) goto ok; - if (yang_parse_filename(filename, yspec) == NULL) + if (yang_parse_filename(h, filename, yspec) == NULL) goto done; if (yang_parse_post(h, yspec, modmin) < 0) goto done; @@ -1752,7 +1757,7 @@ yang_spec_load_dir(clicon_handle h, } /* Create full filename */ snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name); - if ((ym = yang_parse_filename(filename, yspec)) == NULL) + if ((ym = yang_parse_filename(h, filename, yspec)) == NULL) goto done; revm = 0; if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL) diff --git a/lib/src/clixon_yang_schema_mount.c b/lib/src/clixon_yang_schema_mount.c index d1dc6316..3ee11648 100644 --- a/lib/src/clixon_yang_schema_mount.c +++ b/lib/src/clixon_yang_schema_mount.c @@ -56,7 +56,6 @@ #include #include #include -#include #include /* cligen */ @@ -141,7 +140,7 @@ yang_schema_mount_point(yang_stmt *y) * @retval -1 Error */ int -xml_yang_mount_get(cxobj *x, +xml_yang_mount_get(cxobj *xt, yang_stmt **yspec) { int retval = -1; @@ -152,16 +151,16 @@ xml_yang_mount_get(cxobj *x, char *xpath = NULL; // XXX free it int ret; - if ((y = xml_spec(x)) == NULL) + if ((y = xml_spec(xt)) == NULL) goto fail; if ((ret = yang_schema_mount_point(y)) < 0) goto done; if (ret == 0) goto fail; - // XXX + // XXX hardcoded prefix: yangmnt if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL) goto ok; - if (xml2xpath(x, NULL, &xpath) < 0) + if (xml2xpath(xt, NULL, 1, &xpath) < 0) goto done; if ((cvv = yang_cvec_get(yu)) == NULL) goto ok; @@ -203,12 +202,11 @@ xml_yang_mount_set(cxobj *x, (yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){ goto done; } - if (xml2xpath(x, NULL, &xpath) < 0) + if (xml2xpath(x, NULL, 0, &xpath) < 0) goto done; if ((cvv = yang_cvec_get(yu)) != NULL && (cv = cvec_find(cvv, xpath)) != NULL && (yspec0 = cv_void_get(cv)) != NULL){ - assert(0); ys_free(yspec0); cv_void_set(cv, NULL); } @@ -222,7 +220,9 @@ xml_yang_mount_set(cxobj *x, } /*! Free all yspec yang-mounts - * @aparm[in] cvv Cligen-variable vector containing xpath -> yspec mapping + * + * @param[in] cvv Cligen-variable vector containing xpath -> yspec mapping + * @retval 0 OK */ int xml_yang_mount_freeall(cvec *cvv) @@ -243,10 +243,10 @@ xml_yang_mount_freeall(cvec *cvv) * * @param[in] x XML node * @param[in] arg cvec, if match add node - * @retval -1 Error, aborted at first error encounter, return -1 to end user - * @retval 0 OK, continue - * @retval 1 Abort, dont continue with others, return 1 to end user * @retval 2 Locally abort this subtree, continue with others + * @retval 1 Abort, dont continue with others, return 1 to end user + * @retval 0 OK, continue + * @retval -1 Error, aborted at first error encounter, return -1 to end user */ static int find_schema_mounts(cxobj *x, @@ -277,7 +277,7 @@ find_schema_mounts(cxobj *x, * * Brute force: traverse whole XML, match all x that have ymount as yspec * Add yang-library state for all x - * @param[in] h Clicon handle + * @param[in] h Clixon handle * @param[in] xpath XML Xpath * @param[in] nsc XML Namespace context for xpath * @param[in,out] xret Existing XML tree, merge x into this @@ -352,7 +352,7 @@ yang_schema_mount_statedata_yanglib(clicon_handle h, /*! Get schema mount-point state according to RFC 8528 * - * @param[in] h Clicon handle + * @param[in] h Clixon handle * @param[in] yspec Yang spec * @param[in] xpath XML Xpath * @param[in] nsc XML Namespace context for xpath @@ -437,7 +437,12 @@ yang_schema_mount_statedata(clicon_handle h, goto done; } -/*! Get yanglib, parse it and mount it +/*! Get yanglib from user plugin callback, parse it and mount it + * + * @param[in] h Clixon handle + * @param[in] xt + * @retval 0 OK + * @retval -1 Error */ int yang_schema_yanglib_parse_mount(clicon_handle h, @@ -461,6 +466,7 @@ yang_schema_yanglib_parse_mount(clicon_handle h, goto anydata; if (xml_yang_mount_set(xt, yspec) < 0) goto done; + yspec = NULL; retval = 1; done: if (yspec) @@ -474,7 +480,8 @@ yang_schema_yanglib_parse_mount(clicon_handle h, } /*! Check if XML nod is mount-point and return matching YANG child - * @param[in] h Clicon handle + * + * @param[in] h Clixon handle * @param[in] x1 XML node * @param[in] x1c A child of x1 * @param[out] yc YANG child diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index 25d76406..b9a9e8a5 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -132,7 +132,7 @@ main(int argc, if (yang_filename){ if ((yspec = yspec_new()) == NULL) goto done; - if (yang_parse_filename(yang_filename, yspec) == NULL){ + if (yang_parse_filename(NULL, yang_filename, yspec) == NULL){ fprintf(stderr, "yang parse error %s\n", clicon_err_reason); return -1; } diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 8484f3c9..da88f01b 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -383,7 +383,7 @@ main(int argc, char *xpathi = NULL; for (i=0; ixc_size; i++){ xi = xc->xc_nodeset[i]; - if (xml2xpath(xi, nsc, &xpathi) < 0) + if (xml2xpath(xi, nsc, 0, &xpathi) < 0) goto done; fprintf(stdout, "Inverse: %s\n", xpathi); if (xpathi){