summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2024-02-16 16:39:00 +0200
committerGitHub <noreply@github.com>2024-02-16 16:39:00 +0200
commit81cb35dccacb42879c583a4ddd848d087787f6ab (patch)
tree0a88238efaf88e71166032f8fc3624b40205833e
parent23e9e990bad6e988801898514b8c0f295dedb885 (diff)
DYNCFG support deleting orphan configurations (#17023)
* dyncfg: allow deleting orphan configs * restore applied text * make the dyncfg applied text more descriptive
-rw-r--r--src/daemon/config/dyncfg-intercept.c65
-rw-r--r--src/daemon/config/dyncfg-tree.c36
2 files changed, 73 insertions, 28 deletions
diff --git a/src/daemon/config/dyncfg-intercept.c b/src/daemon/config/dyncfg-intercept.c
index 57134e05c8..dd7052e728 100644
--- a/src/daemon/config/dyncfg-intercept.c
+++ b/src/daemon/config/dyncfg-intercept.c
@@ -225,38 +225,44 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
char *cmd_str = get_word(words, num_words, i++);
if(!config || !*config || strcmp(config, PLUGINSD_FUNCTION_CONFIG) != 0)
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: this is not a dyncfg request");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: this is not a dyncfg request");
cmd = dyncfg_cmds2id(cmd_str);
if(cmd == DYNCFG_CMD_NONE)
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: invalid command received");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: invalid command received");
if(cmd == DYNCFG_CMD_ADD) {
add_name = get_word(words, num_words, i++);
if(!add_name || !*add_name)
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: this action requires a name");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: this action requires a name");
if(!called_from_dyncfg_echo) {
char nid[strlen(id) + strlen(add_name) + 2];
snprintfz(nid, sizeof(nid), "%s:%s", id, add_name);
if (dictionary_get(dyncfg_globals.nodes, nid))
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: a configuration with this name already exists");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: a configuration with this name already exists");
}
}
if((cmd == DYNCFG_CMD_ADD || cmd == DYNCFG_CMD_UPDATE || cmd == DYNCFG_CMD_TEST) && !has_payload)
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: this action requires a payload");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: this action requires a payload");
if((cmd != DYNCFG_CMD_ADD && cmd != DYNCFG_CMD_UPDATE && cmd != DYNCFG_CMD_TEST) && has_payload)
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: this action does not require a payload");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: this action does not require a payload");
item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
if(!item) {
@@ -266,7 +272,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
}
if(!item)
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_NOT_FOUND, "dyncfg functions intercept: id is not found");
+ return dyncfg_intercept_early_error(
+ rfe, HTTP_RESP_NOT_FOUND,
+ "dyncfg functions intercept: id is not found");
}
DYNCFG *df = dictionary_acquired_item_value(item);
@@ -278,8 +286,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
case DYNCFG_CMD_SCHEMA:
if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->view_access)) {
make_the_call_to_plugin = false;
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_FORBIDDEN,
- "dyncfg: you don't have enough view permissions to execute this command");
+ rc = dyncfg_default_response(
+ rfe->result.wb, HTTP_RESP_FORBIDDEN,
+ "dyncfg: you don't have enough view permissions to execute this command");
}
break;
@@ -292,15 +301,17 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
case DYNCFG_CMD_RESTART:
if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->edit_access)) {
make_the_call_to_plugin = false;
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_FORBIDDEN,
- "dyncfg: you don't have enough edit permissions to execute this command");
+ rc = dyncfg_default_response(
+ rfe->result.wb, HTTP_RESP_FORBIDDEN,
+ "dyncfg: you don't have enough edit permissions to execute this command");
}
break;
default: {
make_the_call_to_plugin = false;
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR,
- "dyncfg: permissions for this command are not set");
+ rc = dyncfg_default_response(
+ rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR,
+ "dyncfg: permissions for this command are not set");
}
break;
}
@@ -320,8 +331,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
else if (cmd == DYNCFG_CMD_ADD) {
if (df->type != DYNCFG_TYPE_TEMPLATE) {
make_the_call_to_plugin = false;
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: add command is only allowed in templates");
+ rc = dyncfg_default_response(
+ rfe->result.wb, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: add command is only allowed in templates");
nd_log(NDLS_DAEMON, NDLP_ERR,
"DYNCFG: add command can only be applied on templates, not %s: %s",
@@ -331,11 +343,14 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
else if (
cmd == DYNCFG_CMD_ENABLE && df->type == DYNCFG_TYPE_JOB &&
dyncfg_is_user_disabled(string2str(df->template))) {
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot enable a job of a disabled template: %s", rfe->function);
+ nd_log(NDLS_DAEMON, NDLP_ERR,
+ "DYNCFG: cannot enable a job of a disabled template: %s",
+ rfe->function);
make_the_call_to_plugin = false;
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST,
- "dyncfg functions intercept: this job belongs to disabled template");
+ rc = dyncfg_default_response(
+ rfe->result.wb, HTTP_RESP_BAD_REQUEST,
+ "dyncfg functions intercept: this job belongs to disabled template");
}
}
@@ -356,7 +371,7 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
dyncfg_apply_action_on_all_template_jobs(rfe, id, cmd);
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied");
+ rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied to all template job");
make_the_call_to_plugin = false;
}
else if (cmd == DYNCFG_CMD_SCHEMA) {
diff --git a/src/daemon/config/dyncfg-tree.c b/src/daemon/config/dyncfg-tree.c
index 27027b2f04..0983a9ee1f 100644
--- a/src/daemon/config/dyncfg-tree.c
+++ b/src/daemon/config/dyncfg-tree.c
@@ -26,7 +26,7 @@ static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
buffer_json_member_add_string(wb, "template", string2str(df->template));
buffer_json_member_add_string(wb, "status", dyncfg_id2status(df->current.status));
- dyncfg_cmds2json_array(df->cmds, "cmds", wb);
+ dyncfg_cmds2json_array(df->current.status == DYNCFG_STATUS_ORPHAN ? DYNCFG_CMD_REMOVE : df->cmds, "cmds", wb);
buffer_json_member_add_object(wb, "access");
{
http_access2buffer_json_array(wb, "view", df->view_access);
@@ -200,9 +200,39 @@ static int dyncfg_config_execute_cb(struct rrd_function_execute *rfe, void *data
dyncfg_tree_for_host(host, rfe->result.wb, path, id);
}
else {
+ id = action;
+ action = path;
+ path = NULL;
+
+ if(id && *id && dyncfg_cmds2id(action) == DYNCFG_CMD_REMOVE) {
+ const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
+ if(item) {
+ DYNCFG *df = dictionary_acquired_item_value(item);
+
+ if(!rrd_function_available(host, string2str(df->function)))
+ df->current.status = DYNCFG_STATUS_ORPHAN;
+
+ bool delete = (df->current.status == DYNCFG_STATUS_ORPHAN);
+ dictionary_acquired_item_release(dyncfg_globals.nodes, item);
+
+ if(delete) {
+ dictionary_del(dyncfg_globals.nodes, id);
+ dyncfg_file_delete(id);
+ code = dyncfg_default_response(rfe->result.wb, 200, "");
+ goto cleanup;
+ }
+ }
+ }
+
code = HTTP_RESP_NOT_FOUND;
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: unknown config id '%s' in call: '%s'. This can happen if the plugin that registered the dynamic configuration is not running now.", action, rfe->function);
- rrd_call_function_error(rfe->result.wb, "unknown config id given", code);
+ nd_log(NDLS_DAEMON, NDLP_ERR,
+ "DYNCFG: unknown config id '%s' in call: '%s'. "
+ "This can happen if the plugin that registered the dynamic configuration is not running now.",
+ action, rfe->function);
+
+ rrd_call_function_error(
+ rfe->result.wb,
+ "unknown config id given", code);
}
cleanup: