diff options
author | Thomas Haller <thaller@redhat.com> | 2020-11-06 14:19:23 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-11-09 17:53:18 +0100 |
commit | 7cf1f7fe02d8f1a1b00f7c1263bf40f417495cea (patch) | |
tree | 61d3c240bc546710bc99f0b8773a3cd667272a3a | |
parent | f6d3b5f5f41ec2480db073c3163fd7c3cabf5d1f (diff) | |
download | NetworkManager-7cf1f7fe02d8f1a1b00f7c1263bf40f417495cea.tar.gz |
core/ovs: cleanup logic in update handling of ovsdb_got_update()
ovsdb sends monitor updates, with "new" and "old" values that indicate
whether this is an addition, and update, or a removal.
Since we also cache the entries, we might not agree with what ovsdb
says. E.g. if ovsdb says this is an update, but we didn't have the
interface in our cache, we should rather pretend that the interface
was added. Even if this possibly indicates some inconsistency between
what OVS says and what we have cached, we should make the best of it.
Rework the code. On update, we compare the result with our cache
and care less about the "new" / "old" values.
-rw-r--r-- | src/devices/ovs/nm-ovsdb.c | 421 |
1 files changed, 269 insertions, 152 deletions
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index 62146fef46..b78bb8a804 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -236,6 +236,14 @@ _free_interface(OpenvswitchInterface *ovs_interface) nm_g_slice_free(ovs_interface); } +static gboolean +_openvswitch_interface_should_emit_signal(const OpenvswitchInterface *ovs_interface) +{ + /* Currently, the factory only creates NMDevices for + * internal interfaces. We ignore the rest. */ + return nm_streq0(ovs_interface->type, "internal"); +} + /*****************************************************************************/ static void @@ -1187,7 +1195,7 @@ ovsdb_next_command(NMOvsdb *self) * [ "uuid", "185c93f6-0b39-424e-8587-77d074aa7ce0" ], ... ] ] */ static void -_uuids_to_array(GPtrArray *array, const json_t *items) +_uuids_to_array_inplace(GPtrArray *array, const json_t *items) { const char *key; json_t * value; @@ -1201,19 +1209,36 @@ _uuids_to_array(GPtrArray *array, const json_t *items) value = json_array_get(items, index); index++; - if (!value) + if (!value || !key) return; - if (nm_streq0(key, "uuid") && json_is_string(value)) { - g_ptr_array_add(array, g_strdup(json_string_value(value))); - } else if (nm_streq0(key, "set") && json_is_array(value)) { - json_array_foreach (value, set_index, set_value) { - _uuids_to_array(array, set_value); + if (nm_streq(key, "uuid")) { + if (json_is_string(value)) + g_ptr_array_add(array, g_strdup(json_string_value(value))); + continue; + } + if (nm_streq(key, "set")) { + if (json_is_array(value)) { + json_array_foreach (value, set_index, set_value) + _uuids_to_array_inplace(array, set_value); } + continue; } } } +static GPtrArray * +_uuids_to_array(const json_t *items) +{ + GPtrArray *array; + + array = g_ptr_array_new_with_free_func(g_free); + _uuids_to_array_inplace(array, items); + return array; +} + +/*****************************************************************************/ + static char * _connection_uuid_from_external_ids(json_t *external_ids) { @@ -1251,14 +1276,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) json_error_t json_error = { 0, }; - void * iter; - const char * name; - const char * key; - const char * type; - json_t * value; - OpenvswitchBridge * ovs_bridge; - OpenvswitchPort * ovs_port; - OpenvswitchInterface *ovs_interface; + void * iter; + const char *name; + const char *key; + const char *type; + json_t * value; if (json_unpack_ex(msg, &json_error, @@ -1287,16 +1309,13 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) nm_utils_strdup_reset(&priv->db_uuid, s); } - /* Interfaces */ json_object_foreach (interface, key, value) { - json_t * error = NULL; - gboolean old = FALSE; - gboolean new = FALSE; - - if (json_unpack(value, "{s:{}}", "old") == 0) - old = TRUE; + OpenvswitchInterface *ovs_interface; + gs_free char * connection_uuid = NULL; + json_t * error = NULL; + int r; - if (json_unpack(value, + r = json_unpack(value, "{s:{s:s, s:s, s?:o, s:o}}", "new", "name", @@ -1306,81 +1325,109 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) "error", &error, "external_ids", - &external_ids) - == 0) - new = TRUE; - - if (old) { - ovs_interface = g_hash_table_lookup(priv->interfaces, &key); - if (!ovs_interface) { - _LOGW("Interface '%s' was not seen", key); - } else if (!new || !nm_streq(ovs_interface->name, name)) { - old = FALSE; - _LOGT("removed an '%s' interface: %s%s%s", - ovs_interface->type, - ovs_interface->name, - ovs_interface->connection_uuid ? ", " : "", - ovs_interface->connection_uuid ?: ""); - if (nm_streq0(ovs_interface->type, "internal")) { - /* Currently, the factory only creates NMDevices for - * internal interfaces. Ignore the rest. */ - _signal_emit_device_removed(self, - ovs_interface->name, - NM_DEVICE_TYPE_OVS_INTERFACE); - } + &external_ids); + if (r != 0) { + gpointer unused; + + r = json_unpack(value, "{s:{}}", "old"); + if (r != 0) + continue; + + if (!g_hash_table_steal_extended(priv->interfaces, + &key, + (gpointer *) &ovs_interface, + &unused)) + continue; + + _LOGT("obj[iface:%s]: removed an '%s' interface: %s%s%s", + key, + ovs_interface->type, + ovs_interface->name, + NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, + ", ", + ovs_interface->connection_uuid, + "")); + if (_openvswitch_interface_should_emit_signal(ovs_interface)) { + _signal_emit_device_removed(self, + ovs_interface->name, + NM_DEVICE_TYPE_OVS_INTERFACE); + } + _free_interface(ovs_interface); + continue; + } + + ovs_interface = g_hash_table_lookup(priv->interfaces, &key); + + if (ovs_interface + && (!nm_streq0(ovs_interface->name, name) || !nm_streq0(ovs_interface->type, type))) { + if (!g_hash_table_steal(priv->interfaces, ovs_interface)) + nm_assert_not_reached(); + if (_openvswitch_interface_should_emit_signal(ovs_interface)) { + _signal_emit_device_removed(self, + ovs_interface->name, + NM_DEVICE_TYPE_OVS_INTERFACE); } - g_hash_table_remove(priv->interfaces, &key); + nm_clear_pointer(&ovs_interface, _free_interface); } - if (new) { + connection_uuid = _connection_uuid_from_external_ids(external_ids); + + if (ovs_interface) { + gboolean changed = FALSE; + + nm_assert(nm_streq0(ovs_interface->name, name)); + + changed |= nm_utils_strdup_reset(&ovs_interface->type, type); + changed |= nm_utils_strdup_reset_take(&ovs_interface->connection_uuid, + g_steal_pointer(&connection_uuid)); + if (changed) { + _LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s", + key, + type, + ovs_interface->name, + NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, + ", ", + ovs_interface->connection_uuid, + "")); + } + } else { ovs_interface = g_slice_new(OpenvswitchInterface); *ovs_interface = (OpenvswitchInterface){ .interface_uuid = g_strdup(key), .name = g_strdup(name), .type = g_strdup(type), - .connection_uuid = _connection_uuid_from_external_ids(external_ids), + .connection_uuid = g_steal_pointer(&connection_uuid), }; g_hash_table_add(priv->interfaces, ovs_interface); - if (old) { - _LOGT("changed an '%s' interface: %s%s%s", - type, - ovs_interface->name, - ovs_interface->connection_uuid ? ", " : "", - ovs_interface->connection_uuid ?: ""); - } else { - _LOGT("added an '%s' interface: %s%s%s", - ovs_interface->type, - ovs_interface->name, - ovs_interface->connection_uuid ? ", " : "", - ovs_interface->connection_uuid ?: ""); - if (nm_streq0(ovs_interface->type, "internal")) { - /* Currently, the factory only creates NMDevices for - * internal interfaces. Ignore the rest. */ - _signal_emit_device_added(self, - ovs_interface->name, - NM_DEVICE_TYPE_OVS_INTERFACE); - } - } - /* The error is a string. No error is indicated by an empty set, - * because why the fuck not: [ "set": [] ] */ - if (error && json_is_string(error)) { - _signal_emit_interface_failed(self, - ovs_interface->name, - ovs_interface->connection_uuid, - json_string_value(error)); - } + _LOGT("obj[iface:%s]: added an '%s' interface: %s%s%s", + key, + ovs_interface->type, + ovs_interface->name, + NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, + ", ", + ovs_interface->connection_uuid, + "")); + if (_openvswitch_interface_should_emit_signal(ovs_interface)) + _signal_emit_device_added(self, ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE); + } + + /* The error is a string. No error is indicated by an empty set, + * Why not: [ "set": [] ] ? */ + if (error && json_is_string(error)) { + _signal_emit_interface_failed(self, + ovs_interface->name, + ovs_interface->connection_uuid, + json_string_value(error)); } } - /* Ports */ json_object_foreach (port, key, value) { - gboolean old = FALSE; - gboolean new = FALSE; - - if (json_unpack(value, "{s:{}}", "old") == 0) - old = TRUE; + gs_unref_ptrarray GPtrArray *interfaces = NULL; + OpenvswitchPort * ovs_port; + gs_free char * connection_uuid = NULL; + int r; - if (json_unpack(value, + r = json_unpack(value, "{s:{s:s, s:o, s:o}}", "new", "name", @@ -1388,57 +1435,89 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) "external_ids", &external_ids, "interfaces", - &items) - == 0) - new = TRUE; - - if (old) { - ovs_port = g_hash_table_lookup(priv->ports, &key); - if (!new || (ovs_port && !nm_streq0(ovs_port->name, name))) { - old = FALSE; - _LOGT("removed a port: %s%s%s", - ovs_port->name, - ovs_port->connection_uuid ? ", " : "", - ovs_port->connection_uuid ?: ""); - _signal_emit_device_removed(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); - } - g_hash_table_remove(priv->ports, &key); + &items); + if (r != 0) { + gpointer unused; + + r = json_unpack(value, "{s:{}}", "old"); + if (r != 0) + continue; + + if (!g_hash_table_steal_extended(priv->ports, &key, (gpointer *) &ovs_port, &unused)) + continue; + + _LOGT("obj[port:%s]: removed a port: %s%s%s", + key, + ovs_port->name, + NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, + ", ", + ovs_port->connection_uuid, + "")); + _signal_emit_device_removed(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); + _free_port(ovs_port); + continue; + } + + ovs_port = g_hash_table_lookup(priv->ports, &key); + + if (ovs_port && !nm_streq0(ovs_port->name, name)) { + if (!g_hash_table_steal(priv->ports, ovs_port)) + nm_assert_not_reached(); + _signal_emit_device_removed(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); + nm_clear_pointer(&ovs_port, _free_port); } - if (new) { + connection_uuid = _connection_uuid_from_external_ids(external_ids); + interfaces = _uuids_to_array(items); + + if (ovs_port) { + gboolean changed = FALSE; + + nm_assert(nm_streq0(ovs_port->name, name)); + + changed |= nm_utils_strdup_reset(&ovs_port->name, name); + changed |= nm_utils_strdup_reset_take(&ovs_port->connection_uuid, + g_steal_pointer(&connection_uuid)); + if (nm_strv_ptrarray_cmp(ovs_port->interfaces, interfaces) != 0) { + NM_SWAP(&ovs_port->interfaces, &interfaces); + changed = TRUE; + } + if (changed) { + _LOGT("obj[port:%s]: changed a port: %s%s%s", + key, + ovs_port->name, + NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, + ", ", + ovs_port->connection_uuid, + "")); + } + } else { ovs_port = g_slice_new(OpenvswitchPort); *ovs_port = (OpenvswitchPort){ .port_uuid = g_strdup(key), .name = g_strdup(name), - .connection_uuid = _connection_uuid_from_external_ids(external_ids), - .interfaces = g_ptr_array_new_with_free_func(g_free), + .connection_uuid = g_steal_pointer(&connection_uuid), + .interfaces = g_steal_pointer(&interfaces), }; - _uuids_to_array(ovs_port->interfaces, items); g_hash_table_add(priv->ports, ovs_port); - if (old) { - _LOGT("changed a port: %s%s%s", - ovs_port->name, - ovs_port->connection_uuid ? ", " : "", - ovs_port->connection_uuid ?: ""); - } else { - _LOGT("added a port: %s%s%s", - ovs_port->name, - ovs_port->connection_uuid ? ", " : "", - ovs_port->connection_uuid ?: ""); - _signal_emit_device_added(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); - } + _LOGT("obj[port:%s]: added a port: %s%s%s", + key, + ovs_port->name, + NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, + ", ", + ovs_port->connection_uuid, + "")); + _signal_emit_device_added(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); } } - /* Bridges */ json_object_foreach (bridge, key, value) { - gboolean old = FALSE; - gboolean new = FALSE; - - if (json_unpack(value, "{s:{}}", "old") == 0) - old = TRUE; + gs_unref_ptrarray GPtrArray *ports = NULL; + OpenvswitchBridge * ovs_bridge; + gs_free char * connection_uuid = NULL; + int r; - if (json_unpack(value, + r = json_unpack(value, "{s:{s:s, s:o, s:o}}", "new", "name", @@ -1446,45 +1525,83 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) "external_ids", &external_ids, "ports", - &items) - == 0) - new = TRUE; - - if (old) { - ovs_bridge = g_hash_table_lookup(priv->bridges, &key); - if (!new || (ovs_bridge && !nm_streq0(ovs_bridge->name, name))) { - old = FALSE; - _LOGT("removed a bridge: %s%s%s", - ovs_bridge->name, - ovs_bridge->connection_uuid ? ", " : "", - ovs_bridge->connection_uuid ?: ""); - _signal_emit_device_removed(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); - } - g_hash_table_remove(priv->bridges, &key); + &items); + + if (r != 0) { + gpointer unused; + + r = json_unpack(value, "{s:{}}", "old"); + if (r != 0) + continue; + + if (!g_hash_table_steal_extended(priv->bridges, + &key, + (gpointer *) &ovs_bridge, + &unused)) + continue; + + _LOGT("obj[bridge:%s]: removed a bridge: %s%s%s", + key, + ovs_bridge->name, + NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, + ", ", + ovs_bridge->connection_uuid, + "")); + _signal_emit_device_removed(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); + _free_bridge(ovs_bridge); + continue; + } + + ovs_bridge = g_hash_table_lookup(priv->bridges, &key); + + if (ovs_bridge && !nm_streq0(ovs_bridge->name, name)) { + if (!g_hash_table_steal(priv->bridges, ovs_bridge)) + nm_assert_not_reached(); + _signal_emit_device_removed(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); + nm_clear_pointer(&ovs_bridge, _free_bridge); } - if (new) { + connection_uuid = _connection_uuid_from_external_ids(external_ids); + ports = _uuids_to_array(items); + + if (ovs_bridge) { + gboolean changed = FALSE; + + nm_assert(nm_streq0(ovs_bridge->name, name)); + + changed = nm_utils_strdup_reset(&ovs_bridge->name, name); + changed = nm_utils_strdup_reset_take(&ovs_bridge->connection_uuid, + g_steal_pointer(&connection_uuid)); + if (nm_strv_ptrarray_cmp(ovs_bridge->ports, ports) != 0) { + NM_SWAP(&ovs_bridge->ports, &ports); + changed = TRUE; + } + if (changed) { + _LOGT("obj[bridge:%s]: changed a bridge: %s%s%s", + key, + ovs_bridge->name, + NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, + ", ", + ovs_bridge->connection_uuid, + "")); + } + } else { ovs_bridge = g_slice_new(OpenvswitchBridge); *ovs_bridge = (OpenvswitchBridge){ .bridge_uuid = g_strdup(key), .name = g_strdup(name), - .connection_uuid = _connection_uuid_from_external_ids(external_ids), - .ports = g_ptr_array_new_with_free_func(g_free), + .connection_uuid = g_steal_pointer(&connection_uuid), + .ports = g_steal_pointer(&ports), }; - _uuids_to_array(ovs_bridge->ports, items); g_hash_table_add(priv->bridges, ovs_bridge); - if (old) { - _LOGT("changed a bridge: %s%s%s", - ovs_bridge->name, - ovs_bridge->connection_uuid ? ", " : "", - ovs_bridge->connection_uuid ?: ""); - } else { - _LOGT("added a bridge: %s%s%s", - ovs_bridge->name, - ovs_bridge->connection_uuid ? ", " : "", - ovs_bridge->connection_uuid ?: ""); - _signal_emit_device_added(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); - } + _LOGT("obj[bridge:%s]: added a bridge: %s%s%s", + key, + ovs_bridge->name, + NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, + ", ", + ovs_bridge->connection_uuid, + "")); + _signal_emit_device_added(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); } } } |