- removed api_path extension from internal netconf

- Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000
This commit is contained in:
Olof hagsand 2017-05-30 21:57:06 +02:00
parent 683376610c
commit 3453dae0db
36 changed files with 1048 additions and 1307 deletions

View file

@ -224,7 +224,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
*/
/* top: json -> value is also possible */
json : value J_EOF { clicon_debug(1,"json->object"); YYACCEPT; }
json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
;
value : J_TRUE { json_current_body(_JY, "true");}

View file

@ -119,9 +119,12 @@ format_str2int(char *str)
return fv?fv->fv_int:-1;
}
/*! Encode a clicon netconf message
/*! Encode a clicon netconf message using variable argument lists
* @param[in] format Variable agrument list format an XML netconf string
* @retval msg Clicon message to send to eg clicon_msg_send()
* @note if format includes %, they will be expanded according to printf rules.
* if this is a problem, use ("%s", xml) instaead of (xml)
* Notaly this may an issue of RFC 3896 encoded strings
*/
struct clicon_msg *
clicon_msg_encode(char *format, ...)

View file

@ -260,7 +260,7 @@ clicon_rpc_get_config(clicon_handle h,
if (xpath && strlen(xpath))
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
cprintf(cb, "</get-config></rpc>");
if ((msg = clicon_msg_encode(cbuf_get(cb))) == NULL)
if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@ -291,13 +291,12 @@ clicon_rpc_get_config(clicon_handle h,
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
* @param[in] api_path restconf API Path (or "")
* @param[in] xml XML string. Ex: <config><a>..</a><b>...</b></config>
* @retval 0 OK
* @retval -1 Error
* @note xml arg need to have <config> as top element
* @code
* if (clicon_rpc_edit_config(h, "running", OP_MERGE, "/",
* if (clicon_rpc_edit_config(h, "running", OP_MERGE,
* "<config><a>4</a></config>") < 0)
* err;
* @endcode
@ -306,7 +305,6 @@ int
clicon_rpc_edit_config(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
char *xmlstr)
{
int retval = -1;
@ -320,12 +318,10 @@ clicon_rpc_edit_config(clicon_handle h,
cprintf(cb, "<rpc><edit-config><target><%s/></target>", db);
cprintf(cb, "<default-operation>%s</default-operation>",
xml_operation2str(op));
if (api_path && strlen(api_path))
cprintf(cb, "<filter type=\"restconf\" select=\"%s\"/>", api_path);
if (xmlstr)
cprintf(cb, "%s", xmlstr);
cprintf(cb, "</edit-config></rpc>");
if ((msg = clicon_msg_encode(cbuf_get(cb))) == NULL)
if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;

View file

@ -374,7 +374,6 @@ xmldb_get(clicon_handle h,
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
@ -382,16 +381,14 @@ xmldb_get(clicon_handle h,
* cxobj *xt;
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
* err;
* if (xmldb_put(xh, "running", OP_MERGE, NULL, xt) < 0)
* if (xmldb_put(xh, "running", OP_MERGE, xt) < 0)
* err;
* @endcode
* @see xmldb_put_xkey for single key
*/
int
xmldb_put(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
cxobj *xt)
{
int retval = -1;
@ -417,12 +414,12 @@ xmldb_put(clicon_handle h,
if (clicon_xml2cbuf(cb, xt, 0, 0) < 0)
goto done;
clicon_log(LOG_WARNING, "%s: db:%s op:%d api_path:%s xml:%s", __FUNCTION__,
db, op, api_path, cbuf_get(cb));
clicon_log(LOG_WARNING, "%s: db:%s op:%d xml:%s", __FUNCTION__,
db, op, cbuf_get(cb));
cbuf_free(cb);
}
#endif
retval = xa->xa_put_fn(xh, db, op, api_path, xt);
retval = xa->xa_put_fn(xh, db, op, xt);
done:
return retval;
}

View file

@ -121,7 +121,9 @@ tleaf(cxobj *x)
* @param[in] level print 4 spaces per level in front of each line
*/
int
xml2txt(FILE *f, cxobj *x, int level)
xml2txt(FILE *f,
cxobj *x,
int level)
{
cxobj *xe = NULL;
int children=0;
@ -799,13 +801,13 @@ xml_diff(yang_spec *yspec,
* yang: container a -> list b -> key c -> leaf d
* xpath: /a/b/%s/d
* @param[in] ys Yang statement
* @param[in] inclkey If inclkey then include key leaf (eg last leaf d in ex)
* @param[out] cbuf keyfmt
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[out] cb api_path_fmt,
*/
static int
yang2xmlkeyfmt_1(yang_stmt *ys,
int inclkey,
cbuf *cb)
yang2api_path_fmt_1(yang_stmt *ys,
int inclkey,
cbuf *cb)
{
yang_node *yp; /* parent */
yang_stmt *ykey;
@ -817,7 +819,7 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
if (yp != NULL &&
yp->yn_keyword != Y_MODULE &&
yp->yn_keyword != Y_SUBMODULE){
if (yang2xmlkeyfmt_1((yang_stmt *)yp, 1, cb) < 0)
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0)
goto done;
}
if (inclkey){
@ -825,11 +827,14 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
cprintf(cb, "/%s", ys->ys_argument);
}
else{
if (ys->ys_keyword == Y_LEAF && yp && yp->yn_keyword == Y_LIST){
#if 1
if (ys->ys_keyword == Y_LEAF && yp &&
yp->yn_keyword == Y_LIST){
if (yang_key_match(yp, ys->ys_argument) == 0)
cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
}
else
else
#endif
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
cprintf(cb, "/%s", ys->ys_argument);
}
@ -866,19 +871,19 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
return retval;
}
/*! Construct an xml key format from yang statement using wildcards for keys
/*! Construct an api_path_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: /a/b=%s/d
* @param[in] ys Yang statement
* @param[in] inclkey If !inclkey then dont include key leaf
* @param[out] xkfmt XML key format. Needs to be freed after use.
* api_path: /a/b=%s/d
* @param[in] ys Yang statement
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[out] api_path_fmt XML api path. Needs to be freed after use.
*/
int
yang2xmlkeyfmt(yang_stmt *ys,
int inclkey,
char **xkfmt)
yang2api_path_fmt(yang_stmt *ys,
int inclkey,
char **api_path_fmt)
{
int retval = -1;
cbuf *cb = NULL;
@ -887,9 +892,9 @@ yang2xmlkeyfmt(yang_stmt *ys,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (yang2xmlkeyfmt_1(ys, inclkey, cb) < 0)
if (yang2api_path_fmt_1(ys, inclkey, cb) < 0)
goto done;
if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){
if ((*api_path_fmt = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
@ -904,19 +909,20 @@ yang2xmlkeyfmt(yang_stmt *ys,
/*! Transform an xml key format and a vector of values to an XML key
* Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey()
* Example:
* xmlkeyfmt: /aaa/%s
* xmlkeyfmt: /aaa/%s/name
* cvv: key=17
* xmlkey: /aaa/17
* @param[in] xkfmt XML key format, eg /aaa/%s
* @param[in] cvv cligen variable vector, one for every wildchar in xkfmt
* @param[out] xk XML key, eg /aaa/17. Free after use
* xmlkey: /aaa/17/name
* @param[in] api_path_fmt XML key format, eg /aaa/%s/name
* @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt
* @param[out] api_path api_path, eg /aaa/17. Free after use
* @param[out] yang_arg yang-stmt argument name. Free after use
* @note first and last elements of cvv are not used,..
* @see cli_dbxml where this function is called
*/
int
xmlkeyfmt2key(char *xkfmt,
cvec *cvv,
char **xk)
api_path_fmt2api_path(char *api_path_fmt,
cvec *cvv,
char **api_path)
{
int retval = -1;
char c;
@ -927,16 +933,15 @@ xmlkeyfmt2key(char *xkfmt,
char *str;
char *strenc=NULL;
/* Sanity check */
#if 1
j = 0; /* Count % */
for (i=0; i<strlen(xkfmt); i++)
if (xkfmt[i] == '%')
for (i=0; i<strlen(api_path_fmt); i++)
if (api_path_fmt[i] == '%')
j++;
if (j+2 < cvec_len(cvv)) {
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
xkfmt,
api_path_fmt,
j,
cvec_len(cvv),
cv_string_get(cvec_i(cvv, 0)));
@ -948,8 +953,8 @@ xmlkeyfmt2key(char *xkfmt,
goto done;
}
j = 1; /* j==0 is cli string */
for (i=0; i<strlen(xkfmt); i++){
c = xkfmt[i];
for (i=0; i<strlen(api_path_fmt); i++){
c = api_path_fmt[i];
if (esc){
esc = 0;
if (c!='s')
@ -967,10 +972,13 @@ xmlkeyfmt2key(char *xkfmt,
else
if (c == '%')
esc++;
else if (c == '/'){
cprintf(cb, "%c", c);
}
else
cprintf(cb, "%c", c);
}
if ((*xk = strdup(cbuf_get(cb))) == NULL){
if ((*api_path = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
@ -993,14 +1001,14 @@ xmlkeyfmt2key(char *xkfmt,
* xmlkeyfmt: /ip/me/%s (if key)
* cvv: -
* xmlkey: /ipv4/me/a
* @param[in] xkfmt XML key format
* @param[in] cvv cligen variable vector, one for every wildchar in xkfmt
* @param[out] xk XPATH
* @param[in] api_path_fmt XML key format
* @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt
* @param[out] xpath XPATH
*/
int
xmlkeyfmt2xpath(char *xkfmt,
cvec *cvv,
char **xk)
api_path_fmt2xpath(char *api_path_fmt,
cvec *cvv,
char **xpath)
{
int retval = -1;
char c;
@ -1015,12 +1023,12 @@ xmlkeyfmt2xpath(char *xkfmt,
/* Sanity check: count '%' */
#if 1
j = 0; /* Count % */
for (i=0; i<strlen(xkfmt); i++)
if (xkfmt[i] == '%')
for (i=0; i<strlen(api_path_fmt); i++)
if (api_path_fmt[i] == '%')
j++;
if (j < cvec_len(cvv)-1) {
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
xkfmt,
api_path_fmt,
j,
cvec_len(cvv),
cv_string_get(cvec_i(cvv, 0)));
@ -1032,8 +1040,8 @@ xmlkeyfmt2xpath(char *xkfmt,
goto done;
}
j = 1; /* j==0 is cli string */
for (i=0; i<strlen(xkfmt); i++){
c = xkfmt[i];
for (i=0; i<strlen(api_path_fmt); i++){
c = api_path_fmt[i];
if (esc){
esc = 0;
if (c!='s')
@ -1059,13 +1067,13 @@ xmlkeyfmt2xpath(char *xkfmt,
if (skip)
skip=0;
else
if ((c == '=' || c == ',') && xkfmt[i+1]=='%')
if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%')
; /* skip */
else
cprintf(cb, "%c", c);
}
}
if ((*xk = strdup4(cbuf_get(cb))) == NULL){
if ((*xpath = strdup4(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
@ -1094,13 +1102,17 @@ xml_tree_prune_flagged(cxobj *xt,
int test,
int *upmark)
{
int retval = -1;
int submark;
int mark;
cxobj *x;
cxobj *xprev;
int retval = -1;
int submark;
int mark;
cxobj *x;
cxobj *xprev;
int iskey;
int anykey=0;
yang_node *yt;
mark = 0;
yt = xml_spec(xt);
x = NULL;
xprev = x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
@ -1109,8 +1121,20 @@ xml_tree_prune_flagged(cxobj *xt,
xprev = x;
continue; /* mark and stop here */
}
/* If it is key dont remove it yet (see second round) */
if (yt){
if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
goto done;
if (iskey){
anykey++;
xprev = x; /* skip if this is key */
continue;
}
}
if (xml_tree_prune_flagged(x, flag, test, &submark) < 0)
goto done;
/* if xt is list and submark anywhere, then key subs are also marked
*/
if (submark)
mark++;
else{ /* Safe with xml_child_each if last */
@ -1120,6 +1144,22 @@ xml_tree_prune_flagged(cxobj *xt,
}
xprev = x;
}
/* Second round: if any keys were found, and no marks detected, purge now */
if (anykey && !mark){
x = NULL;
xprev = x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
/* If it is key remove it here */
if (yt){
if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
goto done;
if (iskey && xml_purge(x) < 0)
goto done;
x = xprev;
}
xprev = x;
}
}
retval = 0;
done:
if (upmark)
@ -1397,3 +1437,181 @@ api_path2xpath(yang_spec *yspec,
cvec_free(api_path_cvv);
return retval;
}
/*! Create xml tree from api-path as vector
* @param[in] vec APIpath as char* vector
* @param[in] nvec Length of vec
* @param[in] x0 Xpath tree so far
* @param[in] y0 Yang spec for x0
* @param[out] xpathp Resulting xml tree
* @param[out] ypathp Yang spec matching xpathp
* @see api_path2xml
*/
static int
api_path2xml_vec(char **vec,
int nvec,
cxobj *x0,
yang_node *y0,
cxobj **xpathp,
yang_node **ypathp)
{
int retval = -1;
int j;
char *name;
char *restval = NULL;
char *restval_enc;
yang_stmt *ykey;
cxobj *xn = NULL; /* new */
cxobj *xb; /* body */
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
char *val2;
char **valvec = NULL;
int nvalvec;
cxobj *x = NULL;
yang_stmt *y = NULL;
if ((name = vec[0]) == NULL){
*xpathp = x0;
*ypathp = y0;
return 0;
} /* E.g "x=1,2" -> name:x restval=1,2 */
/* restval is RFC 3896 encoded */
if ((restval_enc = index(name, '=')) != NULL){
*restval_enc = '\0';
restval_enc++;
if (percent_decode(restval_enc, &restval) < 0)
goto done;
}
if (y0->yn_keyword == Y_SPEC) /* top-node */
y = yang_find_topnode((yang_spec*)y0, name);
else
y = yang_find_syntax((yang_node*)y0, name);
if (y == NULL){
clicon_err(OE_YANG, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){
case Y_LEAF_LIST:
if (restval==NULL){
clicon_err(OE_XML, 0, "malformed key, expected '=<restval>'");
goto done;
}
if ((x = xml_new_spec(y->ys_argument, x0, y)) == NULL)
goto done;
xml_type_set(x, CX_ELMNT);
if ((xb = xml_new("body", x)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if (xml_value_set(xb, restval) < 0)
goto done;
break;
case Y_LIST:
/* Get the yang list key */
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
if (restval==NULL){
clicon_err(OE_XML, 0, "malformed key, expected '=<restval>'");
goto done;
}
if (valvec)
free(valvec);
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
goto done;
if (cvec_len(cvk) != nvalvec){
clicon_err(OE_XML, errno, "List %s key length mismatch", name);
goto done;
}
cvi = NULL;
/* create list object */
if ((x = xml_new_spec(name, x0, y)) == NULL)
goto done;
xml_type_set(x, CX_ELMNT);
j = 0;
/* Create keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
val2 = valvec[j++];
if ((xn = xml_new(keyname, x)) == NULL)
goto done;
xml_type_set(xn, CX_ELMNT);
if ((xb = xml_new("body", xn)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if (xml_value_set(xb, val2) <0)
goto done;
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
break;
default: /* eg Y_CONTAINER, Y_LEAF */
if ((x = xml_new_spec(name, x0, y)) == NULL)
goto done;
xml_type_set(x, CX_ELMNT);
break;
}
if (api_path2xml_vec(vec+1, nvec-1,
x, (yang_node*)y,
xpathp, ypathp) < 0)
goto done;
retval = 0;
done:
if (restval)
free(restval);
if (valvec)
free(valvec);
return retval;
}
/*! Create xml tree from api-path
* @param[in] api_path API-path as defined in RFC 8040
* @param[out] xpathp Resulting xml tree
* @param[out] ypathp Yang spec matching xpathp
* @see api_path2xml_vec
*/
int
api_path2xml(char *api_path,
yang_spec *yspec,
cxobj *xpath,
cxobj **xpathp,
yang_node **ypathp)
{
int retval = -1;
char **vec = NULL;
int nvec;
clicon_debug(1, "%s 0", __FUNCTION__);
if (*api_path!='/'){
clicon_err(OE_DB, 0, "Invalid key: %s", api_path);
goto done;
}
if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL)
goto done;
/* Remove trailing '/'. Like in /a/ -> /a */
if (nvec > 1 && !strlen(vec[nvec-1]))
nvec--;
if (nvec < 1){
clicon_err(OE_XML, 0, "Malformed key: %s", api_path);
goto done;
}
nvec--; /* NULL-terminated */
if (api_path2xml_vec(vec+1, nvec,
xpath, (yang_node*)yspec,
xpathp, ypathp) < 0)
goto done;
retval = 0;
done:
if (vec)
free(vec);
return retval;
}

View file

@ -79,23 +79,6 @@ clixon_xml_parseerror(void *_ya, char *s)
return;
}
static int
xml_attr_new(struct xml_parse_yacc_arg *ya,
cxobj *xn,
char *name,
char *val)
{
cxobj *xa;
if ((xa = xml_new(name, xn)) == NULL)
return -1;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, val) < 0)
return -1;
return 0;
}
/* note that we dont handle escaped characters correctly
there may also be some leakage here on NULL return
*/
@ -194,17 +177,21 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
xml_namespace(x), xml_name(x), name);
goto done;
}
/* remove all non-terminal bodies (strip pretty-print) */
/* Strip pretty-print. Ad-hoc algorithm
* It ok with x:[body], but not with x:[ex,body]
* It is also ok with x:[attr,body]
* So the rule is: if there is at least on element, then remove all bodies?
*/
if (ya->ya_skipspace){
if (xml_child_nr(x) == 1 && (xml_type(xml_child_i(x, 0))==CX_BODY))
;
else{
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL)
break;
if (xc != NULL){ /* at least one element */
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) {
xml_purge(xc);
xc = NULL; /* reset iterator */
}
}
}
}
retval = 0;
@ -240,14 +227,20 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
name);
goto done;
}
/* remove all non-terminal bodies (strip pretty-print) */
/* Strip pretty-print. Ad-hoc algorithm
* It ok with x:[body], but not with x:[ex,body]
* It is also ok with x:[attr,body]
* So the rule is: if there is at least on element, then remove all bodies?
*/
if (ya->ya_skipspace){
if (xml_child_nr(x) == 1 && (xml_type(xml_child_i(x, 0))==CX_BODY))
;
else{
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL)
break;
if (xc != NULL){ /* at least one element */
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL)
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) {
xml_value_set(xc, ""); /* XXX remove */
}
}
}
retval = 0;
@ -276,8 +269,12 @@ static int
xml_parse_attr(struct xml_parse_yacc_arg *ya, char *id, char *val)
{
int retval = -1;
cxobj *xa;
if (xml_attr_new(ya, ya->ya_xelement, id, val) < 0)
if ((xa = xml_new(id, ya->ya_xelement)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, val) < 0)
goto done;
retval = 0;
done: