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