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

@ -55,9 +55,11 @@ Users may have to change how they access the system
### C/CLI-API changes on existing features ### C/CLI-API changes on existing features
Developers may need to change their code Developers may need to change their code
* Changed return value of `xml_add_attr` from 0/-1 to xa/NULL
* You need to change eg `if (xml_add_attr < 0)` to if (xml_add_attr == NULL)`
* Changed signature of `clicon_netconf_error()` and `netconf_err2cb()` * Changed signature of `clicon_netconf_error()` and `netconf_err2cb()`
* You need to add the clixon handle as first parameter: * You need to add the clixon handle as first parameter:
* `clicon_netconf_error(...)` --> `clicon_netconf_error(h, ...)` * `clicon_netconf_error(...)` --> `clixon_netconf_error(h, ...)`
* `netconf_err2cb(...)` --> `netconf_err2cb(h, ...)` * `netconf_err2cb(...)` --> `netconf_err2cb(h, ...)`
* Changed function name for `clicon_debug` functions. You need to rename as follows: * Changed function name for `clicon_debug` functions. You need to rename as follows:
* clicon_debug() -> clixon_debug() * clicon_debug() -> clixon_debug()
@ -71,8 +73,11 @@ Developers may need to change their code
* New feature: [Customized NETCONF error message](https://github.com/clicon/clixon/issues/454) * New feature: [Customized NETCONF error message](https://github.com/clicon/clixon/issues/454)
* Added new callback `.ca_errmsg` * Added new callback `.ca_errmsg`
* See https://clixon-docs.readthedocs.io/en/latest/errors.html#customized-errors for more info * See https://clixon-docs.readthedocs.io/en/latest/errors.html#customized-errors for more info
* New `clixon-config@2023-11-01.yang` revision
* Added `CLICON_NETCONF_CREATOR_ATTR` option
* New `clixon-lib@2023-11-01.yang` revision * New `clixon-lib@2023-11-01.yang` revision
* Added ignore-compare extension * Added ignore-compare extension
* Added creator meta configuration
### Corrected Bugs ### Corrected Bugs

View file

@ -798,7 +798,7 @@ get_list_pagination(clicon_handle h,
goto done; goto done;
} }
cprintf(cba, "%u", remaining); cprintf(cba, "%u", remaining);
if (xml_add_attr(x1, "remaining", cbuf_get(cba), "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0) if (xml_add_attr(x1, "remaining", cbuf_get(cba), "cp", "http://clicon.org/clixon-netconf-list-pagination") == NULL)
goto done; goto done;
if (cba) if (cba)
cbuf_free(cba); cbuf_free(cba);

View file

@ -473,7 +473,7 @@ cli_dbxml(clicon_handle h,
goto done; goto done;
} }
} }
if (xml_add_attr(xbot, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0) if (xml_add_attr(xbot, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) == NULL)
goto done; goto done;
/* Add body last in case of leaf */ /* Add body last in case of leaf */
if (cvec_len(cvv) > 1 && if (cvec_len(cvv) > 1 &&

View file

@ -712,7 +712,7 @@ netconf_rpc_dispatch(clicon_handle h,
* It may even be wrong if something else is done with the incoming message? * It may even be wrong if something else is done with the incoming message?
*/ */
if ((username = clicon_username_get(h)) != NULL){ if ((username = clicon_username_get(h)) != NULL){
if (xml_add_attr(xn, "username", username, CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0) if (xml_add_attr(xn, "username", username, CLIXON_LIB_PREFIX, CLIXON_LIB_NS) == NULL)
goto done; goto done;
} }
/* Many of these calls are now calling generic clicon_rpc_netconf_xml /* Many of these calls are now calling generic clicon_rpc_netconf_xml

View file

@ -427,7 +427,7 @@ restconf_insert_attributes(cxobj *xdata,
/* First add xmlns:yang attribute */ /* First add xmlns:yang attribute */
if (xmlns_set(xdata, "yang", YANG_XML_NAMESPACE) < 0) if (xmlns_set(xdata, "yang", YANG_XML_NAMESPACE) < 0)
goto done; goto done;
if (xml_add_attr(xdata, "insert", instr, "yang", NULL) < 0) if (xml_add_attr(xdata, "insert", instr, "yang", NULL) == NULL)
goto done; goto done;
} }
if ((pstr = cvec_find_str(qvec, "point")) != NULL){ if ((pstr = cvec_find_str(qvec, "point")) != NULL){
@ -467,7 +467,7 @@ restconf_insert_attributes(cxobj *xdata,
p++; p++;
cprintf(cb, "%s", p); cprintf(cb, "%s", p);
} }
if (xml_add_attr(xdata, attrname, cbuf_get(cb), "yang", NULL) < 0) if (xml_add_attr(xdata, attrname, cbuf_get(cb), "yang", NULL) == NULL)
goto done; goto done;
} }
/* Add prefix/namespaces used in attributes */ /* Add prefix/namespaces used in attributes */

View file

@ -390,11 +390,11 @@ api_data_write(clicon_handle h,
/* Add operation create as attribute. If that fails with Conflict, then /* Add operation create as attribute. If that fails with Conflict, then
* try "replace" (see comment in function header) * try "replace" (see comment in function header)
*/ */
if (xml_add_attr(xdata, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0) if (xml_add_attr(xdata, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) == NULL)
goto done; goto done;
if (xml_add_attr(xdata, "objectcreate", if (xml_add_attr(xdata, "objectcreate",
plain_patch?"false":"true", plain_patch?"false":"true",
CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0) CLIXON_LIB_PREFIX, CLIXON_LIB_NS) == NULL)
goto done; goto done;
/* Top-of tree, no api-path /* Top-of tree, no api-path
* Replace xparent with x, ie bottom of api-path with data * Replace xparent with x, ie bottom of api-path with data

View file

@ -235,6 +235,7 @@ int xml_creator_add(cxobj *xn, char *name);
int xml_creator_rm(cxobj *xn, char *name); int xml_creator_rm(cxobj *xn, char *name);
int xml_creator_find(cxobj *xn, char *name); int xml_creator_find(cxobj *xn, char *name);
size_t xml_creator_len(cxobj *xn); size_t xml_creator_len(cxobj *xn);
cvec *xml_creator_get(cxobj *xn);
int xml_creator_copy_one(cxobj *x0, cxobj *x1); int xml_creator_copy_one(cxobj *x0, cxobj *x1);
int xml_creator_copy_all(cxobj *x0, cxobj *x1); int xml_creator_copy_all(cxobj *x0, cxobj *x1);
int xml_creator_print(FILE *f, cxobj *xn); int xml_creator_print(FILE *f, cxobj *xn);
@ -304,7 +305,7 @@ cxobj *xml_root(cxobj *xn);
int xml_operation(char *opstr, enum operation_type *op); int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op); char *xml_operation2str(enum operation_type op);
int xml_attr_insert2val(char *instr, enum insert_type *ins); int xml_attr_insert2val(char *instr, enum insert_type *ins);
int xml_add_attr(cxobj *xn, char *name, char *value, char *prefix, char *ns); cxobj *xml_add_attr(cxobj *xn, char *name, char *value, char *prefix, char *ns);
int clicon_log_xml(int level, cxobj *x, const char *format, ...) __attribute__ ((format (printf, 3, 4))); int clicon_log_xml(int level, cxobj *x, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
int clixon_debug_xml(int dbglevel, cxobj *x, const char *format, ...) __attribute__ ((format (printf, 3, 4))); int clixon_debug_xml(int dbglevel, cxobj *x, const char *format, ...) __attribute__ ((format (printf, 3, 4)));

View file

@ -442,6 +442,56 @@ disable_nacm_on_empty(cxobj *xt,
return retval; 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 /*! Common read function that reads an XML tree from file
* *
* @param[in] th Datastore text handle * @param[in] th Datastore text handle
@ -543,7 +593,19 @@ xmldb_readfile(clicon_handle h,
xml_flag_set(x0, XML_FLAG_TOP); xml_flag_set(x0, XML_FLAG_TOP);
if (xml_child_nr(x0) == 0 && de) if (xml_child_nr(x0) == 0 && de)
de->de_empty = 1; 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 */ /* Check if we support modstate */
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
if ((msdiff = modstate_diff_new()) == NULL) 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 * An attribute may have a prefix(or NULL). The routine finds the associated
* xmlns binding to find the namespace: <namespace>:<name>. * 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 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] x XML node (where to look for attribute)
* @param[in] name Attribute name * @param[in] name Attribute name
* @param[in] ns (Expected) Namespace of attribute * @param[in] ns (Expected) Namespace of attribute
@ -98,6 +98,7 @@
* @retval 1 OK * @retval 1 OK
* @retval 0 Failed (cbret set) * @retval 0 Failed (cbret set)
* @retval -1 Error * @retval -1 Error
* @note as a side.effect the attribute is removed
*/ */
static int static int
attr_ns_value(cxobj *x, attr_ns_value(cxobj *x,
@ -142,6 +143,70 @@ attr_ns_value(cxobj *x,
goto done; 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 /*! When new body is added, some needs type lookup is made and namespace checked
* *
* This includes identityrefs, paths * This includes identityrefs, paths
@ -209,11 +274,11 @@ check_body_namespace(cxobj *x0,
goto done; goto done;
/* Create xmlns attribute to x0 XXX same code ^*/ /* Create xmlns attribute to x0 XXX same code ^*/
if (prefix){ if (prefix){
if (xml_add_attr(x, prefix, ns0, "xmlns", NULL) < 0) if (xml_add_attr(x, prefix, ns0, "xmlns", NULL) == NULL)
goto done; goto done;
} }
else else
if (xml_add_attr(x, "xmlns", ns0, NULL, NULL) < 0) if (xml_add_attr(x, "xmlns", ns0, NULL, NULL) == NULL)
goto done; goto done;
xml_sort(x); /* Ensure attr is first / XXX xml_insert? */ 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,... */ 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; goto done;
} }
} }
@ -513,7 +578,6 @@ text_modify(clicon_handle h,
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
if (createstr != NULL && if (createstr != NULL &&
(op == OP_REPLACE || op == OP_MERGE || op == OP_CREATE)){ (op == OP_REPLACE || op == OP_MERGE || op == OP_CREATE)){
if (x0 == NULL || xml_defaults_nopresence(x0, 0)){ /* does not exist or is default */ 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"); clicon_data_set(h, "objectexisted", "true");
} }
} }
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
/* Special clixon-lib attribute for keeping track of creator of objects */ /* Special clixon-lib attribute for keeping track of creator of objects */
if ((ret = attr_ns_value(x1, "creator", CLIXON_LIB_NS, cbret, &creator)) < 0) if ((ret = attr_ns_value(x1, "creator", CLIXON_LIB_NS, cbret, &creator)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
}
x1name = xml_name(x1); x1name = xml_name(x1);
if (yang_keyword_get(y0) == Y_LEAF_LIST || 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 /* XXX: Note, if there is an error in adding the object later, the
* original object is not reverted. * original object is not reverted.
* XXX: Here creator attributes in x0 are destroyed
*/ */
if (x0){ if (x0){
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
/* Recursively copy creator attributes from existing tree */ /* Recursively copy creator attributes from existing tree */
if (xml_creator_copy_all(x0, x1) < 0) if (xml_creator_copy_all(x0, x1) < 0)
goto done; goto done;
}
xml_purge(x0); xml_purge(x0);
x0 = NULL; x0 = NULL;
} }
@ -854,8 +921,10 @@ text_modify(clicon_handle h,
#ifdef XML_PARENT_CANDIDATE #ifdef XML_PARENT_CANDIDATE
xml_parent_candidate_set(x0, x0p); xml_parent_candidate_set(x0, x0p);
#endif #endif
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR")){
if (xml_creator_copy_one(x1, x0) < 0) if (xml_creator_copy_one(x1, x0) < 0)
goto done; goto done;
}
changed++; changed++;
/* Get namespace from x1 /* Get namespace from x1
* Check if namespace exists in x0 parent * Check if namespace exists in x0 parent
@ -1229,6 +1298,7 @@ xmldb_put(clicon_handle h,
int firsttime = 0; int firsttime = 0;
int pretty; int pretty;
cxobj *xerr = NULL; cxobj *xerr = NULL;
cxobj *xmeta = NULL;
if (cbret == NULL){ if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is NULL"); clicon_err(OE_XML, EINVAL, "cbret is NULL");
@ -1262,6 +1332,16 @@ xmldb_put(clicon_handle h,
xml_name(x0), DATASTORE_TOP_SYMBOL); xml_name(x0), DATASTORE_TOP_SYMBOL);
goto done; 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> */ /* Here x0 looks like: <config>...</config> */
#if 0 /* debug */ #if 0 /* debug */
if (xml_apply0(x1, -1, xml_sort_verify, NULL) < 0) 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) if (xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__); clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
#endif #endif
/* Write back to datastore cache if first time */ /* Write back to datastore cache if first time */
if (clicon_datastore_cache(h) != DATASTORE_NOCACHE){ if (clicon_datastore_cache(h) != DATASTORE_NOCACHE){
db_elmnt de0 = {0,}; db_elmnt de0 = {0,};
@ -1336,6 +1415,19 @@ xmldb_put(clicon_handle h,
goto done; goto done;
} }
pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY"); 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 (strcmp(format,"json")==0){
if (clixon_json2file(f, x0, pretty, fprintf, 0, 0) < 0) if (clixon_json2file(f, x0, pretty, fprintf, 0, 0) < 0)
goto done; goto done;
@ -1346,6 +1438,10 @@ xmldb_put(clicon_handle h,
*/ */
if (xmodst && xml_purge(xmodst) < 0) if (xmodst && xml_purge(xmodst) < 0)
goto done; goto done;
if (clicon_option_bool(h, "CLICON_NETCONF_CREATOR_ATTR") && xmeta){
if (xml_purge(xmeta) < 0)
goto done;
}
retval = 1; retval = 1;
done: done:
if (f != NULL) if (f != NULL)

View file

@ -348,7 +348,7 @@ json2xml_decode_identityref(cxobj *x,
if (prefix2 == NULL) if (prefix2 == NULL)
prefix2 = yang_find_myprefix(ymod); prefix2 = yang_find_myprefix(ymod);
/* Add "xmlns:prefix2=namespace" */ /* 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; goto done;
} }

View file

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

View file

@ -185,7 +185,8 @@ struct xml{
yang_stmt *x_spec; /* Pointer to specification, eg yang, yang_stmt *x_spec; /* Pointer to specification, eg yang,
by reference, dont free */ by reference, dont free */
cg_var *x_cv; /* Cached value as cligen variable (set by xml_cmp) */ 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 #ifdef XML_EXPLICIT_INDEX
struct search_index *x_search_index; /* explicit search index vectors */ struct search_index *x_search_index; /* explicit search index vectors */
#endif #endif
@ -653,7 +654,7 @@ xml_creator_add(cxobj *xn,
cg_var *cv; cg_var *cv;
if (!is_element(xn)) if (!is_element(xn))
return 0; goto ok;
if (xn->x_creators == NULL){ if (xn->x_creators == NULL){
if ((xn->x_creators = cvec_new(0)) == NULL){ if ((xn->x_creators = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new"); 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) if ((cv = cvec_find(xn->x_creators, name)) == NULL)
cvec_add_string(xn->x_creators, name, NULL); cvec_add_string(xn->x_creators, name, NULL);
ok:
retval = 0; retval = 0;
done: done:
return retval; return retval;
@ -723,6 +725,16 @@ xml_creator_len(cxobj *xn)
return 0; 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 /*! Copy creator info from x0 to x1 single object
* *
* @param[in] x0 Source XML node * @param[in] x0 Source XML node
@ -830,7 +842,7 @@ creator_print_fn(cxobj *x,
return 2; /* Locally abort this subtree, continue with others */ 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 * @param[in] xn XML tree
* @retval see xml_apply * @retval see xml_apply
@ -1355,7 +1367,7 @@ clixon_child_xvec_append(cxobj *xn,
* xml_free(x); * xml_free(x);
* @endcode * @endcode
* @note Differentiates between body/attribute vs element to reduce mem allocation * @note Differentiates between body/attribute vs element to reduce mem allocation
* @see xml_sort_insert * @see xml_insert
*/ */
cxobj * cxobj *
xml_new(char *name, xml_new(char *name,
@ -2649,18 +2661,17 @@ xml_attr_insert2val(char *instr,
* @param[in] value Attribute value * @param[in] value Attribute value
* @param[in] prefix Attribute prefix, or NULL * @param[in] prefix Attribute prefix, or NULL
* @param[in] namespace Attribute prefix, if NULL do not add namespace mapping * @param[in] namespace Attribute prefix, if NULL do not add namespace mapping
* @retval 0 OK * @retval xa Created attribute object
* @retval -1 Error * @retval NULL Error
*/ */
int cxobj *
xml_add_attr(cxobj *xn, xml_add_attr(cxobj *xn,
char *name, char *name,
char *value, char *value,
char *prefix, char *prefix,
char *namespace) char *namespace)
{ {
int retval = -1; cxobj *xa = NULL;
cxobj *xa;
char *ns = NULL; char *ns = NULL;
if ((xa = xml_new(name, xn, CX_ATTR)) == 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) if (ns == NULL && xmlns_set(xn, prefix, namespace) < 0)
goto done; goto done;
} }
retval = 0; if (xml_sort(xn) < 0)
goto done;
ret:
return xa;
done: done:
return retval; if (xa){
xml_free(xa);
xa = NULL;
}
goto ret;
} }
/*! Specialization of clicon_log with xml tree /*! Specialization of clicon_log with xml tree

View file

@ -227,8 +227,13 @@ populate_self_parent(cxobj *xt,
} }
/* Assign spec only if namespaces match */ /* Assign spec only if namespaces match */
if (strcmp(ns, nsy) != 0){ 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 && 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 done;
goto fail; goto fail;
} }

View file

@ -615,7 +615,7 @@ xml_add_default_tag(cxobj *x,
if (xml_flag(x, flags)) { if (xml_flag(x, flags)) {
/* set default attribute */ /* 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; goto done;
} }
retval = 0; retval = 0;

View file

@ -358,12 +358,14 @@ xml_parse_attr(clixon_xml_yacc *xy,
int retval = -1; int retval = -1;
cxobj *xa = NULL; 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_find_type(xy->xy_xelement, prefix, name, CX_ATTR)) == NULL){
if ((xa = xml_new(name, xy->xy_xelement, CX_ATTR)) == NULL) if ((xa = xml_new(name, xy->xy_xelement, CX_ATTR)) == NULL)
goto done; goto done;
if (xml_prefix_set(xa, prefix) < 0) if (xml_prefix_set(xa, prefix) < 0)
goto done; goto done;
} }
if (xml_value_set(xa, attval) < 0) if (xml_value_set(xa, attval) < 0)
goto done; 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; } 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; } _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 */} 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. /*! 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] 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] 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] key_val Key if x is LIST and ins is before/after, val if LEAF_LIST
* @param[in] nsc_key Network namespace for key * @param[in] nsc_key Network namespace for key

139
test/test_datastore_creator.sh Executable file
View file

@ -0,0 +1,139 @@
#!/usr/bin/env bash
# test for data creator attribute: add same object from sessions s1 and s2
# Restart and ensure attributes remain
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
cfg=$dir/conf.xml
fyang=$dir/clixon-example.yang
: ${clixon_util_xpath:=clixon_util_xpath}
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_CLISPEC_DIR>$clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>example</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/run/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/run/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_NETCONF_CREATOR_ATTR>true</CLICON_NETCONF_CREATOR_ATTR>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
container table {
list parameter{
key name;
leaf name{
type string;
}
leaf value{
type string;
}
}
}
}
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
new "wait backend 1"
wait_backend
conf="-d candidate -b $dir -y $fyang"
new "s1 add x"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><default-operation>none</default-operation><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter nc:operation=\"create\" xmlns:nc=\"${BASENS}\" cl:creator=\"s1\" xmlns:cl=\"http://clicon.org/lib\"><name>x</name><value>foo</value></parameter></table></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "datastore get"
expectpart "$(sudo $clixon_util_xpath -f $dir/candidate_db -p /config/creators)" 0 "<creators xmlns=\"http://clicon.org/lib\"><creator><name>s1</name><path>/table/parameter\[name=\"x\"\]</path></creator></creators>"
new "rpc get-config"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "<table xmlns=\"urn:example:clixon\"><parameter><name>x</name><value>foo</value></parameter></table>" ""
# duplicate
new "s1 merge x"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><default-operation>none</default-operation><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter nc:operation=\"merge\" xmlns:nc=\"${BASENS}\" cl:creator=\"s1\" xmlns:cl=\"http://clicon.org/lib\"><name>x</name><value>foo</value></parameter></table></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "datastore get"
expectpart "$(sudo $clixon_util_xpath -f $dir/candidate_db -p /config/creators)" 0 "<creators xmlns=\"http://clicon.org/lib\"><creator><name>s1</name><path>/table/parameter\[name=\"x\"\]</path></creator></creators>"
# New service
new "s2 merge x"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><default-operation>none</default-operation><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter nc:operation=\"merge\" xmlns:nc=\"${BASENS}\" cl:creator=\"s2\" xmlns:cl=\"http://clicon.org/lib\"><name>x</name><value>foo</value></parameter></table></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "datastore get"
expectpart "$(sudo $clixon_util_xpath -f $dir/candidate_db -p /config/creators)" 0 "<creators xmlns=\"http://clicon.org/lib\"><creator><name>s1</name><path>/table/parameter\[name=\"x\"\]</path></creator><creator><name>s2</name><path>/table/parameter\[name=\"x\"\]</path></creator></creators>"
# New entry
new "s1 create y=bar"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><default-operation>none</default-operation><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter nc:operation=\"create\" xmlns:nc=\"${BASENS}\" cl:creator=\"s1\" xmlns:cl=\"http://clicon.org/lib\"><name>y</name><value>bar</value></parameter></table></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "datastore get"
expectpart "$(sudo $clixon_util_xpath -f $dir/candidate_db -p /config/creators)" 0 "<creators xmlns=\"http://clicon.org/lib\"><creator><name>s1</name><path>/table/parameter\[name=\"x\"\]</path><path>/table/parameter\[name=\"y\"\]</path></creator><creator><name>s2</name><path>/table/parameter\[name=\"x\"\]</path></creator></creators>"
# To running
new "commit to running"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "datastore get running"
expectpart "$(sudo $clixon_util_xpath -f $dir/running_db -p /config/creators)" 0 "<creators xmlns=\"http://clicon.org/lib\"><creator><name>s1</name><path>/table/parameter\[name=\"x\"\]</path><path>/table/parameter\[name=\"y\"\]</path></creator><creator><name>s2</name><path>/table/parameter\[name=\"x\"\]</path></creator></creators>"
new "rpc get-config"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<table xmlns=\"urn:example:clixon\"><parameter><name>x</name><value>foo</value></parameter><parameter><name>y</name><value>bar</value></parameter></table>" ""
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
fi
if [ $BE -ne 0 ]; then
new "start backend -s running -f $cfg"
start_backend -s running -f $cfg
fi
new "wait backend 2"
wait_backend
new "datastore get running"
expectpart "$(sudo $clixon_util_xpath -f $dir/running_db -p /config/creators)" 0 "<creators xmlns=\"http://clicon.org/lib\"><creator><name>s1</name><path>/table/parameter\[name=\"x\"\]</path><path>/table/parameter\[name=\"y\"\]</path></creator><creator><name>s2</name><path>/table/parameter\[name=\"x\"\]</path></creator></creators>"
new "rpc get-config"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<table xmlns=\"urn:example:clixon\"><parameter><name>x</name><value>foo</value></parameter><parameter><name>y</name><value>bar</value></parameter></table>" ""
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
fi
rm -rf $dir
new "endtest"
endtest

View file

@ -42,7 +42,7 @@ datarootdir = @datarootdir@
YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
# Note: mirror these to test/config.sh.in # Note: mirror these to test/config.sh.in
YANGSPECS = clixon-config@2023-05-01.yang # 6.3 YANGSPECS = clixon-config@2023-11-01.yang # 6.5
YANGSPECS += clixon-lib@2023-11-01.yang # 6.5 YANGSPECS += clixon-lib@2023-11-01.yang # 6.5
YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang

View file

@ -9,6 +9,9 @@ module clixon-config {
import clixon-autocli { import clixon-autocli {
prefix autocli; prefix autocli;
} }
import clixon-lib {
prefix cl;
}
organization organization
"Clicon / Clixon"; "Clicon / Clixon";
@ -46,6 +49,20 @@ module clixon-config {
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
revision 2023-11-01 {
description
"Added options:
CLICON_CREATOR_ATTR
Released in Clixon 6.5";
}
revision 2023-05-01 {
description
"Added options:
CLICON_CONFIG_EXTEND
CLICON_PLUGIN_DLOPEN_GLOBAL
Moved datastore-format datatype to clixon-lib
Released in Clixon 6.3";
}
revision 2023-03-01 { revision 2023-03-01 {
description description
"Added options: "Added options:
@ -274,27 +291,6 @@ module clixon-config {
} }
} }
} }
typedef datastore_format{
description
"Datastore format (only xml and json implemented in actual data.";
type enumeration{
enum xml{
description
"Save and load xmldb as XML
More specifically, such a file looks like: <config>...</config> provided
DATASTORE_TOP_SYMBOL is 'config'";
}
enum json{
description "Save and load xmldb as JSON";
}
enum text{
description "'Curly' C-like text format";
}
enum cli{
description "CLI format";
}
}
}
typedef datastore_cache{ typedef datastore_cache{
description description
"XML configuration, ie running/candididate/ datastore cache behaviour."; "XML configuration, ie running/candididate/ datastore cache behaviour.";
@ -461,6 +457,15 @@ module clixon-config {
You can override file setting with -E <dir> command-line option. You can override file setting with -E <dir> command-line option.
Note that due to bootstraping this value is only meaningful in the main config file"; Note that due to bootstraping this value is only meaningful in the main config file";
} }
leaf CLICON_CONFIG_EXTEND {
type string;
description
"If specified load an application-specific configuration YANG that overrides
this config.
Normally, that YANG imports clixon-config.
This field is a 'bootstrap' field.
";
}
leaf CLICON_YANG_MAIN_FILE { leaf CLICON_YANG_MAIN_FILE {
type string; type string;
description description
@ -520,7 +525,13 @@ module clixon-config {
leaf CLICON_YANG_SCHEMA_MOUNT{ leaf CLICON_YANG_SCHEMA_MOUNT{
type boolean; type boolean;
description description
"YANG schema mount, RFC 8528"; "YANG schema mount, RFC 8528.
When enabled, mount-points as defined by the 'yangmnt:mount-point' extension can
be populated by other YANGs than the root.
This is controlled by the ca_yang_mount plugin callback by returning a assigning a
yanglib module-set section that corresponds to the mounted YANGs.
Also, schema mount statistics is added to state data
Further, autocli syntax is added by definining a tree resolve wrapper";
default false; default false;
} }
leaf CLICON_BACKEND_REGEXP { leaf CLICON_BACKEND_REGEXP {
@ -571,6 +582,18 @@ module clixon-config {
RFC6242 for example. RFC6242 for example.
This only applies to the external NETCONF"; This only applies to the external NETCONF";
} }
leaf CLICON_NETCONF_CREATOR_ATTR {
type boolean;
default false;
description
"If set, clixon will accept the 'creator' attribute as defined by the
creator annotation in clixon-lib.
It can be used when several clients (such as a 'service') can create the same object.
If one such client/service is deleted, the object is deleted only if all services
that created the object are deleted.
The clixon controller uses this feature, but could in principle be used by other
applications.";
}
leaf CLICON_RESTCONF_API_ROOT { leaf CLICON_RESTCONF_API_ROOT {
type string; type string;
default "/restconf"; default "/restconf";
@ -612,7 +635,7 @@ module clixon-config {
edit operation. edit operation.
Setting this option disables this behaviour, ie the startup configuration is NOT Setting this option disables this behaviour, ie the startup configuration is NOT
automatically updated. automatically updated.
If this option is false, the startup is autoamtically updated following the RFC"; If this option is false, the startup is automatically updated following the RFC";
} }
leaf CLICON_RESTCONF_USER { leaf CLICON_RESTCONF_USER {
type string; type string;
@ -931,7 +954,7 @@ module clixon-config {
Others are experimental (in Clixon 5.5)"; Others are experimental (in Clixon 5.5)";
} }
leaf CLICON_XMLDB_FORMAT { leaf CLICON_XMLDB_FORMAT {
type datastore_format; type cl:datastore_format;
default xml; default xml;
description "XMLDB datastore format."; description "XMLDB datastore format.";
} }
@ -1005,6 +1028,18 @@ module clixon-config {
as well as the CLIgen callbacks. as well as the CLIgen callbacks.
See https://clixon-docs.readthedocs.io/en/latest/backend.html#plugin-callback-guidelines"; See https://clixon-docs.readthedocs.io/en/latest/backend.html#plugin-callback-guidelines";
} }
leaf CLICON_PLUGIN_DLOPEN_GLOBAL {
type boolean;
default false;
description
"Local/global flag for dlopen as described in the man page.
This applies to the opening of all clixon plugins (backend/cli/netconf/restconf)
when loading the shared .so file with dlopen.
If false: Symbols defined in this shared object are not made available to resolve
references in subsequently loaded shared objects (default).
If true: The symbols defined by this shared object will be made available for symbol res
olution of subsequently loaded shared objects.";
}
leaf CLICON_YANG_AUGMENT_ACCEPT_BROKEN { leaf CLICON_YANG_AUGMENT_ACCEPT_BROKEN {
type boolean; type boolean;
default false; default false;
@ -1094,7 +1129,7 @@ module clixon-config {
restconf GET. restconf GET.
The module state data is on the form: The module state data is on the form:
<yang-library><module-set>... <yang-library><module-set>...
instead where the modile state is on the form: instead where the module state is on the form:
<modules-state>... <modules-state>...
See also CLICON_XMLDB_MODSTATE where the module state info is used to tag datastores See also CLICON_XMLDB_MODSTATE where the module state info is used to tag datastores
with module information."; with module information.";
@ -1131,7 +1166,7 @@ module clixon-config {
default false; default false;
description description
"Enable event stream discovery as described in RFC 5277 "Enable event stream discovery as described in RFC 5277
sections 3.2. If enabled, available streams will appear section 3.2. If enabled, available streams will appear
when doing netconf get or restconf GET"; when doing netconf get or restconf GET";
} }
leaf CLICON_STREAM_DISCOVERY_RFC8040 { leaf CLICON_STREAM_DISCOVERY_RFC8040 {

View file

@ -71,6 +71,7 @@ module clixon-lib {
revision 2023-11-01 { revision 2023-11-01 {
description description
"Added ignore-compare extension "Added ignore-compare extension
Added creator meta configuration
Removed obsolete extension autocli-op Removed obsolete extension autocli-op
Released in 6.5.0"; Released in 6.5.0";
} }
@ -209,6 +210,21 @@ module clixon-lib {
Limitations: only objects that are actually added or deleted. Limitations: only objects that are actually added or deleted.
A sub-object will not be noted"; A sub-object will not be noted";
} }
container creators{
config false;
description "Meta-data for creator attribute.";
list creator {
key name;
leaf name {
description "Name of creator / service (instance) name";
type string;
}
leaf-list path {
description "Path to object";
type string;
}
}
}
rpc debug { rpc debug {
description "Set debug level of backend."; description "Set debug level of backend.";
input { input {