Improved netconf error handling for data-not-unique and missing-choice

* Follows RFC7950 Sec 15.1 and 15.6 more closely
* Fixed: [When multiple lists have same key name, need more elaborate error message in case of configuration having duplicate keys](https://github.com/clicon/clixon/issues/362)
This commit is contained in:
Olof hagsand 2022-09-19 14:33:44 +02:00
parent e579d959fa
commit 51d1a2d41c
11 changed files with 143 additions and 65 deletions

View file

@ -540,7 +540,7 @@ text_modify(clicon_handle h,
/* RFC 8040 4.6 PATCH:
* If the target resource instance does not exist, the server MUST NOT create it.
*/
if (netconf_data_missing(cbret, NULL,
if (netconf_data_missing(cbret,
"RFC 8040 4.6. PATCH: If the target resource instance does not exist, the server MUST NOT create it") < 0)
goto done;
goto fail;
@ -714,7 +714,7 @@ text_modify(clicon_handle h,
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
@ -735,7 +735,7 @@ text_modify(clicon_handle h,
}
else {
if (op == OP_DELETE){
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
@ -941,7 +941,7 @@ text_modify(clicon_handle h,
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
@ -1060,7 +1060,7 @@ text_modify_top(clicon_handle h,
I.e., curl -u andy:bar -sS -X DELETE http://localhost/restconf/data
*/
case OP_DELETE:
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
break;

View file

@ -923,18 +923,16 @@ netconf_data_exists(cbuf *cb,
* does not exist. For example, a "delete" operation was attempted on
* data that does not exist.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] missing_choice If set, see RFC7950: 15.6 violates mandatory choice
* @param[in] message Error message
*/
int
netconf_data_missing(cbuf *cb,
char *missing_choice,
char *message)
{
int retval = -1;
cxobj *xret = NULL;
if (netconf_data_missing_xml(&xret, missing_choice, message) < 0)
if (netconf_data_missing_xml(&xret, message) < 0)
goto done;
if (clixon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0)
goto done;
@ -945,18 +943,16 @@ netconf_data_missing(cbuf *cb,
return retval;
}
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A / RFC 7950 15.6(choice)
*
* Request could not be completed because the relevant data model content
* does not exist. For example, a "delete" operation was attempted on
* data that does not exist.
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] missing_choice If set, see RFC7950: 15.6 violates mandatiry choice
* @param[in] message Error message
*/
int
netconf_data_missing_xml(cxobj **xret,
char *missing_choice,
char *message)
{
int retval = -1;
@ -984,12 +980,6 @@ netconf_data_missing_xml(cxobj **xret,
"<error-type>application</error-type>"
"<error-tag>data-missing</error-tag>") < 0)
goto done;
if (missing_choice) /* NYI: RFC7950: 15.6 <error-path> */
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
"<error-app-tag>missing-choice</error-app-tag>"
"<error-info><missing-choice>%s</missing-choice></error-info>",
missing_choice) < 0)
goto done;
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
"<error-severity>error</error-severity>") < 0)
goto done;
@ -1006,7 +996,80 @@ netconf_data_missing_xml(cxobj **xret,
free(encstr);
return retval;
}
/*! Create Netconf data-missing / missing-choice as defeind in RFC 7950 15.6
*
* If a NETCONF operation would result in configuration data where no
* nodes exists in a mandatory choice, the following error MUST be
* returned:
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] x Element with missing choice
* @param[in] name Name of missing mandatory choice
* @param[in] message Error message
*/
int
netconf_missing_choice_xml(cxobj **xret,
cxobj *x,
char *name,
char *message)
{
int retval = -1;
char *encstr = NULL;
cxobj *xerr;
cxobj *xa;
char *path = NULL;
char *encpath = NULL;
if (xret == NULL || name == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret or name is NULL");
goto done;
}
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done;
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)
goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done;
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
"<error-type>application</error-type>"
"<error-tag>data-missing</error-tag>"
"<error-app-tag>missing-choice</error-app-tag>"
"<error-path>%s</error-path>"
"<error-info><missing-choice xmlns=\"%s\">%s</missing-choice></error-info>"
"<error-severity>error</error-severity>",
encpath,
YANG_XML_NAMESPACE,
name) < 0)
goto done;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done;
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
"<error-message>%s</error-message>", encstr) < 0)
goto done;
}
retval = 0;
done:
if (path)
free(path);
if (encstr)
free(encstr);
if (encpath)
free(encpath);
return retval;
}
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
*
* Request could not be completed because the requested operation is not
@ -1272,7 +1335,7 @@ netconf_malformed_message_xml(cxobj **xret,
/*! Create Netconf data-not-unique error message according to RFC 7950 15.1
*
* A NETCONF operation would result in configuration data where a
* "unique" constraint is invalidated.
* "unique" constraint is invalidated.
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] x List element containing duplicate
* @param[in] cvk List of components in x that are non-unique
@ -1287,9 +1350,10 @@ netconf_data_not_unique_xml(cxobj **xret,
cg_var *cvi = NULL;
cxobj *xerr;
cxobj *xinfo;
cbuf *cb = NULL;
cxobj *xa;
char *path = NULL;
char *encpath = NULL;
if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
goto done;
@ -1312,23 +1376,31 @@ netconf_data_not_unique_xml(cxobj **xret,
"<error-app-tag>data-not-unique</error-app-tag>"
"<error-severity>error</error-severity>") < 0)
goto done;
/* error-info: <non-unique> Contains an instance identifier that points to a leaf
* that invalidates the "unique" constraint. This element is present once for each
* non-unique leaf. */
if (cvec_len(cvk)){
if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
if (xml2xpath(x, NULL, &path) < 0)
goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done;
}
while ((cvi = cvec_each(cvk, cvi)) != NULL){
if (clixon_xml_parse_va(YB_NONE, NULL, &xinfo, NULL,
"<non-unique>%s</non-unique>", cv_string_get(cvi)) < 0)
"<non-unique xmlns=\"%s\">%s/%s</non-unique>",
YANG_XML_NAMESPACE,
encpath,
cv_string_get(cvi)) < 0)
goto done;
}
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (path)
free(path);
if (encpath)
free(encpath);
return retval;
}
@ -1351,7 +1423,7 @@ netconf_minmax_elements_xml(cxobj **xret,
int retval = -1;
cxobj *xerr;
char *path = NULL;
cbuf *cb = NULL;
char *encpath = NULL;
cxobj *xa;
if (xret == NULL){
@ -1370,31 +1442,27 @@ netconf_minmax_elements_xml(cxobj **xret,
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (xml_parent(xp)){ /* Dont include root, eg <config> */
if (xml2xpath(xp, NULL, &path) < 0)
if (xml2xpath(xp, NULL, &path) < 0)
goto done;
if (xml_chardata_encode(&encpath, "%s", path) < 0)
goto done;
if (path)
cprintf(cb, "%s", path);
}
cprintf(cb, "/%s", name);
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>protocol</error-type>"
"<error-tag>operation-failed</error-tag>"
"<error-app-tag>too-%s-elements</error-app-tag>"
"<error-severity>error</error-severity>"
"<error-path>%s</error-path>",
"<error-path>%s/%s</error-path>",
max?"many":"few",
cbuf_get(cb)) < 0)
encpath?encpath:"",
name) < 0)
goto done;
retval = 0;
done:
if (path)
free(path);
if (cb)
cbuf_free(cb);
if (encpath)
free(encpath);
return retval;
}

