New plugin callback: ca_yang_patch - for modifying existing YANG modules

C-API: Added `spec` parameter to `xml2xpath()`
This commit is contained in:
Olof Hagsand 2023-02-05 11:09:06 +01:00
parent 48a0fb9968
commit a5220805b1
16 changed files with 318 additions and 193 deletions

View file

@ -49,6 +49,7 @@ Expected: beginning of 2023
* Only schema-ref=inline, not shared-schema * Only schema-ref=inline, not shared-schema
* Only presence containers can be mount-points * Only presence containers can be mount-points
* New plugin callback: `ca_yang_mount` * New plugin callback: `ca_yang_mount`
* To specify which YANG modules should be mounted
* Standards: RFC 8528 * Standards: RFC 8528
* To enable configure with `--enable-yang-schema-mount` * To enable configure with `--enable-yang-schema-mount`
* Netconf monitoring RFC 6022 * Netconf monitoring RFC 6022
@ -86,6 +87,7 @@ Developers may need to change their code
* Added netconf ssh subsystem * Added netconf ssh subsystem
* Renamed from `clixon` built in `docker/base` * Renamed from `clixon` built in `docker/base`
* C-API * C-API
* Added `spec` parameter to `xml2xpath()`, default 0
* Added `clicon_handle` parameter to all `xml_bind_*` calls * Added `clicon_handle` parameter to all `xml_bind_*` calls
* All calls to `clicon_log_xml()` changed to new function `clicon_debug_xml()` * 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()` * 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 ### 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: * Changed debug levels in `clicon_debug()` to be based on maskable flags:
* Added flag names: `CLIXON_DBG_*` * Added flag names: `CLIXON_DBG_*`
* Added maskable flags that can be combined when debugging: * Added maskable flags that can be combined when debugging:

View file

@ -485,7 +485,8 @@ from_client_edit_config(clicon_handle h,
/* <config> yang spec may be set to anyxml by ingress yang check,...*/ /* <config> yang spec may be set to anyxml by ingress yang check,...*/
if (xml_spec(xc) != NULL) if (xml_spec(xc) != NULL)
xml_spec_set(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 <config> is ANYDATA
*/ */
if ((ret = xml_bind_yang(h, xc, YB_MODULE, yspec, &xret)) < 0) if ((ret = xml_bind_yang(h, xc, YB_MODULE, yspec, &xret)) < 0)
goto done; 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 * 2. The backend has restarted and the client uses an old op_id
*/ */
if (op_id != 0 && ce->ce_id != 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 */ /* 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){ if (xml_child_nr_type(xt, CX_ELMNT) == 0){

View file

@ -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); 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 /*! Startup status for use in startup-callback
* Note that for STARTUP_ERR and STARTUP_INVALID, running runs in failsafe mode * Note that for STARTUP_ERR and STARTUP_INVALID, running runs in failsafe mode
* and startup contains the erroneous or invalid database. * and startup contains the erroneous or invalid database.
@ -315,6 +328,8 @@ struct clixon_plugin_api{
plgstart_t *ca_start; /* Plugin start */ plgstart_t *ca_start; /* Plugin start */
plgexit_t *ca_exit; /* Plugin exit */ plgexit_t *ca_exit; /* Plugin exit */
plgextension_t *ca_extension; /* Yang extension/unknown handler */ 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 { union {
struct { /* cli-specific */ struct { /* cli-specific */
cli_prompthook_t *ci_prompt; /* Prompt hook */ 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_end; /* Transaction completed */
trans_cb_t *cb_trans_abort; /* Transaction aborted */ trans_cb_t *cb_trans_abort; /* Transaction aborted */
datastore_upgrade_t *cb_datastore_upgrade; /* General-purpose datastore upgrade */ datastore_upgrade_t *cb_datastore_upgrade; /* General-purpose datastore upgrade */
yang_mount_t *cb_yang_mount; /* RFC 8528 schema mount */
} cau_backend; } cau_backend;
} u; } u;
}; };
@ -365,7 +379,6 @@ struct clixon_plugin_api{
#define ca_trans_end u.cau_backend.cb_trans_end #define ca_trans_end u.cau_backend.cb_trans_end
#define ca_trans_abort u.cau_backend.cb_trans_abort #define ca_trans_abort u.cau_backend.cb_trans_abort
#define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade #define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade
#define ca_yang_mount u.cau_backend.cb_yang_mount
/* /*
* Macros * 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_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_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 */ /* 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_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); int rpc_callback_call(clicon_handle h, cxobj *xe, void *arg, int *nrp, cbuf *cbret);

View file

@ -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_namespace_change(cxobj *x, char *ns, char *prefix);
int xml_sanity(cxobj *x, void *arg); int xml_sanity(cxobj *x, void *arg);
int xml_non_config_data(cxobj *xt, cxobj **xerr); 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_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, cxobj *x1); int assign_namespace_body(cxobj *x0, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);

View file

@ -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 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 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 */ #endif /* _CLIXON_XPATH_H */

View file

@ -53,7 +53,7 @@
*/ */
yang_stmt *yang_parse_file(FILE *fp, const char *name, yang_stmt *ysp); 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); 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); 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_parse_post(clicon_handle h, yang_stmt *yspec, int modmin);
int yang_spec_parse_module(clicon_handle h, const char *module, int yang_spec_parse_module(clicon_handle h, const char *module,

View file

@ -487,7 +487,11 @@ text_modify(clicon_handle h,
char *createstr = NULL; char *createstr = NULL;
yang_stmt *yrestype = NULL; yang_stmt *yrestype = NULL;
char *restype; char *restype;
#ifdef CLIXON_YANG_SCHEMA_MOUNT
int ismount = 0;
yang_stmt *mount_yspec = NULL;
#endif
if (x1 == NULL){ if (x1 == NULL){
clicon_err(OE_XML, EINVAL, "x1 is missing"); clicon_err(OE_XML, EINVAL, "x1 is missing");
goto done; goto done;
@ -908,6 +912,15 @@ text_modify(clicon_handle h,
yc = xml_spec(x1c); yc = xml_spec(x1c);
#endif #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, if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t,
yc, op, yc, op,
username, xnacm, permit, cbret)) < 0) username, xnacm, permit, cbret)) < 0)

View file

@ -1045,7 +1045,7 @@ netconf_missing_choice_xml(cxobj **xret,
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
/* error-path: Path to the element with the missing choice. */ /* 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; goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0) if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done; goto done;
@ -1407,7 +1407,7 @@ netconf_data_not_unique_xml(cxobj **xret,
if (cvec_len(cvk)){ if (cvec_len(cvk)){
if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL) if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL)
goto done; goto done;
if (xml2xpath(x, NULL, &path) < 0) if (xml2xpath(x, NULL, 0, &path) < 0)
goto done; goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0) if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done; goto done;
@ -1465,7 +1465,7 @@ netconf_minmax_elements_xml(cxobj **xret,
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
if (xml_parent(xp)){ /* Dont include root, eg <config> */ if (xml_parent(xp)){ /* Dont include root, eg <config> */
if (xml2xpath(xp, NULL, &path) < 0) if (xml2xpath(xp, NULL, 0, &path) < 0)
goto done; goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0) if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done; goto done;

View file

@ -1069,6 +1069,65 @@ clixon_plugin_yang_mount_all(clicon_handle h,
return retval; 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. * RPC callbacks for both client/frontend and backend plugins.
*/ */

View file

@ -375,10 +375,10 @@ populate_self_top(cxobj *xt,
*/ */
int int
xml_bind_yang(clicon_handle h, xml_bind_yang(clicon_handle h,
cxobj *xt, cxobj *xt,
yang_bind yb, yang_bind yb,
yang_stmt *yspec, yang_stmt *yspec,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
cxobj *xc; /* xml child */ cxobj *xc; /* xml child */
@ -414,11 +414,11 @@ xml_bind_yang(clicon_handle h,
*/ */
static int static int
xml_bind_yang0_opt(clicon_handle h, xml_bind_yang0_opt(clicon_handle h,
cxobj *xt, cxobj *xt,
yang_bind yb, yang_bind yb,
yang_stmt *yspec, yang_stmt *yspec,
cxobj *xsibling, cxobj *xsibling,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
cxobj *xc; /* xml child */ cxobj *xc; /* xml child */
@ -531,10 +531,10 @@ xml_bind_yang0_opt(clicon_handle h,
*/ */
int int
xml_bind_yang0(clicon_handle h, xml_bind_yang0(clicon_handle h,
cxobj *xt, cxobj *xt,
yang_bind yb, yang_bind yb,
yang_stmt *yspec, yang_stmt *yspec,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
cxobj *xc; /* xml child */ cxobj *xc; /* xml child */
@ -579,14 +579,21 @@ xml_bind_yang0(clicon_handle h,
} }
/*! RPC-specific /*! 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 static int
xml_bind_yang_rpc_rpc(clicon_handle h, xml_bind_yang_rpc_rpc(clicon_handle h,
cxobj *x, cxobj *x,
yang_stmt *yrpc, yang_stmt *yrpc,
char *rpcname, char *rpcname,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
cbuf *cb = NULL; 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 * Find the innermost container or list containing an XML element that carries the name of the
* defined action. * defined action.
* Only one action can be invoked in one rpc * 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 * XXX if not more action, consider folding into calling function
*/ */
static int static int
xml_bind_yang_rpc_action(clicon_handle h, xml_bind_yang_rpc_action(clicon_handle h,
cxobj *xn, cxobj *xn,
yang_stmt *yspec, yang_stmt *yspec,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
int ret; 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 <rpc> /*! Find yang spec association of XML node for incoming RPC starting with <rpc>
* *
* Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang * 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] xrpc XML rpc node
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[out] xerr Reason for failure, or NULL * @param[out] xerr Reason for failure, or NULL
@ -818,10 +831,10 @@ xml_bind_yang_rpc(clicon_handle h,
*/ */
int int
xml_bind_yang_rpc_reply(clicon_handle h, xml_bind_yang_rpc_reply(clicon_handle h,
cxobj *xrpc, cxobj *xrpc,
char *name, char *name,
yang_stmt *yspec, yang_stmt *yspec,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
yang_stmt *yrpc = NULL; /* yang node */ yang_stmt *yrpc = NULL; /* yang node */

View file

@ -732,140 +732,6 @@ xml_non_config_data(cxobj *xt,
return retval; 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 /*! Check if the module tree x is in is assigned right XML namespace, assign if not
* @param[in] x XML node * @param[in] x XML node
*(0. You should probably find the XML root and apply this function to that.) *(0. You should probably find the XML root and apply this function to that.)

View file

@ -1164,3 +1164,143 @@ xpath_count(cxobj *xcur,
ctx_free(xc); ctx_free(xc);
return retval; 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;
}

View file

@ -1013,6 +1013,7 @@ done:
/*! Open a file, read into a string and invoke yang parsing /*! Open a file, read into a string and invoke yang parsing
* *
* Similar to clicon_yang_str(), just read a file first * 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] filename Name of file
* @param[in] yspec Yang specification. Should have been created by caller using yspec_new * @param[in] yspec Yang specification. Should have been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module * @retval ymod Top-level yang (sub)module
@ -1022,8 +1023,9 @@ done:
* See top of file for diagram of calling order * See top of file for diagram of calling order
*/ */
yang_stmt * yang_stmt *
yang_parse_filename(const char *filename, yang_parse_filename(clicon_handle h,
yang_stmt *yspec) const char *filename,
yang_stmt *yspec)
{ {
yang_stmt *ymod = NULL; yang_stmt *ymod = NULL;
FILE *fp = NULL; FILE *fp = NULL;
@ -1040,6 +1042,9 @@ yang_parse_filename(const char *filename,
} }
if ((ymod = yang_parse_file(fp, filename, yspec)) < 0) if ((ymod = yang_parse_file(fp, filename, yspec)) < 0)
goto done; goto done;
/* YANG patch hook */
if (h && clixon_plugin_yang_patch_all(h, ymod) < 0)
goto done;
done: done:
if (fp) if (fp)
fclose(fp); fclose(fp);
@ -1097,7 +1102,7 @@ yang_parse_module(clicon_handle h,
goto done; goto done;
} }
filename = cbuf_get(fbuf); filename = cbuf_get(fbuf);
if ((ymod = yang_parse_filename(filename, yspec)) == NULL) if ((ymod = yang_parse_filename(h, filename, yspec)) == NULL)
goto done; goto done;
/* Sanity check that requested module name matches loaded module /* Sanity check that requested module name matches loaded module
* If this does not match, the filename and containing module do not match * 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'; *index(base, '@') = '\0';
if (yang_find(yspec, Y_MODULE, base) != NULL) if (yang_find(yspec, Y_MODULE, base) != NULL)
goto ok; goto ok;
if (yang_parse_filename(filename, yspec) == NULL) if (yang_parse_filename(h, filename, yspec) == NULL)
goto done; goto done;
if (yang_parse_post(h, yspec, modmin) < 0) if (yang_parse_post(h, yspec, modmin) < 0)
goto done; goto done;
@ -1752,7 +1757,7 @@ yang_spec_load_dir(clicon_handle h,
} }
/* Create full filename */ /* Create full filename */
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name); 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; goto done;
revm = 0; revm = 0;
if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL) if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL)

View file

@ -56,7 +56,6 @@
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <sys/param.h> #include <sys/param.h>
/* cligen */ /* cligen */
@ -141,7 +140,7 @@ yang_schema_mount_point(yang_stmt *y)
* @retval -1 Error * @retval -1 Error
*/ */
int int
xml_yang_mount_get(cxobj *x, xml_yang_mount_get(cxobj *xt,
yang_stmt **yspec) yang_stmt **yspec)
{ {
int retval = -1; int retval = -1;
@ -152,16 +151,16 @@ xml_yang_mount_get(cxobj *x,
char *xpath = NULL; // XXX free it char *xpath = NULL; // XXX free it
int ret; int ret;
if ((y = xml_spec(x)) == NULL) if ((y = xml_spec(xt)) == NULL)
goto fail; goto fail;
if ((ret = yang_schema_mount_point(y)) < 0) if ((ret = yang_schema_mount_point(y)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
// XXX // XXX hardcoded prefix: yangmnt
if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL) if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL)
goto ok; goto ok;
if (xml2xpath(x, NULL, &xpath) < 0) if (xml2xpath(xt, NULL, 1, &xpath) < 0)
goto done; goto done;
if ((cvv = yang_cvec_get(yu)) == NULL) if ((cvv = yang_cvec_get(yu)) == NULL)
goto ok; goto ok;
@ -203,12 +202,11 @@ xml_yang_mount_set(cxobj *x,
(yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){ (yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){
goto done; goto done;
} }
if (xml2xpath(x, NULL, &xpath) < 0) if (xml2xpath(x, NULL, 0, &xpath) < 0)
goto done; goto done;
if ((cvv = yang_cvec_get(yu)) != NULL && if ((cvv = yang_cvec_get(yu)) != NULL &&
(cv = cvec_find(cvv, xpath)) != NULL && (cv = cvec_find(cvv, xpath)) != NULL &&
(yspec0 = cv_void_get(cv)) != NULL){ (yspec0 = cv_void_get(cv)) != NULL){
assert(0);
ys_free(yspec0); ys_free(yspec0);
cv_void_set(cv, NULL); cv_void_set(cv, NULL);
} }
@ -222,7 +220,9 @@ xml_yang_mount_set(cxobj *x,
} }
/*! Free all yspec yang-mounts /*! 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 int
xml_yang_mount_freeall(cvec *cvv) xml_yang_mount_freeall(cvec *cvv)
@ -243,10 +243,10 @@ xml_yang_mount_freeall(cvec *cvv)
* *
* @param[in] x XML node * @param[in] x XML node
* @param[in] arg cvec, if match add 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 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 static int
find_schema_mounts(cxobj *x, 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 * Brute force: traverse whole XML, match all x that have ymount as yspec
* Add yang-library state for all x * Add yang-library state for all x
* @param[in] h Clicon handle * @param[in] h Clixon handle
* @param[in] xpath XML Xpath * @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath * @param[in] nsc XML Namespace context for xpath
* @param[in,out] xret Existing XML tree, merge x into this * @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 /*! 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] yspec Yang spec
* @param[in] xpath XML Xpath * @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath * @param[in] nsc XML Namespace context for xpath
@ -437,7 +437,12 @@ yang_schema_mount_statedata(clicon_handle h,
goto done; 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 int
yang_schema_yanglib_parse_mount(clicon_handle h, yang_schema_yanglib_parse_mount(clicon_handle h,
@ -461,6 +466,7 @@ yang_schema_yanglib_parse_mount(clicon_handle h,
goto anydata; goto anydata;
if (xml_yang_mount_set(xt, yspec) < 0) if (xml_yang_mount_set(xt, yspec) < 0)
goto done; goto done;
yspec = NULL;
retval = 1; retval = 1;
done: done:
if (yspec) 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 /*! 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] x1 XML node
* @param[in] x1c A child of x1 * @param[in] x1c A child of x1
* @param[out] yc YANG child * @param[out] yc YANG child

View file

@ -132,7 +132,7 @@ main(int argc,
if (yang_filename){ if (yang_filename){
if ((yspec = yspec_new()) == NULL) if ((yspec = yspec_new()) == NULL)
goto done; 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); fprintf(stderr, "yang parse error %s\n", clicon_err_reason);
return -1; return -1;
} }

View file

@ -383,7 +383,7 @@ main(int argc,
char *xpathi = NULL; char *xpathi = NULL;
for (i=0; i<xc->xc_size; i++){ for (i=0; i<xc->xc_size; i++){
xi = xc->xc_nodeset[i]; xi = xc->xc_nodeset[i];
if (xml2xpath(xi, nsc, &xpathi) < 0) if (xml2xpath(xi, nsc, 0, &xpathi) < 0)
goto done; goto done;
fprintf(stdout, "Inverse: %s\n", xpathi); fprintf(stdout, "Inverse: %s\n", xpathi);
if (xpathi){ if (xpathi){