Skip to content

Commit

Permalink
WIP: Use the np_state->_private->dirty_fields structure for origin-fi…
Browse files Browse the repository at this point in the history
…le tracking

We track the list of filenames a specific (global) setting appeared in (origin-files) in a PATH like string, which separates the different files in reverse order of appearance, separated by colons.

We use "__UNNAMED" as a placeholder when reading from non-files, e.g. an in memory YAML patch, provided by "netplan set".

When writing back the specific setting we can look at the history of origin-files and decide if it should be written back to the latest file, to a (possibly new) fallback file (e.g. when it only appeared in __UNNAMED), or be dropped.
  • Loading branch information
slyon committed Apr 19, 2024
1 parent 2a74e00 commit b8af2ba
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 24 deletions.
94 changes: 80 additions & 14 deletions src/netplan.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ gchar *tmp = NULL;
(_def)->_private->dirty_fields && \
g_hash_table_contains((_def)->_private->dirty_fields, _data_ref))

/*
if (value_ptr && DIRTY(_def, value_ptr)) { \
YAML_SCALAR_PLAIN(event_ptr, emitter_ptr, key); \
YAML_SCALAR_QUOTED(event_ptr, emitter_ptr, (gchar*)g_hash_table_lookup(_def->_private->dirty_fields, &value_ptr)); \
} \
else
*/
#define YAML_STRING(_def, event_ptr, emitter_ptr, key, value_ptr) {\
if (value_ptr) { \
YAML_SCALAR_PLAIN(event_ptr, emitter_ptr, key); \
Expand Down Expand Up @@ -1039,24 +1046,55 @@ netplan_netdef_list_write_yaml(const NetplanState* np_state, GList* netdefs, int
YAML_SCALAR_PLAIN(event, emitter, "network");
YAML_MAPPING_OPEN(event, emitter);
/* We support version 2 only, currently */
//FIXME: only write "version" stanza if dirty
YAML_NONNULL_STRING_PLAIN(event, emitter, "version", "2");

/* fallback to default global handling, if renderer was not set for this file */
NetplanBackend renderer = netplan_state_get_backend(np_state);
/* Try to find a file specific (global) renderer.
* If this is the fallback file (70-netplan-set.yaml or <origin-hint>.yaml),
* a renderer parsed from a YAML patch takes precedence. */
if (out_fname && np_state->global_renderer) {
gpointer value;
renderer = GPOINTER_TO_INT(g_hash_table_lookup(np_state->global_renderer, out_fname));
/* A renderer parsed from an (anonymous) YAML patch takes precendence
* (e.g. "netplan set ..."). Such data does not have any filename
* associated to it in the global_renderer map (i.e. empty string). */
if (is_fallback && g_hash_table_lookup_extended(np_state->global_renderer, "", NULL, &value))
renderer = GPOINTER_TO_INT(value);
//printf("STATE BACKEND OUT %p\n", &np_state->backend);
//printf("out_fname %s %d\n", out_fname, is_fallback);
if (np_state->_private && np_state->_private->dirty_fields) {
char* filepath = g_hash_table_lookup(np_state->_private->dirty_fields, &np_state->backend);
//printf("filepath <%s>\n", filepath);
// <filepath> is set iff this field/setting was read somewhere in the hierarcy (i.e. is "dirty")
if (filepath) {
if ( (is_fallback && !out_fname) // dumping everything
|| (is_fallback && g_str_has_prefix(filepath, __UNNAMED)) // new global settings (from an __UNNAMED file) are written to fallback path
|| g_str_has_prefix(filepath, out_fname) // originates from this very file
) {
YAML_NONNULL_STRING_PLAIN(event, emitter, "renderer", netplan_backend_name(renderer));
} else if (g_strrstr(filepath, out_fname) && !g_str_has_prefix(filepath, out_fname)) { // keep the original value in lower priority file
/*
//XXX: parse just the single file and get the global renderer (original value)
NetplanParser *npp = netplan_parser_new();
NetplanState *np_state = netplan_state_new();
printf("res %d\n", netplan_parser_load_yaml(npp, out_fname, NULL));
printf("res %d\n", netplan_state_import_parser_results(np_state, npp, NULL));
NetplanBackend original_renderer = netplan_state_get_backend(np_state);
printf("ORIGINAL %d\n", original_renderer);
printf("netdefs %d\n", netplan_state_get_netdefs_size(np_state));
FILE *fd = fopen(out_fname, "r");
char file_buffer[1024] = {0};
fread(file_buffer, 1, 1024, fd);
printf("list_write: out_fname: %s\n%s\n", out_fname, file_buffer);
*/
NetplanBackend original_renderer = 0;
gchar **split = g_strsplit(filepath, ":", 0);
for (unsigned i = 0; split[i]; ++i) {
//printf("SPLIT %s\n", split[i]);
char* value_ptr = g_strrstr(split[i], "/");
if (value_ptr)
original_renderer = atoi(value_ptr+1);

}
g_strfreev(split);
//printf("original: %s\n", netplan_backend_name(original_renderer));
YAML_NONNULL_STRING_PLAIN(event, emitter, "renderer", netplan_backend_name(original_renderer));
} else {
//printf("renderer ERROR: filepath: %s, out_fname: %s, is_fallback: %d, renderer: %s\n", filepath, out_fname, is_fallback, netplan_backend_name(renderer));
}
}
}
if (renderer == NETPLAN_BACKEND_NM || renderer == NETPLAN_BACKEND_NETWORKD)
YAML_NONNULL_STRING_PLAIN(event, emitter, "renderer", netplan_backend_name(renderer));

/* Do not write any netdefs, if we're just setting/updating some globals,
* e.g.: netplan set "network.renderer=NetworkManager" */
Expand Down Expand Up @@ -1201,7 +1239,32 @@ netplan_state_update_yaml_hierarchy(const NetplanState* np_state, const char* de
default_path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, "etc", "netplan", default_filename, NULL);
int out_fd = -1;

/* Dump global conf to the default path */
if (np_state->_private && np_state->_private->dirty_fields) {
char *renderer_filepath = g_hash_table_lookup(np_state->_private->dirty_fields, &np_state->backend);
//printf("RENDERER_FILEPATH %s\n", renderer_filepath); //FIXME filepath/value handling
if (renderer_filepath) {
gchar **split = g_strsplit(renderer_filepath, ":", 0);
for (unsigned i = 0; split[i]; ++i) {
//printf("SPLIT %s\n", split[i]);
// ignore __UNNAMED in update_yaml_hierarchy() and only edit files in /etc/netplan/
if (g_strrstr(split[i], "/etc/netplan/")) {
g_hash_table_insert(perfile_netdefs, g_strdup(split[i]), NULL);
break;
}
}
g_strfreev(split);
}
}

/* Dump global conf (renderer) to the default path, if dirty from __UNNAMED source */
if (np_state->_private && np_state->_private->dirty_fields) {
const char* filepath = g_hash_table_lookup(np_state->_private->dirty_fields, &np_state->backend);
if (filepath && g_str_has_prefix(filepath, __UNNAMED)) {
g_hash_table_insert(perfile_netdefs, default_path, NULL);
}
}

/*
if (!np_state->netdefs || g_hash_table_size(np_state->netdefs) == 0) {
if ( has_openvswitch(&np_state->ovs_settings, NETPLAN_BACKEND_NONE, NULL)
|| (np_state->backend != NETPLAN_BACKEND_NONE && np_state->global_renderer &&
Expand All @@ -1210,6 +1273,8 @@ netplan_state_update_yaml_hierarchy(const NetplanState* np_state, const char* de
g_hash_table_insert(perfile_netdefs, default_path, NULL);
}
} else {
*/
if (np_state->netdefs && g_hash_table_size(np_state->netdefs) > 0) {
GList* iter = np_state->netdefs_ordered;
while (iter) {
NetplanNetDefinition* netdef = iter->data;
Expand Down Expand Up @@ -1245,6 +1310,7 @@ netplan_state_update_yaml_hierarchy(const NetplanState* np_state, const char* de
out_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (out_fd < 0)
goto file_error;
//printf("PERDEF %s %d\n", filename, is_fallback);
if (!netplan_netdef_list_write_yaml(np_state, netdefs, out_fd, filename, is_fallback, error))
goto cleanup; // LCOV_EXCL_LINE
close(out_fd);
Expand Down
28 changes: 26 additions & 2 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ get_handler(const mapping_entry_handler* handlers, const char* key)
STATIC gboolean
process_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const mapping_entry_handler* handlers, GList** out_values, GError** error)
{
//fprintf(stderr, "PROCESS MAPPING %s\n", npp->current.filepath);
yaml_node_pair_t* entry;

assert_type(npp, node, YAML_MAPPING_NODE);
Expand All @@ -294,6 +295,7 @@ process_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, c
key = yaml_document_get_node(&npp->doc, entry->key);
value = yaml_document_get_node(&npp->doc, entry->value);
assert_type(npp, key, YAML_SCALAR_NODE);
//fprintf(stderr, "NODE %s\n", scalar(key));
if (npp->null_fields && key_prefix) {
full_key = g_strdup_printf("%s\t%s", key_prefix, scalar(key));
if (g_hash_table_contains(npp->null_fields, full_key))
Expand Down Expand Up @@ -1212,19 +1214,22 @@ static const mapping_entry_handler wifi_access_point_handlers[] = {
STATIC gboolean
parse_renderer(NetplanParser* npp, yaml_node_t* node, NetplanBackend* backend, GError** error)
{
//TODO: mark globals dirty
if (strcmp(scalar(node), "networkd") == 0)
*backend = NETPLAN_BACKEND_NETWORKD;
else if (strcmp(scalar(node), "NetworkManager") == 0)
*backend = NETPLAN_BACKEND_NM;
else
return yaml_error(npp, node, error, "unknown renderer '%s'", scalar(node));
mark_data_as_dirty(npp, backend);
//mark_data_as_dirty(npp, backend);
mark_data_as_dirty_global(npp, backend, *backend);
return TRUE;
}

STATIC gboolean
handle_netdef_renderer(NetplanParser* npp, yaml_node_t* node, __unused const void* _, GError** error)
{
//TODO: mark globals dirty
if (npp->current.netdef->type == NETPLAN_DEF_TYPE_VLAN) {
if (strcmp(scalar(node), "sriov") == 0) {
npp->current.netdef->sriov_vlan_filter = TRUE;
Expand Down Expand Up @@ -3066,6 +3071,8 @@ STATIC gboolean
handle_network_renderer(NetplanParser* npp, yaml_node_t* node, __unused const void* _, GError** error)
{
gboolean res = parse_renderer(npp, node, &npp->global_backend, error);
//printf("### RENDER global_backend %p %s\n", &npp->global_backend, npp->current.filepath); fflush(stdout);
mark_data_as_dirty(npp, &npp->global_backend);
if (!npp->global_renderer)
npp->global_renderer = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
char* key = npp->current.filepath ? g_strdup(npp->current.filepath) : g_strdup("");
Expand Down Expand Up @@ -3327,6 +3334,7 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const char* key_prefi
set_str_if_null(npp->current.netdef->match.original_name, npp->current.netdef->id);
}
npp->current.backend = NETPLAN_BACKEND_NONE;
npp->current.netdef = NULL;
return TRUE;
}

Expand Down Expand Up @@ -3489,6 +3497,8 @@ process_document(NetplanParser* npp, GError** error)
STATIC gboolean
_netplan_parser_load_single_file(NetplanParser* npp, const char *opt_filepath, yaml_document_t *doc, GError** error)
{
//fprintf(stderr, "\n======================================\n");
//fprintf(stderr, "PARSER: load_single_file %s\n", opt_filepath);
int ret = FALSE;

if (opt_filepath) {
Expand Down Expand Up @@ -3615,7 +3625,20 @@ netplan_state_import_parser_results(NetplanState* np_state, NetplanParser* npp,
}
np_state->netdefs_ordered = g_list_concat(np_state->netdefs_ordered, npp->ordered);
np_state->ovs_settings = npp->global_ovs_settings;
np_state->backend = npp->global_backend;
np_state->backend = npp->global_backend; //XXX: copy value vs transfer pointer for _private->dirty_fields
//printf("GLOBAL BACKEND: state %p, parser %p\n", &np_state->backend, &npp->global_backend);
// Handle global data on a per state scope ("renderer" for now)
if (!np_state->_private)
np_state->_private = g_new0(struct private_netdef_data, 1);
if (!np_state->_private->dirty_fields)
np_state->_private->dirty_fields = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
if (npp->_private && npp->_private->dirty_fields) {
//g_hash_table_insert(np_state->_private->dirty_fields, (void*)data_ptr, g_strdup(npp->current.filepath));
char* filepath = g_hash_table_lookup(npp->_private->dirty_fields, &npp->global_backend);
g_hash_table_replace(np_state->_private->dirty_fields, &np_state->backend, filepath);
//printf("STATE BACKEND: %p, %s\n", &np_state->backend, filepath);
}


if (npp->sources) {
if (!np_state->sources)
Expand Down Expand Up @@ -3856,3 +3879,4 @@ netplan_parser_load_nullable_overrides(
yaml_document_delete(&doc);
return TRUE;
}
//TODO: mark globals dirty
2 changes: 2 additions & 0 deletions src/types-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ struct netplan_state {
* char*) and is initialized with g_hash_table_new_full to avoid leaks. */
GHashTable* sources;
GHashTable* global_renderer;
struct private_netdef_data* _private;
};

struct netplan_parser {
Expand Down Expand Up @@ -263,6 +264,7 @@ struct netplan_parser {
GHashTable* null_fields;
GHashTable* null_overrides;
GHashTable* global_renderer;
struct private_netdef_data* _private;
};

struct netplan_state_iterator {
Expand Down
4 changes: 4 additions & 0 deletions src/util-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <glib.h>
#include "netplan.h"

#define __UNNAMED "__UNNAMED"
#define SET_OPT_OUT_PTR(ptr,val) { if (ptr) *ptr = val; }

#define __unused __attribute__((unused))
Expand Down Expand Up @@ -66,6 +67,9 @@ systemd_escape(char* string);
void
mark_data_as_dirty(NetplanParser* npp, const void* data_ptr);

void
mark_data_as_dirty_global(NetplanParser* npp, const void* data_ptr, int value);

const char*
tunnel_mode_to_string(NetplanTunnelMode mode);

Expand Down
100 changes: 92 additions & 8 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -899,14 +899,98 @@ has_openvswitch(const NetplanOVSSettings* ovs, NetplanBackend backend, GHashTabl
void
mark_data_as_dirty(NetplanParser* npp, const void* data_ptr)
{
// We don't support dirty tracking for globals yet.
if (!npp->current.netdef)
return;
if (!npp->current.netdef->_private)
npp->current.netdef->_private = g_new0(struct private_netdef_data, 1);
if (!npp->current.netdef->_private->dirty_fields)
npp->current.netdef->_private->dirty_fields = g_hash_table_new(g_direct_hash, g_direct_equal);
g_hash_table_insert(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, (void*)data_ptr);
//fprintf(stderr, "MARK_DIRTY %p\n", data_ptr);
if (!npp->current.netdef) {
// Handle global data on a per parser scope ("renderer" for now)
if (!npp->_private)
npp->_private = g_new0(struct private_netdef_data, 1);
if (!npp->_private->dirty_fields)
npp->_private->dirty_fields = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
char *old_filepath = (char*)g_hash_table_lookup(npp->_private->dirty_fields, (void*)data_ptr);
const char *filepath = npp->current.filepath ?: __UNNAMED;
//fprintf(stderr, "OLD FILEPATH %s\n", old_filepath);
if (!old_filepath) {
//fprintf(stderr, "XX INSERT %s\n", filepath);
g_hash_table_insert(npp->_private->dirty_fields, (void*)data_ptr, g_strdup(filepath));
} else if (g_strrstr(old_filepath, filepath) != old_filepath) { //FIXME: why the duplication?
char *fpath = g_strdup_printf("%s:%s", filepath, old_filepath);
//fprintf(stderr, "XX PREPEND %s\n", fpath);
g_hash_table_replace(npp->_private->dirty_fields, (void*)data_ptr, fpath);
}
} else {
// Handle per netdef data
if (!npp->current.netdef->_private)
npp->current.netdef->_private = g_new0(struct private_netdef_data, 1);
if (!npp->current.netdef->_private->dirty_fields)
npp->current.netdef->_private->dirty_fields = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);

char *old_filepath = (char*)g_hash_table_lookup(npp->current.netdef->_private->dirty_fields, (void*)data_ptr);
const char *filepath = npp->current.filepath ?: __UNNAMED;;
//fprintf(stderr, "OLD FILEPATH %s\n", old_filepath);
if (!old_filepath) {
//fprintf(stderr, "YY INSERT %s\n", filepath);
g_hash_table_insert(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, g_strdup(filepath));
//filepath = g_strdup_printf("%s:%s", old_filepath, __UNNAMED);
//printf("ADD3 %s\n", filepath);
//g_hash_table_replace(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, filepath);
} else if (g_strrstr(old_filepath, filepath) != old_filepath) { //FIXME: why the duplication?
char *fpath = g_strdup_printf("%s:%s", filepath, old_filepath);
//fprintf(stderr, "YY PREPEND %s\n", fpath);
g_hash_table_replace(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, fpath);
//filepath = g_strdup(npp->current.filepath ?: __UNNAMED);
//printf("ADD4 %s\n", filepath);
}
//g_hash_table_insert(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, g_strdup(npp->current.filepath ?: __UNNAMED)); // XXX: What are the side-effects here?
}
}

//FIXME: no additional _global function
void
mark_data_as_dirty_global(NetplanParser* npp, const void* data_ptr, int value)
{
//fprintf(stderr, "MARK_DIRTY %p %d\n", data_ptr, value);
if (!npp->current.netdef) {
// Handle global data on a per parser scope ("renderer" for now)
if (!npp->_private)
npp->_private = g_new0(struct private_netdef_data, 1);
if (!npp->_private->dirty_fields)
npp->_private->dirty_fields = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
char *old_filepath = (char*)g_hash_table_lookup(npp->_private->dirty_fields, (void*)data_ptr);
const char *filepath = npp->current.filepath ?: __UNNAMED;
//fprintf(stderr, "OLD FILEPATH %s\n", old_filepath);
if (!old_filepath) {
//fprintf(stderr, "XX INSERT %s\n", filepath);
g_hash_table_insert(npp->_private->dirty_fields, (void*)data_ptr, g_strdup_printf("%s/%d", filepath, value));
} else if (g_strrstr(old_filepath, filepath) != old_filepath) { //FIXME: why the duplication?
char *fpath = g_strdup_printf("%s/%d:%s", filepath, value, old_filepath);
//fprintf(stderr, "XX PREPEND %s\n", fpath);
g_hash_table_replace(npp->_private->dirty_fields, (void*)data_ptr, fpath);
}
} else {
// Handle per netdef data
if (!npp->current.netdef->_private)
npp->current.netdef->_private = g_new0(struct private_netdef_data, 1);
if (!npp->current.netdef->_private->dirty_fields)
npp->current.netdef->_private->dirty_fields = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);

char *old_filepath = (char*)g_hash_table_lookup(npp->current.netdef->_private->dirty_fields, (void*)data_ptr);
const char *filepath = npp->current.filepath ?: __UNNAMED;;
//fprintf(stderr, "OLD FILEPATH %s\n", old_filepath);
if (!old_filepath) {
//fprintf(stderr, "YY INSERT %s\n", filepath);
g_hash_table_insert(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, g_strdup(filepath));
//filepath = g_strdup_printf("%s:%s", old_filepath, __UNNAMED);
//printf("ADD3 %s\n", filepath);
//g_hash_table_replace(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, filepath);
} else if (g_strrstr(old_filepath, filepath) != old_filepath) { //FIXME: why the duplication?
char *fpath = g_strdup_printf("%s:%s", filepath, old_filepath);
//fprintf(stderr, "YY PREPEND %s\n", fpath);
g_hash_table_replace(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, fpath);
//filepath = g_strdup(npp->current.filepath ?: __UNNAMED);
//printf("ADD4 %s\n", filepath);
}
//g_hash_table_insert(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, g_strdup(npp->current.filepath ?: __UNNAMED)); // XXX: What are the side-effects here?
}
}

gboolean
Expand Down

0 comments on commit b8af2ba

Please sign in to comment.