diff --git a/Makefile.am b/Makefile.am index 3c294ab767..29d39b4c5e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1273,6 +1273,7 @@ src_libnm_core_impl_lib_h_pub_real = \ src/libnm-core-public/nm-setting-ovs-dpdk.h \ src/libnm-core-public/nm-setting-ovs-external-ids.h \ src/libnm-core-public/nm-setting-ovs-interface.h \ + src/libnm-core-public/nm-setting-ovs-other-config.h \ src/libnm-core-public/nm-setting-ovs-patch.h \ src/libnm-core-public/nm-setting-ovs-port.h \ src/libnm-core-public/nm-setting-ppp.h \ @@ -1353,6 +1354,7 @@ src_libnm_core_impl_lib_c_settings_real = \ src/libnm-core-impl/nm-setting-ovs-dpdk.c \ src/libnm-core-impl/nm-setting-ovs-external-ids.c \ src/libnm-core-impl/nm-setting-ovs-interface.c \ + src/libnm-core-impl/nm-setting-ovs-other-config.c \ src/libnm-core-impl/nm-setting-ovs-patch.c \ src/libnm-core-impl/nm-setting-ovs-port.c \ src/libnm-core-impl/nm-setting-ppp.c \ diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index d20f01a2e2..df668e4ee9 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -341,6 +341,7 @@ print ("NetworkManager version " + client.get_version())]]> + diff --git a/examples/python/gi/ovs-external-ids.py b/examples/python/gi/ovs-external-ids.py index 3bc9de8fd1..fe4fbd9dce 100755 --- a/examples/python/gi/ovs-external-ids.py +++ b/examples/python/gi/ovs-external-ids.py @@ -5,14 +5,15 @@ # # -# set and show OVS external-ids for a connection: +# set and show OVS external-ids and other-config for a connection: # -import sys +import collections import os -import re import pprint +import re import subprocess +import sys import gi @@ -178,14 +179,14 @@ def device_reapply(device, connection, version_id): raise result_error[0] -def ovs_print_external_ids(prefix): +def ovs_print_config(prefix): if not can_sudo(): _print(prefix + ": not running as root and cannot call ovs-vsctl") return cmds = [["ovs-vsctl", "show"]] for typ in ["Bridge", "Port", "Interface"]: - cmds += [["ovs-vsctl", "--columns=name,external-ids", "list", typ]] + cmds += [["ovs-vsctl", "--columns=name,external-ids,other-config", "list", typ]] out = "" for cmd in cmds: @@ -207,7 +208,11 @@ def usage(): ) _print(" DEVICE := [iface] STRING") _print(" GETTER := ( KEY | ~REGEX_KEY ) [... GETTER]") - _print(" SETTER := ( + | - | -KEY | [+]KEY VALUE ) [... SETTER]") + _print(" SETTER := ( +[e:|o:] | -[e:|o:] | -KEY | [+]KEY VALUE ) [... SETTER]") + _print("") + _print( + 'Prefix KEY with "e:" or "o:" to set external-ids or other-config ("e:" is the default)' + ) def die(msg, show_usage=False): @@ -221,6 +226,24 @@ def die_usage(msg): die(msg, show_usage=True) +DataTypeTuple = collections.namedtuple( + "DataTypeTuple", ["short", "name", "setting_type", "property_name"] +) + +DataTypeE = DataTypeTuple( + "external-ids", + "ovs-external-ids", + NM.SettingOvsExternalIDs, + NM.SETTING_OVS_EXTERNAL_IDS_DATA, +) +DataTypeO = DataTypeTuple( + "other-config", + "ovs-other-config", + NM.SettingOvsOtherConfig, + NM.SETTING_OVS_OTHER_CONFIG_DATA, +) + + def parse_args(argv): args = { "mode": MODE_GET, @@ -274,12 +297,14 @@ def parse_args(argv): continue if not a: - die_usage("argument should specify a external-id but is empty string") + die_usage( + "argument should specify a external-id/other-config but is empty string" + ) if a[0] == "-": v = (a, None) i += 1 - elif a == "+": + elif a in ["+", "+o:", "+e:"]: v = (a, None) i += 1 else: @@ -294,7 +319,7 @@ def parse_args(argv): if args["mode"] == MODE_SET: if not args["ids_arg"]: - die_usage("Requires one or more external-ids to set or delete") + die_usage("Requires one or more external-ids/other-config to set or delete") return args @@ -319,9 +344,6 @@ def devices_filter(devices, select_arg): devices = list(sorted(devices, key=device_to_str)) if not select_arg: return devices - # we preserve the order of the selected devices. And - # if devices are selected multiple times, we return - # them multiple times. l = [] f = select_arg for d in devices: @@ -342,9 +364,6 @@ def connections_filter(connections, select_arg): connections = list(sorted(connections, key=connection_to_str)) if not select_arg: return connections - # we preserve the order of the selected connections. And - # if connections are selected multiple times, we return - # them multiple times. l = [] f = select_arg for c in connections: @@ -352,7 +371,7 @@ def connections_filter(connections, select_arg): if f[1] == c.get_id(): l.append(c) elif f[0] == "~id": - if re.match(f[1], c.get_id()): + if re.search(f[1], c.get_id()): l.append(c) elif f[0] == "uuid": if f[1] == c.get_uuid(): @@ -361,7 +380,7 @@ def connections_filter(connections, select_arg): if f[1] == c.get_connection_type(): l.append(c) elif f[0] == "~type": - if re.match(f[1], c.get_connection_type()): + if re.search(f[1], c.get_connection_type()): l.append(c) else: assert f[0] == "*" @@ -384,7 +403,7 @@ def ids_select(ids, mode, ids_arg): if mode == MODE_GET: if d[0] == "~": r = re.compile(d[1:]) - keys.update([k for k in ids if r.match(k)]) + keys.update([k for k in ids if r.search(k)]) else: keys.update([k for k in ids if k == d]) if d not in requested: @@ -400,44 +419,67 @@ def ids_select(ids, mode, ids_arg): def connection_print(connection, mode, ids_arg, dbus_path, prefix=""): - sett = connection.get_setting(NM.SettingOvsExternalIDs) - - if sett is not None: - all_ids = list(sett.get_data_keys()) - keys, requested = ids_select(all_ids, mode, ids_arg) - num_str = "%s" % (len(all_ids)) - else: - keys = [] - requested = [] + def _num_str(connection, data_type): + sett = connection.get_setting(data_type.setting_type) num_str = "none" + if sett is not None: + all_ids = list(sett.get_data_keys()) + num_str = "%s" % (len(all_ids)) + return num_str _print( - "%s%s [%s]" % (prefix, connection_to_str(connection, show_type=True), num_str) + "%s%s [e:%s, o:%s]" + % ( + prefix, + connection_to_str(connection, show_type=True), + _num_str(connection, DataTypeE), + _num_str(connection, DataTypeO), + ) ) if dbus_path: _print("%s %s" % (prefix, dbus_path)) - if sett is not None: - dd = sett.get_property(NM.SETTING_OVS_EXTERNAL_IDS_DATA) - else: - dd = {} - for k in keys: - v = sett.get_data(k) - assert v is not None - assert v == dd.get(k, None) - _print('%s "%s" = "%s"' % (prefix, k, v)) - for k in requested: - _print('%s "%s" = ' % (prefix, k)) + + for data_type in [DataTypeE, DataTypeO]: + + sett = connection.get_setting(data_type.setting_type) + if sett is not None: + all_ids = list(sett.get_data_keys()) + keys, requested = ids_select(all_ids, mode, ids_arg) + else: + keys = [] + requested = [] + + if sett is not None: + dd = sett.get_property(data_type.property_name) + else: + dd = {} + for k in keys: + v = sett.get_data(k) + assert v is not None + assert v == dd.get(k, None) + _print('%s %s: "%s" = "%s"' % (prefix, data_type.short, k, v)) + for k in requested: + _print('%s %s: "%s" = ' % (prefix, data_type.short, k)) def sett_update(connection, ids_arg): - sett = connection.get_setting(NM.SettingOvsExternalIDs) - for d in ids_arg: op = d[0][0] key = d[0][1:] val = d[1] + if key == "o" or key.startswith("o:"): + data_type = DataTypeO + key = key[2:] + elif key == "e" or key.startswith("e:"): + data_type = DataTypeE + key = key[2:] + else: + data_type = DataTypeE + + sett = connection.get_setting(data_type.setting_type) + oldval = None if sett is not None: oldval = sett.get_data(key) @@ -446,15 +488,17 @@ def sett_update(connection, ids_arg): assert val is None if key == "": if sett is None: - _print(" DEL: setting (ovs-external-ids group was not present)") + _print( + " DEL: setting (%s group was not present)" % (data_type.name,) + ) else: - connection.remove_setting(NM.SettingOvsExternalIDs) + connection.remove_setting(data_type.setting_type) sett = None - _print(" DEL: setting") + _print(" DEL: setting (%s)" % (data_type.name,)) continue if sett is None: - _print(' DEL: "%s" (ovs-external-ids group was not present)' % (key)) + _print(' DEL: "%s" (%s group was not present)' % (key, data_type.name)) continue if oldval is None: _print(' DEL: "%s" (id was unset)' % (key)) @@ -466,21 +510,22 @@ def sett_update(connection, ids_arg): if key == "": assert val is None if sett is None: - sett = NM.SettingOvsExternalIDs.new() + sett = data_type.setting_type.new() connection.add_setting(sett) - _print(" SET: setting (external-ids group was added)") + _print(" SET: setting (%s group was added)" % (data_type.name,)) continue - _print(" SET: setting (external-ids group was present)") + _print(" SET: setting (%s group was present)" % (data_type.name,)) continue assert val is not None if sett is None: - sett = NM.SettingOvsExternalIDs.new() + sett = data_type.setting_type.new() connection.add_setting(sett) _print( - ' SET: "%s" = "%s" (external-ids group was not present)' % (key, val) + ' SET: "%s" = "%s" (%s group was not present)' + % (key, val, data_type.name) ) elif oldval is None: _print(' SET: "%s" = "%s" (new)' % (key, val)) @@ -579,7 +624,7 @@ def do_apply(nmc, device, ids_arg, do_test): ) _print() - ovs_print_external_ids("BEFORE-OVS-VSCTL: ") + ovs_print_config("BEFORE-OVS-VSCTL: ") _print() connection = NM.SimpleConnection.new_clone(connection_orig) @@ -619,7 +664,7 @@ def do_apply(nmc, device, ids_arg, do_test): ) _print() - ovs_print_external_ids("AFTER-OVS-VSCTL: ") + ovs_print_config("AFTER-OVS-VSCTL: ") ############################################################################### @@ -636,7 +681,7 @@ if __name__ == "__main__": if len(devices) != 1: _print( - "To apply the external-ids of a device, exactly one connection must be selected. Instead, %s devices matched ([%s])" + "To apply the external-ids/other-config of a device, exactly one connection must be selected. Instead, %s devices matched ([%s])" % (len(devices), ", ".join([device_to_str(c) for c in devices])) ) die_usage("Select unique device to apply") @@ -649,7 +694,7 @@ if __name__ == "__main__": if args["mode"] == MODE_SET: if len(connections) != 1: _print( - "To set the external-ids of a connection, exactly one connection must be selected via id|uuid. Instead, %s connection matched ([%s])" + "To set the external-ids/other-config of a connection, exactly one connection must be selected via id|uuid. Instead, %s connection matched ([%s])" % ( len(connections), ", ".join([connection_to_str(c) for c in connections]), @@ -659,6 +704,8 @@ if __name__ == "__main__": do_set(nmc, connections[0], args["ids_arg"], do_test=args["do_test"]) else: if len(connections) < 1: - _print("No connection selected for printing the external ids") + _print( + "No connection selected for printing the external ids/other-config" + ) die_usage("Select connection to get") do_get(connections, args["ids_arg"]) diff --git a/po/POTFILES.in b/po/POTFILES.in index cb1fa08193..dcd2983d38 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -102,6 +102,7 @@ src/libnm-core-impl/nm-setting-olpc-mesh.c src/libnm-core-impl/nm-setting-ovs-bridge.c src/libnm-core-impl/nm-setting-ovs-external-ids.c src/libnm-core-impl/nm-setting-ovs-interface.c +src/libnm-core-impl/nm-setting-ovs-other-config.c src/libnm-core-impl/nm-setting-ovs-patch.c src/libnm-core-impl/nm-setting-ovs-port.c src/libnm-core-impl/nm-setting-ppp.c diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index c6232374d3..c4e163d1b7 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -59,6 +59,7 @@ #include "settings/nm-settings.h" #include "nm-setting-ethtool.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "nm-setting-user.h" #include "nm-auth-utils.h" #include "nm-keep-alive.h" @@ -12813,7 +12814,9 @@ can_reapply_change(NMDevice *self, goto out_fail; } - if (nm_streq(setting_name, NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME) + if (NM_IN_STRSET(setting_name, + NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME) && NM_DEVICE_GET_CLASS(self)->can_reapply_change_ovs_external_ids) { /* TODO: this means, you cannot reapply changes to the external-ids for * OVS system interfaces. */ diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c index 10c7cfdbd2..7b319af344 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.c +++ b/src/core/devices/ovs/nm-device-ovs-bridge.c @@ -16,6 +16,7 @@ #include "nm-setting-connection.h" #include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "libnm-core-intern/nm-core-internal.h" #define _NMLOG_DEVICE_TYPE NMDeviceOvsBridge @@ -129,7 +130,9 @@ nm_device_ovs_reapply_connection(NMDevice *self, NMConnection *con_old, NMConnec 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)); + _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS), + _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_OTHER_CONFIG), + _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_OTHER_CONFIG)); } /*****************************************************************************/ diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c index bda1fca3ad..d6422434c7 100644 --- a/src/core/devices/ovs/nm-ovsdb.c +++ b/src/core/devices/ovs/nm-ovsdb.c @@ -17,6 +17,7 @@ #include "devices/nm-device.h" #include "nm-manager.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "nm-priv-helper-call.h" #include "libnm-platform/nm-platform.h" @@ -24,6 +25,8 @@ #define OVSDB_MAX_FAILURES 3 +#define OTHER_CONFIG_HWADDR "hwaddr" + /*****************************************************************************/ #if JANSSON_VERSION_HEX < 0x020400 @@ -32,6 +35,7 @@ typedef enum { STRDICT_TYPE_EXTERNAL_IDS, + STRDICT_TYPE_OTHER_CONFIG, } StrdictType; typedef struct { @@ -40,6 +44,7 @@ typedef struct { char *connection_uuid; GPtrArray *interfaces; /* interface uuids */ GArray *external_ids; + GArray *other_config; } OpenvswitchPort; typedef struct { @@ -48,6 +53,7 @@ typedef struct { char *connection_uuid; GPtrArray *ports; /* port uuids */ GArray *external_ids; + GArray *other_config; } OpenvswitchBridge; typedef struct { @@ -56,6 +62,7 @@ typedef struct { char *type; char *connection_uuid; GArray *external_ids; + GArray *other_config; } OpenvswitchInterface; /*****************************************************************************/ @@ -98,6 +105,8 @@ typedef union { char *connection_uuid; GHashTable *external_ids_old; GHashTable *external_ids_new; + GHashTable *other_config_old; + GHashTable *other_config_new; } set_reapply; } OvsdbMethodPayload; @@ -231,7 +240,9 @@ static void cleanup_check_ready(NMOvsdb *self); xifname, \ xconnection_uuid, \ xexternal_ids_old, \ - xexternal_ids_new) \ + xexternal_ids_new, \ + xother_config_old, \ + xother_config_new) \ (&((const OvsdbMethodPayload){ \ .set_reapply = \ { \ @@ -240,6 +251,8 @@ static void cleanup_check_ready(NMOvsdb *self); .connection_uuid = (char *) NM_CONSTCAST(char, (xconnection_uuid)), \ .external_ids_old = (xexternal_ids_old), \ .external_ids_new = (xexternal_ids_new), \ + .other_config_old = (xother_config_old), \ + .other_config_new = (xother_config_new), \ }, \ })) @@ -300,6 +313,8 @@ _call_complete(OvsdbMethodCall *call, json_t *response, GError *error) nm_clear_g_free(&call->payload.set_reapply.connection_uuid); nm_clear_pointer(&call->payload.set_reapply.external_ids_old, g_hash_table_destroy); nm_clear_pointer(&call->payload.set_reapply.external_ids_new, g_hash_table_destroy); + nm_clear_pointer(&call->payload.set_reapply.other_config_old, g_hash_table_destroy); + nm_clear_pointer(&call->payload.set_reapply.other_config_new, g_hash_table_destroy); break; } @@ -316,6 +331,7 @@ _free_bridge(OpenvswitchBridge *ovs_bridge) g_free(ovs_bridge->connection_uuid); g_ptr_array_free(ovs_bridge->ports, TRUE); nm_g_array_unref(ovs_bridge->external_ids); + nm_g_array_unref(ovs_bridge->other_config); nm_g_slice_free(ovs_bridge); } @@ -327,6 +343,7 @@ _free_port(OpenvswitchPort *ovs_port) g_free(ovs_port->connection_uuid); g_ptr_array_free(ovs_port->interfaces, TRUE); nm_g_array_unref(ovs_port->external_ids); + nm_g_array_unref(ovs_port->other_config); nm_g_slice_free(ovs_port); } @@ -338,6 +355,7 @@ _free_interface(OpenvswitchInterface *ovs_interface) g_free(ovs_interface->connection_uuid); g_free(ovs_interface->type); nm_g_array_unref(ovs_interface->external_ids); + nm_g_array_unref(ovs_interface->other_config); nm_g_slice_free(ovs_interface); } @@ -459,8 +477,12 @@ ovsdb_call_method(NMOvsdb *self, nm_g_hash_table_ref(payload->set_reapply.external_ids_old); call->payload.set_reapply.external_ids_new = nm_g_hash_table_ref(payload->set_reapply.external_ids_new); + call->payload.set_reapply.other_config_old = + nm_g_hash_table_ref(payload->set_reapply.other_config_old); + call->payload.set_reapply.other_config_new = + nm_g_hash_table_ref(payload->set_reapply.other_config_new); _LOGT_call(call, - "new: set-external-ids con-uuid=%s, interface=%s", + "new: set external-ids/other-config con-uuid=%s, interface=%s", call->payload.set_reapply.connection_uuid, call->payload.set_reapply.ifname); break; @@ -607,12 +629,12 @@ _set_bridge_mac(json_t *params, const char *ifname, const char *mac) "other_config", "delete", "set", - "hwaddr", + OTHER_CONFIG_HWADDR, "other_config", "insert", "map", - "hwaddr", + OTHER_CONFIG_HWADDR, mac, "where", @@ -680,8 +702,11 @@ _set_port_interfaces(json_t *params, const char *ifname, json_t *new_interfaces) } static json_t * -_j_create_strdict_new(NMConnection *connection, StrdictType strdict_type) +_j_create_strdict_new(NMConnection *connection, + StrdictType strdict_type, + const char *other_config_hwaddr) { + NMSettingOvsOtherConfig *s_other_config = NULL; NMSettingOvsExternalIDs *s_external_ids = NULL; json_t *array; const char *const *strv = NULL; @@ -690,7 +715,7 @@ _j_create_strdict_new(NMConnection *connection, StrdictType strdict_type) const char *uuid; nm_assert(NM_IS_CONNECTION(connection)); - nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS)); + nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS, STRDICT_TYPE_OTHER_CONFIG)); array = json_array(); @@ -699,6 +724,11 @@ _j_create_strdict_new(NMConnection *connection, StrdictType strdict_type) nm_assert(uuid); json_array_append_new(array, json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid)); + } else { + if (other_config_hwaddr) { + json_array_append_new(array, + json_pack("[s, s]", OTHER_CONFIG_HWADDR, other_config_hwaddr)); + } } if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) { @@ -706,15 +736,27 @@ _j_create_strdict_new(NMConnection *connection, StrdictType strdict_type) if (s_external_ids) strv = nm_setting_ovs_external_ids_get_data_keys(s_external_ids, &n_strv); } else { - nm_assert_not_reached(); + s_other_config = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_OTHER_CONFIG); + if (s_other_config) + strv = nm_setting_ovs_other_config_get_data_keys(s_other_config, &n_strv); } for (i = 0; i < n_strv; i++) { const char *k = strv[i]; + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && other_config_hwaddr + && nm_streq(k, OTHER_CONFIG_HWADDR)) { + /* "hwaddr" is explicitly overwritten. */ + continue; + } + json_array_append_new( array, - json_pack("[s, s]", k, nm_setting_ovs_external_ids_get_data(s_external_ids, k))); + json_pack("[s, s]", + k, + strdict_type == STRDICT_TYPE_EXTERNAL_IDS + ? nm_setting_ovs_external_ids_get_data(s_external_ids, k) + : nm_setting_ovs_other_config_get_data(s_other_config, k))); } return json_pack("[s, o]", "map", array); @@ -732,13 +774,21 @@ _j_create_strv_array_update(json_t *mutations, const char *key; const char *val; + /* This is called during reapply. We accept reapplying all settings, + * except other_config:hwaddr. That one cannot change and is specially + * handled below. The reason is that we knew the correct "hwaddr" during + * _j_create_strdict_new(), but we don't do now. At least not easily, + * and it's not clear that reapply of the MAC address is really useful. */ + nm_assert((!!connection_uuid) == (strdict_type == STRDICT_TYPE_EXTERNAL_IDS)); - nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS)); + nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS, STRDICT_TYPE_OTHER_CONFIG)); array = NULL; if (hash_old) { g_hash_table_iter_init(&iter, hash_old); while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR)) + continue; if (!array) array = json_array(); json_array_append_new(array, json_string(key)); @@ -747,6 +797,8 @@ _j_create_strv_array_update(json_t *mutations, if (hash_new) { g_hash_table_iter_init(&iter, hash_new); while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR)) + continue; if (nm_g_hash_table_contains(hash_old, key)) continue; if (!array) @@ -786,6 +838,8 @@ _j_create_strv_array_update(json_t *mutations, if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) continue; } + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR)) + continue; json_array_append_new(array, json_pack("[s, s]", key, val)); } } @@ -868,7 +922,7 @@ _insert_interface(json_t *params, json_array_append_new(options, json_array()); } - row = json_pack("{s:s, s:s, s:o, s:o}", + row = json_pack("{s:s, s:s, s:o, s:o, s:o}", "name", nm_connection_get_interface_name(interface), "type", @@ -876,7 +930,9 @@ _insert_interface(json_t *params, "options", options, "external_ids", - _j_create_strdict_new(interface, STRDICT_TYPE_EXTERNAL_IDS)); + _j_create_strdict_new(interface, STRDICT_TYPE_EXTERNAL_IDS, NULL), + "other_config", + _j_create_strdict_new(interface, STRDICT_TYPE_OTHER_CONFIG, NULL)); if (cloned_mac) json_object_set_new(row, "mac", json_string(cloned_mac)); @@ -963,7 +1019,10 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces) json_object_set_new(row, "interfaces", json_pack("[s, O]", "set", new_interfaces)); json_object_set_new(row, "external_ids", - _j_create_strdict_new(port, STRDICT_TYPE_EXTERNAL_IDS)); + _j_create_strdict_new(port, STRDICT_TYPE_EXTERNAL_IDS, NULL)); + json_object_set_new(row, + "other_config", + _j_create_strdict_new(port, STRDICT_TYPE_OTHER_CONFIG, NULL)); /* Create a new one. */ json_array_append_new(params, @@ -1025,13 +1084,10 @@ _insert_bridge(json_t *params, json_object_set_new(row, "ports", json_pack("[s, O]", "set", new_ports)); json_object_set_new(row, "external_ids", - _j_create_strdict_new(bridge, STRDICT_TYPE_EXTERNAL_IDS)); - - if (cloned_mac) { - json_object_set_new(row, - "other_config", - json_pack("[s, [[s, s]]]", "map", "hwaddr", cloned_mac)); - } + _j_create_strdict_new(bridge, STRDICT_TYPE_EXTERNAL_IDS, NULL)); + json_object_set_new(row, + "other_config", + _j_create_strdict_new(bridge, STRDICT_TYPE_OTHER_CONFIG, cloned_mac)); /* Create a new one. */ json_array_append_new(params, @@ -1393,9 +1449,9 @@ ovsdb_next_command(NMOvsdb *self) switch (call->command) { case OVSDB_MONITOR: msg = json_pack("{s:I, s:s, s:[s, n, {" - " s:[{s:[s, s, s]}]," - " s:[{s:[s, s, s]}]," " s:[{s:[s, s, s, s]}]," + " s:[{s:[s, s, s, s]}]," + " s:[{s:[s, s, s, s, s]}]," " s:[{s:[]}]" "}]}", "id", @@ -1409,16 +1465,19 @@ ovsdb_next_command(NMOvsdb *self) "name", "ports", "external_ids", + "other_config", "Port", "columns", "name", "interfaces", "external_ids", + "other_config", "Interface", "columns", "name", "type", "external_ids", + "other_config", "error", "Open_vSwitch", "columns"); @@ -1470,6 +1529,11 @@ ovsdb_next_command(NMOvsdb *self) call->payload.set_reapply.connection_uuid, call->payload.set_reapply.external_ids_old, call->payload.set_reapply.external_ids_new); + _j_create_strv_array_update(mutations, + STRDICT_TYPE_OTHER_CONFIG, + NULL, + call->payload.set_reapply.other_config_old, + call->payload.set_reapply.other_config_new); json_array_append_new( params, @@ -1684,6 +1748,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) json_t *interface = NULL; json_t *items; json_t *external_ids; + json_t *other_config; json_error_t json_error = { 0, }; @@ -1723,12 +1788,13 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) json_object_foreach (interface, key, value) { OpenvswitchInterface *ovs_interface; gs_unref_array GArray *external_ids_arr = NULL; + gs_unref_array GArray *other_config_arr = NULL; const char *connection_uuid = NULL; json_t *error = NULL; int r; r = json_unpack(value, - "{s:{s:s, s:s, s?:o, s:o}}", + "{s:{s:s, s:s, s?:o, s:o, s:o}}", "new", "name", &name, @@ -1737,7 +1803,9 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) "error", &error, "external_ids", - &external_ids); + &external_ids, + "other_config", + &other_config); if (r != 0) { gpointer unused; @@ -1783,6 +1851,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) _strdict_extract(external_ids, &external_ids_arr); connection_uuid = _strdict_find_key(external_ids_arr, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID); + _strdict_extract(other_config, &other_config_arr); if (ovs_interface) { gboolean changed = FALSE; @@ -1795,10 +1864,16 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) NM_SWAP(&ovs_interface->external_ids, &external_ids_arr); changed = TRUE; } + if (!_strdict_equals(ovs_interface->other_config, other_config_arr)) { + NM_SWAP(&ovs_interface->other_config, &other_config_arr); + changed = TRUE; + } if (changed) { gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; - _LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s, external-ids=%s", + _LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s, external-ids=%s, " + "other-config=%s", key, type, ovs_interface->name, @@ -1806,10 +1881,12 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) ", ", ovs_interface->connection_uuid, ""), - (strtmp1 = _strdict_to_string(ovs_interface->external_ids))); + (strtmp1 = _strdict_to_string(ovs_interface->external_ids)), + (strtmp2 = _strdict_to_string(ovs_interface->other_config))); } } else { gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; ovs_interface = g_slice_new(OpenvswitchInterface); *ovs_interface = (OpenvswitchInterface){ @@ -1818,17 +1895,20 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) .type = g_strdup(type), .connection_uuid = g_strdup(connection_uuid), .external_ids = g_steal_pointer(&external_ids_arr), + .other_config = g_steal_pointer(&other_config_arr), }; g_hash_table_add(priv->interfaces, ovs_interface); - _LOGT("obj[iface:%s]: added an '%s' interface: %s%s%s, external-ids=%s", - key, - ovs_interface->type, - ovs_interface->name, - NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, - ", ", - ovs_interface->connection_uuid, - ""), - (strtmp1 = _strdict_to_string(ovs_interface->external_ids))); + _LOGT( + "obj[iface:%s]: added an '%s' interface: %s%s%s, external-ids=%s, other-config=%s", + key, + ovs_interface->type, + ovs_interface->name, + NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, + ", ", + ovs_interface->connection_uuid, + ""), + (strtmp1 = _strdict_to_string(ovs_interface->external_ids)), + (strtmp2 = _strdict_to_string(ovs_interface->other_config))); _signal_emit_device_added(self, ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE, @@ -1849,16 +1929,19 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) gs_unref_ptrarray GPtrArray *interfaces = NULL; OpenvswitchPort *ovs_port; gs_unref_array GArray *external_ids_arr = NULL; + gs_unref_array GArray *other_config_arr = NULL; const char *connection_uuid = NULL; int r; r = json_unpack(value, - "{s:{s:s, s:o, s:o}}", + "{s:{s:s, s:o, s:o, s:o}}", "new", "name", &name, "external_ids", &external_ids, + "other_config", + &other_config, "interfaces", &items); if (r != 0) { @@ -1895,6 +1978,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) _strdict_extract(external_ids, &external_ids_arr); connection_uuid = _strdict_find_key(external_ids_arr, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID); + _strdict_extract(other_config, &other_config_arr); interfaces = _uuids_to_array(items); @@ -1913,20 +1997,27 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) NM_SWAP(&ovs_port->external_ids, &external_ids_arr); changed = TRUE; } + if (!_strdict_equals(ovs_port->other_config, other_config_arr)) { + NM_SWAP(&ovs_port->other_config, &other_config_arr); + changed = TRUE; + } if (changed) { gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; - _LOGT("obj[port:%s]: changed a port: %s%s%s, external-ids=%s", + _LOGT("obj[port:%s]: changed a port: %s%s%s, external-ids=%s, other-config=%s", key, ovs_port->name, NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, ", ", ovs_port->connection_uuid, ""), - (strtmp1 = _strdict_to_string(ovs_port->external_ids))); + (strtmp1 = _strdict_to_string(ovs_port->external_ids)), + (strtmp2 = _strdict_to_string(ovs_port->other_config))); } } else { gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; ovs_port = g_slice_new(OpenvswitchPort); *ovs_port = (OpenvswitchPort){ @@ -1935,16 +2026,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) .connection_uuid = g_strdup(connection_uuid), .interfaces = g_steal_pointer(&interfaces), .external_ids = g_steal_pointer(&external_ids_arr), + .other_config = g_steal_pointer(&other_config_arr), }; g_hash_table_add(priv->ports, ovs_port); - _LOGT("obj[port:%s]: added a port: %s%s%s, external-ids=%s", + _LOGT("obj[port:%s]: added a port: %s%s%s, external-ids=%s, other-config=%s", key, ovs_port->name, NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, ", ", ovs_port->connection_uuid, ""), - (strtmp1 = _strdict_to_string(ovs_port->external_ids))); + (strtmp1 = _strdict_to_string(ovs_port->external_ids)), + (strtmp2 = _strdict_to_string(ovs_port->other_config))); _signal_emit_device_added(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT, NULL); } } @@ -1953,16 +2046,19 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) gs_unref_ptrarray GPtrArray *ports = NULL; OpenvswitchBridge *ovs_bridge; gs_unref_array GArray *external_ids_arr = NULL; + gs_unref_array GArray *other_config_arr = NULL; const char *connection_uuid = NULL; int r; r = json_unpack(value, - "{s:{s:s, s:o, s:o}}", + "{s:{s:s, s:o, s:o, s:o}}", "new", "name", &name, "external_ids", &external_ids, + "other_config", + &other_config, "ports", &items); @@ -2003,6 +2099,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) _strdict_extract(external_ids, &external_ids_arr); connection_uuid = _strdict_find_key(external_ids_arr, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID); + _strdict_extract(other_config, &other_config_arr); ports = _uuids_to_array(items); @@ -2021,20 +2118,27 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) NM_SWAP(&ovs_bridge->external_ids, &external_ids_arr); changed = TRUE; } + if (!_strdict_equals(ovs_bridge->other_config, other_config_arr)) { + NM_SWAP(&ovs_bridge->other_config, &other_config_arr); + changed = TRUE; + } if (changed) { gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; - _LOGT("obj[bridge:%s]: changed a bridge: %s%s%s, external-ids=%s", + _LOGT("obj[bridge:%s]: changed a bridge: %s%s%s, external-ids=%s, other-config=%s", key, ovs_bridge->name, NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, ", ", ovs_bridge->connection_uuid, ""), - (strtmp1 = _strdict_to_string(ovs_bridge->external_ids))); + (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)), + (strtmp2 = _strdict_to_string(ovs_bridge->external_ids))); } } else { gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; ovs_bridge = g_slice_new(OpenvswitchBridge); *ovs_bridge = (OpenvswitchBridge){ @@ -2043,16 +2147,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) .connection_uuid = g_strdup(connection_uuid), .ports = g_steal_pointer(&ports), .external_ids = g_steal_pointer(&external_ids_arr), + .other_config = g_steal_pointer(&other_config_arr), }; g_hash_table_add(priv->bridges, ovs_bridge); - _LOGT("obj[bridge:%s]: added a bridge: %s%s%s, external-ids=%s", + _LOGT("obj[bridge:%s]: added a bridge: %s%s%s, external-ids=%s, other-config=%s", key, ovs_bridge->name, NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, ", ", ovs_bridge->connection_uuid, ""), - (strtmp1 = _strdict_to_string(ovs_bridge->external_ids))); + (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)), + (strtmp2 = _strdict_to_string(ovs_bridge->other_config))); _signal_emit_device_added(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE, NULL); } } @@ -2795,10 +2901,14 @@ nm_ovsdb_set_reapply(NMOvsdb *self, const char *ifname, const char *connection_uuid, NMSettingOvsExternalIDs *s_external_ids_old, - NMSettingOvsExternalIDs *s_external_ids_new) + NMSettingOvsExternalIDs *s_external_ids_new, + NMSettingOvsOtherConfig *s_other_config_old, + NMSettingOvsOtherConfig *s_other_config_new) { gs_unref_hashtable GHashTable *external_ids_old = NULL; gs_unref_hashtable GHashTable *external_ids_new = NULL; + gs_unref_hashtable GHashTable *other_config_old = NULL; + gs_unref_hashtable GHashTable *other_config_new = NULL; external_ids_old = s_external_ids_old @@ -2809,6 +2919,15 @@ nm_ovsdb_set_reapply(NMOvsdb *self, ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_external_ids_new)) : NULL; + other_config_old = + s_other_config_old + ? nm_strdict_clone(_nm_setting_ovs_other_config_get_data(s_other_config_old)) + : NULL; + other_config_new = + s_other_config_new + ? nm_strdict_clone(_nm_setting_ovs_other_config_get_data(s_other_config_new)) + : NULL; + ovsdb_call_method(self, NULL, NULL, @@ -2818,7 +2937,9 @@ nm_ovsdb_set_reapply(NMOvsdb *self, ifname, connection_uuid, external_ids_old, - external_ids_new)); + external_ids_new, + other_config_old, + other_config_new)); } /*****************************************************************************/ diff --git a/src/core/devices/ovs/nm-ovsdb.h b/src/core/devices/ovs/nm-ovsdb.h index e6e35ccd7f..a022ff00ad 100644 --- a/src/core/devices/ovs/nm-ovsdb.h +++ b/src/core/devices/ovs/nm-ovsdb.h @@ -47,14 +47,14 @@ void nm_ovsdb_set_interface_mtu(NMOvsdb *self, NMOvsdbCallback callback, gpointer user_data); -struct _NMSettingOvsExternalIDs; - -void nm_ovsdb_set_reapply(NMOvsdb *self, - NMDeviceType device_type, - const char *ifname, - const char *connection_uuid, - struct _NMSettingOvsExternalIDs *s_external_ids_old, - struct _NMSettingOvsExternalIDs *s_external_ids_new); +void nm_ovsdb_set_reapply(NMOvsdb *self, + NMDeviceType device_type, + const char *ifname, + const char *connection_uuid, + NMSettingOvsExternalIDs *s_external_ids_old, + NMSettingOvsExternalIDs *s_external_ids_new, + NMSettingOvsOtherConfig *s_other_config_old, + NMSettingOvsOtherConfig *s_other_config_new); gboolean nm_ovsdb_is_ready(NMOvsdb *self); diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index 78d2467c01..e6807e9d82 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -1894,6 +1894,7 @@ global: nm_range_ref; nm_range_to_str; nm_range_unref; + nm_setting_ip_config_get_auto_route_ext_gw; nm_setting_ip_config_get_dhcp_iaid; nm_setting_ip_config_get_dhcp_iaid; nm_setting_ip_tunnel_get_fwmark; @@ -1901,6 +1902,11 @@ global: nm_setting_loopback_get_type; nm_setting_loopback_new; nm_setting_ovs_interface_get_ofport_request; + nm_setting_ovs_other_config_get_data; + nm_setting_ovs_other_config_get_data_keys; + nm_setting_ovs_other_config_get_type; + nm_setting_ovs_other_config_new; + nm_setting_ovs_other_config_set_data; nm_setting_ovs_port_add_trunk; nm_setting_ovs_port_clear_trunks; nm_setting_ovs_port_get_num_trunks; @@ -1908,7 +1914,6 @@ global: nm_setting_ovs_port_remove_trunk; nm_setting_ovs_port_remove_trunk_by_value; nm_setting_vlan_get_protocol; - nm_setting_ip_config_get_auto_route_ext_gw; nm_utils_ensure_gtypes; nm_version_info_capability_get_type; } libnm_1_40_0; diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.c b/src/libnm-core-aux-intern/nm-libnm-core-utils.c index 0e464fba42..c4bfd2bd73 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -630,3 +630,104 @@ nm_utils_dnsname_normalize(int addr_family, const char *dns, char **out_free) *out_free = s; return s; } + +/*****************************************************************************/ + +/** + * nm_setting_ovs_other_config_check_key: + * @key: (allow-none): the key to check + * @error: a #GError, %NULL to ignore. + * + * Checks whether @key is a valid key for OVS' other-config. + * This means, the key cannot be %NULL, not too large and valid ASCII. + * Also, only digits and numbers are allowed with a few special + * characters. + * + * Returns: %TRUE if @key is a valid user data key. + */ +gboolean +nm_setting_ovs_other_config_check_key(const char *key, GError **error) +{ + gsize len; + + g_return_val_if_fail(!error || !*error, FALSE); + + if (!key || !key[0]) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("missing key")); + return FALSE; + } + len = strlen(key); + if (len > 255u) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key is too long")); + return FALSE; + } + if (!g_utf8_validate(key, len, NULL)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key must be UTF8")); + return FALSE; + } + if (!NM_STRCHAR_ALL(key, ch, nm_ascii_is_regular_char(ch))) { + /* Probably OVS is more forgiving about what makes a valid key for + * an other-key. However, we are strict (at least, for now). */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key contains invalid characters")); + return FALSE; + } + + return TRUE; +} + +/** + * nm_setting_ovs_other_config_check_val: + * @val: (allow-none): the value to check + * @error: a #GError, %NULL to ignore. + * + * Checks whether @val is a valid user data value. This means, + * value is not %NULL, not too large and valid UTF-8. + * + * Returns: %TRUE if @val is a valid user data value. + */ +gboolean +nm_setting_ovs_other_config_check_val(const char *val, GError **error) +{ + gsize len; + + g_return_val_if_fail(!error || !*error, FALSE); + + if (!val) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("value is missing")); + return FALSE; + } + + len = strlen(val); + if (len > (2u * 1024u)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("value is too large")); + return FALSE; + } + + if (!g_utf8_validate(val, len, NULL)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("value is not valid UTF8")); + return FALSE; + } + + return TRUE; +} diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.h b/src/libnm-core-aux-intern/nm-libnm-core-utils.h index 63e8be4ec1..18af1e1a1c 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.h +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.h @@ -308,4 +308,9 @@ const char *nm_utils_dnsname_construct(int ad const char *nm_utils_dnsname_normalize(int addr_family, const char *dns, char **out_free); +/*****************************************************************************/ + +gboolean nm_setting_ovs_other_config_check_key(const char *key, GError **error); +gboolean nm_setting_ovs_other_config_check_val(const char *val, GError **error); + #endif /* __NM_LIBNM_SHARED_UTILS_H__ */ diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in index 0c2bdb9f74..a5c1fd1699 100644 --- a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in +++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in @@ -1879,6 +1879,14 @@ gprop-type="gchararray" /> + + + diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build index f1bd2a081c..6408ae178a 100644 --- a/src/libnm-core-impl/meson.build +++ b/src/libnm-core-impl/meson.build @@ -33,6 +33,7 @@ libnm_core_settings_sources = files( 'nm-setting-ovs-dpdk.c', 'nm-setting-ovs-external-ids.c', 'nm-setting-ovs-interface.c', + 'nm-setting-ovs-other-config.c', 'nm-setting-ovs-patch.c', 'nm-setting-ovs-port.c', 'nm-setting-ppp.c', diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index 724a51c7f1..9a6ffc6f24 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -28,12 +28,14 @@ #include "nm-setting-private.h" #include "nm-setting-user.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "libnm-core-intern/nm-keyfile-utils.h" #define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options" -#define OVS_EXTERNAL_IDS_DATA_PREFIX "data." +/* used for "ovs-external-ids.data" and "ovs-other-config.data". */ +#define STRDICT_DATA_PREFIX "data." /*****************************************************************************/ @@ -1060,23 +1062,29 @@ ip_routing_rule_parser_full(KeyfileReaderInfo *info, } static void -_parser_full_ovs_external_ids_data(KeyfileReaderInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) +_parser_full_strdict_data(KeyfileReaderInfo *info, + const NMMetaSettingInfo *setting_info, + const NMSettInfoProperty *property_info, + const ParseInfoProperty *pip, + NMSetting *setting) { - const char *setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME; - gs_strfreev char **keys = NULL; + gs_strfreev char **keys = NULL; gsize n_keys; gsize i; + gboolean is_exid; - nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)); - nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); - nm_assert(nm_streq(setting_name, setting_info->setting_name)); - nm_assert(nm_streq(setting_name, nm_setting_get_name(setting))); + if (NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)) { + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + is_exid = TRUE; + } else { + nm_assert(NM_IS_SETTING_OVS_OTHER_CONFIG(setting)); + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_OTHER_CONFIG_DATA)); + is_exid = FALSE; + } - keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_name, &n_keys, NULL); + nm_assert(nm_streq(setting_info->setting_name, nm_setting_get_name(setting))); + + keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_info->setting_name, &n_keys, NULL); for (i = 0; i < n_keys; i++) { const char *key = keys[i]; @@ -1084,16 +1092,20 @@ _parser_full_ovs_external_ids_data(KeyfileReaderInfo *info, gs_free char *value = NULL; const char *name; - if (!NM_STR_HAS_PREFIX(key, OVS_EXTERNAL_IDS_DATA_PREFIX)) + if (!NM_STR_HAS_PREFIX(key, STRDICT_DATA_PREFIX)) continue; - value = nm_keyfile_plugin_kf_get_string(info->keyfile, setting_name, key, NULL); + value = + nm_keyfile_plugin_kf_get_string(info->keyfile, setting_info->setting_name, key, NULL); if (!value) continue; - name = &key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)]; + name = &key[NM_STRLEN(STRDICT_DATA_PREFIX)]; name = nm_keyfile_key_decode(name, &name_to_free); - nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value); + if (is_exid) + nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value); + else + nm_setting_ovs_other_config_set_data(NM_SETTING_OVS_OTHER_CONFIG(setting), name, value); } } @@ -2569,24 +2581,32 @@ tfilter_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, con } static void -_writer_full_ovs_external_ids_data(KeyfileWriterInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) +_writer_full_strdict_data(KeyfileWriterInfo *info, + const NMMetaSettingInfo *setting_info, + const NMSettInfoProperty *property_info, + const ParseInfoProperty *pip, + NMSetting *setting) { GHashTable *hash; NMUtilsNamedValue data_static[300u / sizeof(NMUtilsNamedValue)]; gs_free NMUtilsNamedValue *data_free = NULL; const NMUtilsNamedValue *data; guint data_len; - char full_key_static[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + 300u]; + char full_key_static[NM_STRLEN(STRDICT_DATA_PREFIX) + 300u]; guint i; + gboolean is_exid; - nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)); - nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + if (NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)) { + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + is_exid = TRUE; + } else { + nm_assert(NM_IS_SETTING_OVS_OTHER_CONFIG(setting)); + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_OTHER_CONFIG_DATA)); + is_exid = FALSE; + } - hash = _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting)); + hash = is_exid ? _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting)) + : _nm_setting_ovs_other_config_get_data(NM_SETTING_OVS_OTHER_CONFIG(setting)); if (!hash) return; @@ -2594,7 +2614,7 @@ _writer_full_ovs_external_ids_data(KeyfileWriterInfo *info, if (data_len == 0) return; - memcpy(full_key_static, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)); + memcpy(full_key_static, STRDICT_DATA_PREFIX, NM_STRLEN(STRDICT_DATA_PREFIX)); for (i = 0; i < data_len; i++) { const char *key = data[i].name; @@ -2608,15 +2628,16 @@ _writer_full_ovs_external_ids_data(KeyfileWriterInfo *info, escaped_key = nm_keyfile_key_encode(key, &escaped_key_to_free); len = strlen(escaped_key) + 1u; - if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)) { - full_key_free = g_new(char, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + len); + if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(STRDICT_DATA_PREFIX)) { + full_key_free = g_new(char, NM_STRLEN(STRDICT_DATA_PREFIX) + len); full_key = full_key_free; - memcpy(full_key, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)); + memcpy(full_key, STRDICT_DATA_PREFIX, NM_STRLEN(STRDICT_DATA_PREFIX)); } - memcpy(&full_key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)], escaped_key, len); + memcpy(&full_key[NM_STRLEN(STRDICT_DATA_PREFIX)], escaped_key, len); nm_keyfile_plugin_kf_set_string(info->keyfile, - NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + is_exid ? NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME + : NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, full_key, val); } @@ -3077,10 +3098,18 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_EXTERNAL_IDS_DATA, .parser_no_check_key = TRUE, - .parser_full = _parser_full_ovs_external_ids_data, - .writer_full = _writer_full_ovs_external_ids_data, - .has_parser_full = TRUE, - .has_writer_full = TRUE, ), ), ), + .parser_full = _parser_full_strdict_data, + .writer_full = _writer_full_strdict_data, + .has_parser_full = TRUE, + .has_writer_full = TRUE, ), ), ), + PARSE_INFO_SETTING( + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_OTHER_CONFIG_DATA, + .parser_no_check_key = TRUE, + .parser_full = _parser_full_strdict_data, + .writer_full = _writer_full_strdict_data, + .has_parser_full = TRUE, + .has_writer_full = TRUE, ), ), ), PARSE_INFO_SETTING(NM_META_SETTING_TYPE_SERIAL, PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_SERIAL_PARITY, .parser = parity_parser, ), ), ), diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c index e93a1555dc..190826718a 100644 --- a/src/libnm-core-impl/nm-meta-setting-base-impl.c +++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c @@ -40,9 +40,10 @@ #include "nm-setting-match.h" #include "nm-setting-olpc-mesh.h" #include "nm-setting-ovs-bridge.h" -#include "nm-setting-ovs-interface.h" #include "nm-setting-ovs-dpdk.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-other-config.h" #include "nm-setting-ovs-patch.h" #include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" @@ -410,6 +411,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_OVS_DPDK_SETTING_NAME, .get_setting_gtype = nm_setting_ovs_dpdk_get_type, }, + [NM_META_SETTING_TYPE_OVS_OTHER_CONFIG] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_other_config_get_type, + }, [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] = { .meta_type = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, @@ -654,6 +662,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = { NM_META_SETTING_TYPE_ETHTOOL, NM_META_SETTING_TYPE_MATCH, NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, NM_META_SETTING_TYPE_PPP, NM_META_SETTING_TYPE_PPPOE, NM_META_SETTING_TYPE_TEAM_PORT, diff --git a/src/libnm-core-impl/nm-setting-ovs-external-ids.c b/src/libnm-core-impl/nm-setting-ovs-external-ids.c index d0f9104165..5468438098 100644 --- a/src/libnm-core-impl/nm-setting-ovs-external-ids.c +++ b/src/libnm-core-impl/nm-setting-ovs-external-ids.c @@ -10,6 +10,7 @@ #include "nm-setting-private.h" #include "nm-utils-private.h" #include "nm-connection-private.h" +#include "nm-setting-ovs-other-config.h" #define MAX_NUM_KEYS 256 @@ -126,7 +127,7 @@ _nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GE const char *slave_type; nm_assert(!connection || NM_IS_CONNECTION(connection)); - nm_assert(NM_IN_SET(gtype, NM_TYPE_SETTING_OVS_EXTERNAL_IDS)); + nm_assert(NM_IN_SET(gtype, NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NM_TYPE_SETTING_OVS_OTHER_CONFIG)); nm_assert(!error || !*error); if (!connection) { @@ -245,6 +246,8 @@ nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, guin NMSettingOvsExternalIDs *self = setting; NMSettingOvsExternalIDsPrivate *priv; + NM_SET_OUT(out_len, 0); + g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL); priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self); diff --git a/src/libnm-core-impl/nm-setting-ovs-other-config.c b/src/libnm-core-impl/nm-setting-ovs-other-config.c new file mode 100644 index 0000000000..ca46ae9576 --- /dev/null +++ b/src/libnm-core-impl/nm-setting-ovs-other-config.c @@ -0,0 +1,403 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2020, 2022 Red Hat, Inc. + */ + +#include "libnm-core-impl/nm-default-libnm-core.h" + +#include "nm-setting-ovs-other-config.h" + +#include "nm-setting-private.h" +#include "nm-utils-private.h" +#include "nm-connection-private.h" + +#define MAX_NUM_KEYS 256 + +/*****************************************************************************/ + +/** + * SECTION:nm-setting-ovs-other-config + * @short_description: Other-config settings for OVS + * + * The #NMSettingOvsOtherConfig object is a #NMSetting subclass that allows to + * configure other_config settings for OVS. See also "other_config" in the + * "ovs-vswitchd.conf.db" manual for the keys that OVS supports. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsOtherConfig, PROP_DATA, ); + +typedef struct { + GHashTable *data; + const char **data_keys; +} NMSettingOvsOtherConfigPrivate; + +/** + * NMSettingOvsOtherConfig: + * + * OVS Other Config Settings + * + * Since: 1.42 + */ +struct _NMSettingOvsOtherConfig { + NMSetting parent; + NMSettingOvsOtherConfigPrivate _priv; +}; + +struct _NMSettingOvsOtherConfigClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE(NMSettingOvsOtherConfig, nm_setting_ovs_other_config, NM_TYPE_SETTING) + +#define NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMSettingOvsOtherConfig, NM_IS_SETTING_OVS_OTHER_CONFIG) + +/*****************************************************************************/ + +static GHashTable * +_create_data_hash(void) +{ + return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); +} + +GHashTable * +_nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *self) +{ + return NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self)->data; +} + +/** + * nm_setting_ovs_other_config_get_data_keys: + * @setting: the #NMSettingOvsOtherConfig + * @out_len: (out): the length of the returned array + * + * Returns: (array length=out_len) (transfer none): a + * %NULL-terminated array containing each key from the table. + * + * Since: 1.42 + **/ +const char *const * +nm_setting_ovs_other_config_get_data_keys(NMSettingOvsOtherConfig *setting, guint *out_len) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + NM_SET_OUT(out_len, 0); + + g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (priv->data_keys) { + NM_SET_OUT(out_len, g_hash_table_size(priv->data)); + return priv->data_keys; + } + + priv->data_keys = nm_strdict_get_keys(priv->data, TRUE, out_len); + + /* don't return %NULL, but hijack the @data_keys fields as a pseudo + * empty strv array. */ + return priv->data_keys ?: ((const char **) &priv->data_keys); +} + +/*****************************************************************************/ + +/** + * nm_setting_ovs_other_config_get_data: + * @setting: the #NMSettingOvsOtherConfig instance + * @key: the other-config to lookup + * + * Since: 1.42 + * + * Returns: (transfer none): the value associated with @key or %NULL if no such + * value exists. + */ +const char * +nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *setting, const char *key) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL); + g_return_val_if_fail(key, NULL); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (!priv->data) + return NULL; + + return g_hash_table_lookup(priv->data, key); +} + +/** + * nm_setting_ovs_other_config_set_data: + * @setting: the #NMSettingOvsOtherConfig instance + * @key: the key to set + * @val: (allow-none): the value to set or %NULL to clear a key. + * + * Since: 1.42 + */ +void +nm_setting_ovs_other_config_set_data(NMSettingOvsOtherConfig *setting, + const char *key, + const char *val) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + g_return_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self)); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (!val) { + if (priv->data && g_hash_table_remove(priv->data, key)) + goto out_changed; + return; + } + + if (priv->data) { + const char *val2; + + if (g_hash_table_lookup_extended(priv->data, key, NULL, (gpointer *) &val2)) { + if (nm_streq(val, val2)) + return; + } + } else + priv->data = _create_data_hash(); + + g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val)); + +out_changed: + nm_clear_g_free(&priv->data_keys); + _notify(self, PROP_DATA); +} + +/*****************************************************************************/ + +static gboolean +verify(NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(setting); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (priv->data) { + gs_free_error GError *local = NULL; + const char *const *keys; + guint len; + guint i; + + keys = nm_setting_ovs_other_config_get_data_keys(self, &len); + + for (i = 0; i < len; i++) { + const char *key = keys[i]; + const char *val = g_hash_table_lookup(priv->data, key); + + if (!nm_setting_ovs_other_config_check_key(key, &local)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid key \"%s\": %s"), + key, + local->message); + } else if (!nm_setting_ovs_other_config_check_val(val, &local)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid value for \"%s\": %s"), + key, + local->message); + } else + continue; + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_DATA); + return FALSE; + } + } + + if (priv->data && g_hash_table_size(priv->data) > MAX_NUM_KEYS) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("maximum number of entries reached (%u instead of %u)"), + g_hash_table_size(priv->data), + (unsigned) MAX_NUM_KEYS); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_DATA); + return FALSE; + } + + if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_OTHER_CONFIG, + connection, + error)) + return FALSE; + + return TRUE; +} + +static NMTernary +compare_fcn_data(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) +{ + NMSettingOvsOtherConfigPrivate *priv; + NMSettingOvsOtherConfigPrivate *pri2; + + if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) + return NM_TERNARY_DEFAULT; + + if (!set_b) + return TRUE; + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_a)); + pri2 = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_b)); + return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + GHashTableIter iter; + GHashTable *data; + const char *key; + const char *val; + + switch (prop_id) { + case PROP_DATA: + data = _create_data_hash(); + if (priv->data) { + g_hash_table_iter_init(&iter, priv->data); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(data, g_strdup(key), g_strdup(val)); + } + g_value_take_boxed(value, data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_DATA: + { + gs_unref_hashtable GHashTable *old = NULL; + GHashTableIter iter; + GHashTable *data; + const char *key; + const char *val; + + nm_clear_g_free(&priv->data_keys); + + old = g_steal_pointer(&priv->data); + + data = g_value_get_boxed(value); + if (nm_g_hash_table_size(data) <= 0) + return; + + priv->data = _create_data_hash(); + g_hash_table_iter_init(&iter, data); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val)); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_other_config_init(NMSettingOvsOtherConfig *self) +{} + +/** + * nm_setting_ovs_other_config_new: + * + * Creates a new #NMSettingOvsOtherConfig object with default values. + * + * Returns: (transfer full) (type NMSettingOvsOtherConfig): the new empty + * #NMSettingOvsOtherConfig object + * + * Since: 1.42 + */ +NMSetting * +nm_setting_ovs_other_config_new(void) +{ + return g_object_new(NM_TYPE_SETTING_OVS_OTHER_CONFIG, NULL); +} + +static void +finalize(GObject *object) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + g_free(priv->data_keys); + if (priv->data) + g_hash_table_unref(priv->data); + + G_OBJECT_CLASS(nm_setting_ovs_other_config_parent_class)->finalize(object); +} + +static void +nm_setting_ovs_other_config_class_init(NMSettingOvsOtherConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + setting_class->verify = verify; + + /** + * NMSettingOvsOtherConfig:data: (type GHashTable(utf8,utf8)) + * + * A dictionary of key/value pairs with other_config settings for OVS. + * See also "other_config" in the "ovs-vswitchd.conf.db" manual for the keys + * that OVS supports. + * + * Since: 1.42 + **/ + obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_OVS_OTHER_CONFIG_DATA, + "", + "", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_properties_override_gobj( + properties_override, + obj_properties[PROP_DATA], + NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("a{ss}"), + .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus, + .typdata_to_dbus.gprop_type = + NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT, + .compare_fcn = compare_fcn_data, + .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, + .from_dbus_is_full = TRUE)); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + NULL, + properties_override, + 0); +} diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index b57c23888f..de60afd1cb 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -121,7 +121,7 @@ test_nm_meta_setting_types_by_priority(void) G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == G_N_ELEMENTS(nm_meta_setting_types_by_priority)); - G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 53); + G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 54); arr = g_ptr_array_new_with_free_func(g_object_unref); diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h index 5d07f2c2c8..9e9d55aa09 100644 --- a/src/libnm-core-intern/nm-core-internal.h +++ b/src/libnm-core-intern/nm-core-internal.h @@ -395,6 +395,7 @@ GSList *_nm_vpn_plugin_info_list_load_dir(const char *dirname, /*****************************************************************************/ GHashTable *_nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *self); +GHashTable *_nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *self); /*****************************************************************************/ diff --git a/src/libnm-core-intern/nm-meta-setting-base-impl.h b/src/libnm-core-intern/nm-meta-setting-base-impl.h index ea2175f84c..0c2def90cd 100644 --- a/src/libnm-core-intern/nm-meta-setting-base-impl.h +++ b/src/libnm-core-intern/nm-meta-setting-base-impl.h @@ -135,6 +135,7 @@ typedef enum _nm_packed { NM_META_SETTING_TYPE_OVS_DPDK, NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, NM_META_SETTING_TYPE_OVS_INTERFACE, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, NM_META_SETTING_TYPE_OVS_PATCH, NM_META_SETTING_TYPE_OVS_PORT, NM_META_SETTING_TYPE_PPP, diff --git a/src/libnm-core-public/meson.build b/src/libnm-core-public/meson.build index beeeaedd47..c54071303c 100644 --- a/src/libnm-core-public/meson.build +++ b/src/libnm-core-public/meson.build @@ -38,6 +38,7 @@ libnm_core_headers = files( 'nm-setting-ovs-dpdk.h', 'nm-setting-ovs-external-ids.h', 'nm-setting-ovs-interface.h', + 'nm-setting-ovs-other-config.h', 'nm-setting-ovs-patch.h', 'nm-setting-ovs-port.h', 'nm-setting-ppp.h', diff --git a/src/libnm-core-public/nm-core-types.h b/src/libnm-core-public/nm-core-types.h index 4db0c86e3d..f285a0f6f1 100644 --- a/src/libnm-core-public/nm-core-types.h +++ b/src/libnm-core-public/nm-core-types.h @@ -42,6 +42,7 @@ typedef struct _NMSettingMatch NMSettingMatch; typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh; typedef struct _NMSettingOvsBridge NMSettingOvsBridge; typedef struct _NMSettingOvsDpdk NMSettingOvsDpdk; +typedef struct _NMSettingOvsOtherConfig NMSettingOvsOtherConfig; typedef struct _NMSettingOvsExternalIDs NMSettingOvsExternalIDs; typedef struct _NMSettingOvsInterface NMSettingOvsInterface; typedef struct _NMSettingOvsPatch NMSettingOvsPatch; diff --git a/src/libnm-core-public/nm-setting-ovs-other-config.h b/src/libnm-core-public/nm-setting-ovs-other-config.h new file mode 100644 index 0000000000..35de8ab2f8 --- /dev/null +++ b/src/libnm-core-public/nm-setting-ovs-other-config.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2020, 2022 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_OTHER_CONFIG_H__ +#define __NM_SETTING_OVS_OTHER_CONFIG_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_OTHER_CONFIG (nm_setting_ovs_other_config_get_type()) +#define NM_SETTING_OVS_OTHER_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_OVS_OTHER_CONFIG, NMSettingOvsOtherConfig)) +#define NM_SETTING_OVS_OTHER_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + NM_TYPE_SETTING_OVS_OTHER_CONFIG, \ + NMSettingOvsOtherConfigClass)) +#define NM_IS_SETTING_OVS_OTHER_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_OVS_OTHER_CONFIG)) +#define NM_IS_SETTING_OVS_OTHER_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_OVS_OTHER_CONFIG)) +#define NM_SETTING_OVS_OTHER_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + NM_TYPE_SETTING_OVS_OTHER_CONFIG, \ + NMSettingOvsOtherConfigClass)) + +#define NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME "ovs-other-config" + +#define NM_SETTING_OVS_OTHER_CONFIG_DATA "data" + +typedef struct _NMSettingOvsOtherConfigClass NMSettingOvsOtherConfigClass; + +NM_AVAILABLE_IN_1_42 +GType nm_setting_ovs_other_config_get_type(void); + +NM_AVAILABLE_IN_1_42 +NMSetting *nm_setting_ovs_other_config_new(void); + +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_42 +const char *const *nm_setting_ovs_other_config_get_data_keys(NMSettingOvsOtherConfig *setting, + guint *out_len); + +NM_AVAILABLE_IN_1_42 +const char *nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *setting, const char *key); + +NM_AVAILABLE_IN_1_42 +void nm_setting_ovs_other_config_set_data(NMSettingOvsOtherConfig *setting, + const char *key, + const char *val); + +/*****************************************************************************/ + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_OTHER_CONFIG_H__ */ diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c index e93a1555dc..190826718a 100644 --- a/src/libnmc-setting/nm-meta-setting-base-impl.c +++ b/src/libnmc-setting/nm-meta-setting-base-impl.c @@ -40,9 +40,10 @@ #include "nm-setting-match.h" #include "nm-setting-olpc-mesh.h" #include "nm-setting-ovs-bridge.h" -#include "nm-setting-ovs-interface.h" #include "nm-setting-ovs-dpdk.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-other-config.h" #include "nm-setting-ovs-patch.h" #include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" @@ -410,6 +411,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_OVS_DPDK_SETTING_NAME, .get_setting_gtype = nm_setting_ovs_dpdk_get_type, }, + [NM_META_SETTING_TYPE_OVS_OTHER_CONFIG] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_other_config_get_type, + }, [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] = { .meta_type = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, @@ -654,6 +662,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = { NM_META_SETTING_TYPE_ETHTOOL, NM_META_SETTING_TYPE_MATCH, NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, NM_META_SETTING_TYPE_PPP, NM_META_SETTING_TYPE_PPPOE, NM_META_SETTING_TYPE_TEAM_PORT, diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h index ea2175f84c..0c2def90cd 100644 --- a/src/libnmc-setting/nm-meta-setting-base-impl.h +++ b/src/libnmc-setting/nm-meta-setting-base-impl.h @@ -135,6 +135,7 @@ typedef enum _nm_packed { NM_META_SETTING_TYPE_OVS_DPDK, NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, NM_META_SETTING_TYPE_OVS_INTERFACE, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, NM_META_SETTING_TYPE_OVS_PATCH, NM_META_SETTING_TYPE_OVS_PORT, NM_META_SETTING_TYPE_PPP, diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c index 4315c373a4..199f30bfb5 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.c +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -8413,6 +8413,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_OLPC_MESH N_("OLPC Mesh connection") #define SETTING_PRETTY_NAME_OVS_BRIDGE N_("Open vSwitch bridge settings") #define SETTING_PRETTY_NAME_OVS_DPDK N_("Open vSwitch DPDK interface settings") +#define SETTING_PRETTY_NAME_OVS_OTHER_CONFIG N_("OVS Other Config") #define SETTING_PRETTY_NAME_OVS_EXTERNAL_IDS N_("OVS External IDs") #define SETTING_PRETTY_NAME_OVS_INTERFACE N_("Open vSwitch interface settings") #define SETTING_PRETTY_NAME_OVS_PATCH N_("Open vSwitch patch interface settings") @@ -8611,6 +8612,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (OVS_DPDK, TRUE), ), ), + SETTING_INFO_EMPTY (OVS_OTHER_CONFIG), SETTING_INFO_EMPTY (OVS_EXTERNAL_IDS), SETTING_INFO (OVS_INTERFACE, .valid_parts = NM_META_SETTING_VALID_PARTS ( diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in index e32ece0275..9d0a34883a 100644 --- a/src/libnmc-setting/settings-docs.h.in +++ b/src/libnmc-setting/settings-docs.h.in @@ -438,5 +438,6 @@ #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0).") #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_PRIORITY N_("The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority). A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname.") #define DESCRIBE_DOC_NM_SETTING_LOOPBACK_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.") -#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with exernal-ids for OVS.") +#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with external-ids for OVS.") +#define DESCRIBE_DOC_NM_SETTING_OVS_OTHER_CONFIG_DATA N_("A dictionary of key/value pairs with other_config settings for OVS. See also \"other_config\" in the \"ovs-vswitchd.conf.db\" manual for the keys that OVS supports.") #define DESCRIBE_DOC_NM_SETTING_VETH_PEER N_("This property specifies the peer interface name of the veth. This property is mandatory.") diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in index 3fd4f6d3fc..469360e24b 100644 --- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in +++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in @@ -848,6 +848,8 @@ + + diff --git a/tools/check-docs.sh b/tools/check-docs.sh index ecf42d0387..2746848c1e 100755 --- a/tools/check-docs.sh +++ b/tools/check-docs.sh @@ -88,6 +88,7 @@ F1_EXTRA=" xml/annotation-glossary.xml xml/api-index-full.xml xml/nm-setting-ovs-external-ids.xml +xml/nm-setting-ovs-other-config.xml xml/nm-version-macros.xml xml/nm-secret-agent-old.xml xml/nm-vpn-plugin-old.xml