summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-11-02 17:29:26 +0100
committerThomas Haller <thaller@redhat.com>2020-11-09 17:53:15 +0100
commit6100b52e5ccc80b9c11e35907c0537d930f50da6 (patch)
treec67a5b0d92053c93e512b38ff88e5dc328937866
parent0d083f5dabe10a8bbd32fdfce032394b877c5bee (diff)
downloadNetworkManager-6100b52e5ccc80b9c11e35907c0537d930f50da6.tar.gz
libnm: add NMSettingOvsExternalIDs
-rw-r--r--Makefile.am2
-rw-r--r--clients/cli/generate-docs-nm-settings-nmcli.xml.in2
-rw-r--r--clients/common/nm-meta-setting-desc.c2
-rw-r--r--clients/common/settings-docs.h.in1
-rw-r--r--docs/libnm/libnm-docs.xml1
-rw-r--r--libnm-core/meson.build6
-rw-r--r--libnm-core/nm-core-internal.h4
-rw-r--r--libnm-core/nm-core-types.h1
-rw-r--r--libnm-core/nm-keyfile/nm-keyfile.c107
-rw-r--r--libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h4
-rw-r--r--libnm-core/nm-setting-ovs-external-ids.c536
-rw-r--r--libnm-core/nm-setting-ovs-external-ids.h68
-rw-r--r--libnm/libnm.ver7
-rw-r--r--po/POTFILES.in1
-rw-r--r--shared/nm-meta-setting.c8
-rw-r--r--shared/nm-meta-setting.h1
16 files changed, 747 insertions, 4 deletions
diff --git a/Makefile.am b/Makefile.am
index 473a763d74..dce16fe8c8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -954,6 +954,7 @@ libnm_core_lib_h_pub_real = \
libnm-core/nm-setting-olpc-mesh.h \
libnm-core/nm-setting-ovs-bridge.h \
libnm-core/nm-setting-ovs-dpdk.h \
+ libnm-core/nm-setting-ovs-external-ids.h \
libnm-core/nm-setting-ovs-interface.h \
libnm-core/nm-setting-ovs-patch.h \
libnm-core/nm-setting-ovs-port.h \
@@ -1027,6 +1028,7 @@ libnm_core_lib_c_settings_real = \
libnm-core/nm-setting-olpc-mesh.c \
libnm-core/nm-setting-ovs-bridge.c \
libnm-core/nm-setting-ovs-dpdk.c \
+ libnm-core/nm-setting-ovs-external-ids.c \
libnm-core/nm-setting-ovs-interface.c \
libnm-core/nm-setting-ovs-patch.c \
libnm-core/nm-setting-ovs-port.c \
diff --git a/clients/cli/generate-docs-nm-settings-nmcli.xml.in b/clients/cli/generate-docs-nm-settings-nmcli.xml.in
index 25675a1ec7..f2f589fe8d 100644
--- a/clients/cli/generate-docs-nm-settings-nmcli.xml.in
+++ b/clients/cli/generate-docs-nm-settings-nmcli.xml.in
@@ -784,6 +784,8 @@
<property name="devargs"
description="Open vSwitch DPDK device arguments." />
</setting>
+ <setting name="ovs-external-ids" >
+ </setting>
<setting name="ovs-interface" >
<property name="type"
description="The interface type. Either &quot;internal&quot;, &quot;system&quot;, &quot;patch&quot;, &quot;dpdk&quot;, or empty." />
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index 2609c706fe..86b555fe5f 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -7946,6 +7946,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_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")
#define SETTING_PRETTY_NAME_OVS_PORT N_("Open vSwitch port settings")
@@ -8129,6 +8130,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
),
),
SETTING_INFO (OVS_DPDK),
+ SETTING_INFO_EMPTY (OVS_EXTERNAL_IDS),
SETTING_INFO (OVS_INTERFACE,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in
index 5b93627245..78198ec973 100644
--- a/clients/common/settings-docs.h.in
+++ b/clients/common/settings-docs.h.in
@@ -294,6 +294,7 @@
#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_RSTP_ENABLE N_("Enable or disable RSTP.")
#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.")
#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_DEVARGS N_("Open vSwitch DPDK device arguments.")
+#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_INTERFACE_TYPE N_("The interface type. Either \"internal\", \"system\", \"patch\", \"dpdk\", or empty.")
#define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("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.")
#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.")
diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml
index 47b1fa1c08..d0be0eb475 100644
--- a/docs/libnm/libnm-docs.xml
+++ b/docs/libnm/libnm-docs.xml
@@ -338,6 +338,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-patch.xml"/>
<xi:include href="xml/nm-setting-ovs-port.xml"/>
+ <xi:include href="xml/nm-setting-ovs-external-ids.xml"/>
<xi:include href="xml/nm-setting-ppp.xml"/>
<xi:include href="xml/nm-setting-pppoe.xml"/>
<xi:include href="xml/nm-setting-proxy.xml"/>
diff --git a/libnm-core/meson.build b/libnm-core/meson.build
index aa8823c991..7b59a8c204 100644
--- a/libnm-core/meson.build
+++ b/libnm-core/meson.build
@@ -43,8 +43,9 @@ libnm_core_headers = files(
'nm-setting-match.h',
'nm-setting-olpc-mesh.h',
'nm-setting-ovs-bridge.h',
- 'nm-setting-ovs-interface.h',
'nm-setting-ovs-dpdk.h',
+ 'nm-setting-ovs-external-ids.h',
+ 'nm-setting-ovs-interface.h',
'nm-setting-ovs-patch.h',
'nm-setting-ovs-port.h',
'nm-setting-ppp.h',
@@ -143,8 +144,9 @@ libnm_core_settings_sources = files(
'nm-setting-match.c',
'nm-setting-olpc-mesh.c',
'nm-setting-ovs-bridge.c',
- 'nm-setting-ovs-interface.c',
'nm-setting-ovs-dpdk.c',
+ 'nm-setting-ovs-external-ids.c',
+ 'nm-setting-ovs-interface.c',
'nm-setting-ovs-patch.c',
'nm-setting-ovs-port.c',
'nm-setting-ppp.c',
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index f296a77fa6..fcc38565d3 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -476,6 +476,10 @@ GSList *_nm_vpn_plugin_info_list_load_dir(const char * dirname,
/*****************************************************************************/
+GHashTable *_nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *self);
+
+/*****************************************************************************/
+
typedef struct {
const char *name;
gboolean numeric;
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index c2ac48e618..64d6464e2b 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -39,6 +39,7 @@ typedef struct _NMSettingMatch NMSettingMatch;
typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh;
typedef struct _NMSettingOvsBridge NMSettingOvsBridge;
typedef struct _NMSettingOvsDpdk NMSettingOvsDpdk;
+typedef struct _NMSettingOvsExternalIDs NMSettingOvsExternalIDs;
typedef struct _NMSettingOvsInterface NMSettingOvsInterface;
typedef struct _NMSettingOvsPatch NMSettingOvsPatch;
typedef struct _NMSettingOvsPort NMSettingOvsPort;
diff --git a/libnm-core/nm-keyfile/nm-keyfile.c b/libnm-core/nm-keyfile/nm-keyfile.c
index 1042d2d604..d3a1357281 100644
--- a/libnm-core/nm-keyfile/nm-keyfile.c
+++ b/libnm-core/nm-keyfile/nm-keyfile.c
@@ -24,9 +24,14 @@
#include "nm-core-internal.h"
#include "nm-keyfile.h"
#include "nm-setting-user.h"
+#include "nm-setting-ovs-external-ids.h"
#include "nm-keyfile-utils.h"
+#define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options"
+
+#define OVS_EXTERNAL_IDS_DATA_PREFIX "data."
+
/*****************************************************************************/
typedef struct _ParseInfoProperty ParseInfoProperty;
@@ -990,6 +995,44 @@ 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)
+{
+ const char * setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME;
+ gs_strfreev char **keys = NULL;
+ gsize n_keys;
+ gsize i;
+
+ 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)));
+
+ keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_name, &n_keys, NULL);
+
+ for (i = 0; i < n_keys; i++) {
+ const char * key = keys[i];
+ gs_free char *name_to_free = NULL;
+ gs_free char *value = NULL;
+ const char * name;
+
+ if (!NM_STR_HAS_PREFIX(key, OVS_EXTERNAL_IDS_DATA_PREFIX))
+ continue;
+
+ value = nm_keyfile_plugin_kf_get_string(info->keyfile, setting_name, key, NULL);
+ if (!value)
+ continue;
+
+ name = &key[NM_STRLEN(OVS_EXTERNAL_IDS_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);
+ }
+}
+
+static void
ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key)
{
int addr_family;
@@ -2214,8 +2257,6 @@ bridge_vlan_writer(KeyfileWriterInfo *info,
}
}
-#define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options"
-
static void
wired_s390_options_parser_full(KeyfileReaderInfo * info,
const NMMetaSettingInfo * setting_info,
@@ -2361,6 +2402,60 @@ 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)
+{
+ 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];
+ guint i;
+
+ nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting));
+ nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA));
+
+ hash = _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting));
+ if (!hash)
+ return;
+
+ data = nm_utils_named_values_from_strdict(hash, &data_len, data_static, &data_free);
+ if (data_len == 0)
+ return;
+
+ memcpy(full_key_static, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX));
+
+ for (i = 0; i < data_len; i++) {
+ const char * key = data[i].name;
+ const char * val = data[i].value_str;
+ gs_free char *escaped_key_to_free = NULL;
+ const char * escaped_key;
+ gsize len;
+ gs_free char *full_key_free = NULL;
+ char * full_key = full_key_static;
+
+ 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);
+ full_key = full_key_free;
+ memcpy(full_key, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX));
+ }
+ memcpy(&full_key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)], escaped_key, len);
+
+ nm_keyfile_plugin_kf_set_string(info->keyfile,
+ NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
+ full_key,
+ val);
+ }
+}
+
+static void
write_hash_of_string(GKeyFile *file, NMSetting *setting, const char *key, const GValue *value)
{
GHashTable * hash;
@@ -2799,6 +2894,14 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
.writer_full = ip_routing_rule_writer_full,
.has_parser_full = TRUE,
.has_writer_full = TRUE, ), ), ),
+ PARSE_INFO_SETTING(
+ 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, ), ), ),
PARSE_INFO_SETTING(NM_META_SETTING_TYPE_SERIAL,
PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_SERIAL_PARITY,
.parser = parity_parser, ), ), ),
diff --git a/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h b/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h
index ca29eade24..2361bf8c30 100644
--- a/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h
+++ b/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h
@@ -98,6 +98,10 @@ gboolean nm_utils_vlan_priority_map_parse_str(NMVlanPriorityMap map_type,
/*****************************************************************************/
+#define NM_OVS_EXTERNAL_ID_NM_PREFIX "NM."
+
+/*****************************************************************************/
+
static inline int
nm_setting_ip_config_get_addr_family(NMSettingIPConfig *s_ip)
{
diff --git a/libnm-core/nm-setting-ovs-external-ids.c b/libnm-core/nm-setting-ovs-external-ids.c
new file mode 100644
index 0000000000..90fda48317
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-external-ids.c
@@ -0,0 +1,536 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*
+ * Copyright (C) 2017 - 2020 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-ovs-external-ids.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-external-ids
+ * @short_description: External-IDs for OVS database
+ *
+ * The #NMSettingOvsExternalIDs object is a #NMSetting subclass that allow to
+ * configure external ids for OVS.
+ **/
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsExternalIDs, PROP_DATA, );
+
+typedef struct {
+ GHashTable * data;
+ const char **data_keys;
+} NMSettingOvsExternalIDsPrivate;
+
+/**
+ * NMSettingOvsExternalIDs:
+ *
+ * OVS External IDs Settings
+ */
+struct _NMSettingOvsExternalIDs {
+ NMSetting parent;
+ NMSettingOvsExternalIDsPrivate _priv;
+};
+
+struct _NMSettingOvsExternalIDsClass {
+ NMSettingClass parent;
+};
+
+G_DEFINE_TYPE(NMSettingOvsExternalIDs, nm_setting_ovs_external_ids, NM_TYPE_SETTING)
+
+#define NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self) \
+ _NM_GET_PRIVATE(self, NMSettingOvsExternalIDs, NM_IS_SETTING_OVS_EXTERNAL_IDS)
+
+/*****************************************************************************/
+
+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
+ * @error: a #GError, %NULL to ignore.
+ *
+ * Checks whether @key is a valid key for OVS' external-ids.
+ * 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. They key must also not start with "NM.".
+ *
+ * Since: 1.30
+ *
+ * Returns: %TRUE if @key is a valid user data key.
+ */
+gboolean
+nm_setting_ovs_external_ids_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, _exid_key_char_is_regular(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,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("key contains invalid characters"));
+ return FALSE;
+ }
+
+ if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) {
+ /* these keys are reserved. */
+ g_set_error_literal(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("key cannot start with \"NM.\""));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_ovs_external_ids_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.
+ *
+ * Since: 1.30
+ *
+ * Returns: %TRUE if @val is a valid user data value.
+ */
+gboolean
+nm_setting_ovs_external_ids_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 > (8u * 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;
+}
+
+/*****************************************************************************/
+
+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_external_ids_get_data(NMSettingOvsExternalIDs *self)
+{
+ return NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self)->data;
+}
+
+/**
+ * nm_setting_ovs_external_ids_get_data_keys:
+ * @setting: the #NMSettingOvsExternalIDs
+ * @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.
+ **/
+const char *const *
+nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, guint *out_len)
+{
+ NMSettingOvsExternalIDs * self = setting;
+ NMSettingOvsExternalIDsPrivate *priv;
+
+ g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL);
+
+ priv = NM_SETTING_OVS_EXTERNAL_IDS_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_utils_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_external_ids_get_data:
+ * @setting: the #NMSettingOvsExternalIDs instance
+ * @key: the external-id to lookup
+ *
+ * Since: 1.30
+ *
+ * Returns: (transfer none): the value associated with @key or %NULL if no such
+ * value exists.
+ */
+const char *
+nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *setting, const char *key)
+{
+ NMSettingOvsExternalIDs * self = setting;
+ NMSettingOvsExternalIDsPrivate *priv;
+
+ g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL);
+ g_return_val_if_fail(key, NULL);
+
+ priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
+
+ if (!priv->data)
+ return NULL;
+
+ return g_hash_table_lookup(priv->data, key);
+}
+
+/**
+ * nm_setting_ovs_external_ids_set_data:
+ * @setting: the #NMSettingOvsExternalIDs instance
+ * @key: the key to set
+ * @val: (allow-none): the value to set or %NULL to clear a key.
+ *
+ * Since: 1.30
+ */
+void
+nm_setting_ovs_external_ids_set_data(NMSettingOvsExternalIDs *setting,
+ const char * key,
+ const char * val)
+{
+ NMSettingOvsExternalIDs * self = setting;
+ NMSettingOvsExternalIDsPrivate *priv;
+
+ g_return_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self));
+
+ priv = NM_SETTING_OVS_EXTERNAL_IDS_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)
+{
+ NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(setting);
+ NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
+
+ if (priv->data) {
+ gs_free_error GError *local = NULL;
+ GHashTableIter iter;
+ const char * key;
+ const char * val;
+
+ 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,
+ NM_CONNECTION_ERROR_FAILED,
+ _("invalid key \"%s\": %s"),
+ key,
+ local->message);
+ } else if (!nm_setting_ovs_external_ids_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_EXTERNAL_IDS_SETTING_NAME,
+ NM_SETTING_OVS_EXTERNAL_IDS_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 user data 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_EXTERNAL_IDS_SETTING_NAME,
+ NM_SETTING_OVS_EXTERNAL_IDS_DATA);
+ return FALSE;
+ }
+
+ if (connection) {
+ const char *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)) {
+ 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"));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static NMTernary
+compare_property(const NMSettInfoSetting *sett_info,
+ guint property_idx,
+ NMConnection * con_a,
+ NMSetting * set_a,
+ NMConnection * con_b,
+ NMSetting * set_b,
+ NMSettingCompareFlags flags)
+{
+ NMSettingOvsExternalIDsPrivate *priv;
+ NMSettingOvsExternalIDsPrivate *pri2;
+
+ if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)) {
+ if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+ return NM_TERNARY_DEFAULT;
+
+ if (!set_b)
+ return TRUE;
+
+ priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_a));
+ pri2 = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_b));
+ return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal);
+ }
+
+ return NM_SETTING_CLASS(nm_setting_ovs_external_ids_parent_class)
+ ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+}
+
+/*****************************************************************************/
+
+static void
+get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(object);
+ NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_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)
+{
+ NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(object);
+ NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_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_external_ids_init(NMSettingOvsExternalIDs *self)
+{}
+
+/**
+ * nm_setting_ovs_external_ids_new:
+ *
+ * Creates a new #NMSettingOvsExternalIDs object with default values.
+ *
+ * Returns: (transfer full) (type NMSettingOvsExternalIDs): the new empty
+ * #NMSettingOvsExternalIDs object
+ *
+ * Since: 1.30
+ */
+gpointer
+nm_setting_ovs_external_ids_new(void)
+{
+ return g_object_new(NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NULL);
+}
+
+static void
+finalize(GObject *object)
+{
+ NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(object);
+ NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
+
+ g_free(priv->data_keys);
+ if (priv->data)
+ g_hash_table_unref(priv->data);
+
+ G_OBJECT_CLASS(nm_setting_ovs_external_ids_parent_class)->finalize(object);
+}
+
+static void
+nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *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->compare_property = compare_property;
+ setting_class->verify = verify;
+
+ /**
+ * NMSettingOvsExternalIDs:data: (type GHashTable(utf8,utf8))
+ *
+ * A dictionary of key/value pairs with exernal-ids for OVS.
+ *
+ * Since: 1.30
+ **/
+ obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_OVS_EXTERNAL_IDS_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_strdict);
+
+ g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+ _nm_setting_class_commit_full(setting_class,
+ NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
+ NULL,
+ properties_override);
+}
diff --git a/libnm-core/nm-setting-ovs-external-ids.h b/libnm-core/nm-setting-ovs-external-ids.h
new file mode 100644
index 0000000000..0f2442b48f
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-external-ids.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*
+ * Copyright (C) 2017 - 2020 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_OVS_EXTERNAL_IDS_H__
+#define __NM_SETTING_OVS_EXTERNAL_IDS_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_EXTERNAL_IDS (nm_setting_ovs_external_ids_get_type())
+#define NM_SETTING_OVS_EXTERNAL_IDS(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NMSettingOvsExternalIDs))
+#define NM_SETTING_OVS_EXTERNAL_IDS_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), \
+ NM_TYPE_SETTING_OVS_EXTERNAL_IDS, \
+ NMSettingOvsExternalIDsClass))
+#define NM_IS_SETTING_OVS_EXTERNAL_IDS(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_OVS_EXTERNAL_IDS))
+#define NM_IS_SETTING_OVS_EXTERNAL_IDS_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_OVS_EXTERNAL_IDS))
+#define NM_SETTING_OVS_EXTERNAL_IDS_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), \
+ NM_TYPE_SETTING_OVS_EXTERNAL_IDS, \
+ NMSettingOvsExternalIDsClass))
+
+#define NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME "ovs-external-ids"
+
+#define NM_SETTING_OVS_EXTERNAL_IDS_DATA "data"
+
+typedef struct _NMSettingOvsExternalIDsClass NMSettingOvsExternalIDsClass;
+
+NM_AVAILABLE_IN_1_30
+GType nm_setting_ovs_external_ids_get_type(void);
+
+NM_AVAILABLE_IN_1_30
+void *nm_setting_ovs_external_ids_new(void);
+
+/*****************************************************************************/
+
+NM_AVAILABLE_IN_1_30
+const char *const *nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting,
+ guint * out_len);
+
+NM_AVAILABLE_IN_1_30
+const char *nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *setting, const char *key);
+
+NM_AVAILABLE_IN_1_30
+void nm_setting_ovs_external_ids_set_data(NMSettingOvsExternalIDs *setting,
+ const char * key,
+ const char * val);
+
+/*****************************************************************************/
+
+NM_AVAILABLE_IN_1_30
+gboolean nm_setting_ovs_external_ids_check_key(const char *key, GError **error);
+NM_AVAILABLE_IN_1_30
+gboolean nm_setting_ovs_external_ids_check_val(const char *val, GError **error);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_EXTERNAL_IDS_H__ */
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index baf1e06f86..0086fc01d2 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1766,5 +1766,12 @@ global:
nm_keyfile_read;
nm_keyfile_warn_severity_get_type;
nm_keyfile_write;
+ nm_setting_ovs_external_ids_check_key;
+ nm_setting_ovs_external_ids_check_val;
+ nm_setting_ovs_external_ids_get_data;
+ nm_setting_ovs_external_ids_get_data_keys;
+ nm_setting_ovs_external_ids_get_type;
+ nm_setting_ovs_external_ids_new;
+ nm_setting_ovs_external_ids_set_data;
nm_utils_print;
} libnm_1_28_0;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ea2eafa3f3..f2718ac648 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -82,6 +82,7 @@ libnm-core/nm-setting-macvlan.c
libnm-core/nm-setting-match.c
libnm-core/nm-setting-olpc-mesh.c
libnm-core/nm-setting-ovs-bridge.c
+libnm-core/nm-setting-ovs-external-ids.c
libnm-core/nm-setting-ovs-interface.c
libnm-core/nm-setting-ovs-patch.c
libnm-core/nm-setting-ovs-port.c
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index 51b90e6f05..5356b9a5de 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -33,6 +33,7 @@
#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-patch.h"
#include "nm-setting-ovs-port.h"
#include "nm-setting-ppp.h"
@@ -312,6 +313,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_EXTERNAL_IDS] =
+ {
+ .meta_type = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
+ .setting_priority = NM_SETTING_PRIORITY_AUX,
+ .setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
+ .get_setting_gtype = nm_setting_ovs_external_ids_get_type,
+ },
[NM_META_SETTING_TYPE_OVS_INTERFACE] =
{
.meta_type = NM_META_SETTING_TYPE_OVS_INTERFACE,
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index dadf2f7217..9d268e0ec2 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -128,6 +128,7 @@ typedef enum {
NM_META_SETTING_TYPE_MATCH,
NM_META_SETTING_TYPE_OVS_BRIDGE,
NM_META_SETTING_TYPE_OVS_DPDK,
+ NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
NM_META_SETTING_TYPE_OVS_INTERFACE,
NM_META_SETTING_TYPE_OVS_PATCH,
NM_META_SETTING_TYPE_OVS_PORT,