View file

@ -818,7 +818,7 @@ check_mandatory(cxobj *xt,
if (x == NULL){
/* @see RFC7950: 15.6 Error Message for Data That Violates
* a Mandatory "choice" Statement */
if (xret && netconf_data_missing_xml(xret, yang_argument_get(yc), NULL) < 0)
if (xret && netconf_missing_choice_xml(xret, xt, yang_argument_get(yc), NULL) < 0)
goto done;
goto fail;
}

View file

@ -138,17 +138,15 @@ unique_search_xpath(cxobj *x,
goto done;
}
/*! New element last in list, check if already exists if sp return -1
/*! New element last in list, return error if already exists
*
* @param[in] vec Vector of existing entries (new is last)
* @param[in] i1 The new entry is placed at vec[i1]
* @param[in] vlen Lenght of entry
* @param[in] vlen Length of vec
* @param[in] sorted Sorted by system, ie sorted by key, otherwise no assumption
* @retval 0 OK, entry is unique
* @retval -1 Duplicate detected
* @note This is currently quadratic complexity. It could be improved by inserting new element sorted and binary search.
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
*/
static int
check_insert_duplicate(char **vec,
@ -684,9 +682,9 @@ xml_yang_minmax_recurse(cxobj *xt,
nr=1;
/* new list check */
if (ret &&
keyw == Y_LIST &&
(ret = xml_yang_minmax_newlist(x, xt, y, xret)) < 0)
goto done;
keyw == Y_LIST)
if ((ret = xml_yang_minmax_newlist(x, xt, y, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
yprev = y;