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__ */