Creator attribute changes: added as xmldb metadata

clixon-config.yang: New revision and Added `CLICON_NETCONF_CREATOR_ATTR` option
clixon-lib.yang: Added creator meta
Changed return value of xml_add_attr
This commit is contained in:
Olof hagsand 2023-11-28 13:35:17 +01:00
parent be3001acf5
commit bbcb4a7b03
20 changed files with 474 additions and 93 deletions

View file

@ -442,6 +442,56 @@ disable_nacm_on_empty(cxobj *xt,
return retval;
}
/*! Read creator data from meta-data and add to xml internal annotation
*
* Iterate creator meta-data using xpaths and create internal creator annotations
* @param[in] h Clixon handle
* @param[in] xt XML top node
* @param[in] xn XML creators node
* On the form:
* <creator>
* <name>testA[name='foo']</name>
* <xpath>...</xpath>
* ...
* </creator>
* @see xml_creator_metadata_write
*/
static int
xml_creator_metadata_read(clicon_handle h,
cxobj *xn)
{
int retval = -1;
cxobj *xt;
cxobj *xc;
cxobj *xp;
cxobj *x;
char *name;
char *xpath;
xt = xml_root(xn);
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xc), "creator"))
continue;
if ((name = xml_find_body(xc, "name")) == NULL)
continue;
xp = NULL;
while ((xp = xml_child_each(xc, xp, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xp), "path"))
continue;
if ((xpath = xml_body(xp)) == NULL)
continue;
if ((x = xpath_first(xt, NULL, "%s", xpath)) == NULL)
continue;
if (xml_creator_add(x, name) < 0)
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Common read function that reads an XML tree from file
*
* @param[in] th Datastore text handle
@ -543,7 +593,19 @@ xmldb_readfile(clicon_handle h,
xml_flag_set(x0, XML_FLAG_TOP);
if (xml_child_nr(x0) == 0 && de)
de->de_empty = 1;
/* Check if creator attributes are supported*/
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
if ((x = xpath_first(x0, NULL, "creators")) != NULL){
ns = NULL;
if (xml2ns(x, NULL, &ns) < 0)
goto done;
if (strcmp(ns, CLIXON_LIB_NS) == 0){
if (xml_creator_metadata_read(h, x) < 0)
goto done;
xml_purge(x);
}
}
}
/* Check if we support modstate */
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
if ((msdiff = modstate_diff_new()) == NULL)

View file

