From da619f548ada848b544eeeb481c3b50e38b52577 Mon Sep 17 00:00:00 2001 From: Daniel Salzman Date: Thu, 24 Jul 2025 08:37:26 +0200 Subject: [PATCH] server: optimize catalogs_generate() depending on the reload type --- src/knot/zone/zonedb-load.c | 80 +++++----- .../tests/catalog/generate/data/generic.upd | 3 + .../tests/catalog/generate/data/generic.zone | 3 + tests-extra/tests/catalog/generate/test.py | 147 +++++++++++++----- 4 files changed, 156 insertions(+), 77 deletions(-) create mode 100644 tests-extra/tests/catalog/generate/data/generic.upd create mode 100644 tests-extra/tests/catalog/generate/data/generic.zone diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index a12b6fa0cc..e7dc1658ab 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -406,6 +406,15 @@ static void reg_reverse(conf_t *conf, knot_zonedb_t *db_new, zone_t *zone) } } +static bool same_group(zone_t *old_z, zone_t *new_z) +{ + if (old_z->catalog_group == NULL || new_z->catalog_group == NULL) { + return (old_z->catalog_group == new_z->catalog_group); + } else { + return (strcmp(old_z->catalog_group, new_z->catalog_group) == 0); + } +} + static knot_zonedb_t *create_zonedb_commit(conf_t *conf, server_t *server) { knot_zonedb_t *db_old = server->zone_db; // If NULL, zonedb is beeing initialized. @@ -437,11 +446,18 @@ static knot_zonedb_t *create_zonedb_commit(conf_t *conf, server_t *server) } } knot_zonedb_insert(db_new, zone); + catalog_generate_add(zone, db_new, false); reg_reverse(conf, db_new, zone); } else if (type & CONF_IO_TUNSET) { + zone_t *zone = knot_zonedb_find(db_new, name); knot_zonedb_del(db_new, name); + catalog_generate_rem(zone, db_new); } else { zone_t *zone = knot_zonedb_find(db_new, name); + zone_t *old = knot_zonedb_find(db_old, name); + if (!same_group(old, zone)) { + catalog_generate_add(zone, db_new, true); + } reg_reverse(conf, db_new, zone); } } @@ -489,10 +505,13 @@ static knot_zonedb_t *create_zonedb_catalog(conf_t *conf, server_t *server, case CAT_UPD_ADD: zone = add_member_zone(upd, db_new, server, conf); knot_zonedb_insert(db_new, zone); + catalog_generate_add(zone, db_new, false); reg_reverse(conf, db_new, zone); break; case CAT_UPD_REM: + zone = knot_zonedb_find(db_new, upd->member); knot_zonedb_del(db_new, upd->member); + catalog_generate_rem(zone, db_new); break; case CAT_UPD_UNIQ: case CAT_UPD_PROP: @@ -503,6 +522,10 @@ static knot_zonedb_t *create_zonedb_catalog(conf_t *conf, server_t *server, ptrlist_add(expired_contents, zone_expire(zone, true), NULL); knot_sem_post(&zone->cow_lock); } + zone_t *old = knot_zonedb_find(db_old, upd->member); + if (!same_group(old, zone)) { + catalog_generate_add(zone, db_new, true); + } reg_reverse(conf, db_new, zone); break; default: @@ -514,41 +537,6 @@ static knot_zonedb_t *create_zonedb_catalog(conf_t *conf, server_t *server, return db_new; } -static bool same_group(zone_t *old_z, zone_t *new_z) -{ - if (old_z->catalog_group == NULL || new_z->catalog_group == NULL) { - return (old_z->catalog_group == new_z->catalog_group); - } else { - return (strcmp(old_z->catalog_group, new_z->catalog_group) == 0); - } -} - -static void catalogs_generate(struct knot_zonedb *db_new, struct knot_zonedb *db_old) -{ - if (db_old != NULL) { - knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old); - for (; !knot_zonedb_iter_finished(it); knot_zonedb_iter_next(it)) { - zone_t *zone = knot_zonedb_iter_val(it); - if (knot_zonedb_find(db_new, zone->name) == NULL) { - catalog_generate_rem(zone, db_new); - } - } - knot_zonedb_iter_free(it); - } - - knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_new); - for (; !knot_zonedb_iter_finished(it); knot_zonedb_iter_next(it)) { - zone_t *zone = knot_zonedb_iter_val(it); - zone_t *old = knot_zonedb_find(db_old, zone->name); - if (old == NULL) { - catalog_generate_add(zone, db_new, false); - } else if (!same_group(zone, old)) { - catalog_generate_add(zone, db_new, true); - } - } - knot_zonedb_iter_free(it); -} - static knot_zonedb_t *create_zonedb_full(conf_t *conf, server_t *server, list_t *expired_contents) { @@ -631,9 +619,28 @@ static knot_zonedb_t *create_zonedb_full(conf_t *conf, server_t *server, } catalog_it_free(cat_it); + /* Update generated catalogs - remove members. */ + if (db_old != NULL) { + knot_zonedb_iter_t *db_it = knot_zonedb_iter_begin(db_old); + for (; !knot_zonedb_iter_finished(db_it); knot_zonedb_iter_next(db_it)) { + zone_t *zone = knot_zonedb_iter_val(db_it); + if (knot_zonedb_find(db_new, zone->name) == NULL) { + catalog_generate_rem(zone, db_new); + } + } + knot_zonedb_iter_free(db_it); + } + + /* Update generated catalogs - add members, updated reversed zones. */ knot_zonedb_iter_t *db_it = knot_zonedb_iter_begin(db_new); for (; !knot_zonedb_iter_finished(db_it); knot_zonedb_iter_next(db_it)) { zone_t *zone = knot_zonedb_iter_val(db_it); + zone_t *old = knot_zonedb_find(db_old, zone->name); + if (old == NULL) { + catalog_generate_add(zone, db_new, false); + } else if (!same_group(old, zone)) { + catalog_generate_add(zone, db_new, true); + } reg_reverse(conf, db_new, zone); } knot_zonedb_iter_free(db_it); @@ -847,9 +854,6 @@ void zonedb_reload(conf_t *conf, server_t *server, reload_t mode) return; } - // TODO: optimize within create_zonedb. - catalogs_generate(db_new, server->zone_db); - /* Switch the databases. */ knot_zonedb_t **db_current = &server->zone_db; knot_zonedb_t *db_old = rcu_xchg_pointer(db_current, db_new); diff --git a/tests-extra/tests/catalog/generate/data/generic.upd b/tests-extra/tests/catalog/generate/data/generic.upd new file mode 100644 index 0000000000..52bd567c26 --- /dev/null +++ b/tests-extra/tests/catalog/generate/data/generic.upd @@ -0,0 +1,3 @@ +@ SOA dns1 hostmaster 10 10800 3600 1209600 7200 + NS dns1 +dns1 A 192.0.2.1 diff --git a/tests-extra/tests/catalog/generate/data/generic.zone b/tests-extra/tests/catalog/generate/data/generic.zone new file mode 100644 index 0000000000..46c183b47d --- /dev/null +++ b/tests-extra/tests/catalog/generate/data/generic.zone @@ -0,0 +1,3 @@ +@ SOA dns1 hostmaster 1 10800 3600 1209600 7200 + NS dns1 +dns1 A 192.0.2.1 diff --git a/tests-extra/tests/catalog/generate/test.py b/tests-extra/tests/catalog/generate/test.py index 0ffa65c4ea..4d892eb124 100644 --- a/tests-extra/tests/catalog/generate/test.py +++ b/tests-extra/tests/catalog/generate/test.py @@ -3,15 +3,19 @@ '''Test of Catalog zone generation.''' from dnstest.test import Test -from dnstest.utils import set_err, detail_log +from dnstest.utils import compare, set_err, detail_log +from dnstest.libknot import libknot import os import random +import shutil import time +USE_CTL = random.choice([True, False, False]) + t = Test() -def wait_for_zonefile(server, role, zonename, max_age, timeout): - fn = os.path.join(server.dir, role, zonename + "zone") +def wait_for_zonefile(server, zonename, max_age, timeout): + fn = os.path.join(server.dir, "catalog", zonename + "zone") while timeout > 0: if os.path.exists(fn): age = time.time() - os.path.getmtime(fn) @@ -40,45 +44,81 @@ slave.cat_hidden(zone) slave.dnssec(catz[0]).enable = True slave.dnssec(catz[0]).single_type_signing = True +def ctl_begin(): + ctl = libknot.control.KnotCtl() + ctl.connect(os.path.join(master.dir, "knot.sock")) + ctl.send_block(cmd="conf-begin") + resp = ctl.receive_block() + return ctl + +def ctl_end(ctl): + ctl.send_block(cmd="conf-commit") + resp = ctl.receive_block() + ctl.send(libknot.control.KnotCtlType.END) + ctl.close() + +def ctl_add_zone(ctl, zone_name): + ctl.send_block(cmd="conf-set", section="zone", item="domain", data=zone_name) + resp = ctl.receive_block() + ctl.send_block(cmd="conf-set", section="zone", item="template", identifier=zone_name, data="catalog-default") + resp = ctl.receive_block() + ctl.send_block(cmd="conf-set", section="zone", item="catalog-zone", identifier=zone_name, data=catz[0].name) + resp = ctl.receive_block() + ctl.send_block(cmd="conf-set", section="zone", item="catalog-role", identifier=zone_name, data="member") + resp = ctl.receive_block() + ctl.send_block(cmd="conf-set", section="zone", item="file", identifier=zone_name, data=os.path.join(master.dir, "generic.zone")) + resp = ctl.receive_block() + t.start() # testcase 1: initial catalog zone with 1 member slave.zones_wait(zone) -# testcase 2: adding member zones online/offline -add_online = random.choice([True, False]) - +# testcase 2: adding member zones dynamically/online/offline zone_add = t.zone("flags.") + t.zone("records.") -t.link(zone_add, master, slave) -for z in zone_add: - master.cat_member(z, catz) - slave.cat_hidden(z) - -master.gen_confile() - -if add_online: - master.reload() +if USE_CTL: + shutil.copy(t.data_dir + "generic.zone", os.path.join(master.dir, "generic.zone")) + ctl = ctl_begin() + ctl_add_zone(ctl, "flags.") + ctl_add_zone(ctl, "records.") + ctl_end(ctl) else: - master.stop() - t.sleep(1) - master.start() + t.link(zone_add, master, slave) + for z in zone_add: + master.cat_member(z, catz) + slave.cat_hidden(z) + + master.gen_confile() + + add_online = random.choice([True, False]) + if add_online: + master.reload() + else: + master.stop() + t.sleep(1) + master.start() slave.zones_wait(zone + zone_add) -# testcase 3: removing member zone online/offline -rem_online = random.choice([True, False]) - +# testcase 3: removing member zone dynamically/online/offline serial_bef_rem = slave.zone_wait(catz, tsig=True) master.ctl("-f zone-purge example.com") -master.zones.pop("example.com.") -master.gen_confile() - -if rem_online: - master.reload() +if USE_CTL: + ctl = ctl_begin() + ctl.send_block(cmd="conf-unset", section="zone", item="domain", data="example.com.") + resp = ctl.receive_block() + ctl_end(ctl) else: - master.stop() - t.sleep(1) - master.start() + master.zones.pop("example.com.") + master.gen_confile() + + add_online = random.choice([True, False]) + if add_online: + master.reload() + else: + master.stop() + t.sleep(1) + master.start() slave.zone_wait(catz, serial_bef_rem, tsig=True) t.sleep(2) # allow the member zone to actually be purged @@ -91,23 +131,52 @@ resp0.check_count(1, "DNSKEY") dnskey0 = resp0.resp.answer[0].to_rdataset() slave.stop() -temp_rem = master.zones.pop("records.") -master.gen_confile() -master.reload() -t.sleep(7) -master.ctl("-f zone-purge +orphan records.") -master.zones["records."] = temp_rem -master.gen_confile() -master.reload() +if USE_CTL: + ctl = ctl_begin() + ctl.send_block(cmd="conf-unset", section="zone", item="domain", data="records.") + resp = ctl.receive_block() + ctl_end(ctl) + t.sleep(7) + master.ctl("-f zone-purge +orphan records.") + ctl = ctl_begin() + ctl_add_zone(ctl, "records.") + ctl_end(ctl) +else: + temp_rem = master.zones.pop("records.") + master.gen_confile() + master.reload() + t.sleep(7) + master.ctl("-f zone-purge +orphan records.") + master.zones["records."] = temp_rem + master.gen_confile() + master.reload() slave.start() -wait_for_zonefile(slave, "master", "records.", 3, 30) +wait_for_zonefile(slave, "records.", 3, 30) slave.ctl("zone-refresh") -wait_for_zonefile(slave, "master", "records.", 3, 30) +wait_for_zonefile(slave, "records.", 3, 30) resp1 = slave.dig("records.", "DNSKEY") resp1.check_count(1, "DNSKEY") dnskey1 = resp1.resp.answer[0].to_rdataset() if dnskey0 == dnskey1: set_err("ZONE NOT PURGED") +#testcase 5: reload and don't reload a zone depending on the config change +if USE_CTL: + shutil.copy(t.data_dir + "generic.upd", os.path.join(master.dir, "generic.zone")) + + ctl = ctl_begin() + ctl.send_block(cmd="conf-set", section="zone", identifier="flags.", item="zone-max-size", data="10000") + resp = ctl.receive_block() + ctl.send_block(cmd="conf-set", section="zone", identifier="records.", item="comment", data="don't reload") + resp = ctl.receive_block() + ctl_end(ctl) + + t.sleep(2) + resp = master.dig("flags.", "SOA") + compare(resp.soa_serial(), 10, "master zone reloaded") + resp = master.dig("records.", "SOA") + compare(resp.soa_serial(), 1, "master zone not reloaded") + slave.zone_wait(zone_add[0], 1) + t.end() -- 2.47.3