From 7055539c9f7bc95ff72ab5a0ff602471b7a4114b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 5 Nov 2020 13:58:58 +0100 Subject: [PATCH] core/ovs: support setting OVS external-ids Also support reapply. During reapply we try to preserve keys that are added externally. However, the current implementation does not properly use transactions to ensure there is no race here. --- src/devices/nm-device.c | 5 + src/devices/nm-device.h | 2 + src/devices/ovs/nm-device-ovs-bridge.c | 55 +++++- src/devices/ovs/nm-device-ovs-bridge.h | 5 + src/devices/ovs/nm-device-ovs-interface.c | 30 +-- src/devices/ovs/nm-device-ovs-port.c | 18 +- src/devices/ovs/nm-ovsdb.c | 214 ++++++++++++++++++++-- src/devices/ovs/nm-ovsdb.h | 9 + 8 files changed, 294 insertions(+), 44 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 98b4d339e4..45c07f63d4 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -52,6 +52,7 @@ #include "settings/nm-settings-connection.h" #include "settings/nm-settings.h" #include "nm-setting-ethtool.h" +#include "nm-setting-ovs-external-ids.h" #include "nm-setting-user.h" #include "nm-auth-utils.h" #include "nm-keep-alive.h" @@ -12450,6 +12451,10 @@ can_reapply_change(NMDevice * self, goto out_fail; } + if (nm_streq(setting_name, NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME) + && NM_DEVICE_GET_CLASS(self)->can_reapply_change_ovs_external_ids) + return TRUE; + out_fail: g_set_error(error, NM_DEVICE_ERROR, diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index c330411bc4..2885bcbb80 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -457,6 +457,8 @@ typedef struct _NMDeviceClass { bool act_stage1_prepare_set_hwaddr_ethernet : 1; + bool can_reapply_change_ovs_external_ids : 1; + } NMDeviceClass; GType nm_device_get_type(void); diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c index 6c92544c9c..d3a41edde6 100644 --- a/src/devices/ovs/nm-device-ovs-bridge.c +++ b/src/devices/ovs/nm-device-ovs-bridge.c @@ -6,6 +6,8 @@ #include "nm-default.h" #include "nm-device-ovs-bridge.h" + +#include "nm-device-ovs-interface.h" #include "nm-device-ovs-port.h" #include "nm-ovsdb.h" @@ -13,6 +15,8 @@ #include "nm-active-connection.h" #include "nm-setting-connection.h" #include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-external-ids.h" +#include "nm-core-internal.h" #define _NMLOG_DEVICE_TYPE NMDeviceOvsBridge #include "devices/nm-device-logging.h" @@ -87,6 +91,39 @@ static void release_slave(NMDevice *device, NMDevice *slave, gboolean configure) {} +void +nm_device_ovs_reapply_connection(NMDevice *self, NMConnection *con_old, NMConnection *con_new) +{ + NMDeviceType device_type; + GType type; + + nm_assert(NM_IS_DEVICE(self)); + nm_assert(g_type_parent(G_TYPE_FROM_INSTANCE(self)) == NM_TYPE_DEVICE); + + /* NMDevice's reapply_connection() doesn't do anything. No need to call the parent + * implementation. */ + + _LOGD(LOGD_DEVICE, "reapplying settings for OVS device"); + + type = G_OBJECT_TYPE(self); + if (type == NM_TYPE_DEVICE_OVS_INTERFACE) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (type == NM_TYPE_DEVICE_OVS_PORT) + device_type = NM_DEVICE_TYPE_OVS_PORT; + else { + nm_assert(type == NM_TYPE_DEVICE_OVS_BRIDGE); + device_type = NM_DEVICE_TYPE_OVS_BRIDGE; + } + + nm_ovsdb_set_external_ids( + nm_ovsdb_get(), + device_type, + nm_device_get_ip_iface(self), + nm_connection_get_uuid(con_new), + _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_EXTERNAL_IDS), + _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS)); +} + /*****************************************************************************/ static void @@ -114,12 +151,14 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass) device_class->connection_type_check_compatible = NM_SETTING_OVS_BRIDGE_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(); - device_class->is_master = TRUE; - device_class->get_type_description = get_type_description; - device_class->create_and_realize = create_and_realize; - device_class->unrealize = unrealize; - device_class->get_generic_capabilities = get_generic_capabilities; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->is_master = TRUE; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->unrealize = unrealize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->enslave_slave = enslave_slave; + device_class->release_slave = release_slave; + device_class->can_reapply_change_ovs_external_ids = TRUE; + device_class->reapply_connection = nm_device_ovs_reapply_connection; } diff --git a/src/devices/ovs/nm-device-ovs-bridge.h b/src/devices/ovs/nm-device-ovs-bridge.h index ba04bfebdd..304babdce1 100644 --- a/src/devices/ovs/nm-device-ovs-bridge.h +++ b/src/devices/ovs/nm-device-ovs-bridge.h @@ -22,4 +22,9 @@ typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass; GType nm_device_ovs_bridge_get_type(void); +/*****************************************************************************/ + +void +nm_device_ovs_reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_new); + #endif /* __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ */ diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index 0084c8bc58..a438fa6345 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -6,6 +6,8 @@ #include "nm-default.h" #include "nm-device-ovs-interface.h" + +#include "nm-device-ovs-bridge.h" #include "nm-ovsdb.h" #include "devices/nm-device-private.h" @@ -382,17 +384,19 @@ nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *klass) device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_OPENVSWITCH); - device_class->can_update_from_platform_link = can_update_from_platform_link; - device_class->deactivate = deactivate; - device_class->deactivate_async = deactivate_async; - device_class->get_type_description = get_type_description; - device_class->create_and_realize = create_and_realize; - device_class->get_generic_capabilities = get_generic_capabilities; - device_class->is_available = is_available; - device_class->check_connection_compatible = check_connection_compatible; - device_class->link_changed = link_changed; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; - device_class->can_unmanaged_external_down = can_unmanaged_external_down; - device_class->set_platform_mtu = set_platform_mtu; - device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; + device_class->can_update_from_platform_link = can_update_from_platform_link; + device_class->deactivate = deactivate; + device_class->deactivate_async = deactivate_async; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->is_available = is_available; + device_class->check_connection_compatible = check_connection_compatible; + device_class->link_changed = link_changed; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->can_unmanaged_external_down = can_unmanaged_external_down; + device_class->set_platform_mtu = set_platform_mtu; + device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; + device_class->can_reapply_change_ovs_external_ids = TRUE; + device_class->reapply_connection = nm_device_ovs_reapply_connection; } diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c index d3a462753a..d77b475a03 100644 --- a/src/devices/ovs/nm-device-ovs-port.c +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -6,7 +6,9 @@ #include "nm-default.h" #include "nm-device-ovs-port.h" + #include "nm-device-ovs-interface.h" +#include "nm-device-ovs-bridge.h" #include "nm-ovsdb.h" #include "devices/nm-device-private.h" @@ -182,11 +184,13 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass) device_class->connection_type_check_compatible = NM_SETTING_OVS_PORT_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(); - device_class->is_master = TRUE; - device_class->get_type_description = get_type_description; - device_class->create_and_realize = create_and_realize; - device_class->get_generic_capabilities = get_generic_capabilities; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; - device_class->enslave_slave = enslave_slave; - device_class->release_slave = release_slave; + device_class->is_master = TRUE; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->enslave_slave = enslave_slave; + device_class->release_slave = release_slave; + device_class->can_reapply_change_ovs_external_ids = TRUE; + device_class->reapply_connection = nm_device_ovs_reapply_connection; } diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index 44117dbc61..5d8bf97d69 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -15,6 +15,7 @@ #include "nm-core-utils.h" #include "nm-core-internal.h" #include "devices/nm-device.h" +#include "nm-setting-ovs-external-ids.h" /*****************************************************************************/ @@ -62,6 +63,7 @@ typedef enum { OVSDB_ADD_INTERFACE, OVSDB_DEL_INTERFACE, OVSDB_SET_INTERFACE_MTU, + OVSDB_SET_EXTERNAL_IDS, } OvsdbCommand; #define CALL_ID_UNSPEC G_MAXUINT64 @@ -83,6 +85,13 @@ typedef union { char * ifname; guint32 mtu; } set_interface_mtu; + struct { + NMDeviceType device_type; + char * ifname; + char * connection_uuid; + GHashTable * exid_old; + GHashTable * exid_new; + } set_external_ids; } OvsdbMethodPayload; typedef struct { @@ -198,6 +207,33 @@ static void ovsdb_next_command(NMOvsdb *self); }, \ })) +#define OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(xdevice_type, \ + xifname, \ + xconnection_uuid, \ + xexid_old, \ + xexid_new) \ + (&((const OvsdbMethodPayload){ \ + .set_external_ids = \ + { \ + .device_type = xdevice_type, \ + .ifname = (char *) NM_CONSTCAST(char, (xifname)), \ + .connection_uuid = (char *) NM_CONSTCAST(char, (xconnection_uuid)), \ + .exid_old = (xexid_old), \ + .exid_new = (xexid_new), \ + }, \ + })) + +/*****************************************************************************/ + +static NM_UTILS_LOOKUP_STR_DEFINE(_device_type_to_table, + NMDeviceType, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(NULL), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_TYPE_OVS_BRIDGE, "Bridge"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_TYPE_OVS_PORT, "Port"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_TYPE_OVS_INTERFACE, + "Interface"), + NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), ); + /*****************************************************************************/ static void @@ -240,6 +276,12 @@ _call_complete(OvsdbMethodCall *call, json_t *response, GError *error) case OVSDB_SET_INTERFACE_MTU: nm_clear_g_free(&call->payload.set_interface_mtu.ifname); break; + case OVSDB_SET_EXTERNAL_IDS: + nm_clear_g_free(&call->payload.set_external_ids.ifname); + nm_clear_g_free(&call->payload.set_external_ids.connection_uuid); + nm_clear_pointer(&call->payload.set_external_ids.exid_old, g_hash_table_destroy); + nm_clear_pointer(&call->payload.set_external_ids.exid_new, g_hash_table_destroy); + break; } nm_g_slice_free(call); @@ -432,6 +474,20 @@ ovsdb_call_method(NMOvsdb * self, call->payload.set_interface_mtu.ifname, call->payload.set_interface_mtu.mtu); break; + case OVSDB_SET_EXTERNAL_IDS: + call->payload.set_external_ids.device_type = payload->set_external_ids.device_type; + call->payload.set_external_ids.ifname = g_strdup(payload->set_external_ids.ifname); + call->payload.set_external_ids.connection_uuid = + g_strdup(payload->set_external_ids.connection_uuid); + call->payload.set_external_ids.exid_old = + nm_g_hash_table_ref(payload->set_external_ids.exid_old); + call->payload.set_external_ids.exid_new = + nm_g_hash_table_ref(payload->set_external_ids.exid_new); + _LOGT_call(call, + "new: set-external-ids con-uuid=%s, interface=%s", + call->payload.set_external_ids.connection_uuid, + call->payload.set_external_ids.ifname); + break; } if (call->cancellable) { @@ -651,6 +707,94 @@ _set_port_interfaces(json_t *params, const char *ifname, json_t *new_interfaces) ifname)); } +static json_t * +_j_create_external_ids_array_new(NMConnection *connection) +{ + json_t * array; + const char *const * external_ids = NULL; + guint n_external_ids = 0; + guint i; + const char * uuid; + NMSettingOvsExternalIDs *s_exid; + + nm_assert(NM_IS_CONNECTION(connection)); + + array = json_array(); + + uuid = nm_connection_get_uuid(connection); + nm_assert(uuid); + json_array_append_new(array, json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid)); + + s_exid = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_EXTERNAL_IDS); + if (s_exid) + external_ids = nm_setting_ovs_external_ids_get_data_keys(s_exid, &n_external_ids); + for (i = 0; i < n_external_ids; i++) { + const char *k = external_ids[i]; + + json_array_append_new( + array, + json_pack("[s, s]", k, nm_setting_ovs_external_ids_get_data(s_exid, k))); + } + + return json_pack("[s, o]", "map", array); +} + +static json_t * +_j_create_external_ids_array_update(const char *connection_uuid, + GHashTable *exid_old, + GHashTable *exid_new) +{ + GHashTableIter iter; + json_t * mutations; + json_t * array; + const char * key; + const char * val; + + nm_assert(connection_uuid); + + mutations = json_array(); + + if (exid_old) { + array = NULL; + g_hash_table_iter_init(&iter, exid_old); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { + if (nm_g_hash_table_contains(exid_new, key)) + continue; + if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) + continue; + + if (!array) + array = json_array(); + + json_array_append_new(array, json_string(key)); + } + if (array) { + json_array_append_new( + mutations, + json_pack("[s, s, [s, o]]", "external_ids", "delete", "set", array)); + } + } + + array = json_array(); + + json_array_append_new( + array, + json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, connection_uuid)); + + if (exid_new) { + g_hash_table_iter_init(&iter, exid_new); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { + if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) + continue; + json_array_append_new(array, json_pack("[s, s]", key, val)); + } + } + + json_array_append_new(mutations, + json_pack("[s, s, [s, o]]", "external_ids", "insert", "map", array)); + return mutations; +} + /** * _insert_interface: * @@ -701,7 +845,7 @@ _insert_interface(json_t * params, json_array_append_new(options, json_array()); } - row = json_pack("{s:s, s:s, s:o, s:[s, [[s, s]]]}", + row = json_pack("{s:s, s:s, s:o, s:o}", "name", nm_connection_get_interface_name(interface), "type", @@ -709,9 +853,7 @@ _insert_interface(json_t * params, "options", options, "external_ids", - "map", - NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, - nm_connection_get_uuid(interface)); + _j_create_external_ids_array_new(interface)); if (cloned_mac) json_object_set_new(row, "mac", json_string(cloned_mac)); @@ -776,12 +918,7 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces) json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(port))); json_object_set_new(row, "interfaces", json_pack("[s, O]", "set", new_interfaces)); - json_object_set_new(row, - "external_ids", - json_pack("[s, [[s, s]]]", - "map", - NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, - nm_connection_get_uuid(port))); + json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(port)); /* Create a new one. */ json_array_append_new(params, @@ -841,12 +978,7 @@ _insert_bridge(json_t * params, json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(bridge))); json_object_set_new(row, "ports", json_pack("[s, O]", "set", new_ports)); - json_object_set_new(row, - "external_ids", - json_pack("[s, [[s, s]]]", - "map", - NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, - nm_connection_get_uuid(bridge))); + json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(bridge)); if (cloned_mac) { json_object_set_new(row, @@ -1264,6 +1396,25 @@ ovsdb_next_command(NMOvsdb *self) "==", call->payload.set_interface_mtu.ifname)); break; + case OVSDB_SET_EXTERNAL_IDS: + json_array_append_new( + params, + json_pack("{s:s, s:s, s:o, s:[[s, s, s]]}", + "op", + "mutate", + "table", + _device_type_to_table(call->payload.set_external_ids.device_type), + "mutations", + _j_create_external_ids_array_update( + call->payload.set_external_ids.connection_uuid, + call->payload.set_external_ids.exid_old, + call->payload.set_external_ids.exid_new), + "where", + "name", + "==", + call->payload.set_external_ids.ifname)); + break; + default: nm_assert_not_reached(); break; @@ -2335,6 +2486,37 @@ nm_ovsdb_set_interface_mtu(NMOvsdb * self, OVSDB_METHOD_PAYLOAD_SET_INTERFACE_MTU(ifname, mtu)); } +void +nm_ovsdb_set_external_ids(NMOvsdb * self, + NMDeviceType device_type, + const char * ifname, + const char * connection_uuid, + NMSettingOvsExternalIDs *s_exid_old, + NMSettingOvsExternalIDs *s_exid_new) +{ + gs_unref_hashtable GHashTable *exid_old = NULL; + gs_unref_hashtable GHashTable *exid_new = NULL; + + exid_old = s_exid_old + ? nm_utils_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_old)) + : NULL; + exid_new = s_exid_new + ? nm_utils_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_new)) + : NULL; + + ovsdb_call_method(self, + NULL, + NULL, + NULL, + FALSE, + OVSDB_SET_EXTERNAL_IDS, + OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(device_type, + ifname, + connection_uuid, + exid_old, + exid_new)); +} + /*****************************************************************************/ static void diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h index c9eee19c39..100e2ea59c 100644 --- a/src/devices/ovs/nm-ovsdb.h +++ b/src/devices/ovs/nm-ovsdb.h @@ -46,4 +46,13 @@ void nm_ovsdb_set_interface_mtu(NMOvsdb * self, NMOvsdbCallback callback, gpointer user_data); +struct _NMSettingOvsExternalIDs; + +void nm_ovsdb_set_external_ids(NMOvsdb * self, + NMDeviceType device_type, + const char * ifname, + const char * connection_uuid, + struct _NMSettingOvsExternalIDs *s_exid_old, + struct _NMSettingOvsExternalIDs *s_exid_new); + #endif /* __NETWORKMANAGER_OVSDB_H__ */