@ -89,7 +89,7 @@
* An attribute may have a prefix(or NULL). The routine finds the associated
* xmlns binding to find the namespace: <namespace>:<name>.
* If such an attribute is not found, failure is returned with cbret set,
* If such an attribute its found, its string value is returned.
* If such an attribute is found, its string value is returned and removed from XML
* @param[in] x XML node (where to look for attribute)
* @param[in] name Attribute name
* @param[in] ns (Expected) Namespace of attribute
@ -98,6 +98,7 @@
* @retval 1 OK
* @retval 0 Failed (cbret set)
* @retval -1 Error
* @note as a side.effect the attribute is removed
*/
static int
attr_ns_value(cxobj *x,
@ -142,6 +143,70 @@ attr_ns_value(cxobj *x,
goto done;
}
/*! Add creator data to metadata xml object on the form name:xpath*
*
* Callback function type for xml_apply
* @param[in] x XML node
* @param[in] arg General-purpose argument
* @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
* On the form:
* <creator>
* <name>testA[name='foo']</name>
* <xpath>...</xpath>
* ...
* </creator>
* @see xml_creator_metadata_read
*/
static int
xml_creator_metadata_write(cxobj *x,
void *arg)
{
int retval = -1;
cxobj *xmeta = (cxobj*)arg;
cxobj *xmc;
cvec *cvv;
cg_var *cv;
char *val;
cvec *nsc = NULL;
char *xpath = NULL;
if ((cvv = xml_creator_get(x)) == NULL){
retval = 0;
goto done;
}
/* Note this requires x to have YANG spec */
if (xml2xpath(x, nsc, 0, 0, &xpath) < 0)
goto done;
cv = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL){
val = cv_name_get(cv);
/* Find existing entry where name is val */
xmc = NULL;
while ((xmc = xml_child_each(xmeta, xmc, CX_ELMNT)) != NULL){
if (strcmp(xml_find_body(xmc, "name"), val) == 0)
break;
}
if (xmc != NULL){
if (clixon_xml_parse_va(YB_NONE, NULL, &xmc, NULL, "<path>%s</path>", xpath) < 0)
goto done;
}
else {
if (clixon_xml_parse_va(YB_NONE, NULL, &xmeta, NULL,
"<creator><name>%s</name><path>%s</path></creator>",
val, xpath) < 0)
goto done;
}
}
retval = 2;
done:
if (xpath)
free(xpath);
return retval;
}
/*! When new body is added, some needs type lookup is made and namespace checked
*
* This includes identityrefs, paths
@ -209,11 +274,11 @@ check_body_namespace(cxobj *x0,
goto done;
/* Create xmlns attribute to x0 XXX same code ^*/
if (prefix){
if (xml_add_attr(x, prefix, ns0, "xmlns", NULL) < 0)
if (xml_add_attr(x, prefix, ns0, "xmlns", NULL) == NULL)
goto done;
}
else
if (xml_add_attr(x, "xmlns", ns0, NULL, NULL) < 0)
if (xml_add_attr(x, "xmlns", ns0, NULL, NULL) == NULL)
goto done;
xml_sort(x); /* Ensure attr is first / XXX xml_insert? */
}
@ -231,7 +296,7 @@ check_body_namespace(cxobj *x0,
;
}
else{ /* Add it according to the kludge,... */
if (xml_add_attr(x0, prefix, ns0, "xmlns", NULL) < 0)
if (xml_add_attr(x0, prefix, ns0, "xmlns", NULL) == NULL)
goto done;
}
}
@ -513,7 +578,6 @@ text_modify(clicon_handle h,
goto done;
if (ret == 0)
goto fail;
if (createstr != NULL &&
(op == OP_REPLACE || op == OP_MERGE || op == OP_CREATE)){
if (x0 == NULL || xml_defaults_nopresence(x0, 0)){ /* does not exist or is default */
@ -532,11 +596,13 @@ text_modify(clicon_handle h,
clicon_data_set(h, "objectexisted", "true");
}
}
/* Special clixon-lib attribute for keeping track of creator of objects */
if ((ret = attr_ns_value(x1, "creator", CLIXON_LIB_NS, cbret, &creator)) < 0)
goto done;
if (ret == 0)
goto fail;
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
/* Special clixon-lib attribute for keeping track of creator of objects */
if ((ret = attr_ns_value(x1, "creator", CLIXON_LIB_NS, cbret, &creator)) < 0)
goto done;
if (ret == 0)
goto fail;
}
x1name = xml_name(x1);
if (yang_keyword_get(y0) == Y_LEAF_LIST ||
@ -798,12 +864,13 @@ text_modify(clicon_handle h,
}
/* XXX: Note, if there is an error in adding the object later, the
* original object is not reverted.
* XXX: Here creator attributes in x0 are destroyed
*/
if (x0){
/* Recursively copy creator attributes from existing tree */
if (xml_creator_copy_all(x0, x1) < 0)
goto done;
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
/* Recursively copy creator attributes from existing tree */
if (xml_creator_copy_all(x0, x1) < 0)
goto done;
}
xml_purge(x0);
x0 = NULL;
}
@ -854,8 +921,10 @@ text_modify(clicon_handle h,
#ifdef XML_PARENT_CANDIDATE
xml_parent_candidate_set(x0, x0p);
#endif
if (xml_creator_copy_one(x1, x0) < 0)
goto done;
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
if (xml_creator_copy_one(x1, x0) < 0)
goto done;
}
changed++;
/* Get namespace from x1
* Check if namespace exists in x0 parent
@ -1229,6 +1298,7 @@ xmldb_put(clicon_handle h,
int firsttime = 0;
int pretty;
cxobj *xerr = NULL;
cxobj *xmeta = NULL;
if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is NULL");
@ -1262,6 +1332,16 @@ xmldb_put(clicon_handle h,
xml_name(x0), DATASTORE_TOP_SYMBOL);
goto done;
}
/* XXX Ad-hoc:
* In yang mounts yang specs may not be available
* in initial parsing, but may be at a later stage.
* But it should be possible to find a more precise triggering
*/
if (clicon_option_bool(h, "CLICON_YANG_SCHEMA_MOUNT")){
if ((ret = xml_bind_yang(h, x0, YB_MODULE, yspec, NULL)) < 0)
goto done;
}
/* Here x0 looks like: <config>...</config> */
#if 0 /* debug */
if (xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
@ -1301,7 +1381,6 @@ xmldb_put(clicon_handle h,
if (xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
#endif
/* Write back to datastore cache if first time */
if (clicon_datastore_cache(h) != DATASTORE_NOCACHE){
db_elmnt de0 = {0,};
@ -1336,6 +1415,19 @@ xmldb_put(clicon_handle h,
goto done;
}
pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY");
/* Add creator attribute */
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
if ((xmeta = xml_new("creators", x0, CX_ELMNT)) == NULL)
goto done;
if (xmlns_set(xmeta, NULL, CLIXON_LIB_NS) < 0)
goto done;
if (xml_apply(x0, CX_ELMNT, xml_creator_metadata_write, xmeta) < 0)
goto done;
if (xml_child_nr_type(xmeta, CX_ELMNT) == 0){
xml_purge(xmeta);
xmeta = NULL;
}
}
if (strcmp(format,"json")==0){
if (clixon_json2file(f, x0, pretty, fprintf, 0, 0) < 0)
goto done;
@ -1346,6 +1438,10 @@ xmldb_put(clicon_handle h,
*/
if (xmodst && xml_purge(xmodst) < 0)
goto done;
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR") && xmeta){
if (xml_purge(xmeta) < 0)
goto done;
}
retval = 1;
done:
if (f != NULL)

View file

@ -348,7 +348,7 @@ json2xml_decode_identityref(cxobj *x,
if (prefix2 == NULL)
prefix2 = yang_find_myprefix(ymod);
/* Add "xmlns:prefix2=namespace" */
if (xml_add_attr(x, prefix2, ns, "xmlns", NULL) < 0)
if (xml_add_attr(x, prefix2, ns, "xmlns", NULL) == NULL)
goto done;
}

View file

@ -178,7 +178,7 @@ netconf_invalid_value_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -302,7 +302,7 @@ netconf_missing_attribute_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -418,7 +418,7 @@ netconf_bad_attribute_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -514,7 +514,7 @@ netconf_common_xml(cxobj **xret,
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
@ -789,7 +789,7 @@ netconf_access_denied_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1034,7 +1034,7 @@ netconf_data_missing_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1093,7 +1093,7 @@ netconf_missing_choice_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1167,7 +1167,7 @@ netconf_operation_not_supported_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1285,7 +1285,7 @@ netconf_operation_failed_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1374,7 +1374,7 @@ netconf_malformed_message_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1460,7 +1460,7 @@ netconf_data_not_unique_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@ -1531,7 +1531,7 @@ netconf_minmax_elements_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;

View file

@ -185,7 +185,8 @@ struct xml{
yang_stmt *x_spec; /* Pointer to specification, eg yang,
by reference, dont free */
cg_var *x_cv; /* Cached value as cligen variable (set by xml_cmp) */
cvec *x_creators; /* Support clixon-lib creator annotation */
cvec *x_creators; /* Support clixon-lib creator annotation.
Only if CLICON_NETCONF_CREATOR_ATTR = true */
#ifdef XML_EXPLICIT_INDEX
struct search_index *x_search_index; /* explicit search index vectors */
#endif
@ -653,7 +654,7 @@ xml_creator_add(cxobj *xn,
cg_var *cv;
if (!is_element(xn))
return 0;
goto ok;
if (xn->x_creators == NULL){
if ((xn->x_creators = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new");
@ -662,6 +663,7 @@ xml_creator_add(cxobj *xn,
}
if ((cv = cvec_find(xn->x_creators, name)) == NULL)
cvec_add_string(xn->x_creators, name, NULL);
ok:
retval = 0;
done:
return retval;
@ -723,6 +725,16 @@ xml_creator_len(cxobj *xn)
return 0;
}
/*! Get all creator attributes
*/
cvec*
xml_creator_get(cxobj *xn)
{
if (!is_element(xn))
return 0;
return xn->x_creators;
}
/*! Copy creator info from x0 to x1 single object
*
* @param[in] x0 Source XML node
@ -830,7 +842,7 @@ creator_print_fn(cxobj *x,
return 2; /* Locally abort this subtree, continue with others */
}
/*! Print XML and creator tags where they exists, for debugging
/*! Print XML and creator tags where they exists recursively, for debugging
*
* @param[in] xn XML tree
* @retval see xml_apply
@ -1355,7 +1367,7 @@ clixon_child_xvec_append(cxobj *xn,
* xml_free(x);
* @endcode
* @note Differentiates between body/attribute vs element to reduce mem allocation
* @see xml_sort_insert
* @see xml_insert
*/
cxobj *
xml_new(char *name,
@ -2021,10 +2033,10 @@ xml_find_type_value(cxobj *xt,
* @see xml_find_value where a body can be found as well
*/
cxobj *
xml_find_type(cxobj *xt,
const char *prefix,
const char *name,
enum cxobj_type type)
xml_find_type(cxobj *xt,
const char *prefix,
const char *name,
enum cxobj_type type)
{
cxobj *x = NULL;
int pmatch; /* prefix match */
@ -2649,18 +2661,17 @@ xml_attr_insert2val(char *instr,
* @param[in] value Attribute value
* @param[in] prefix Attribute prefix, or NULL
* @param[in] namespace Attribute prefix, if NULL do not add namespace mapping
* @retval 0 OK
* @retval -1 Error
* @retval xa Created attribute object
* @retval NULL Error
*/
int
cxobj *
xml_add_attr(cxobj *xn,
char *name,
char *value,
char *prefix,
char *namespace)
{
int retval = -1;
cxobj *xa;
cxobj *xa = NULL;
char *ns = NULL;
if ((xa = xml_new(name, xn, CX_ATTR)) == NULL)
@ -2675,9 +2686,16 @@ xml_add_attr(cxobj *xn,
if (ns == NULL && xmlns_set(xn, prefix, namespace) < 0)
goto done;
}
retval = 0;
if (xml_sort(xn) < 0)
goto done;
ret:
return xa;
done:
return retval;
if (xa){
xml_free(xa);
xa = NULL;
}
goto ret;
}
/*! Specialization of clicon_log with xml tree

View file

@ -227,8 +227,13 @@ populate_self_parent(cxobj *xt,
}
/* Assign spec only if namespaces match */
if (strcmp(ns, nsy) != 0){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "Namespace mismatch: %s in XML does not match %s in yang", ns, nsy);
if (xerr &&
netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
netconf_bad_element_xml(xerr, "application", name, cbuf_get(cb)) < 0)
goto done;
goto fail;
}

View file

@ -615,7 +615,7 @@ xml_add_default_tag(cxobj *x,
if (xml_flag(x, flags)) {
/* set default attribute */
if (xml_add_attr(x, "default", "true", IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, NULL) < 0)
if (xml_add_attr(x, "default", "true", IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, NULL) == NULL)
goto done;
}
retval = 0;

View file

@ -358,12 +358,14 @@ xml_parse_attr(clixon_xml_yacc *xy,
int retval = -1;
cxobj *xa = NULL;
/* XXX: here duplicates of same attributes are removed
* This is probably not according standard?
*/
if ((xa = xml_find_type(xy->xy_xelement, prefix, name, CX_ATTR)) == NULL){
if ((xa = xml_new(name, xy->xy_xelement, CX_ATTR)) == NULL)
goto done;
if (xml_prefix_set(xa, prefix) < 0)
goto done;
}
if (xml_value_set(xa, attval) < 0)
goto done;
@ -484,12 +486,14 @@ pi : BQMARK NAME EQMARK {_PARSE_DEBUG("pi -> <? NAME ?>"); free($2); }
;
attrs : attrs attr
|
attrs : attrs attr { _PARSE_DEBUG("attrs -> attrs attr"); }
| { _PARSE_DEBUG("attrs ->"); }
;
attr : NAME '=' attvalue { if (xml_parse_attr(_XY, NULL, $1, $3) < 0) YYABORT; }
| NAME ':' NAME '=' attvalue { if (xml_parse_attr(_XY, $1, $3, $5) < 0) YYABORT; }
attr : NAME '=' attvalue { if (xml_parse_attr(_XY, NULL, $1, $3) < 0) YYABORT;
_PARSE_DEBUG("attr -> NAME = attvalue"); }
| NAME ':' NAME '=' attvalue { if (xml_parse_attr(_XY, $1, $3, $5) < 0) YYABORT;
_PARSE_DEBUG("attr -> NAME : NAME = attvalue"); }
;
attvalue : '\"' STRING '\"' { $$=$2; /* $2 must be consumed */}

View file

@ -1057,7 +1057,7 @@ xml_insert2(cxobj *xp,
/*! Insert xc as child to xp in sorted place. Remove xc from previous parent.
*
* @param[in] xp Parent xml node. If NULL just remove from old parent.
* @param[in] x Child xml node to insert under xp
* @param[in] xi Child xml node to insert under xp
* @param[in] ins Insert operation (if ordered-by user)
* @param[in] key_val Key if x is LIST and ins is before/after, val if LEAF_LIST
* @param[in] nsc_key Network namespace for key