* Auto-cli changed singature of yang2cli()

* Auto-cli: create generated CLI for sub-parts of a YANG spec
This commit is contained in:
Olof hagsand 2020-11-26 17:32:09 +01:00
parent f69daf773e
commit dda3244252
9 changed files with 282 additions and 126 deletions

View file

@ -280,13 +280,15 @@ xml_yang_root(cxobj *x,
return retval;
}
/*! Construct an xml key format from yang statement using wildcards for keys
/*! Construct an api-path key format from yang statement using wildcards for keys
* Recursively construct it to the top.
* Example:
* yang: container a -> list b -> key c -> leaf d
* xpath: /modname:a/b/%s/d
* path: /modname:a/b/%s/d
* @param[in] ys Yang statement
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[in] yp0 Build the path of ys only to this level not root (optional)
* @param[in] yp0path Use this path if stop at yp0 (not root)
* @param[out] cb api_path_fmt,
* @retval 0 OK
* @retval -1 Error
@ -295,6 +297,8 @@ xml_yang_root(cxobj *x,
static int
yang2api_path_fmt_1(yang_stmt *ys,
int inclkey,
yang_stmt *yp0,
char *yp0_path,
cbuf *cb)
{
yang_stmt *yp; /* parent */
@ -308,11 +312,14 @@ yang2api_path_fmt_1(yang_stmt *ys,
clicon_err(OE_YANG, EINVAL, "yang expected parent %s", yang_argument_get(ys));
goto done;
}
if (yp != NULL && /* XXX rm */
if (yp == yp0){ /* Skip building path to root if match, use given path */
cprintf(cb, "%s/", yp0_path);
}
else if (yp != NULL && /* XXX rm */
yang_keyword_get(yp) != Y_MODULE &&
yang_keyword_get(yp) != Y_SUBMODULE){
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, yp0, yp0_path, cb) < 0) /* recursive call */
goto done;
if (yang_keyword_get(yp) != Y_CHOICE && yang_keyword_get(yp) != Y_CASE){
#if 0
@ -389,9 +396,11 @@ yang2api_path_fmt_1(yang_stmt *ys,
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
int
yang2api_path_fmt(yang_stmt *ys,
int inclkey,
char **api_path_fmt)
yang2api_path_fmt(yang_stmt *ys,
int inclkey,
yang_stmt *yp0,
char *yp0_path,
char **api_path_fmt)
{
int retval = -1;
cbuf *cb = NULL;
@ -400,7 +409,7 @@ yang2api_path_fmt(yang_stmt *ys,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (yang2api_path_fmt_1(ys, inclkey, cb) < 0)
if (yang2api_path_fmt_1(ys, inclkey, yp0, yp0_path, cb) < 0)
goto done;
if ((*api_path_fmt = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@ -917,7 +926,6 @@ api_path2xml_vec(char **vec,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* restval is RFC 3896 encoded */
if ((restval_enc = index(nodeid, '=')) != NULL){
*restval_enc = '\0';
@ -931,13 +939,15 @@ api_path2xml_vec(char **vec,
if (yang_keyword_get(y0) == Y_SPEC){ /* top-node */
if (prefix == NULL){
cprintf(cberr, "api-path element '%s', expected prefix:name", nodeid);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
if (xerr &&
netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){
cprintf(cberr, "No such yang module prefix");
if (netconf_unknown_element_xml(xerr, "application", prefix, cbuf_get(cberr)) < 0)
if (xerr &&
netconf_unknown_element_xml(xerr, "application", prefix, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@ -948,14 +958,16 @@ api_path2xml_vec(char **vec,
yang_find_schemanode(y0, name):
yang_find_datanode(y0, name);
if (y == NULL){
if (netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
if (xerr &&
netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
goto done;
goto fail;
}
if (prefix && namespace == NULL){
if ((ymod = yang_find_module_by_name(ys_spec(y0), prefix)) == NULL){
cprintf(cberr, "api-path element prefix: '%s', no such yang module", prefix);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
if (xerr &&
netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@ -963,14 +975,17 @@ api_path2xml_vec(char **vec,
}
switch (yang_keyword_get(y)){
case Y_LEAF_LIST:
if (0 && restval==NULL){
#if 0
if (restval==NULL){
clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
goto done;
}
#endif
if (x0 == NULL)
break;
if ((x = xml_new(yang_argument_get(y), x0, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(x, y);
if ((xb = xml_new("body", x, CX_BODY)) == NULL)
goto done;
if (restval && xml_value_set(xb, restval) < 0)
@ -985,7 +1000,8 @@ api_path2xml_vec(char **vec,
if (restval==NULL){
if (strict){
cprintf(cberr, "malformed key =%s, expected '=restval'", nodeid);
if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
if (xerr &&
netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@ -998,17 +1014,19 @@ api_path2xml_vec(char **vec,
goto done;
if ((nvalvec != cvec_len(cvk)) && strict){
cprintf(cberr, "List key %s length mismatch", name);
if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
if (xerr &&
netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
}
cvi = NULL;
/* create list object */
if ((x = xml_new(name, x0, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(x, y);
if (x0){
if ((x = xml_new(name, x0, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(x, y);
}
vi = 0;
/* Create keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL){
@ -1016,24 +1034,27 @@ api_path2xml_vec(char **vec,
if ((ykey = yang_find(y, Y_LEAF, keyname)) == NULL){
cprintf(cberr, "List statement \"%s\" has no key leaf \"%s\"",
yang_argument_get(y), keyname);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
if (xerr &&
netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
if ((xn = xml_new(keyname, x, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(xn, ykey);
if ((xb = xml_new("body", xn, CX_BODY)) == NULL)
goto done;
if (vi++ < nvalvec){
if (xml_value_set(xb, valvec[vi-1]) < 0)
if (x != NULL){
if ((xn = xml_new(keyname, x, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(xn, ykey);
if ((xb = xml_new("body", xn, CX_BODY)) == NULL)
goto done;
if (vi++ < nvalvec){
if (xml_value_set(xb, valvec[vi-1]) < 0)
goto done;
}
}
}
break;
default: /* eg Y_CONTAINER, Y_LEAF */
if ((x = xml_find_type(x0, NULL, name, CX_ELMNT)) == NULL){ /* eg key of list */
if (x0 &&
(x = xml_find_type(x0, NULL, name, CX_ELMNT)) == NULL){ /* eg key of list */
if ((x = xml_new(name, x0, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(x, y);
@ -1089,6 +1110,10 @@ api_path2xml_vec(char **vec,
* </subif-entry></config>
* xbotp: <subid/>
* ybotp: Y_LEAF subid
* @code
* if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
* err;
* @endcode
* @note "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* @see api_path2xpath For api-path to xpath translation (maybe could be combined?)
*/
@ -1109,17 +1134,13 @@ api_path2xml(char *api_path,
cbuf *cberr = NULL;
clicon_debug(2, "%s api_path:%s", __FUNCTION__, api_path);
if (xtop == NULL){
clicon_err(OE_XML, EINVAL, "xtop is NULL");
goto done;
}
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (*api_path != '/'){
cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@ -1130,7 +1151,7 @@ api_path2xml(char *api_path,
nvec--;
if (nvec < 1){
cprintf(cberr, "Malformed api-path: %s: too short)", api_path);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@ -1139,9 +1160,12 @@ api_path2xml(char *api_path,
xtop, yspec, nodeclass, strict,
xbotp, ybotp, xerr)) < 1)
goto done;
xml_yang_root(*xbotp, &xroot);
if (xmlns_assign(xroot) < 0)
goto done;
/* Fix namespace */
if (xbotp){
xml_yang_root(*xbotp, &xroot);
if (xmlns_assign(xroot) < 0)
goto done;
}
// ok:
retval = 1;
done: