Skip to content

Commit

Permalink
Add a way to report rpc-errors from a plugin
Browse files Browse the repository at this point in the history
Add plugin_rpc_err(), which works something like clixon_err, but it
saves error information from a plugin for reporting rpc-errors that
need to be returned.

Signed-off-by: Corey Minyard <corey@minyard.net>
  • Loading branch information
cminyard committed Sep 27, 2024
1 parent fd219aa commit e9aa452
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 10 deletions.
10 changes: 8 additions & 2 deletions apps/backend/backend_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,8 +711,14 @@ from_client_edit_config(clixon_handle h,
}
}
if ((ret = candidate_commit(h, NULL, "candidate", myid, 0, cbret)) < 0){ /* Assume validation fail, nofatal */
if (netconf_operation_failed(cbret, "application", clixon_err_reason())< 0)
goto done;
if (clixon_err_category()) {
if (netconf_operation_failed(cbret, "application",
clixon_err_reason()) < 0)
goto done;
} else if (plugin_rpc_err_set()) {
if (netconf_gen_rpc_err(cbret) < 0)
goto done;
}
xmldb_copy(h, "running", "candidate");
goto ok;
}
Expand Down
158 changes: 152 additions & 6 deletions apps/backend/backend_commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,138 @@ validate_common(clixon_handle h,
goto done;
}

static char *_plugin_rpc_err_ns;
static char *_plugin_rpc_err_type;
static char *_plugin_rpc_err_tag;
static char *_plugin_rpc_err_info;
static char *_plugin_rpc_err_severity;
static cbuf *_plugin_rpc_err_message;

/*! Save an RPC error to be reported by clixon.
*
* @param[in] h Clixon
* @param[in] ns Namespace. If NULL the netconf base ns will be used
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] severity Severity: "error" or "warning"
* @param[in] tag Error tag, like "invalid-value" or "unknown-attribute"
* @param[in] info bad-attribute or bad-element xml. If NULL not included.
* @param[in] fmt Error message format. May be NULL.
* @retval 0 Success
* @retval -1 Error
*/
int
plugin_rpc_err(clixon_handle h, const char *ns,
const char *type, const char *tag, const char *info,
const char *severity,
const char *fmt, ...)
{
char *n_ns = NULL, *n_type = NULL, *n_tag = NULL;
char *n_info = NULL, *n_severity = NULL;
cbuf *n_message = NULL;
int err;

if (ns) {
n_ns = strdup(ns);
if (!n_ns)
goto out_nomem;
}
n_type = strdup(type);
if (!n_type)
goto out_nomem;
n_tag = strdup(tag);
if (!n_tag)
goto out_nomem;
if (ns) {
n_ns = strdup(ns);
if (!n_ns)
goto out_nomem;
}
if (info) {
n_info = strdup(info);
if (!n_info)
goto out_nomem;
}
n_severity = strdup(severity);
if (!n_severity)
goto out_nomem;
if (fmt) {
va_list args;
n_message = cbuf_new();
if (!n_message)
goto out_nomem;
va_start(args, fmt);
err = vcprintf(n_message, fmt, args);
va_end(args);
if (err < 0)
goto out_nomem;
}
/*
* Everything is allocated, we cannot fail from here, so clean up the
* old stuff.
*/
if (_plugin_rpc_err_ns)
free(_plugin_rpc_err_ns);
if (_plugin_rpc_err_type)
free(_plugin_rpc_err_type);
if (_plugin_rpc_err_tag)
free(_plugin_rpc_err_tag);
if (_plugin_rpc_err_info)
free(_plugin_rpc_err_info);
if (_plugin_rpc_err_severity)
free(_plugin_rpc_err_severity);
if (_plugin_rpc_err_message)
cbuf_free(_plugin_rpc_err_message);

_plugin_rpc_err_ns = n_ns;
_plugin_rpc_err_type = n_type;
_plugin_rpc_err_tag = n_tag;
_plugin_rpc_err_info = n_info;
_plugin_rpc_err_severity = n_severity;
_plugin_rpc_err_message = n_message;
return 0;

out_nomem:
if (n_ns)
free(n_ns);
if (n_type)
free(n_type);
if (n_tag)
free(n_tag);
if (n_info)
free(n_info);
if (n_severity)
free(n_severity);
if (n_message)
cbuf_free(n_message);
return -1;
}

int
plugin_rpc_err_set(void)
{
return _plugin_rpc_err_type != NULL;
}

int
netconf_gen_rpc_err(cbuf *cbret)
{
char *type = _plugin_rpc_err_type;
int ret;

/* Type marks it as set, clear it before handling. */
_plugin_rpc_err_type = NULL;
ret = netconf_common_rpc_err(cbret, _plugin_rpc_err_ns,
type,
_plugin_rpc_err_tag,
_plugin_rpc_err_severity,
_plugin_rpc_err_info,
cbuf_get(_plugin_rpc_err_message));
free(type);

return ret;
}


/*! Start a validate transaction
*
* @param[in] h Clixon handle
Expand Down Expand Up @@ -630,9 +762,16 @@ candidate_validate(clixon_handle h,
* use clixon_err.
* TODO: -1 return should be fatal error, not failed validation
*/
if (!cbuf_len(cbret) &&
netconf_operation_failed(cbret, "application", clixon_err_reason())< 0)
goto done;
if (!cbuf_len(cbret))
goto done;
if (clixon_err_category()) {
if (netconf_operation_failed(cbret, "application",
clixon_err_reason()) < 0)
goto done;
} else if (plugin_rpc_err_set()) {
if (netconf_gen_rpc_err(cbret) < 0)
goto done;
}
goto fail;
}
if (ret == 0){
Expand Down Expand Up @@ -844,9 +983,16 @@ from_client_commit(clixon_handle h,
}
if ((ret = candidate_commit(h, xe, "candidate", myid, 0, cbret)) < 0){ /* Assume validation fail, nofatal */
clixon_debug(CLIXON_DBG_BACKEND, "Commit candidate failed");
if (ret < 0)
if (netconf_operation_failed(cbret, "application", clixon_err_reason())< 0)
goto done;
if (ret < 0) {
if (clixon_err_category()) {
if (netconf_operation_failed(cbret, "application",
clixon_err_reason()) < 0)
goto done;
} else if (plugin_rpc_err_set()) {
if (netconf_gen_rpc_err(cbret) < 0)
goto done;
}
}
goto ok;
}
if (clicon_option_bool(h, "CLICON_AUTOLOCK"))
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/backend_plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,9 @@ plugin_transaction_call_one(clixon_handle h,
if (clixon_resource_check(h, &wh, clixon_plugin_name_get(cp), fnname) < 0)
goto done;
if (rv < 0) {
if (!clixon_err_category()) /* sanity: log if clixon_err() is not called ! */
clixon_log(h, LOG_NOTICE, "%s: Plugin '%s' callback does not make clixon_err call on error",
if (!plugin_rpc_err_set() && !clixon_err_category())
/* sanity: log if err is not called ! */
clixon_log(h, LOG_NOTICE, "%s: Plugin '%s' callback does not make clixon_err or plugin_rpc_err call on error",
fnname, clixon_plugin_name_get(cp));
goto done;
}
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/clixon_backend_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,10 @@ int plugin_transaction_end_all(clixon_handle h, transaction_data_t *td);
int plugin_transaction_abort_one(clixon_plugin_t *cp, clixon_handle h, transaction_data_t *td);
int plugin_transaction_abort_all(clixon_handle h, transaction_data_t *td);

int plugin_rpc_err(clixon_handle h, const char *ns,
const char *type, const char *tag, const char *info,
const char *severity, const char *fmt, ...);
int plugin_rpc_err_set(void);
int netconf_gen_rpc_err(cbuf *cbret);

#endif /* _CLIXON_BACKEND_PLUGIN_H_ */
2 changes: 2 additions & 0 deletions lib/clixon/clixon_netconf_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ int netconf_missing_attribute_xml(cxobj **xret, char *type, char *info, char *me
int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_bad_attribute_xml(cxobj **xret, char *type, char *info, char *message);
int netconf_unknown_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_common_rpc_err(cbuf *cb, char *ns, char *type, char *tag, char *info,
char *severity, char *message);
int netconf_missing_element(cbuf *cb, char *type, char *element, char *message);
int netconf_missing_yang_xml(cxobj **xret, char *path, char *app_tag, char *info, char *message);
int netconf_missing_element_xml(cxobj **xret, char *type, char *element, char *message);
Expand Down
56 changes: 56 additions & 0 deletions lib/src/clixon_netconf_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,62 @@ netconf_unknown_attribute(cbuf *cb,
goto done;
}

/*! Create Netconf rpc-error XML tree with all parameters available.
*
* An unexpected attribute is present.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] ns Namespace. If NULL the netconf base ns will be used
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] severity Severity: "error" or "warning"
* @param[in] tag Error tag, like "invalid-value" or "unknown-attribute"
* @param[in] info bad-attribute or bad-element xml. If NULL not included.
* @param[in] message Error message. May be NULL.
* @retval 0 OK
* @retval -1 Error
*/
int
netconf_common_rpc_err(cbuf *cb,
char *ns,
char *type,
char *tag,
char *severity,
char *info,
char *message)
{
int retval = -1;
char *encstr = NULL;

if (!ns)
ns = NETCONF_BASE_NAMESPACE;

if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
"<error-type>%s</error-type>"
"<error-tag>%s</error-tag>"
"<error-severity>%s</error-severity>",
ns, type, tag, severity) < 0)
goto err;
if (info){
if (cprintf(cb, "<error-info>%s</error-info>", info) < 0)
goto err;
}
if (message){
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
goto done;
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
goto err;
}
if (cprintf(cb, "</rpc-error></rpc-reply>") < 0)
goto err;
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
err:
clixon_err(OE_XML, errno, "cprintf");
goto done;
}

/*! Common Netconf element XML tree according to RFC 6241 App A
*
* @param[out] xret Error XML tree. Free with xml_free after use
Expand Down

0 comments on commit e9aa452

Please sign in to comment.