Materialization of zones

This commit is contained in:
Colin Vidal
2025-02-24 10:35:39 +01:00
parent 90399dcd7a
commit 6d24bfeb62
2 changed files with 358 additions and 102 deletions
+173 -23
View File
@@ -45,11 +45,44 @@
#define NODEINDEXFLG 'i'
#define PROPERTYFLG 'p'
/*
* Used to avoid repeated hashing to figure out if we're in those special nodes
*
* - root options is special as it gets "materialized", if on running mode, from
* options node in builtin and user nodes.
*
* - root views is special as each of each sub-nodes is considered as a view
* instance
*
* - a zones node inside a view instance is special as each sub-node is
* considered a zone instance
*
* - a zone instance gets "materialized" from root options and the view instance
* it belongs.
*/
#define OPTIONS "options"
#define VIEWS "views"
#define ZONES "zones"
static uint32_t isc__cfgmgr_hashoptions;
static uint32_t isc__cfgmgr_hashviews;
static uint32_t isc__cfgmgr_hashzones;
#define NODE_FLG_OPTIONS 1 << 0
#define NODE_FLG_VIEWS 1 << 1
#define NODE_FLG_VIEW 1 << 2
#define NODE_FLG_ZONES 1 << 3
#define NODE_FLG_ZONE 1 << 4
/*
* Defines an opened node.
*/
typedef struct isc__cfgmgr_node isc__cfgmgr_node_t;
struct isc__cfgmgr_node {
char *name;
char *prefix;
uint32_t hash;
uint32_t flags;
ISC_LINK(isc__cfgmgr_node_t) link;
};
typedef ISC_LIST(isc__cfgmgr_node_t) isc__cfgmgr_nodes_t;
@@ -238,6 +271,10 @@ isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath) {
goto cleanup;
}
isc__cfgmgr_hashoptions = isc_hash32(OPTIONS, strlen(OPTIONS), false);
isc__cfgmgr_hashviews = isc_hash32(VIEWS, strlen(VIEWS), false);
isc__cfgmgr_hashzones = isc_hash32(ZONES, strlen(ZONES), false);
goto out;
cleanup:
@@ -452,7 +489,7 @@ isc__cfgmgr_curhash(void) {
if (node == NULL) {
const char *rootprefix = isc__cfgmgr_curprefix();
uint64_t roothash = isc_hash64(rootprefix, strlen(rootprefix),
uint64_t roothash = isc_hash32(rootprefix, strlen(rootprefix),
false);
INSIST(roothash > 0);
@@ -514,6 +551,56 @@ isc__cfgmgr_addindex(MDB_cursor *cursor, const char *name) {
REQUIRE(mdb_cursor_put(cursor, &dbkey, &dbval, 0) == 0);
}
static void
isc__cfgmgr_setnodeflags(isc__cfgmgr_node_t *node) {
isc__cfgmgr_node_t *curnode = isc__cfgmgr_curnode();
uint32_t hash = isc_hash32(node->name, strlen(node->name), false);
/*
* At root level, can be options or views special nodes
*/
if (curnode == NULL) {
if (hash == isc__cfgmgr_hashoptions) {
node->flags |= NODE_FLG_OPTIONS;
} else if (hash == isc__cfgmgr_hashviews) {
node->flags |= NODE_FLG_VIEWS;
}
}
/*
* Inside the views special node, marking the new node as a view
* instance.
*/
else if (curnode->flags & NODE_FLG_VIEWS)
{
node->flags |= NODE_FLG_VIEW;
}
/*
* Knowing that a node is a zone instance doesn't seems needed right
* now... I'm leaving the code below for now, just in case, but likely
* to be removed later.
*/
/*
* Inside a view instance node, looking for the zones special node.
*/
else if (curnode->flags & NODE_FLG_VIEW &&
hash == isc__cfgmgr_hashzones)
{
node->flags |= NODE_FLG_ZONES;
}
/*
* Inside a view zones special instance node, marking the new node as a
* zone instance node
*/
else if (curnode->flags & NODE_FLG_ZONES)
{
node->flags |= NODE_FLG_ZONE;
}
}
void
isc_cfgmgr_open(const char *name) {
isc__cfgmgr_node_t *node = NULL;
@@ -526,14 +613,17 @@ isc_cfgmgr_open(const char *name) {
.name = isc_mem_allocate(isc__cfgmgr_mctx, strlen(name) + 1),
.prefix = isc_mem_get(isc__cfgmgr_mctx, BUFLEN),
.link = ISC_LINK_INITIALIZER,
.hash = isc_hash64(name, strlen(name), true)
};
strcpy(node->name, name);
INSIST(node->hash > 0);
REQUIRE(snprintf(node->prefix, BUFLEN, "%s%s/", isc__cfgmgr_curprefix(),
name) < BUFLEN);
node->hash = isc_hash32(node->prefix, strlen(node->prefix), true);
INSIST(node->hash > 0);
isc__cfgmgr_setnodeflags(node);
if (isc__cfgmgr_ctx.readonly == false) {
isc__cfgmgr_addindex(isc__cfgmgr_ctx.builtincursor, name);
isc__cfgmgr_addindex(isc__cfgmgr_ctx.usercursor, name);
@@ -769,35 +859,95 @@ static isc_result_t
isc__cfgmgr_write(MDB_cursor *cursor, const char *name,
const isc_cfgmgr_val_t *value, bool materialize);
typedef struct isc__cfgmgr_materializeprop isc__cfgmgr_materializeprop_t;
struct isc__cfgmgr_materializeprop {
const char *name;
const isc_cfgmgr_val_t *value;
};
static void
isc__cfgmgr_materialize(const char *name, const isc_cfgmgr_val_t *value) {
isc__cfgmgr_materialize_apply(void *state, const char *nodename) {
isc_cfgmgr_mode_t valmode;
isc_cfgmgr_val_t val;
isc_result_t result;
isc__cfgmgr_materializeprop_t *property = state;
UNUSED(nodename);
result = isc__cfgmgr_read(isc__cfgmgr_ctx.runningcursor, property->name,
&val, &valmode);
INSIST(result == ISC_R_NOTFOUND || result == ISC_R_SUCCESS);
if ((result == ISC_R_NOTFOUND && property->value != NULL) ||
(result == ISC_R_SUCCESS && valmode <= isc__cfgmgr_ctx.mode))
{
REQUIRE(isc__cfgmgr_write(isc__cfgmgr_ctx.runningcursor,
property->name, property->value,
false) == ISC_R_SUCCESS);
}
}
static void
isc__cfgmgr_materialize_foreachview(void *state, const char *nodename) {
UNUSED(nodename);
isc_cfgmgr_open("zones");
isc_cfgmgr_foreachnodes(isc__cfgmgr_materialize_apply, state);
isc_cfgmgr_close();
}
static void
isc__cfgmgr_materialize_zone(isc__cfgmgr_materializeprop_t *property,
bool inview) {
if (inview) {
isc_cfgmgr_open("zones");
isc_cfgmgr_foreachnodes(isc__cfgmgr_materialize_apply, property);
isc_cfgmgr_close();
} else {
isc_cfgmgr_open("views");
isc_cfgmgr_foreachnodes(isc__cfgmgr_materialize_foreachview,
property);
isc_cfgmgr_close();
}
}
/*
* There is an issue with the materialization on zone: it will copy _all_
* options properties/sub-nodes into each zones. While this is not a problem for
* reading data (nobody will attempt to read data not expected to be in a zone)
* this will be a problem when dumping the zone.
*
* This is probably something to figure out with the validation of the
* configuration, which could be used as a filter in the copy callbacks during
* the materialization.
*
* isc_cfgmgr_delnode doest not supports materialization (i.e. it won't remove
* deleted properties if they hasn't been overriden). Not sure the _delnode API
* is actually useful. If it is (and we keep it) then it needs to be fixed.
*/
static void
isc__cfgmgr_materialize(const char *name, const isc_cfgmgr_val_t *value) {
isc__cfgmgr_node_t *node = isc__cfgmgr_curnode();
isc__cfgmgr_materializeprop_t property = { .name = name,
.value = value };
if (isc__cfgmgr_ctx.mode == ISC_CFGMGR_MODERUNNING) {
return;
}
/*
* The only nodes which "accepts" materialization
* (view/zones cases TBD)
* (value == NULL TDB)
* (delnode flow TBD)
*/
if (strcmp(isc__cfgmgr_curprefix(), "/options/") != 0) {
return;
}
result = isc__cfgmgr_read(isc__cfgmgr_ctx.runningcursor, name, &val,
&valmode);
INSIST(result == ISC_R_NOTFOUND || result == ISC_R_SUCCESS);
if (result == ISC_R_NOTFOUND ||
(result == ISC_R_SUCCESS && valmode < ISC_CFGMGR_MODERUNNING))
{
REQUIRE(isc__cfgmgr_write(isc__cfgmgr_ctx.runningcursor, name,
value, false) == ISC_R_SUCCESS);
if (node->flags & NODE_FLG_OPTIONS) {
/*
* Materialization of the changed options property in the
* running options node as well as all the running zones.
*/
isc__cfgmgr_materialize_apply(&property, NULL);
isc__cfgmgr_materialize_zone(&property, false);
} else if (node->flags & NODE_FLG_VIEW) {
/*
* Materialization of the changed view property in all its
* running zones
*/
isc__cfgmgr_materialize_zone(&property, true);
}
}
+185 -79
View File
@@ -1459,7 +1459,7 @@ struct isc__cfgmgr_dump {
size_t indent;
size_t lvl;
isc_buffer_t buffer;
char data[512];
char data[1024];
};
static void
@@ -1506,6 +1506,21 @@ isc__cfgmgr_dumptestnode(void *state, const char *name) {
isc_buffer_putstr(&dump->buffer, " ");
}
isc_buffer_printf(&dump->buffer, "};\n");
}
static void
cmpdump(isc_cfgmgr_mode_t mode, const char *expected) {
isc__cfgmgr_dump_t dump = {
.lvl = 0,
.indent = 2,
};
isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data));
dump.data[0] = 0;
isc_cfgmgr_setmode(mode);
isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump);
assert_string_equal(dump.buffer.base, expected);
}
ISC_RUN_TEST_IMPL(isc_cfgmgr_dumptests) {
@@ -1566,41 +1581,32 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_dumptests) {
isc_cfgmgr_close(); /* foo1 */
isc_cfgmgr_close(); /* foo */
isc__cfgmgr_dump_t dump = {
.lvl = 0,
.indent = 8,
};
isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data));
dump.data[0] = 0;
isc_cfgmgr_commit();
isc_cfgmgr_transaction();
isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump);
const char expected[] = "bar {\n"
" another-stuff barstr;\n"
" propbar1 42;\n"
"};\n"
"foo {\n"
" foo1 {\n"
" foo11 {\n"
" fooprop 123;\n"
" };\n"
" foo12 {\n"
" fooprop 456;\n"
" prop 123;\n"
" };\n"
" foo13 {\n"
" fooprop 789;\n"
" prop 123;\n"
" };\n"
" };\n"
" foo2 {\n"
" another-foo-stuff foo2str;\n"
" };\n"
"};\n";
assert_string_equal(dump.buffer.base, expected);
cmpdump(ISC_CFGMGR_MODERUNNING,
"bar {\n"
" another-stuff barstr;\n"
" propbar1 42;\n"
"};\n"
"foo {\n"
" foo1 {\n"
" foo11 {\n"
" fooprop 123;\n"
" };\n"
" foo12 {\n"
" fooprop 456;\n"
" prop 123;\n"
" };\n"
" foo13 {\n"
" fooprop 789;\n"
" prop 123;\n"
" };\n"
" };\n"
" foo2 {\n"
" another-foo-stuff foo2str;\n"
" };\n"
"};\n");
isc_cfgmgr_rollback();
result = isc_cfgmgr_rwtransaction();
@@ -1629,20 +1635,17 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_dumptests) {
isc_cfgmgr_close();
isc_cfgmgr_close();
isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data));
isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump);
const char expected2[] = "bar {\n"
" another-stuff barstr;\n"
" propbar1 42;\n"
"};\n"
"foo {\n"
" propfoo 987;\n"
" foo14 {\n"
" anotherpropfoo14 654;\n"
" };\n"
"};\n";
assert_string_equal(dump.buffer.base, expected2);
cmpdump(ISC_CFGMGR_MODERUNNING,
"bar {\n"
" another-stuff barstr;\n"
" propbar1 42;\n"
"};\n"
"foo {\n"
" propfoo 987;\n"
" foo14 {\n"
" anotherpropfoo14 654;\n"
" };\n"
"};\n");
isc_cfgmgr_rollback();
isc_cfgmgr_deinit();
@@ -1658,6 +1661,38 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_materialization) {
result = isc_cfgmgr_rwtransaction();
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_open("views");
isc_cfgmgr_open("fooview");
val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 991 };
result = isc_cfgmgr_write("prop2", &val);
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_open("zones");
isc_cfgmgr_open("foo.org");
val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 661 };
result = isc_cfgmgr_write("prop1", &val);
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_close();
isc_cfgmgr_close();
isc_cfgmgr_close();
isc_cfgmgr_open("barview");
isc_cfgmgr_open("zones");
isc_cfgmgr_open("bar.org");
val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 662 };
result = isc_cfgmgr_write("prop1", &val);
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_close();
isc_cfgmgr_open("baz.org");
val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 663 };
result = isc_cfgmgr_write("prop1", &val);
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_close();
isc_cfgmgr_close();
isc_cfgmgr_close();
isc_cfgmgr_close(); /* views */
isc_cfgmgr_open("options");
isc_cfgmgr_setmode(ISC_CFGMGR_MODERUNNING);
@@ -1690,44 +1725,115 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_materialization) {
isc_cfgmgr_close();
isc__cfgmgr_dump_t dump = {
.lvl = 0,
.indent = 8,
};
isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data));
dump.data[0] = 0;
cmpdump(ISC_CFGMGR_MODEBUILTIN, "options {\n"
" prop1 1;\n"
" prop2 2;\n"
" prop3 3;\n"
"};\n");
isc_cfgmgr_setmode(ISC_CFGMGR_MODEBUILTIN);
isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump);
const char expectedbuiltin[] = "options {\n"
" prop1 1;\n"
" prop2 2;\n"
" prop3 3;\n"
"};\n";
assert_string_equal(dump.buffer.base, expectedbuiltin);
cmpdump(ISC_CFGMGR_MODEUSER, "options {\n"
" prop2 21;\n"
"};\n");
isc_cfgmgr_setmode(ISC_CFGMGR_MODEUSER);
isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data));
isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump);
const char expecteduser[] = "options {\n"
" prop2 21;\n"
"};\n";
assert_string_equal(dump.buffer.base, expecteduser);
isc_cfgmgr_setmode(ISC_CFGMGR_MODERUNNING);
isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data));
isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump);
const char expectedrunning[] = "options {\n"
" prop1 31;\n"
" prop2 21;\n"
" prop3 3;\n"
" prop4 34;\n"
"};\n";
assert_string_equal(dump.buffer.base, expectedrunning);
cmpdump(ISC_CFGMGR_MODERUNNING, "options {\n"
" prop1 31;\n"
" prop2 21;\n"
" prop3 3;\n"
" prop4 34;\n"
"};\n"
"views {\n"
" barview {\n"
" zones {\n"
" bar.org {\n"
" prop1 662;\n"
" prop2 21;\n"
" prop3 3;\n"
" prop4 34;\n"
" };\n"
" baz.org {\n"
" prop1 663;\n"
" prop2 21;\n"
" prop3 3;\n"
" prop4 34;\n"
" };\n"
" };\n"
" };\n"
" fooview {\n"
" prop2 991;\n"
" zones {\n"
" foo.org {\n"
" prop1 661;\n"
" prop2 991;\n"
" prop3 3;\n"
" prop4 34;\n"
" };\n"
" };\n"
" };\n"
"};\n");
result = isc_cfgmgr_commit();
assert_int_equal(result, ISC_R_SUCCESS);
/*
* deleting prop3 and prop2 from builtin: only prop3 will be removed
* from options, as it's unmodified.
*/
result = isc_cfgmgr_rwtransaction();
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_setmode(ISC_CFGMGR_MODEBUILTIN);
isc_cfgmgr_open("options");
result = isc_cfgmgr_write("prop2", NULL);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_cfgmgr_write("prop3", NULL);
assert_int_equal(result, ISC_R_SUCCESS);
isc_cfgmgr_close();
cmpdump(ISC_CFGMGR_MODEBUILTIN, "options {\n"
" prop1 1;\n"
"};\n");
cmpdump(ISC_CFGMGR_MODEUSER, "options {\n"
" prop2 21;\n"
"};\n");
cmpdump(ISC_CFGMGR_MODERUNNING, "options {\n"
" prop1 31;\n"
" prop2 21;\n"
" prop4 34;\n"
"};\n"
"views {\n"
" barview {\n"
" zones {\n"
" bar.org {\n"
" prop1 662;\n"
" };\n"
" baz.org {\n"
" prop1 663;\n"
" };\n"
" };\n"
" };\n"
" fooview {\n"
" prop2 991;\n"
" zones {\n"
" foo.org {\n"
" prop1 661;\n"
" };\n"
" };\n"
" };\n"
"};\n");
isc_cfgmgr_rollback();
/*
* TODO
* Then a zone is added _after_ all of those options changes. It must
* contains all of those as well
*/
isc_cfgmgr_deinit();
}