summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2023-01-11 21:49:53 +0100
committerThomas Haller <thaller@redhat.com>2023-01-11 21:49:53 +0100
commitb6122540fab63a25133ac80aac3810fe38149ed9 (patch)
treeb7ebccab3ce2855b9323b12e2245b53e7315261a
parent2c056cf9a397c3bf96216a2a7a4a06743814445d (diff)
parenta259303e1d5aa593cef2cfdff7b4d7f8c488288e (diff)
downloadNetworkManager-b6122540fab63a25133ac80aac3810fe38149ed9.tar.gz
ovs: merge branch 'th/ovs-other-config'
https://bugzilla.redhat.com/show_bug.cgi?id=2151455 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1498
-rw-r--r--Makefile.am2
-rw-r--r--docs/libnm/libnm-docs.xml1
-rwxr-xr-xexamples/python/gi/ovs-external-ids.py157
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/core/devices/nm-device.c5
-rw-r--r--src/core/devices/ovs/nm-device-ovs-bridge.c16
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c557
-rw-r--r--src/core/devices/ovs/nm-ovsdb.h16
-rw-r--r--src/libnm-client-impl/libnm.ver7
-rw-r--r--src/libnm-core-aux-intern/nm-libnm-core-utils.c101
-rw-r--r--src/libnm-core-aux-intern/nm-libnm-core-utils.h5
-rw-r--r--src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in8
-rw-r--r--src/libnm-core-impl/meson.build1
-rw-r--r--src/libnm-core-impl/nm-keyfile.c101
-rw-r--r--src/libnm-core-impl/nm-meta-setting-base-impl.c11
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-external-ids.c122
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-other-config.c403
-rw-r--r--src/libnm-core-impl/nm-utils-private.h3
-rw-r--r--src/libnm-core-impl/tests/test-setting.c2
-rw-r--r--src/libnm-core-intern/nm-core-internal.h1
-rw-r--r--src/libnm-core-intern/nm-meta-setting-base-impl.h1
-rw-r--r--src/libnm-core-public/meson.build1
-rw-r--r--src/libnm-core-public/nm-core-types.h1
-rw-r--r--src/libnm-core-public/nm-setting-ovs-other-config.h63
-rw-r--r--src/libnm-glib-aux/nm-macros-internal.h26
-rw-r--r--src/libnmc-setting/nm-meta-setting-base-impl.c11
-rw-r--r--src/libnmc-setting/nm-meta-setting-base-impl.h1
-rw-r--r--src/libnmc-setting/nm-meta-setting-desc.c2
-rw-r--r--src/libnmc-setting/settings-docs.h.in3
-rw-r--r--src/nmcli/gen-metadata-nm-settings-nmcli.xml.in2
-rwxr-xr-xtools/check-docs.sh1
31 files changed, 1287 insertions, 345 deletions
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())]]></programlisting></in
<xi:include href="xml/nm-setting-ovs-dpdk.xml"/>
<xi:include href="xml/nm-setting-ovs-external-ids.xml"/>
<xi:include href="xml/nm-setting-ovs-interface.xml"/>
+ <xi:include href="xml/nm-setting-ovs-other-config.xml"/>
<xi:include href="xml/nm-setting-ovs-patch.xml"/>
<xi:include href="xml/nm-setting-ovs-port.xml"/>
<xi:include href="xml/nm-setting-ppp.xml"/>
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" = <unset>' % (prefix, k))
+ for data_type in [DataTypeE, DataTypeO]:
-def sett_update(connection, ids_arg):
+ 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 = []
- sett = connection.get_setting(NM.SettingOvsExternalIDs)
+ 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" = <unset>' % (prefix, data_type.short, k))
+
+
+def sett_update(connection, ids_arg):
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 048afb02b5..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
@@ -124,13 +125,14 @@ nm_device_ovs_reapply_connection(NMDevice *self, NMConnection *con_old, NMConnec
device_type = NM_DEVICE_TYPE_OVS_BRIDGE;
}
- nm_ovsdb_set_external_ids(
- nm_ovsdb_get(),
- device_type,
- nm_device_get_ip_iface(self),
- nm_connection_get_uuid(con_new),
- _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_EXTERNAL_IDS),
- _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS));
+ nm_ovsdb_set_reapply(nm_ovsdb_get(),
+ device_type,
+ nm_device_get_ip_iface(self),
+ nm_connection_get_uuid(con_new),
+ _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_EXTERNAL_IDS),
+ _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS),
+ _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 8da232ab0f..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,18 +25,26 @@
#define OVSDB_MAX_FAILURES 3
+#define OTHER_CONFIG_HWADDR "hwaddr"
+
/*****************************************************************************/
#if JANSSON_VERSION_HEX < 0x020400
#warning "requires at least libjansson 2.4"
#endif
+typedef enum {
+ STRDICT_TYPE_EXTERNAL_IDS,
+ STRDICT_TYPE_OTHER_CONFIG,
+} StrdictType;
+
typedef struct {
char *port_uuid;
char *name;
char *connection_uuid;
GPtrArray *interfaces; /* interface uuids */
GArray *external_ids;
+ GArray *other_config;
} OpenvswitchPort;
typedef struct {
@@ -44,6 +53,7 @@ typedef struct {
char *connection_uuid;
GPtrArray *ports; /* port uuids */
GArray *external_ids;
+ GArray *other_config;
} OpenvswitchBridge;
typedef struct {
@@ -52,6 +62,7 @@ typedef struct {
char *type;
char *connection_uuid;
GArray *external_ids;
+ GArray *other_config;
} OpenvswitchInterface;
/*****************************************************************************/
@@ -66,7 +77,7 @@ typedef enum {
OVSDB_ADD_INTERFACE,
OVSDB_DEL_INTERFACE,
OVSDB_SET_INTERFACE_MTU,
- OVSDB_SET_EXTERNAL_IDS,
+ OVSDB_SET_REAPPLY,
} OvsdbCommand;
#define CALL_ID_UNSPEC G_MAXUINT64
@@ -92,9 +103,11 @@ typedef union {
NMDeviceType device_type;
char *ifname;
char *connection_uuid;
- GHashTable *exid_old;
- GHashTable *exid_new;
- } set_external_ids;
+ GHashTable *external_ids_old;
+ GHashTable *external_ids_new;
+ GHashTable *other_config_old;
+ GHashTable *other_config_new;
+ } set_reapply;
} OvsdbMethodPayload;
typedef struct {
@@ -223,20 +236,24 @@ static void cleanup_check_ready(NMOvsdb *self);
}, \
}))
-#define OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(xdevice_type, \
- xifname, \
- xconnection_uuid, \
- xexid_old, \
- xexid_new) \
- (&((const OvsdbMethodPayload){ \
- .set_external_ids = \
- { \
- .device_type = xdevice_type, \
- .ifname = (char *) NM_CONSTCAST(char, (xifname)), \
- .connection_uuid = (char *) NM_CONSTCAST(char, (xconnection_uuid)), \
- .exid_old = (xexid_old), \
- .exid_new = (xexid_new), \
- }, \
+#define OVSDB_METHOD_PAYLOAD_SET_REAPPLY(xdevice_type, \
+ xifname, \
+ xconnection_uuid, \
+ xexternal_ids_old, \
+ xexternal_ids_new, \
+ xother_config_old, \
+ xother_config_new) \
+ (&((const OvsdbMethodPayload){ \
+ .set_reapply = \
+ { \
+ .device_type = xdevice_type, \
+ .ifname = (char *) NM_CONSTCAST(char, (xifname)), \
+ .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), \
+ }, \
}))
/*****************************************************************************/
@@ -291,11 +308,13 @@ _call_complete(OvsdbMethodCall *call, json_t *response, GError *error)
case OVSDB_SET_INTERFACE_MTU:
nm_clear_g_free(&call->payload.set_interface_mtu.ifname);
break;
- case OVSDB_SET_EXTERNAL_IDS:
- nm_clear_g_free(&call->payload.set_external_ids.ifname);
- nm_clear_g_free(&call->payload.set_external_ids.connection_uuid);
- nm_clear_pointer(&call->payload.set_external_ids.exid_old, g_hash_table_destroy);
- nm_clear_pointer(&call->payload.set_external_ids.exid_new, g_hash_table_destroy);
+ case OVSDB_SET_REAPPLY:
+ nm_clear_g_free(&call->payload.set_reapply.ifname);
+ 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;
}
@@ -312,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);
}
@@ -323,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);
}
@@ -334,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);
}
@@ -447,19 +469,22 @@ ovsdb_call_method(NMOvsdb *self,
call->payload.set_interface_mtu.ifname,
call->payload.set_interface_mtu.mtu);
break;
- case OVSDB_SET_EXTERNAL_IDS:
- call->payload.set_external_ids.device_type = payload->set_external_ids.device_type;
- call->payload.set_external_ids.ifname = g_strdup(payload->set_external_ids.ifname);
- call->payload.set_external_ids.connection_uuid =
- g_strdup(payload->set_external_ids.connection_uuid);
- call->payload.set_external_ids.exid_old =
- nm_g_hash_table_ref(payload->set_external_ids.exid_old);
- call->payload.set_external_ids.exid_new =
- nm_g_hash_table_ref(payload->set_external_ids.exid_new);
+ case OVSDB_SET_REAPPLY:
+ call->payload.set_reapply.device_type = payload->set_reapply.device_type;
+ call->payload.set_reapply.ifname = g_strdup(payload->set_reapply.ifname);
+ call->payload.set_reapply.connection_uuid = g_strdup(payload->set_reapply.connection_uuid);
+ call->payload.set_reapply.external_ids_old =
+ 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",
- call->payload.set_external_ids.connection_uuid,
- call->payload.set_external_ids.ifname);
+ "new: set external-ids/other-config con-uuid=%s, interface=%s",
+ call->payload.set_reapply.connection_uuid,
+ call->payload.set_reapply.ifname);
break;
}
@@ -592,21 +617,30 @@ _set_bridge_ports(json_t *params, const char *ifname, json_t *new_ports)
static void
_set_bridge_mac(json_t *params, const char *ifname, const char *mac)
{
- json_array_append_new(params,
- json_pack("{s:s, s:s, s:{s:[s, [[s, s]]]}, s:[[s, s, s]]}",
- "op",
- "update",
- "table",
- "Bridge",
- "row",
- "other_config",
- "map",
- "hwaddr",
- mac,
- "where",
- "name",
- "==",
- ifname));
+ json_array_append_new(
+ params,
+ json_pack("{s:s, s:s, s:[[s, s, [s, [s]]], [s, s, [s, [[s, s]]]]], s:[[s, s, s]]}",
+ "op",
+ "mutate",
+ "table",
+ "Bridge",
+ "mutations",
+
+ "other_config",
+ "delete",
+ "set",
+ OTHER_CONFIG_HWADDR,
+
+ "other_config",
+ "insert",
+ "map",
+ OTHER_CONFIG_HWADDR,
+ mac,
+
+ "where",
+ "name",
+ "==",
+ ifname));
}
/**
@@ -668,91 +702,155 @@ _set_port_interfaces(json_t *params, const char *ifname, json_t *new_interfaces)
}
static json_t *
-_j_create_external_ids_array_new(NMConnection *connection)
+_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 *external_ids = NULL;
- guint n_external_ids = 0;
+ const char *const *strv = NULL;
+ guint n_strv = 0;
guint i;
const char *uuid;
- NMSettingOvsExternalIDs *s_exid;
nm_assert(NM_IS_CONNECTION(connection));
+ nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS, STRDICT_TYPE_OTHER_CONFIG));
array = json_array();
- uuid = nm_connection_get_uuid(connection);
- nm_assert(uuid);
- json_array_append_new(array, json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid));
+ if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) {
+ uuid = nm_connection_get_uuid(connection);
+ nm_assert(uuid);
+ json_array_append_new(array,
+ json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid));
+ } else {
+ if (other_config_hwaddr) {
+ json_array_append_new(array,
+ json_pack("[s, s]", OTHER_CONFIG_HWADDR, other_config_hwaddr));
+ }
+ }
- s_exid = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_EXTERNAL_IDS);
- if (s_exid)
- external_ids = nm_setting_ovs_external_ids_get_data_keys(s_exid, &n_external_ids);
- for (i = 0; i < n_external_ids; i++) {
- const char *k = external_ids[i];
+ if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) {
+ s_external_ids = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_EXTERNAL_IDS);
+ if (s_external_ids)
+ strv = nm_setting_ovs_external_ids_get_data_keys(s_external_ids, &n_strv);
+ } else {
+ 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_exid, 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);
}
-static json_t *
-_j_create_external_ids_array_update(const char *connection_uuid,
- GHashTable *exid_old,
- GHashTable *exid_new)
+static void
+_j_create_strv_array_update(json_t *mutations,
+ StrdictType strdict_type,
+ const char *connection_uuid,
+ GHashTable *hash_old,
+ GHashTable *hash_new)
{
GHashTableIter iter;
- json_t *mutations;
json_t *array;
const char *key;
const char *val;
- nm_assert(connection_uuid);
+ /* 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. */
- mutations = json_array();
+ nm_assert((!!connection_uuid) == (strdict_type == STRDICT_TYPE_EXTERNAL_IDS));
+ nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS, STRDICT_TYPE_OTHER_CONFIG));
- if (exid_old) {
- array = NULL;
- g_hash_table_iter_init(&iter, exid_old);
+ array = NULL;
+ if (hash_old) {
+ g_hash_table_iter_init(&iter, hash_old);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) {
- if (nm_g_hash_table_contains(exid_new, key))
+ if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR))
continue;
- if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX))
+ if (!array)
+ array = json_array();
+ json_array_append_new(array, json_string(key));
+ }
+ }
+ 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)
array = json_array();
-
json_array_append_new(array, json_string(key));
}
- if (array) {
- json_array_append_new(
- mutations,
- json_pack("[s, s, [s, o]]", "external_ids", "delete", "set", array));
+ }
+ if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) {
+ if (!nm_g_hash_table_contains(hash_old, NM_OVS_EXTERNAL_ID_NM_PREFIX)
+ && !nm_g_hash_table_contains(hash_new, NM_OVS_EXTERNAL_ID_NM_PREFIX)) {
+ if (!array)
+ array = json_array();
+ json_array_append_new(array, json_string(NM_OVS_EXTERNAL_ID_NM_PREFIX));
}
}
+ if (array) {
+ json_array_append_new(
+ mutations,
+ json_pack("[s, s, [s, o]]",
+ strdict_type == STRDICT_TYPE_EXTERNAL_IDS ? "external_ids" : "other_config",
+ "delete",
+ "set",
+ array));
+ }
array = json_array();
- json_array_append_new(
- array,
- json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, connection_uuid));
-
- if (exid_new) {
- g_hash_table_iter_init(&iter, exid_new);
+ if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) {
+ json_array_append_new(
+ array,
+ json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, connection_uuid));
+ }
+ if (hash_new) {
+ g_hash_table_iter_init(&iter, hash_new);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
- if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX))
+ if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) {
+ 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));
}
}
- json_array_append_new(mutations,
- json_pack("[s, s, [s, o]]", "external_ids", "insert", "map", array));
- return mutations;
+ json_array_append_new(
+ mutations,
+ json_pack("[s, s, [s, o]]",
+ strdict_type == STRDICT_TYPE_EXTERNAL_IDS ? "external_ids" : "other_config",
+ "insert",
+ "map",
+ array));
}
/**
@@ -824,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",
@@ -832,7 +930,9 @@ _insert_interface(json_t *params,
"options",
options,
"external_ids",
- _j_create_external_ids_array_new(interface));
+ _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));
@@ -917,7 +1017,12 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces)
json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(port)));
json_object_set_new(row, "interfaces", json_pack("[s, O]", "set", new_interfaces));
- json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(port));
+ json_object_set_new(row,
+ "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,
@@ -977,13 +1082,12 @@ _insert_bridge(json_t *params,
json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(bridge)));
json_object_set_new(row, "ports", json_pack("[s, O]", "set", new_ports));
- json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(bridge));
-
- if (cloned_mac) {
- json_object_set_new(row,
- "other_config",
- json_pack("[s, [[s, s]]]", "map", "hwaddr", cloned_mac));
- }
+ json_object_set_new(row,
+ "external_ids",
+ _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,
@@ -1345,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",
@@ -1361,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");
@@ -1411,24 +1518,38 @@ ovsdb_next_command(NMOvsdb *self)
"==",
call->payload.set_interface_mtu.ifname));
break;
- case OVSDB_SET_EXTERNAL_IDS:
+ case OVSDB_SET_REAPPLY:
+ {
+ json_t *mutations;
+
+ mutations = json_array();
+
+ _j_create_strv_array_update(mutations,
+ STRDICT_TYPE_EXTERNAL_IDS,
+ 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,
json_pack("{s:s, s:s, s:o, s:[[s, s, s]]}",
"op",
"mutate",
"table",
- _device_type_to_table(call->payload.set_external_ids.device_type),
+ _device_type_to_table(call->payload.set_reapply.device_type),
"mutations",
- _j_create_external_ids_array_update(
- call->payload.set_external_ids.connection_uuid,
- call->payload.set_external_ids.exid_old,
- call->payload.set_external_ids.exid_new),
+ mutations,
"where",
"name",
"==",
- call->payload.set_external_ids.ifname));
+ call->payload.set_reapply.ifname));
break;
+ }
default:
nm_assert_not_reached();
@@ -1513,19 +1634,18 @@ _uuids_to_array(const json_t *items)
}
static void
-_external_ids_extract(json_t *external_ids, GArray **out_array, const char **out_connection_uuid)
+_strdict_extract(json_t *strdict, GArray **out_array)
{
json_t *array;
json_t *value;
gsize index;
nm_assert(out_array && !*out_array);
- nm_assert(!out_connection_uuid || !*out_connection_uuid);
- if (!nm_streq0("map", json_string_value(json_array_get(external_ids, 0))))
+ if (!nm_streq0("map", json_string_value(json_array_get(strdict, 0))))
return;
- array = json_array_get(external_ids, 1);
+ array = json_array_get(strdict, 1);
json_array_foreach (array, index, value) {
const char *key = json_string_value(json_array_get(value, 0));
@@ -1546,16 +1666,26 @@ _external_ids_extract(json_t *external_ids, GArray **out_array, const char **out
.name = g_strdup(key),
.value_str = g_strdup(val),
};
-
- if (out_connection_uuid && nm_streq(v->name, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID)) {
- *out_connection_uuid = v->value_str;
- out_connection_uuid = NULL;
- }
}
}
+static const char *
+_strdict_find_key(GArray *array, const char *key)
+{
+ gssize idx;
+
+ idx = nm_utils_named_value_list_find(nm_g_array_first_p(array, NMUtilsNamedValue),
+ nm_g_array_len(array),
+ key,
+ FALSE);
+ if (idx < 0)
+ return NULL;
+
+ return nm_g_array_index(array, NMUtilsNamedValue, idx).value_str;
+}
+
static gboolean
-_external_ids_equal(const GArray *arr1, const GArray *arr2)
+_strdict_equals(const GArray *arr1, const GArray *arr2)
{
guint n;
guint i;
@@ -1568,16 +1698,16 @@ _external_ids_equal(const GArray *arr1, const GArray *arr2)
const NMUtilsNamedValue *n1 = &nm_g_array_index(arr1, NMUtilsNamedValue, i);
const NMUtilsNamedValue *n2 = &nm_g_array_index(arr2, NMUtilsNamedValue, i);
- if (!nm_streq0(n1->name, n2->name))
+ if (!nm_streq(n1->name, n2->name))
return FALSE;
- if (!nm_streq0(n1->value_str, n2->value_str))
+ if (!nm_streq(n1->value_str, n2->value_str))
return FALSE;
}
return TRUE;
}
static char *
-_external_ids_to_string(const GArray *arr)
+_strdict_to_string(const GArray *arr)
{
NMStrBuf strbuf;
guint i;
@@ -1592,9 +1722,9 @@ _external_ids_to_string(const GArray *arr)
if (i > 0)
nm_str_buf_append_c(&strbuf, ',');
- nm_str_buf_append_printf(&strbuf, " \"%s\" = \"%s\"]", n->name, n->value_str);
+ nm_str_buf_append_printf(&strbuf, " \"%s\" = \"%s\" ", n->name, n->value_str);
}
- nm_str_buf_append(&strbuf, " ]");
+ nm_str_buf_append(&strbuf, "]");
return nm_str_buf_finalize(&strbuf, NULL);
}
@@ -1618,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,
};
@@ -1657,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,
@@ -1671,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;
@@ -1714,7 +1848,10 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
nm_clear_pointer(&ovs_interface, _free_interface);
}
- _external_ids_extract(external_ids, &external_ids_arr, &connection_uuid);
+ _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;
@@ -1723,14 +1860,20 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
changed |= nm_strdup_reset(&ovs_interface->type, type);
changed |= nm_strdup_reset(&ovs_interface->connection_uuid, connection_uuid);
- if (!_external_ids_equal(ovs_interface->external_ids, external_ids_arr)) {
+ if (!_strdict_equals(ovs_interface->external_ids, external_ids_arr)) {
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 *strtmp = NULL;
+ 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,
@@ -1738,10 +1881,12 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
", ",
ovs_interface->connection_uuid,
""),
- (strtmp = _external_ids_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 *strtmp = NULL;
+ gs_free char *strtmp1 = NULL;
+ gs_free char *strtmp2 = NULL;
ovs_interface = g_slice_new(OpenvswitchInterface);
*ovs_interface = (OpenvswitchInterface){
@@ -1750,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,
- ""),
- (strtmp = _external_ids_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,
@@ -1781,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) {
@@ -1824,7 +1975,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
nm_clear_pointer(&ovs_port, _free_port);
}
- _external_ids_extract(external_ids, &external_ids_arr, &connection_uuid);
+ _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);
if (ovs_port) {
@@ -1838,24 +1993,31 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
NM_SWAP(&ovs_port->interfaces, &interfaces);
changed = TRUE;
}
- if (!_external_ids_equal(ovs_port->external_ids, external_ids_arr)) {
+ if (!_strdict_equals(ovs_port->external_ids, external_ids_arr)) {
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 *strtmp = NULL;
+ 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,
""),
- (strtmp = _external_ids_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 *strtmp = NULL;
+ gs_free char *strtmp1 = NULL;
+ gs_free char *strtmp2 = NULL;
ovs_port = g_slice_new(OpenvswitchPort);
*ovs_port = (OpenvswitchPort){
@@ -1864,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,
""),
- (strtmp = _external_ids_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);
}
}
@@ -1882,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);
@@ -1929,7 +2096,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
nm_clear_pointer(&ovs_bridge, _free_bridge);
}
- _external_ids_extract(external_ids, &external_ids_arr, &connection_uuid);
+ _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);
if (ovs_bridge) {
@@ -1943,24 +2114,31 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
NM_SWAP(&ovs_bridge->ports, &ports);
changed = TRUE;
}
- if (!_external_ids_equal(ovs_bridge->external_ids, external_ids_arr)) {
+ if (!_strdict_equals(ovs_bridge->external_ids, external_ids_arr)) {
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 *strtmp = NULL;
+ 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,
""),
- (strtmp = _external_ids_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 *strtmp = NULL;
+ gs_free char *strtmp1 = NULL;
+ gs_free char *strtmp2 = NULL;
ovs_bridge = g_slice_new(OpenvswitchBridge);
*ovs_bridge = (OpenvswitchBridge){
@@ -1969,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,
""),
- (strtmp = _external_ids_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);
}
}
@@ -2716,31 +2896,50 @@ nm_ovsdb_set_interface_mtu(NMOvsdb *self,
}
void
-nm_ovsdb_set_external_ids(NMOvsdb *self,
- NMDeviceType device_type,
- const char *ifname,
- const char *connection_uuid,
- NMSettingOvsExternalIDs *s_exid_old,
- NMSettingOvsExternalIDs *s_exid_new)
+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)
{
- gs_unref_hashtable GHashTable *exid_old = NULL;
- gs_unref_hashtable GHashTable *exid_new = NULL;
-
- exid_old =
- s_exid_old ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_old)) : NULL;
- exid_new =
- s_exid_new ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_new)) : NULL;
+ 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
+ ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_external_ids_old))
+ : NULL;
+ external_ids_new =
+ s_external_ids_new
+ ? 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,
FALSE,
- OVSDB_SET_EXTERNAL_IDS,
- OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(device_type,
- ifname,
- connection_uuid,
- exid_old,
- exid_new));
+ OVSDB_SET_REAPPLY,
+ OVSDB_METHOD_PAYLOAD_SET_REAPPLY(device_type,
+ ifname,
+ connection_uuid,
+ external_ids_old,
+ 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 610190ed7e..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_external_ids(NMOvsdb *self,
- NMDeviceType device_type,
- const char *ifname,
- const char *connection_uuid,
- struct _NMSettingOvsExternalIDs *s_exid_old,
- struct _NMSettingOvsExternalIDs *s_exid_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"
/>
</setting>
+ <setting name="ovs-other-config"
+ gtype="NMSettingOvsOtherConfig"
+ >
+ <property name="data"
+ dbus-type="a{ss}"
+ gprop-type="GHashTable"
+ />
+ </setting>
<setting name="ovs-patch"
gtype="NMSettingOvsPatch"
>
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 f4e31f81ce..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
@@ -53,15 +54,6 @@ G_DEFINE_TYPE(NMSettingOvsExternalIDs, nm_setting_ovs_external_ids, NM_TYPE_SETT
/*****************************************************************************/
-static gboolean
-_exid_key_char_is_regular(char ch)
-{
- /* allow words of printable characters, plus some
- * special characters, for example to support base64 encoding. */
- return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
- || NM_IN_SET(ch, '-', '_', '+', '/', '=', '.');
-}
-
/**
* nm_setting_ovs_external_ids_check_key:
* @key: (allow-none): the key to check
@@ -105,7 +97,7 @@ nm_setting_ovs_external_ids_check_key(const char *key, GError **error)
_("key must be UTF8"));
return FALSE;
}
- if (!NM_STRCHAR_ALL(key, ch, _exid_key_char_is_regular(ch))) {
+ 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 external-id. However, we are strict (at least, for now). */
g_set_error_literal(error,
@@ -127,6 +119,56 @@ nm_setting_ovs_external_ids_check_key(const char *key, GError **error)
return TRUE;
}
+gboolean
+_nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ const char *type;
+ const char *slave_type;
+
+ nm_assert(!connection || NM_IS_CONNECTION(connection));
+ nm_assert(NM_IN_SET(gtype, NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NM_TYPE_SETTING_OVS_OTHER_CONFIG));
+ nm_assert(!error || !*error);
+
+ if (!connection) {
+ /* We don't know. It's valid. */
+ return TRUE;
+ }
+
+ type = nm_connection_get_connection_type(connection);
+ if (!type) {
+ NMSetting *s_base;
+
+ s_base = _nm_connection_find_base_type_setting(connection);
+ if (s_base)
+ type = nm_setting_get_name(s_base);
+ }
+ if (NM_IN_STRSET(type,
+ NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+ NM_SETTING_OVS_PORT_SETTING_NAME,
+ NM_SETTING_OVS_INTERFACE_SETTING_NAME))
+ return TRUE;
+
+ if ((s_con = nm_connection_get_setting_connection(connection))
+ && _nm_connection_detect_slave_type_full(s_con,
+ connection,
+ &slave_type,
+ NULL,
+ NULL,
+ NULL,
+ NULL)
+ && nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
+ return TRUE;
+
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("OVS %s can only be added to a profile of type OVS "
+ "bridge/port/interface or to OVS system interface"),
+ gtype == NM_TYPE_SETTING_OVS_EXTERNAL_IDS ? "external-ids" : "other-config");
+ return FALSE;
+}
+
/**
* nm_setting_ovs_external_ids_check_val:
* @val: (allow-none): the value to check
@@ -204,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);
@@ -302,12 +346,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
if (priv->data) {
gs_free_error GError *local = NULL;
- GHashTableIter iter;
- const char *key;
- const char *val;
+ const char *const *keys;
+ guint len;
+ guint i;
+
+ keys = nm_setting_ovs_external_ids_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);
- g_hash_table_iter_init(&iter, priv->data);
- while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
if (!nm_setting_ovs_external_ids_check_key(key, &local)) {
g_set_error(error,
NM_CONNECTION_ERROR,
@@ -336,7 +384,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
- _("maximum number of user data entries reached (%u instead of %u)"),
+ _("maximum number of entries reached (%u instead of %u)"),
g_hash_table_size(priv->data),
(unsigned) MAX_NUM_KEYS);
g_prefix_error(error,
@@ -346,44 +394,10 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
- if (connection) {
- NMSettingConnection *s_con;
- const char *type;
- const char *slave_type;
-
- type = nm_connection_get_connection_type(connection);
- if (!type) {
- NMSetting *s_base;
-
- s_base = _nm_connection_find_base_type_setting(connection);
- if (s_base)
- type = nm_setting_get_name(s_base);
- }
- if (NM_IN_STRSET(type,
- NM_SETTING_OVS_BRIDGE_SETTING_NAME,
- NM_SETTING_OVS_PORT_SETTING_NAME,
- NM_SETTING_OVS_INTERFACE_SETTING_NAME))
- goto connection_type_is_good;
-
- if ((s_con = nm_connection_get_setting_connection(connection))
- && _nm_connection_detect_slave_type_full(s_con,
- connection,
- &slave_type,
- NULL,
- NULL,
- NULL,
- NULL)
- && nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
- goto connection_type_is_good;
-
- g_set_error_literal(error,
- NM_CONNECTION_ERROR,
- NM_CONNECTION_ERROR_INVALID_PROPERTY,
- _("OVS external IDs can only be added to a profile of type OVS "
- "bridge/port/interface or to OVS system interface"));
+ if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_EXTERNAL_IDS,
+ connection,
+ error))
return FALSE;
- }
-connection_type_is_good:
return TRUE;
}
@@ -519,7 +533,7 @@ nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *klass)
/**
* NMSettingOvsExternalIDs:data: (type GHashTable(utf8,utf8))
*
- * A dictionary of key/value pairs with exernal-ids for OVS.
+ * A dictionary of key/value pairs with external-ids for OVS.
*
* Since: 1.30
**/
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/nm-utils-private.h b/src/libnm-core-impl/nm-utils-private.h
index a89a466983..e2e1aff789 100644
--- a/src/libnm-core-impl/nm-utils-private.h
+++ b/src/libnm-core-impl/nm-utils-private.h
@@ -70,4 +70,7 @@ const char *const *nmtst_system_encodings_for_lang(const char *lang);
const char *const *nmtst_system_encodings_get_default(void);
const char *const *nmtst_system_encodings_get(void);
+gboolean
+_nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GError **error);
+
#endif
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 <NetworkManager.h> 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/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h
index 974355b263..71a17e3e37 100644
--- a/src/libnm-glib-aux/nm-macros-internal.h
+++ b/src/libnm-glib-aux/nm-macros-internal.h
@@ -1101,6 +1101,32 @@ nm_ascii_is_newline(char ch)
return NM_IN_SET(ch, '\n', '\t');
}
+static inline gboolean
+nm_ascii_is_regular_char(char ch)
+{
+ /* Checks whether "ch" is "regular", which basically
+ * means it's either a digit, a alpha, or some special
+ * characters that are suitable for base64 encoding.
+ *
+ * The meaning of what "regular" means is not well defined,
+ * but it's used to validate the keys for "ovs.external-ids"
+ * dictionary. */
+ switch (ch) {
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '0' ... '9':
+ case '-':
+ case '_':
+ case '+':
+ case '/':
+ case '=':
+ case '.':
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
#define nm_str_skip_leading_spaces(str) \
({ \
typeof(*(str)) *_str_sls = (str); \
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 @@
<property name="ofport-request"
description="Open vSwitch openflow port number. Defaults to zero which means that port number will not be specified and it will be chosen randomly by ovs. OpenFlow ports are the network interfaces for passing packets between OpenFlow processing and the rest of the network. OpenFlow switches connect logically to each other via their OpenFlow ports." />
</setting>
+ <setting name="ovs-other-config" >
+ </setting>
<setting name="ovs-patch" >
<property name="peer"
description="Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer." />
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