Commit 72373bc0 authored by Dominik Taborsky's avatar Dominik Taborsky

zone_update: add iterators and improve the unit test

parent 1677b3a6
......@@ -545,11 +545,221 @@ int zone_update_commit(conf_t *conf, zone_update_t *update)
return KNOT_EOK;
}
static void select_next_node(zone_update_iter_t *it)
{
int compare = 0;
if (it->t_node) {
if (it->add_node) {
/* Both original and new node exists. Choose the 'smaller' node to return. */
compare = knot_dname_cmp(it->t_node->owner, it->add_node->owner);
if (compare <= 0) {
// Return the original node.
it->next_n = it->t_node;
it->t_node = NULL;
if (compare == 0) {
it->add_node = NULL;
}
} else {
// Return the new node.
it->next_n = it->add_node;
it->add_node = NULL;
}
} else {
// Return the original node.
it->next_n = it->t_node;
it->t_node = NULL;
}
} else {
if (it->add_node) {
// Return the new node.
it->next_n = it->add_node;
it->add_node = NULL;
} else {
// Iteration done.
it->next_n = NULL;
}
}
}
static int iter_init_tree_iters(zone_update_iter_t *it, zone_update_t *update,
bool nsec3)
{
zone_tree_t *tree;
/* Set zone iterator. */
zone_contents_t *_contents = NULL;
if (update->flags & UPDATE_FULL) {
_contents = update->new_cont;
} else if (update->flags & UPDATE_INCREMENTAL) {
_contents = update->zone->contents;
} else {
return KNOT_EINVAL;
}
/* Begin iteration. We can safely assume _contents is a valid pointer. */
tree = nsec3 ? _contents->nsec3_nodes : _contents->nodes;
hattrie_build_index(tree);
it->t_it = hattrie_iter_begin(nsec3 ? _contents->nsec3_nodes : _contents->nodes, true);
if (it->t_it == NULL) {
return KNOT_ENOMEM;
}
/* Set changeset iterator. */
if (update->flags & UPDATE_INCREMENTAL && !changeset_empty(&update->change)) {
tree = nsec3 ? update->change.add->nsec3_nodes :
update->change.add->nodes;
if (tree == NULL) {
it->add_it = NULL;
} else {
hattrie_build_index(tree);
it->add_it = hattrie_iter_begin(tree, true);
if (it->add_it == NULL) {
hattrie_iter_free(it->t_it);
return KNOT_ENOMEM;
}
}
} else {
it->add_it = NULL;
}
return KNOT_EOK;
}
static int iter_get_added_node(zone_update_iter_t *it)
{
hattrie_iter_next(it->add_it);
if (hattrie_iter_finished(it->add_it)) {
hattrie_iter_free(it->add_it);
it->add_it = NULL;
return KNOT_ENOENT;
}
it->add_node = (zone_node_t *)(*hattrie_iter_val(it->add_it));
return KNOT_EOK;
}
static int iter_get_synth_node(zone_update_iter_t *it)
{
hattrie_iter_next(it->t_it);
if (hattrie_iter_finished(it->t_it)) {
hattrie_iter_free(it->t_it);
it->t_it = NULL;
return KNOT_ENOENT;
}
const zone_node_t *n = (zone_node_t *)(*hattrie_iter_val(it->t_it));
if (it->update->flags & UPDATE_FULL) {
it->t_node = n;
} else {
it->t_node = zone_update_get_node(it->update, n->owner);
if (it->t_node == NULL) {
return KNOT_ENOMEM;
}
}
return KNOT_EOK;
}
static int iter_init(zone_update_iter_t *it, zone_update_t *update, const bool nsec3)
{
memset(it, 0, sizeof(*it));
it->update = update;
it->nsec3 = nsec3;
int ret = iter_init_tree_iters(it, update, nsec3);
if (ret != KNOT_EOK) {
return ret;
}
if (it->add_it) {
it->add_node = (zone_node_t *)(*hattrie_iter_val(it->add_it));
assert(it->add_node);
}
if (it->t_it) {
it->t_node = (zone_node_t *)(*hattrie_iter_val(it->t_it));
assert(it->t_node);
if (it->update->flags & UPDATE_INCREMENTAL) {
it->t_node = zone_update_get_node(it->update, it->t_node->owner);
if (it->t_node == NULL) {
return KNOT_ENOMEM;
}
}
}
select_next_node(it);
return KNOT_EOK;
}
int zone_update_iter(zone_update_iter_t *it, zone_update_t *update)
{
return iter_init(it, update, false);
}
int zone_update_iter_nsec3(zone_update_iter_t *it, zone_update_t *update)
{
if (update->flags & UPDATE_FULL) {
if (update->new_cont->nsec3_nodes == NULL) {
// No NSEC3 tree.
return KNOT_ENOENT;
}
} else {
if (update->change.add->nsec3_nodes == NULL &&
update->change.remove->nsec3_nodes == NULL) {
// No NSEC3 changes.
return KNOT_ENOENT;
}
}
return iter_init(it, update, true);
}
int zone_update_iter_next(zone_update_iter_t *it)
{
if (it == NULL) {
return KNOT_EINVAL;
}
// Get nodes from both iterators if needed.
if (it->t_it && it->t_node == NULL) {
int ret = iter_get_synth_node(it);
if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
return ret;
}
}
if (it->add_it && it->add_node == NULL) {
int ret = iter_get_added_node(it);
if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
return ret;
}
}
select_next_node(it);
return KNOT_EOK;
}
const zone_node_t *zone_update_iter_val(zone_update_iter_t *it)
{
if (it) {
return it->next_n;
} else {
return NULL;
}
}
int zone_update_iter_finish(zone_update_iter_t *it)
{
hattrie_iter_free(it->t_it);
return KNOT_EOK;
}
bool zone_update_no_change(zone_update_t *update)
{
if (update->flags & UPDATE_INCREMENTAL) {
return changeset_empty(&update->change);
} else {
return false; //FIXME: there's no zone_contents_empty() function, do we need it?
return false; //!\todo: there's no zone_contents_empty() function, do we need it?
}
}
......@@ -37,10 +37,20 @@ typedef struct {
zone_contents_t *new_cont; /*!< New zone contents for full updates. */
changeset_t change; /*!< Changes we want to apply. */
apply_ctx_t a_ctx; /*!< Context for applying changesets. */
uint8_t flags; /*!< Zone update flags. */
uint32_t flags; /*!< Zone update flags. */
knot_mm_t mm; /*!< Memory context used for intermediate nodes. */
} zone_update_t;
typedef struct {
zone_update_t *update; /*!< The update we're iterating over. */
hattrie_iter_t *t_it; /*!< Iterator for the original zone in the case of INCREMENTAL update or the new zone in case of FULL update. */
hattrie_iter_t *add_it; /*!< Iterator for the added nodes in the changeset. Available in the INCREMENTAL update only. */
const zone_node_t *t_node; /*!< The original node (INCREMENTAL update) or new node (FULL update). */
const zone_node_t *add_node; /*!< The additions to that node (INCREMENTAL update only). */
const zone_node_t *next_n; /*!< The smaller of t_node and ch_node (INCREMENTAL update) or next new node (FULL update). */
bool nsec3; /*!< Set when we're using the NSEC3 node tree. */
} zone_update_iter_t;
typedef enum {
UPDATE_FULL = 1 << 0, /*!< Replace the old zone by a complete new one. */
UPDATE_INCREMENTAL = 1 << 1, /*!< Apply changes to the old zone. */
......@@ -122,6 +132,10 @@ void zone_update_clear(zone_update_t *update);
/*!
* \brief Adds an RRSet to the zone.
*
* \warning Do not edit the zone_update when any iterator is active. Any
* zone_update modifications will invalidate the trie iterators
* in the zone_update iterator(s).
*
* \param update Zone update.
*
* \return KNOT_E*
......@@ -131,6 +145,10 @@ int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset);
/*!
* \brief Removes an RRSet from the zone.
*
* \warning Do not edit the zone_update when any iterator is active. Any
* zone_update modifications will invalidate the trie iterators
* in the zone_update iterator(s).
*
* \param update Zone update.
*
* \return KNOT_E*
......@@ -147,6 +165,63 @@ int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset);
*/
int zone_update_commit(conf_t *conf, zone_update_t *update);
/*!
* \brief Setup a zone_update iterator for both FULL and INCREMENTAL updates.
*
* \warning Do not init or use iterators when the zone is edited. Any
* zone_update modifications will invalidate the trie iterators
* in the zone_update iterator.
*
* \param it Iterator.
* \param update Zone update.
*
* \return KNOT_E*
*/
int zone_update_iter(zone_update_iter_t *it, zone_update_t *update);
/*!
* \brief Setup a zone_update iterator for both FULL and INCREMENTAL updates.
* Version for iterating over nsec3 nodes.
*
* \warning Do not init or use iterators when the zone is edited. Any
* zone_update modifications will invalidate the trie iterators
* in the zone_update iterator.
*
*
* \param it Iterator.
* \param update Zone update.
*
* \return KNOT_E*
*/
int zone_update_iter_nsec3(zone_update_iter_t *it, zone_update_t *update);
/*!
* \brief Move the iterator to the next item.
*
* \param it Iterator.
*
* \return KNOT_E*
*/
int zone_update_iter_next(zone_update_iter_t *it);
/*!
* \brief Get the value of the iterator.
*
* \param it Iterator.
*
* \return A (synthesized or added) node with all its current data.
*/
const zone_node_t *zone_update_iter_val(zone_update_iter_t *it);
/*!
* \brief Finish the iterator and clean it up.
*
* \param it Iterator.
*
* \return KNOT_E*
*/
int zone_update_iter_finish(zone_update_iter_t *it);
/*!
* \brief Returns bool whether there are any changes at all.
*
......
......@@ -17,13 +17,16 @@
#include <assert.h>
#include <tap/basic.h>
#include "test_conf.h"
#include "contrib/macros.h"
#include "contrib/getline.h"
#include "knot/updates/zone-update.h"
#include "knot/zone/node.h"
#include "zscanner/scanner.h"
static const char *zone_str =
"test. 3600 IN SOA a.ns.test. hostmaster.nic.cz. 1406641065 900 300 604800 900 \n"
static const char *zone_str1 =
"test. 3600 IN SOA a.ns.test. hostmaster.nic.cz. 1406641065 900 300 604800 900 \n";
static const char *zone_str2 =
"test. IN TXT \"test\"\n";
static const char *add_str =
......@@ -32,105 +35,216 @@ static const char *add_str =
static const char *del_str =
"test. IN TXT \"test\"\n";
bool to_zone;
knot_rrset_t rrset;
static void process_rr(zs_scanner_t *scanner)
/*!< \brief Returns true if node contains given RR in its RRSets. */
static bool node_contains_rr(const zone_node_t *node,
const knot_rrset_t *rr)
{
// get zone to insert into
zone_contents_t *zc = scanner->process.data;
const knot_rdataset_t *zone_rrs = node_rdataset(node, rr->type);
if (zone_rrs) {
assert(rr->rrs.rr_count == 1);
const bool compare_ttls = false;
return knot_rdataset_member(zone_rrs,
knot_rdataset_at(&rr->rrs, 0),
compare_ttls);
} else {
return false;
}
}
// create data
static void process_rr(zs_scanner_t *scanner)
{
knot_rrset_init(&rrset, scanner->r_owner, scanner->r_type, scanner->r_class);
int ret = knot_rrset_add_rdata(&rrset, scanner->r_data,
scanner->r_data_length,
scanner->r_ttl, NULL);
assert(ret == KNOT_EOK);
if (to_zone) {
// Add initial node to zone
zone_node_t *n = NULL;
ret = zone_contents_add_rr(zc, &rrset, &n);
UNUSED(n);
knot_rdataset_clear(&rrset.rrs, NULL);
assert(ret == KNOT_EOK);
}
}
int main(int argc, char *argv[])
void test_full(zone_t *zone, zs_scanner_t *sc)
{
zone_update_t update;
int ret = zone_update_init(&update, zone, UPDATE_FULL);
ok(ret == KNOT_EOK, "zone update: init full");
plan_lazy();
if (zs_set_input_string(sc, zone_str1, strlen(zone_str1)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
int ret = KNOT_EOK;
to_zone = true;
ret = zone_update_add(&update, &rrset);
knot_rdataset_clear(&rrset.rrs, NULL);
ok(ret == KNOT_EOK, "full zone update: first addition");
knot_dname_t *apex = knot_dname_from_str_alloc("test");
assert(apex);
zone_contents_t *zc = zone_contents_new(apex);
assert(zc);
zone_t zone = { .contents = zc, .name = apex };
if (zs_set_input_string(sc, zone_str2, strlen(zone_str2)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
// Parse initial node
zs_scanner_t sc;
if (zs_init(&sc, "test.", KNOT_CLASS_IN, 3600) != 0 ||
zs_set_processing(&sc, process_rr, NULL, zc) != 0) {
ret = zone_update_add(&update, &rrset);
zone_node_t *node = zone_contents_find_node_for_rr(update.new_cont, &rrset);
bool rrset_present = node_contains_rr(node, &rrset);
ok(ret == KNOT_EOK && rrset_present, "full zone update: second addition");
ret = zone_update_remove(&update, &rrset);
node = zone_contents_find_node_for_rr(update.new_cont, &rrset);
rrset_present = node_contains_rr(node, &rrset);
ok(ret == KNOT_EOK && !rrset_present, "full zone update: removal");
ret = zone_update_add(&update, &rrset);
node = zone_contents_find_node_for_rr(update.new_cont, &rrset);
rrset_present = node_contains_rr(node, &rrset);
ok(ret == KNOT_EOK && rrset_present, "full zone update: last addition");
knot_rdataset_clear(&rrset.rrs, NULL);
zone_update_iter_t it;
ret = zone_update_iter(&it, &update);
ok(ret == KNOT_EOK, "full zone update: init iter");
const zone_node_t *iter_node = zone_update_iter_val(&it);
assert(iter_node);
if (zs_set_input_string(sc, zone_str1, strlen(zone_str1)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
rrset_present = node_contains_rr(iter_node, &rrset);
ok(rrset_present, "full zone update: first iter value check");
knot_rdataset_clear(&rrset.rrs, NULL);
if (zs_set_input_string(&sc, zone_str, strlen(zone_str)) != 0 ||
zs_parse_all(&sc) != 0) {
if (zs_set_input_string(sc, zone_str2, strlen(zone_str2)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
rrset_present = node_contains_rr(iter_node, &rrset);
ok(rrset_present, "full zone update: second iter value check");
ret = zone_update_iter_next(&it);
ok(ret == KNOT_EOK, "full zone update: iter next");
iter_node = zone_update_iter_val(&it);
ok(iter_node == NULL, "full zone update: iter val past end");
ret = zone_update_iter_finish(&it);
ok(ret == KNOT_EOK, "full zone update: iter finish");
ret = zone_update_commit(conf(), &update);
node = zone_contents_find_node_for_rr(zone->contents, &rrset);
rrset_present = node_contains_rr(node, &rrset);
ok(ret == KNOT_EOK && rrset_present, "full zone update: commit");
// Initial node added, now just parse the RRs
to_zone = false;
knot_rdataset_clear(&rrset.rrs, NULL);
}
void test_incremental(zone_t *zone, zs_scanner_t *sc)
{
int ret = KNOT_EOK;
zone_update_t update;
zone_update_init(&update, &zone, UPDATE_INCREMENTAL);
ok(update.zone == &zone && changeset_empty(&update.change) && update.mm.alloc,
zone_update_init(&update, zone, UPDATE_INCREMENTAL);
ok(update.zone == zone && changeset_empty(&update.change) && update.mm.alloc,
"incremental zone update: init");
// Check that old node is returned without changes
ok(zc->apex == zone_update_get_node(&update, zc->apex->owner) &&
ok(zone->contents->apex == zone_update_get_apex(&update) &&
zone_update_no_change(&update),
"incremental zone update: no change");
// Parse RR for addition and add it
if (zs_set_input_string(&sc, add_str, strlen(add_str)) != 0 ||
zs_parse_all(&sc) != 0) {
if (zs_set_input_string(sc, add_str, strlen(add_str)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
ret = zone_update_add(&update, &rrset);
knot_rdataset_clear(&rrset.rrs, NULL);
ok(ret == KNOT_EOK, "incremental zone update: addition");
// Check that apex TXT has two RRs now
const zone_node_t *synth_node = zone_update_get_node(&update, zc->apex->owner);
const zone_node_t *synth_node = zone_update_get_apex(&update);
ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->rr_count == 2,
"incremental zone update: add change");
// Parse RR for removal and remove it
if (zs_set_input_string(&sc, del_str, strlen(del_str)) != 0 ||
zs_parse_all(&sc) != 0) {
if (zs_set_input_string(sc, del_str, strlen(del_str)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
ret = zone_update_remove(&update, &rrset);
knot_rdataset_clear(&rrset.rrs, NULL);
ok(ret == KNOT_EOK, "incremental zone update: removal");
// Check that apex TXT has one RR again
synth_node = zone_update_get_node(&update, zc->apex->owner);
synth_node = zone_update_get_apex(&update);
ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->rr_count == 1,
"incremental zone update: del change");
zone_update_clear(&update);
ok(update.zone == NULL && changeset_empty(&update.change), "incremental zone update: cleanup");
zone_update_iter_t it;
ret = zone_update_iter(&it, &update);
ok(ret == KNOT_EOK, "incremental zone update: init iter");
const zone_node_t *iter_node = zone_update_iter_val(&it);
assert(iter_node);
bool rrset_present = node_contains_rr(iter_node, &rrset);
ok(!rrset_present, "incremental zone update: first iter value check");
knot_rdataset_clear(&rrset.rrs, NULL);
if (zs_set_input_string(sc, add_str, strlen(add_str)) != 0 ||
zs_parse_all(sc) != 0) {
assert(0);
}
rrset_present = node_contains_rr(iter_node, &rrset);
ok(rrset_present, "incremental zone update: second iter value check");
ret = zone_update_iter_next(&it);
ok(ret == KNOT_EOK, "incremental zone update: iter next");
iter_node = zone_update_iter_val(&it);
ok(iter_node == NULL, "incremental zone update: iter val past end");
ret = zone_update_iter_finish(&it);
ok(ret == KNOT_EOK, "incremental zone update: iter finish");
ret = zone_update_commit(conf(), &update);
iter_node = zone_contents_find_node_for_rr(zone->contents, &rrset);
rrset_present = node_contains_rr(iter_node, &rrset);
ok(ret == KNOT_EOK && rrset_present, "incremental zone update: commit");
knot_rdataset_clear(&rrset.rrs, NULL);
}
int main(int argc, char *argv[])
{
plan_lazy();
/* Load test configuration. */
const char *conf_str = "server:\n identity: bogus.ns\n version: 0.11\n nsid: \n"
"zone:\n - domain: test.\n zonefile-sync: -1\n"
" storage: /tmp\n";
int ret = test_conf(conf_str, NULL);
if (ret != KNOT_EOK) {
return ret;
}
// Set up empty zone
knot_dname_t *apex = knot_dname_from_str_alloc("test");
assert(apex);
zone_t *zone = zone_new(apex);
// Parse initial node
zs_scanner_t sc;
if (zs_init(&sc, "test.", KNOT_CLASS_IN, 3600) != 0 ||
zs_set_processing(&sc, process_rr, NULL, NULL) != 0) {
assert(0);
}
// Test FULL update, commit it and use the result to test the INCREMENTAL update
test_full(zone, &sc);
test_incremental(zone, &sc);
zs_deinit(&sc);
zone_contents_deep_free(&zc);
zone_free(&zone);
knot_dname_free(&apex, NULL);
conf_free(conf());
return 0;
}
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