DYNCFG support deleting orphan configurations (#17023)
* dyncfg: allow deleting orphan configs * restore applied text * make the dyncfg applied text more descriptive
Costa Tsaousis committed
Feb 16, 2024 at 16:39 UTC
81cb35dccacb42879c583a4ddd848d087787f6ab
2 files changed
+73
-28
src/daemon/config/dyncfg-intercept.c
+40
-25
@@ -225,38 +225,44 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
225
char *cmd_str = get_word(words, num_words, i++);
226
227
if(!config || !*config || strcmp(config, PLUGINSD_FUNCTION_CONFIG) != 0)
228
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
229
- "dyncfg functions intercept: this is not a dyncfg request");
228
+ return dyncfg_intercept_early_error(
229
+ rfe, HTTP_RESP_BAD_REQUEST,
230
+ "dyncfg functions intercept: this is not a dyncfg request");
231
232
cmd = dyncfg_cmds2id(cmd_str);
233
if(cmd == DYNCFG_CMD_NONE)
233
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
234
- "dyncfg functions intercept: invalid command received");
234
+ return dyncfg_intercept_early_error(
235
+ rfe, HTTP_RESP_BAD_REQUEST,
236
+ "dyncfg functions intercept: invalid command received");
237
238
if(cmd == DYNCFG_CMD_ADD) {
239
add_name = get_word(words, num_words, i++);
240
241
if(!add_name || !*add_name)
240
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
241
- "dyncfg functions intercept: this action requires a name");
242
+ return dyncfg_intercept_early_error(
243
+ rfe, HTTP_RESP_BAD_REQUEST,
244
+ "dyncfg functions intercept: this action requires a name");
245
246
if(!called_from_dyncfg_echo) {
247
char nid[strlen(id) + strlen(add_name) + 2];
248
snprintfz(nid, sizeof(nid), "%s:%s", id, add_name);
249
250
if (dictionary_get(dyncfg_globals.nodes, nid))
248
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
249
- "dyncfg functions intercept: a configuration with this name already exists");
251
+ return dyncfg_intercept_early_error(
252
+ rfe, HTTP_RESP_BAD_REQUEST,
253
+ "dyncfg functions intercept: a configuration with this name already exists");
254
}
255
}
256
257
if((cmd == DYNCFG_CMD_ADD || cmd == DYNCFG_CMD_UPDATE || cmd == DYNCFG_CMD_TEST) && !has_payload)
254
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
255
- "dyncfg functions intercept: this action requires a payload");
258
+ return dyncfg_intercept_early_error(
259
+ rfe, HTTP_RESP_BAD_REQUEST,
260
+ "dyncfg functions intercept: this action requires a payload");
261
262
if((cmd != DYNCFG_CMD_ADD && cmd != DYNCFG_CMD_UPDATE && cmd != DYNCFG_CMD_TEST) && has_payload)
258
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
259
- "dyncfg functions intercept: this action does not require a payload");
263
+ return dyncfg_intercept_early_error(
264
+ rfe, HTTP_RESP_BAD_REQUEST,
265
+ "dyncfg functions intercept: this action does not require a payload");
266
267
item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
268
if(!item) {
@@ -266,7 +272,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
272
}
273
274
if(!item)
269
- return dyncfg_intercept_early_error(rfe, HTTP_RESP_NOT_FOUND, "dyncfg functions intercept: id is not found");
275
+ return dyncfg_intercept_early_error(
276
+ rfe, HTTP_RESP_NOT_FOUND,
277
+ "dyncfg functions intercept: id is not found");
278
}
279
280
DYNCFG *df = dictionary_acquired_item_value(item);
@@ -278,8 +286,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
286
case DYNCFG_CMD_SCHEMA:
287
if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->view_access)) {
288
make_the_call_to_plugin = false;
281
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_FORBIDDEN,
282
- "dyncfg: you don't have enough view permissions to execute this command");
289
+ rc = dyncfg_default_response(
290
+ rfe->result.wb, HTTP_RESP_FORBIDDEN,
291
+ "dyncfg: you don't have enough view permissions to execute this command");
292
}
293
break;
294
@@ -292,15 +301,17 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
301
case DYNCFG_CMD_RESTART:
302
if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->edit_access)) {
303
make_the_call_to_plugin = false;
295
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_FORBIDDEN,
296
- "dyncfg: you don't have enough edit permissions to execute this command");
304
+ rc = dyncfg_default_response(
305
+ rfe->result.wb, HTTP_RESP_FORBIDDEN,
306
+ "dyncfg: you don't have enough edit permissions to execute this command");
307
}
308
break;
309
310
default: {
311
make_the_call_to_plugin = false;
302
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR,
303
- "dyncfg: permissions for this command are not set");
312
+ rc = dyncfg_default_response(
313
+ rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR,
314
+ "dyncfg: permissions for this command are not set");
315
}
316
break;
317
}
@@ -320,8 +331,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
331
else if (cmd == DYNCFG_CMD_ADD) {
332
if (df->type != DYNCFG_TYPE_TEMPLATE) {
333
make_the_call_to_plugin = false;
323
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST,
324
- "dyncfg functions intercept: add command is only allowed in templates");
334
+ rc = dyncfg_default_response(
335
+ rfe->result.wb, HTTP_RESP_BAD_REQUEST,
336
+ "dyncfg functions intercept: add command is only allowed in templates");
337
338
nd_log(NDLS_DAEMON, NDLP_ERR,
339
"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 __
343
else if (
344
cmd == DYNCFG_CMD_ENABLE && df->type == DYNCFG_TYPE_JOB &&
345
dyncfg_is_user_disabled(string2str(df->template))) {
334
- nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot enable a job of a disabled template: %s", rfe->function);
346
+ nd_log(NDLS_DAEMON, NDLP_ERR,
347
+ "DYNCFG: cannot enable a job of a disabled template: %s",
348
+ rfe->function);
349
350
make_the_call_to_plugin = false;
337
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST,
338
- "dyncfg functions intercept: this job belongs to disabled template");
351
+ rc = dyncfg_default_response(
352
+ rfe->result.wb, HTTP_RESP_BAD_REQUEST,
353
+ "dyncfg functions intercept: this job belongs to disabled template");
354
}
355
}
356
@@ -356,7 +371,7 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
371
372
dyncfg_apply_action_on_all_template_jobs(rfe, id, cmd);
373
359
- rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied");
374
+ rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied to all template job");
375
make_the_call_to_plugin = false;
376
}
377
else if (cmd == DYNCFG_CMD_SCHEMA) {
src/daemon/config/dyncfg-tree.c
+33
-3
@@ -26,7 +26,7 @@ static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
26
buffer_json_member_add_string(wb, "template", string2str(df->template));
27
28
buffer_json_member_add_string(wb, "status", dyncfg_id2status(df->current.status));
29
- dyncfg_cmds2json_array(df->cmds, "cmds", wb);
29
+ dyncfg_cmds2json_array(df->current.status == DYNCFG_STATUS_ORPHAN ? DYNCFG_CMD_REMOVE : df->cmds, "cmds", wb);
30
buffer_json_member_add_object(wb, "access");
31
{
32
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
200
dyncfg_tree_for_host(host, rfe->result.wb, path, id);
201
}
202
else {
203
+ id = action;
204
+ action = path;
205
+ path = NULL;
206
+
207
+ if(id && *id && dyncfg_cmds2id(action) == DYNCFG_CMD_REMOVE) {
208
+ const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
209
+ if(item) {
210
+ DYNCFG *df = dictionary_acquired_item_value(item);
211
+
212
+ if(!rrd_function_available(host, string2str(df->function)))
213
+ df->current.status = DYNCFG_STATUS_ORPHAN;
214
+
215
+ bool delete = (df->current.status == DYNCFG_STATUS_ORPHAN);
216
+ dictionary_acquired_item_release(dyncfg_globals.nodes, item);
217
+
218
+ if(delete) {
219
+ dictionary_del(dyncfg_globals.nodes, id);
220
+ dyncfg_file_delete(id);
221
+ code = dyncfg_default_response(rfe->result.wb, 200, "");
222
+ goto cleanup;
223
+ }
224
+ }
225
+ }
226
+
227
code = HTTP_RESP_NOT_FOUND;
204
- 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);
205
- rrd_call_function_error(rfe->result.wb, "unknown config id given", code);
228
+ nd_log(NDLS_DAEMON, NDLP_ERR,
229
+ "DYNCFG: unknown config id '%s' in call: '%s'. "
230
+ "This can happen if the plugin that registered the dynamic configuration is not running now.",
231
+ action, rfe->function);
232
+
233
+ rrd_call_function_error(
234
+ rfe->result.wb,
235
+ "unknown config id given", code);
236
}
237
238
cleanup: