New plugin callback: ca_yang_patch - for modifying existing YANG modules
C-API: Added `spec` parameter to `xml2xpath()`
This commit is contained in:
parent
48a0fb9968
commit
a5220805b1
16 changed files with 318 additions and 193 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -485,7 +485,8 @@ from_client_edit_config(clicon_handle h,
|
|||
/* <config> 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 <config> 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){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <config> */
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 <rpc>
|
||||
*
|
||||
* 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 */
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@
|
|||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
/* 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ main(int argc,
|
|||
char *xpathi = NULL;
|
||||
for (i=0; i<xc->xc_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){
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue