Commit 70f927c4 authored by Mark Karpilovskij's avatar Mark Karpilovskij Committed by Daniel Salzman

ctl: added option to purge orphaned zones

parent dc5e2d95
......@@ -148,9 +148,11 @@ requires a ttl value specified.
Remove zone data within the transaction.
.TP
\fBzone\-purge\fP \fIzone\fP\&... [\fIfilter\fP\&...]
Purge zone data, file, journal, timers, and kaspdb.
Filters are \fB+expire\fP, \fB+timers\fP, \fB+zonefile\fP, \fB+journal\fP,
and \fB+kaspdb\fP\&.
Purge zone data, zone file, journal, timers, and/or KASP data of specified zones.
Available filters are \fB+expire\fP, \fB+zonefile\fP, \fB+journal\fP, \fB+timers\fP,
and \fB+kaspdb\fP\&. If no filter is specified, all filters are enabled.
It the zone is no longer configured, add \fB+orphan\fP filter (zone file cannot
be purged in this case).
.TP
\fBzone\-stats\fP \fIzone\fP [\fImodule\fP[\fB\&.\fP\fIcounter\fP]]
Show zone statistics counter(s). To print also counters with value 0, use
......
......@@ -125,9 +125,11 @@ Actions
Remove zone data within the transaction.
**zone-purge** *zone*... [*filter*...]
Purge zone data, file, journal, timers, and kaspdb.
Filters are **+expire**, **+timers**, **+zonefile**, **+journal**,
and **+kaspdb**.
Purge zone data, zone file, journal, timers, and/or KASP data of specified zones.
Available filters are **+expire**, **+zonefile**, **+journal**, **+timers**,
and **+kaspdb**. If no filter is specified, all filters are enabled.
It the zone is no longer configured, add **+orphan** filter (zone file cannot
be purged in this case).
**zone-stats** *zone* [*module*\ [\ **.**\ *counter*\ ]]
Show zone statistics counter(s). To print also counters with value 0, use
......
......@@ -34,6 +34,7 @@
#include "contrib/macros.h"
#include "contrib/mempattern.h"
#include "contrib/string.h"
#include "contrib/ucw/lists.h"
#include "zscanner/scanner.h"
#include "contrib/strtonum.h"
......@@ -997,7 +998,128 @@ static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
return zone_update_remove_node(zone->control_update, owner);
}
}
}
static bool zone_exists(const knot_dname_t *zone, void *data)
{
assert(zone);
assert(data);
knot_zonedb_t *db = data;
return knot_zonedb_find(db, zone) != NULL;
}
static bool zone_names_distinct(const knot_dname_t *zone, void *data)
{
assert(zone);
assert(data);
knot_dname_t *zone_to_purge = data;
return !knot_dname_is_equal(zone, zone_to_purge);
}
static int orphans_purge(ctl_args_t *args)
{
assert(args->data[KNOT_CTL_IDX_FILTER] != NULL);
bool only_orphan = (strlen(args->data[KNOT_CTL_IDX_FILTER]) == 1);
if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
// Purge KASP DB.
if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
list_t zones;
init_list(&zones);
if (kasp_db_open(*kaspdb()) == KNOT_EOK &&
kasp_db_list_zones(*kaspdb(), &zones) == KNOT_EOK) {
ptrnode_t *zn;
WALK_LIST(zn, zones) {
knot_dname_t *zone_name = (knot_dname_t *)zn->d;
if (!zone_exists(zone_name, args->server->zone_db)) {
(void)kasp_db_delete_all(*kaspdb(), zone_name);
}
knot_dname_free(&zone_name, NULL);
}
ptrlist_free(&zones, NULL);
}
}
// Purge zone journals of unconfigured zones.
if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
list_t zones;
init_list(&zones);
if (journal_db_list_zones(&args->server->journal_db, &zones) == KNOT_EOK) {
ptrnode_t *zn;
WALK_LIST(zn, zones) {
journal_t journal = { 0 };
knot_dname_t *zone_name = (knot_dname_t *)zn->d;
if (!zone_exists(zone_name, args->server->zone_db) &&
journal_open(&journal, &args->server->journal_db,
zone_name) == KNOT_EOK) {
journal_scrape(&journal);
journal_close(&journal);
}
knot_dname_free(&zone_name, NULL);
}
ptrlist_free(&zones, NULL);
}
}
// Purge timers of unconfigured zones.
if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
(void)zone_timers_sweep(args->server->timers_db,
zone_exists, args->server->zone_db);
}
} else {
uint8_t buff[KNOT_DNAME_MAXLEN];
while (true) {
knot_dname_t *zone_name =
knot_dname_from_str(buff, args->data[KNOT_CTL_IDX_ZONE],
sizeof(buff));
if (zone_name == NULL) {
log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
"control, error (%s)",
knot_strerror(KNOT_EINVAL));
send_error(args, knot_strerror(KNOT_EINVAL));
return KNOT_EINVAL;
}
knot_dname_to_lower(zone_name);
if (!zone_exists(zone_name, args->server->zone_db)) {
// Purge KASP DB.
if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
if (kasp_db_open(*kaspdb()) == KNOT_EOK) {
(void) kasp_db_delete_all(*kaspdb(), zone_name);
}
}
// Purge zone journal.
if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
journal_t journal = { 0 };
if (journal_open(&journal, &args->server->journal_db,
zone_name) == KNOT_EOK) {
(void)journal_scrape(&journal);
journal_close(&journal);
}
}
// Purge zone timers.
if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
(void)zone_timers_sweep(args->server->timers_db,
zone_names_distinct, zone_name);
}
}
// Get next zone name.
int ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
break;
}
ctl_log_data(&args->data);
}
}
return KNOT_EOK;
}
static int zone_purge(zone_t *zone, ctl_args_t *args)
......@@ -1227,7 +1349,11 @@ static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
case CTL_ZONE_UNSET:
return zones_apply(args, zone_txn_unset);
case CTL_ZONE_PURGE:
return zones_apply(args, zone_purge);
if (MATCH_AND_FILTER(args, CTL_FILTER_PURGE_ORPHAN)) {
return orphans_purge(args);
} else {
return zones_apply(args, zone_purge);
}
case CTL_ZONE_STATS:
return zones_apply(args, zone_stats);
default:
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -44,6 +44,7 @@
#define CTL_FILTER_PURGE_ZONEFILE 'f'
#define CTL_FILTER_PURGE_JOURNAL 'j'
#define CTL_FILTER_PURGE_KASPDB 'k'
#define CTL_FILTER_PURGE_ORPHAN 'o'
/*! Control commands. */
typedef enum {
......
......@@ -1916,7 +1916,11 @@ int journal_db_list_zones(journal_db_t **db, list_t *zones)
if (txn->ret == KNOT_EOK && *compare_metaflag == '\0') {
compare_metaflag -= metaflag_len;
if (strcmp(compare_metaflag, MDKEY_PERZONE_FLAGS) == 0) {
char *found_zone = knot_dname_to_str_alloc((const knot_dname_t *)key.data);
knot_dname_t *found_zone = knot_dname_copy((const knot_dname_t *)key.data, NULL);
if (found_zone == NULL) {
txn->ret = KNOT_ENOMEM;
break;
}
ptrlist_add(zones, found_zone, NULL);
}
}
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -320,16 +320,6 @@ static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
}
}
static bool zone_exists(const knot_dname_t *zone, void *data)
{
assert(zone);
assert(data);
knot_zonedb_t *db = data;
return knot_zonedb_find(db, zone) != NULL;
}
void zonedb_reload(conf_t *conf, server_t *server)
{
if (conf == NULL || server == NULL) {
......@@ -353,15 +343,6 @@ void zonedb_reload(conf_t *conf, server_t *server)
/* Wait for readers to finish reading old zone database. */
synchronize_rcu();
/* Sweep the timer database. */
if (server->timers_db != NULL) {
int ret = zone_timers_sweep(server->timers_db, zone_exists, db_new);
if (ret != KNOT_EOK) {
log_warning("failed to clear persistent timer DB (%s)",
knot_strerror(ret));
}
}
/* Remove old zone DB. */
remove_old_zonedb(conf, db_old, db_new);
}
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -281,8 +281,9 @@ int list_zones(char *path)
ret = journal_db_list_zones(&jdb, &zones);
if (ret == KNOT_EOK) {
ptrnode_t *zn;
char buff[KNOT_DNAME_TXT_MAXLEN + 1];
WALK_LIST(zn, zones) {
printf("%s\n", (char *)zn->d);
printf("%s\n", knot_dname_to_str(buff, (knot_dname_t *)zn->d, sizeof(buff)));
free(zn->d);
}
ptrlist_free(&zones, NULL);
......
......@@ -707,6 +707,7 @@ const filter_desc_t zone_purge_filters[MAX_FILTERS] = {
{ "+zonefile", CTL_FILTER_PURGE_ZONEFILE },
{ "+journal", CTL_FILTER_PURGE_JOURNAL },
{ "+kaspdb", CTL_FILTER_PURGE_KASPDB },
{ "+orphan", CTL_FILTER_PURGE_ORPHAN },
};
const filter_desc_t null_filter = { 0 };
......@@ -1130,7 +1131,7 @@ static const cmd_help_t cmd_help_table[] = {
{ CMD_ZONE_GET, "<zone> [<owner> [<type>]]", "Get zone data within the transaction." },
{ CMD_ZONE_SET, "<zone> <owner> [<ttl>] <type> <rdata>", "Add zone record within the transaction." },
{ CMD_ZONE_UNSET, "<zone> <owner> [<type> [<rdata>]]", "Remove zone data within the transaction." },
{ CMD_ZONE_PURGE, "<zone>... [<filter>...]", "Purge zone data, file, journal, and timers." },
{ CMD_ZONE_PURGE, "<zone>... [<filter>...]", "Purge zone data, zone file, journal, timers, and KASP data." },
{ CMD_ZONE_STATS, "<zone> [<module>[.<counter>]]", "Show zone statistics counter(s)."},
{ CMD_ZONE_STATUS, "<zone> [<filter>...]", "Show the zone status." },
{ "", "", "" },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment