diff options
Diffstat (limited to 'libnm-core')
-rw-r--r-- | libnm-core/nm-core-internal.h | 13 | ||||
-rw-r--r-- | libnm-core/nm-setting-team-port.c | 344 | ||||
-rw-r--r-- | libnm-core/nm-setting-team.c | 694 | ||||
-rw-r--r-- | libnm-core/nm-team-utils.c | 1990 | ||||
-rw-r--r-- | libnm-core/nm-team-utils.h | 246 | ||||
-rw-r--r-- | libnm-core/nm-utils-private.h | 112 | ||||
-rw-r--r-- | libnm-core/nm-utils.c | 752 | ||||
-rw-r--r-- | libnm-core/tests/test-general.c | 59 | ||||
-rw-r--r-- | libnm-core/tests/test-setting.c | 86 |
9 files changed, 2717 insertions, 1579 deletions
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 95b50d03a0..ef5f9a6959 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -547,19 +547,6 @@ gboolean nm_team_link_watchers_equal (const GPtrArray *a, const GPtrArray *b, gboolean ignore_order); -gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, gboolean port); -GValue *_nm_utils_team_config_get (const char *conf, - const char *key, - const char *key2, - const char *key3, - gboolean port_config); - -gboolean _nm_utils_team_config_set (char **conf, - const char *key, - const char *key2, - const char *key3, - const GValue *value); - /*****************************************************************************/ guint32 _nm_utils_parse_tc_handle (const char *str, diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c index d083943ed6..220a8c4186 100644 --- a/libnm-core/nm-setting-team-port.c +++ b/libnm-core/nm-setting-team-port.c @@ -30,6 +30,7 @@ #include "nm-utils-private.h" #include "nm-connection-private.h" #include "nm-setting-connection.h" +#include "nm-team-utils.h" /** * SECTION:nm-setting-team-port @@ -41,34 +42,10 @@ /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE (NMSettingTeamPort, - PROP_CONFIG, - PROP_QUEUE_ID, - PROP_PRIO, - PROP_STICKY, - PROP_LACP_PRIO, - PROP_LACP_KEY, - PROP_LINK_WATCHERS, -); - -static const _NMUtilsTeamPropertyKeys _prop_to_keys[_PROPERTY_ENUMS_LAST] = { - [PROP_CONFIG] = { }, - [PROP_QUEUE_ID] = { .key1 = "queue_id", .default_int = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT, }, - [PROP_PRIO] = { .key1 = "prio", }, - [PROP_STICKY] = { .key1 = "sticky", }, - [PROP_LACP_PRIO] = { .key1 = "lacp_prio", .default_int = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT, }, - [PROP_LACP_KEY] = { .key1 = "lacp_key", }, - [PROP_LINK_WATCHERS] = { .key1 = "link_watch", }, -}; +static GParamSpec *obj_properties[_NM_TEAM_ATTRIBUTE_PORT_NUM] = { NULL, }; typedef struct { - char *config; - int queue_id; - int prio; - gboolean sticky; - int lacp_prio; - int lacp_key; - GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */ + NMTeamSetting *team_setting; } NMSettingTeamPortPrivate; G_DEFINE_TYPE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING) @@ -77,6 +54,17 @@ G_DEFINE_TYPE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING) /*****************************************************************************/ +#define _maybe_changed(self, changed) \ + nm_team_setting_maybe_changed (NM_SETTING (_NM_ENSURE_TYPE (NMSettingTeamPort *, self)), (const GParamSpec *const*) obj_properties, (changed)) + +#define _maybe_changed_with_assert(self, changed) \ + G_STMT_START { \ + if (!_maybe_changed ((self), (changed))) \ + nm_assert_not_reached (); \ + } G_STMT_END + +/*****************************************************************************/ + /** * nm_setting_team_port_get_config: * @setting: the #NMSettingTeamPort @@ -88,7 +76,7 @@ nm_setting_team_port_get_config (NMSettingTeamPort *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), NULL); - return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->config; + return nm_team_setting_config_get (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting); } /** @@ -104,7 +92,7 @@ nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), -1); - return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->queue_id; + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting->d.port.queue_id; } /** @@ -120,7 +108,7 @@ nm_setting_team_port_get_prio (NMSettingTeamPort *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); - return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->prio; + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting->d.port.prio; } /** @@ -136,7 +124,7 @@ nm_setting_team_port_get_sticky (NMSettingTeamPort *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), FALSE); - return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->sticky; + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting->d.port.sticky; } /** @@ -152,7 +140,7 @@ nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); - return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->lacp_prio; + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting->d.port.lacp_prio; } /** @@ -168,7 +156,7 @@ nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); - return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->lacp_key; + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting->d.port.lacp_key; } /** @@ -182,11 +170,9 @@ nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting) guint nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); - g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); - return priv->link_watchers->len; + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting->d.link_watchers->len; } /** @@ -201,12 +187,15 @@ nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting) NMTeamLinkWatcher * nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + NMSettingTeamPortPrivate *priv; g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), NULL); - g_return_val_if_fail (idx < priv->link_watchers->len, NULL); - return priv->link_watchers->pdata[idx]; + priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + g_return_val_if_fail (idx < priv->team_setting->d.link_watchers->len, NULL); + + return priv->team_setting->d.link_watchers->pdata[idx]; } /** @@ -225,20 +214,12 @@ gboolean nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting, NMTeamLinkWatcher *link_watcher) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); - guint i; - g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), FALSE); g_return_val_if_fail (link_watcher != NULL, FALSE); - for (i = 0; i < priv->link_watchers->len; i++) { - if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) - return FALSE; - } - - g_ptr_array_add (priv->link_watchers, _nm_team_link_watcher_ref (link_watcher)); - _notify (setting, PROP_LINK_WATCHERS); - return TRUE; + return _maybe_changed (setting, + nm_team_setting_value_link_watchers_add (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting, + link_watcher)); } /** @@ -253,13 +234,17 @@ nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting, void nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + NMSettingTeamPortPrivate *priv; g_return_if_fail (NM_IS_SETTING_TEAM_PORT (setting)); - g_return_if_fail (idx < priv->link_watchers->len); - g_ptr_array_remove_index (priv->link_watchers, idx); - _notify (setting, PROP_LINK_WATCHERS); + priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + g_return_if_fail (idx < priv->team_setting->d.link_watchers->len); + + _maybe_changed_with_assert (setting, + nm_team_setting_value_link_watchers_remove (priv->team_setting, + idx)); } /** @@ -277,19 +262,12 @@ gboolean nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting, NMTeamLinkWatcher *link_watcher) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); - guint i; - g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), FALSE); + g_return_val_if_fail (link_watcher, FALSE); - for (i = 0; i < priv->link_watchers->len; i++) { - if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) { - g_ptr_array_remove_index (priv->link_watchers, i); - _notify (setting, PROP_LINK_WATCHERS); - return TRUE; - } - } - return FALSE; + return _maybe_changed (setting, + nm_team_setting_value_link_watchers_remove_by_value (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting, + link_watcher)); } /** @@ -303,14 +281,12 @@ nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting, void nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); - g_return_if_fail (NM_IS_SETTING_TEAM_PORT (setting)); - if (priv->link_watchers->len != 0) { - g_ptr_array_set_size (priv->link_watchers, 0); - _notify (setting, PROP_LINK_WATCHERS); - } + _maybe_changed (setting, + nm_team_setting_value_link_watchers_set_list (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting, + NULL, + 0)); } static GVariant * @@ -360,29 +336,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->config) { - if (strlen (priv->config) > 1*1024*1024) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("team config exceeds size limit")); - g_prefix_error (error, - "%s.%s: ", - NM_SETTING_TEAM_PORT_SETTING_NAME, - NM_SETTING_TEAM_PORT_CONFIG); - return FALSE; - } - - if (!nm_utils_is_json_object (priv->config, error)) { - g_prefix_error (error, - "%s.%s: ", - NM_SETTING_TEAM_PORT_SETTING_NAME, - NM_SETTING_TEAM_PORT_CONFIG); - /* We treat an empty string as no config for compatibility. */ - return *priv->config ? FALSE : NM_SETTING_VERIFY_NORMALIZABLE; - } - } - - /* NOTE: normalizable/normalizable-errors must appear at the end with decreasing severity. - * Take care to properly order statements with priv->config above. */ + if (!nm_team_setting_verify (priv->team_setting, error)) + return FALSE; return TRUE; } @@ -407,27 +362,26 @@ compare_property (const NMSettInfoSetting *sett_info, return TRUE; a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (set_a); b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (set_b); - return nm_team_link_watchers_equal (a_priv->link_watchers, - b_priv->link_watchers, + return nm_team_link_watchers_equal (a_priv->team_setting->d.link_watchers, + b_priv->team_setting->d.link_watchers, TRUE); } if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_PORT_CONFIG)) { if (set_b) { - a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (set_a); - b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (set_b); - if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) { /* If we are trying to match a connection in order to assume it (and thus * @flags contains INFERRABLE), use the "relaxed" matching for team * configuration. Otherwise, for all other purposes (including connection * comparison before an update), resort to the default string comparison. */ - return _nm_utils_team_config_equal (a_priv->config, - b_priv->config, - TRUE); + return TRUE; } - return nm_streq0 (a_priv->config, b_priv->config); + a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (set_a); + b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (set_b); + + return nm_streq0 (nm_team_setting_config_get (a_priv->team_setting), + nm_team_setting_config_get (b_priv->team_setting)); } return TRUE; @@ -442,6 +396,37 @@ compare_property (const NMSettInfoSetting *sett_info, flags); } +static void +duplicate_copy_properties (const NMSettInfoSetting *sett_info, + NMSetting *src, + NMSetting *dst) +{ + _maybe_changed (NM_SETTING_TEAM_PORT (dst), + nm_team_setting_reset (NM_SETTING_TEAM_PORT_GET_PRIVATE (dst)->team_setting, + NM_SETTING_TEAM_PORT_GET_PRIVATE (src)->team_setting)); +} + +static gboolean +init_from_dbus (NMSetting *setting, + GHashTable *keys, + GVariant *setting_dict, + GVariant *connection_dict, + guint /* NMSettingParseFlags */ parse_flags, + GError **error) +{ + guint32 changed = 0; + gboolean success; + + success = nm_team_setting_reset_from_dbus (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->team_setting, + setting_dict, + keys, + &changed, + parse_flags, + error); + _maybe_changed (NM_SETTING_TEAM_PORT (setting), changed); + return success; +} + /*****************************************************************************/ static void @@ -452,26 +437,25 @@ get_property (GObject *object, guint prop_id, NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); switch (prop_id) { - case PROP_CONFIG: - g_value_set_string (value, nm_setting_team_port_get_config (setting)); - break; - case PROP_QUEUE_ID: - g_value_set_int (value, priv->queue_id); - break; - case PROP_PRIO: - g_value_set_int (value, priv->prio); + case NM_TEAM_ATTRIBUTE_CONFIG: + g_value_set_string (value, + nm_team_setting_config_get (priv->team_setting)); break; - case PROP_STICKY: - g_value_set_boolean (value, priv->sticky); + case NM_TEAM_ATTRIBUTE_PORT_STICKY: + g_value_set_boolean (value, + nm_team_setting_value_get_bool (priv->team_setting, + prop_id)); break; - case PROP_LACP_PRIO: - g_value_set_int (value, priv->lacp_prio); + case NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID: + case NM_TEAM_ATTRIBUTE_PORT_PRIO: + case NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO: + case NM_TEAM_ATTRIBUTE_PORT_LACP_KEY: + g_value_set_int (value, + nm_team_setting_value_get_int32 (priv->team_setting, + prop_id)); break; - case PROP_LACP_KEY: - g_value_set_int (value, priv->lacp_key); - break; - case PROP_LINK_WATCHERS: - g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers, + case NM_TEAM_ATTRIBUTE_LINK_WATCHERS: + g_value_take_boxed (value, _nm_utils_copy_array (priv->team_setting->d.link_watchers, (NMUtilsCopyFunc) _nm_team_link_watcher_ref, (GDestroyNotify) nm_team_link_watcher_unref)); break; @@ -485,81 +469,40 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object); - const GValue *align_value = NULL; - gboolean align_config = FALSE; - -#define JSON_TO_VAL(typ, id) _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], TRUE) + NMSettingTeamPort *setting = NM_SETTING_TEAM_PORT (object); + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + guint32 changed; + const GPtrArray *v_ptrarr; switch (prop_id) { - case PROP_CONFIG: - g_free (priv->config); - priv->config = g_value_dup_string (value); - priv->queue_id = JSON_TO_VAL (int, PROP_QUEUE_ID); - priv->prio = JSON_TO_VAL (int, PROP_PRIO); - priv->sticky = JSON_TO_VAL (boolean, PROP_STICKY); - priv->lacp_prio = JSON_TO_VAL (int, PROP_LACP_PRIO); - priv->lacp_key = JSON_TO_VAL (int, PROP_LACP_KEY); - - g_ptr_array_unref (priv->link_watchers); - priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS); - break; - case PROP_QUEUE_ID: - if (priv->queue_id == g_value_get_int (value)) - break; - priv->queue_id = g_value_get_int (value); - if (priv->queue_id != NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT) - align_value = value; - align_config = TRUE; - break; - case PROP_PRIO: - if (priv->prio == g_value_get_int (value)) - break; - priv->prio = g_value_get_int (value); - if (priv->prio) - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_CONFIG: + changed = nm_team_setting_config_set (priv->team_setting, g_value_get_string (value)); break; - case PROP_STICKY: - if (priv->sticky == g_value_get_boolean (value)) - break; - priv->sticky = g_value_get_boolean (value); - if (priv->sticky) - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_PORT_STICKY: + changed = nm_team_setting_value_set_bool (priv->team_setting, + prop_id, + g_value_get_boolean (value)); break; - case PROP_LACP_PRIO: - if (priv->lacp_prio == g_value_get_int (value)) - break; - priv->lacp_prio = g_value_get_int (value); - /* from libteam sources: lacp_prio default value is 0xff */ - if (priv->lacp_prio != NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT) - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID: + case NM_TEAM_ATTRIBUTE_PORT_PRIO: + case NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO: + case NM_TEAM_ATTRIBUTE_PORT_LACP_KEY: + changed = nm_team_setting_value_set_int32 (priv->team_setting, + prop_id, + g_value_get_int (value)); break; - case PROP_LACP_KEY: - if (priv->lacp_key == g_value_get_int (value)) - break; - priv->lacp_key = g_value_get_int (value); - if (priv->lacp_key) - align_value = value; - align_config = TRUE; - break; - case PROP_LINK_WATCHERS: - g_ptr_array_unref (priv->link_watchers); - priv->link_watchers = _nm_utils_copy_array (g_value_get_boxed (value), - (NMUtilsCopyFunc) _nm_team_link_watcher_ref, - (GDestroyNotify) nm_team_link_watcher_unref); - if (priv->link_watchers->len) - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_LINK_WATCHERS: + v_ptrarr = g_value_get_boxed (value); + changed = nm_team_setting_value_link_watchers_set_list (priv->team_setting, + v_ptrarr ? (const NMTeamLinkWatcher *const*) v_ptrarr->pdata : NULL, + v_ptrarr ? v_ptrarr->len : 0u); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + return; } - if (align_config) - _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value); + + _maybe_changed (setting, changed & ~(((guint32) 1) << prop_id)); } /*****************************************************************************/ @@ -569,9 +512,7 @@ nm_setting_team_port_init (NMSettingTeamPort *setting) { NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); - priv->queue_id = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT; - priv->lacp_prio = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT; - priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + priv->team_setting = nm_team_setting_new (TRUE, NULL); } /** @@ -592,8 +533,7 @@ finalize (GObject *object) { NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object); - g_free (priv->config); - g_ptr_array_unref (priv->link_watchers); + nm_team_setting_free (priv->team_setting); G_OBJECT_CLASS (nm_setting_team_port_parent_class)->finalize (object); } @@ -611,8 +551,10 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) object_class->set_property = set_property; object_class->finalize = finalize; - setting_class->compare_property = compare_property; - setting_class->verify = verify; + setting_class->compare_property = compare_property; + setting_class->verify = verify; + setting_class->duplicate_copy_properties = duplicate_copy_properties; + setting_class->init_from_dbus = init_from_dbus; /** * NMSettingTeamPort:config: @@ -628,7 +570,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * description: Team port configuration in JSON. See man teamd.conf for details. * ---end--- */ - obj_properties[PROP_CONFIG] = + obj_properties[NM_TEAM_ATTRIBUTE_CONFIG] = g_param_spec_string (NM_SETTING_TEAM_PORT_CONFIG, "", "", NULL, G_PARAM_READWRITE | @@ -643,7 +585,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_QUEUE_ID] = + obj_properties[NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID] = g_param_spec_int (NM_SETTING_TEAM_PORT_QUEUE_ID, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -656,7 +598,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_PRIO] = + obj_properties[NM_TEAM_ATTRIBUTE_PORT_PRIO] = g_param_spec_int (NM_SETTING_TEAM_PORT_PRIO, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -669,7 +611,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_STICKY] = + obj_properties[NM_TEAM_ATTRIBUTE_PORT_STICKY] = g_param_spec_boolean (NM_SETTING_TEAM_PORT_STICKY, "", "", FALSE, G_PARAM_READWRITE | @@ -682,7 +624,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_LACP_PRIO] = + obj_properties[NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO] = g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_PRIO, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -695,7 +637,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_LACP_KEY] = + obj_properties[NM_TEAM_ATTRIBUTE_PORT_LACP_KEY] = g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_KEY, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -715,19 +657,19 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_LINK_WATCHERS] = + obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS] = g_param_spec_boxed (NM_SETTING_TEAM_PORT_LINK_WATCHERS, "", "", G_TYPE_PTR_ARRAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); _properties_override_add_transform (properties_override, - obj_properties[PROP_LINK_WATCHERS], + obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS], G_VARIANT_TYPE ("aa{sv}"), team_link_watchers_to_dbus, team_link_watchers_from_dbus); - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_properties), obj_properties); _nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TEAM_PORT, NULL, properties_override); diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c index 663d7d819c..01a1a12232 100644 --- a/libnm-core/nm-setting-team.c +++ b/libnm-core/nm-setting-team.c @@ -27,6 +27,7 @@ #include "nm-utils.h" #include "nm-utils-private.h" +#include "nm-team-utils.h" #include "nm-connection-private.h" /** @@ -721,61 +722,10 @@ nm_team_link_watcher_get_flags (const NMTeamLinkWatcher *watcher) /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE (NMSettingTeam, - PROP_CONFIG, - PROP_NOTIFY_PEERS_COUNT, - PROP_NOTIFY_PEERS_INTERVAL, - PROP_MCAST_REJOIN_COUNT, - PROP_MCAST_REJOIN_INTERVAL, - PROP_RUNNER, - PROP_RUNNER_HWADDR_POLICY, - PROP_RUNNER_TX_HASH, - PROP_RUNNER_TX_BALANCER, - PROP_RUNNER_TX_BALANCER_INTERVAL, - PROP_RUNNER_ACTIVE, - PROP_RUNNER_FAST_RATE, - PROP_RUNNER_SYS_PRIO, - PROP_RUNNER_MIN_PORTS, - PROP_RUNNER_AGG_SELECT_POLICY, - PROP_LINK_WATCHERS, -); - -static const _NMUtilsTeamPropertyKeys _prop_to_keys[_PROPERTY_ENUMS_LAST] = { - [PROP_CONFIG] = { }, - [PROP_NOTIFY_PEERS_COUNT] = { .key1 = "notify_peers", .key2 = "count", }, - [PROP_NOTIFY_PEERS_INTERVAL] = { .key1 = "notify_peers", .key2 = "interval", }, - [PROP_MCAST_REJOIN_COUNT] = { .key1 = "mcast_rejoin", .key2 = "count", }, - [PROP_MCAST_REJOIN_INTERVAL] = { .key1 = "mcast_rejoin", .key2 = "interval", }, - [PROP_RUNNER] = { .key1 = "runner", .key2 = "name", .default_str = NM_SETTING_TEAM_RUNNER_DEFAULT, }, - [PROP_RUNNER_HWADDR_POLICY] = { .key1 = "runner", .key2 = "hwaddr_policy", }, - [PROP_RUNNER_TX_HASH] = { .key1 = "runner", .key2 = "tx_hash", }, - [PROP_RUNNER_TX_BALANCER] = { .key1 = "runner", .key2 = "tx_balancer", .key3 = "name", }, - [PROP_RUNNER_TX_BALANCER_INTERVAL] = { .key1 = "runner", .key2 = "tx_balancer", .key3 = "balancing_interval", .default_int = -1 }, - [PROP_RUNNER_ACTIVE] = { .key1 = "runner", .key2 = "active", }, - [PROP_RUNNER_FAST_RATE] = { .key1 = "runner", .key2 = "fast_rate", }, - [PROP_RUNNER_SYS_PRIO] = { .key1 = "runner", .key2 = "sys_prio", .default_int = -1, }, - [PROP_RUNNER_MIN_PORTS] = { .key1 = "runner", .key2 = "min_ports", .default_int = -1, }, - [PROP_RUNNER_AGG_SELECT_POLICY] = { .key1 = "runner", .key2 = "agg_select_policy", }, - [PROP_LINK_WATCHERS] = { .key1 = "link_watch", }, -}; +static GParamSpec *obj_properties[_NM_TEAM_ATTRIBUTE_MASTER_NUM] = { NULL, }; typedef struct { - char *config; - int notify_peers_count; - int notify_peers_interval; - int mcast_rejoin_count; - int mcast_rejoin_interval; - char *runner; - char *runner_hwaddr_policy; - GPtrArray *runner_tx_hash; - char *runner_tx_balancer; - int runner_tx_balancer_interval; - gboolean runner_active; - gboolean runner_fast_rate; - int runner_sys_prio; - int runner_min_ports; - char *runner_agg_select_policy; - GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */ + NMTeamSetting *team_setting; } NMSettingTeamPrivate; G_DEFINE_TYPE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING) @@ -784,6 +734,15 @@ G_DEFINE_TYPE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING) /*****************************************************************************/ +#define _maybe_changed(self, changed) \ + nm_team_setting_maybe_changed (NM_SETTING (_NM_ENSURE_TYPE (NMSettingTeam *, self)), (const GParamSpec *const*) obj_properties, (changed)) + +#define _maybe_changed_with_assert(self, changed) \ + G_STMT_START { \ + if (!_maybe_changed ((self), (changed))) \ + nm_assert_not_reached (); \ + } G_STMT_END + /** * nm_setting_team_get_config: * @setting: the #NMSettingTeam @@ -795,7 +754,7 @@ nm_setting_team_get_config (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->config; + return nm_team_setting_config_get (NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting); } /** @@ -811,7 +770,7 @@ nm_setting_team_get_notify_peers_count (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->notify_peers_count; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.notify_peers_count; } /** @@ -827,7 +786,7 @@ nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->notify_peers_interval; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.notify_peers_interval; } /** @@ -843,7 +802,7 @@ nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->mcast_rejoin_count; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.mcast_rejoin_count; } /** @@ -859,7 +818,7 @@ nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->mcast_rejoin_interval; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.mcast_rejoin_interval; } /** @@ -875,7 +834,7 @@ nm_setting_team_get_runner (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner; } /** @@ -891,7 +850,7 @@ nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_hwaddr_policy; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_hwaddr_policy; } /** @@ -907,7 +866,7 @@ nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_tx_balancer; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_tx_balancer; } /** @@ -923,7 +882,7 @@ nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_tx_balancer_interval; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_tx_balancer_interval; } /** @@ -939,7 +898,7 @@ nm_setting_team_get_runner_active (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_active; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_active; } /** @@ -955,7 +914,7 @@ nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_fast_rate; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_fast_rate; } /** @@ -971,7 +930,7 @@ nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_sys_prio; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_sys_prio; } /** @@ -987,7 +946,7 @@ nm_setting_team_get_runner_min_ports (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_min_ports; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_min_ports; } /** @@ -1003,7 +962,7 @@ nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting) { g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_agg_select_policy; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_agg_select_policy; } /** @@ -1019,19 +978,22 @@ nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting) **/ gboolean nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting, - const char *txhash) + const char *txhash) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + const GPtrArray *arr; guint i; g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); g_return_val_if_fail (txhash != NULL, FALSE); - if (priv->runner_tx_hash) { - for (i = 0; i < priv->runner_tx_hash->len; i++) { - if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) { - g_ptr_array_remove_index (priv->runner_tx_hash, i); - _notify (setting, PROP_RUNNER_TX_HASH); + arr = priv->team_setting->d.master.runner_tx_hash; + if (arr) { + for (i = 0; i < arr->len; i++) { + if (nm_streq (txhash, arr->pdata[i])) { + _maybe_changed_with_assert (setting, + nm_team_setting_value_master_runner_tx_hash_remove (priv->team_setting, + i)); return TRUE; } } @@ -1050,11 +1012,12 @@ nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting, guint nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + const GPtrArray *arr; g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return priv->runner_tx_hash ? priv->runner_tx_hash->len : 0; + arr = NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_tx_hash; + return arr ? arr->len : 0u; } /** @@ -1069,13 +1032,16 @@ nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting) const char * nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + const GPtrArray *arr; g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - g_return_val_if_fail (priv->runner_tx_hash, NULL); - g_return_val_if_fail (idx < priv->runner_tx_hash->len, NULL); - return priv->runner_tx_hash->pdata[idx]; + arr = NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.master.runner_tx_hash; + + g_return_val_if_fail (arr, NULL); + g_return_val_if_fail (idx < arr->len, NULL); + + return arr->pdata[idx]; } /** @@ -1090,14 +1056,18 @@ nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx) void nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + NMSettingTeamPrivate *priv; g_return_if_fail (NM_IS_SETTING_TEAM (setting)); - g_return_if_fail (priv->runner_tx_hash); - g_return_if_fail (idx < priv->runner_tx_hash->len); - g_ptr_array_remove_index (priv->runner_tx_hash, idx); - _notify (setting, PROP_RUNNER_TX_HASH); + priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_if_fail (priv->team_setting->d.master.runner_tx_hash); + g_return_if_fail (idx < priv->team_setting->d.master.runner_tx_hash->len); + + _maybe_changed_with_assert (setting, + nm_team_setting_value_master_runner_tx_hash_remove (priv->team_setting, + idx)); } /** @@ -1115,23 +1085,12 @@ nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx) gboolean nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - guint i; - g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); - g_return_val_if_fail (txhash != NULL, FALSE); - g_return_val_if_fail (txhash[0] != '\0', FALSE); + g_return_val_if_fail (txhash, FALSE); - if (!priv->runner_tx_hash) - priv->runner_tx_hash = g_ptr_array_new_with_free_func (g_free); - for (i = 0; i < priv->runner_tx_hash->len; i++) { - if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) - return FALSE; - } - - g_ptr_array_add (priv->runner_tx_hash, g_strdup (txhash)); - _notify (setting, PROP_RUNNER_TX_HASH); - return TRUE; + return _maybe_changed (setting, + nm_team_setting_value_master_runner_tx_hash_add (NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting, + txhash)); } /** @@ -1145,11 +1104,9 @@ nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash) guint nm_setting_team_get_num_link_watchers (NMSettingTeam *setting) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); - return priv->link_watchers->len; + return NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.link_watchers->len; } /** @@ -1164,12 +1121,15 @@ nm_setting_team_get_num_link_watchers (NMSettingTeam *setting) NMTeamLinkWatcher * nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + const GPtrArray *arr; g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); - g_return_val_if_fail (idx < priv->link_watchers->len, NULL); - return priv->link_watchers->pdata[idx]; + arr = NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting->d.link_watchers; + + g_return_val_if_fail (idx < arr->len, NULL); + + return arr->pdata[idx]; } /** @@ -1188,20 +1148,12 @@ gboolean nm_setting_team_add_link_watcher (NMSettingTeam *setting, NMTeamLinkWatcher *link_watcher) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - guint i; - g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); g_return_val_if_fail (link_watcher != NULL, FALSE); - for (i = 0; i < priv->link_watchers->len; i++) { - if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) - return FALSE; - } - - g_ptr_array_add (priv->link_watchers, _nm_team_link_watcher_ref (link_watcher)); - _notify (setting, PROP_LINK_WATCHERS); - return TRUE; + return _maybe_changed (setting, + nm_team_setting_value_link_watchers_add (NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting, + link_watcher)); } /** @@ -1216,13 +1168,17 @@ nm_setting_team_add_link_watcher (NMSettingTeam *setting, void nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + NMSettingTeamPrivate *priv; g_return_if_fail (NM_IS_SETTING_TEAM (setting)); - g_return_if_fail (idx < priv->link_watchers->len); - g_ptr_array_remove_index (priv->link_watchers, idx); - _notify (setting, PROP_LINK_WATCHERS); + priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_if_fail (idx < priv->team_setting->d.link_watchers->len); + + _maybe_changed_with_assert (setting, + nm_team_setting_value_link_watchers_remove (priv->team_setting, + idx)); } /** @@ -1240,19 +1196,12 @@ gboolean nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting, NMTeamLinkWatcher *link_watcher) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - guint i; - g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + g_return_val_if_fail (link_watcher, FALSE); - for (i = 0; i < priv->link_watchers->len; i++) { - if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) { - g_ptr_array_remove_index (priv->link_watchers, i); - _notify (setting, PROP_LINK_WATCHERS); - return TRUE; - } - } - return FALSE; + return _maybe_changed (setting, + nm_team_setting_value_link_watchers_remove_by_value (NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting, + link_watcher)); } /** @@ -1264,15 +1213,14 @@ nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting, * Since: 1.12 **/ void -nm_setting_team_clear_link_watchers (NMSettingTeam *setting) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - +nm_setting_team_clear_link_watchers (NMSettingTeam *setting) +{ g_return_if_fail (NM_IS_SETTING_TEAM (setting)); - if (priv->link_watchers->len != 0) { - g_ptr_array_set_size (priv->link_watchers, 0); - _notify (setting, PROP_LINK_WATCHERS); - } + _maybe_changed (setting, + nm_team_setting_value_link_watchers_set_list (NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting, + NULL, + 0)); } static GVariant * @@ -1292,90 +1240,12 @@ static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - guint i; if (!_nm_connection_verify_required_interface_name (connection, error)) return FALSE; - if (priv->config) { - if (strlen (priv->config) > 1*1024*1024) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("team config exceeds size limit")); - g_prefix_error (error, - "%s.%s: ", - NM_SETTING_TEAM_SETTING_NAME, - NM_SETTING_TEAM_CONFIG); - return FALSE; - } - - if (!nm_utils_is_json_object (priv->config, error)) { - g_prefix_error (error, - "%s.%s: ", - NM_SETTING_TEAM_SETTING_NAME, - NM_SETTING_TEAM_CONFIG); - /* We treat an empty string as no config for compatibility. */ - return *priv->config ? FALSE : NM_SETTING_VERIFY_NORMALIZABLE; - } - } - - if ( priv->runner - && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_BROADCAST) - && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_ROUNDROBIN) - && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_RANDOM) - && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP) - && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) - && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_LACP)) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, - _("invalid runner \"%s\""), priv->runner); - - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_TEAM_RUNNER); + if (!nm_team_setting_verify (priv->team_setting, error)) return FALSE; - } - - /* Validate link watchers */ - for (i = 0; i < priv->link_watchers->len; i++) { - NMTeamLinkWatcher *link_watcher = priv->link_watchers->pdata[i]; - const char *name = nm_team_link_watcher_get_name (link_watcher); - - if (!name) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, - _("missing link watcher name")); - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), - NM_SETTING_TEAM_LINK_WATCHERS); - return FALSE; - } - if (!NM_IN_STRSET (name, - NM_TEAM_LINK_WATCHER_ETHTOOL, - NM_TEAM_LINK_WATCHER_ARP_PING, - NM_TEAM_LINK_WATCHER_NSNA_PING)) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, - _("unknown link watcher \"%s\""), name); - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), - NM_SETTING_TEAM_LINK_WATCHERS); - return FALSE; - } - - if (NM_IN_STRSET (name, - NM_TEAM_LINK_WATCHER_ARP_PING, - NM_TEAM_LINK_WATCHER_NSNA_PING) - && !nm_team_link_watcher_get_target_host (link_watcher)) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, - _("missing target host")); - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), - NM_SETTING_TEAM_LINK_WATCHERS); - return FALSE; - } - if (nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) - && !nm_team_link_watcher_get_source_host (link_watcher)) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, - _("missing source address")); - g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), - NM_SETTING_TEAM_LINK_WATCHERS); - return FALSE; - } - } - /* NOTE: normalizable/normalizable-errors must appear at the end with decreasing severity. - * Take care to properly order statements with priv->config above. */ return TRUE; } @@ -1398,27 +1268,26 @@ compare_property (const NMSettInfoSetting *sett_info, return TRUE; a_priv = NM_SETTING_TEAM_GET_PRIVATE (set_a); b_priv = NM_SETTING_TEAM_GET_PRIVATE (set_b); - return nm_team_link_watchers_equal (a_priv->link_watchers, - b_priv->link_watchers, + return nm_team_link_watchers_equal (a_priv->team_setting->d.link_watchers, + b_priv->team_setting->d.link_watchers, TRUE); } if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_CONFIG)) { if (set_b) { - a_priv = NM_SETTING_TEAM_GET_PRIVATE (set_a); - b_priv = NM_SETTING_TEAM_GET_PRIVATE (set_b); - if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) { /* If we are trying to match a connection in order to assume it (and thus * @flags contains INFERRABLE), use the "relaxed" matching for team * configuration. Otherwise, for all other purposes (including connection * comparison before an update), resort to the default string comparison. */ - return _nm_utils_team_config_equal (a_priv->config, - b_priv->config, - TRUE); + return TRUE; } - return nm_streq0 (a_priv->config, b_priv->config); + a_priv = NM_SETTING_TEAM_GET_PRIVATE (set_a); + b_priv = NM_SETTING_TEAM_GET_PRIVATE (set_b); + + return nm_streq0 (nm_team_setting_config_get (a_priv->team_setting), + nm_team_setting_config_get (b_priv->team_setting)); } return TRUE; @@ -1433,56 +1302,38 @@ compare_property (const NMSettInfoSetting *sett_info, flags); } -#define JSON_TO_VAL(typ, id) _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], FALSE) - static void -_align_team_properties (NMSettingTeam *setting) +duplicate_copy_properties (const NMSettInfoSetting *sett_info, + NMSetting *src, + NMSetting *dst) { - NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - char **strv; - gsize i; - - priv->notify_peers_count = JSON_TO_VAL (int, PROP_NOTIFY_PEERS_COUNT); - priv->notify_peers_interval = JSON_TO_VAL (int, PROP_NOTIFY_PEERS_INTERVAL); - priv->mcast_rejoin_count = JSON_TO_VAL (int, PROP_MCAST_REJOIN_COUNT); - priv->mcast_rejoin_interval = JSON_TO_VAL (int, PROP_MCAST_REJOIN_INTERVAL); - priv->runner_tx_balancer_interval = JSON_TO_VAL (int, PROP_RUNNER_TX_BALANCER_INTERVAL); - priv->runner_sys_prio = JSON_TO_VAL (int, PROP_RUNNER_SYS_PRIO); - priv->runner_min_ports = JSON_TO_VAL (int, PROP_RUNNER_MIN_PORTS); - - priv->runner_active = JSON_TO_VAL (boolean, PROP_RUNNER_ACTIVE); - priv->runner_fast_rate = JSON_TO_VAL (boolean, PROP_RUNNER_FAST_RATE); - - g_free (priv->runner); - g_free (priv->runner_hwaddr_policy); - g_free (priv->runner_tx_balancer); - g_free (priv->runner_agg_select_policy); - priv->runner = JSON_TO_VAL (string, PROP_RUNNER); - priv->runner_hwaddr_policy = JSON_TO_VAL (string, PROP_RUNNER_HWADDR_POLICY); - priv->runner_tx_balancer = JSON_TO_VAL (string, PROP_RUNNER_TX_BALANCER); - priv->runner_agg_select_policy = JSON_TO_VAL (string, PROP_RUNNER_AGG_SELECT_POLICY); - - strv = JSON_TO_VAL (strv, PROP_RUNNER_TX_HASH); - if (_nm_utils_strv_cmp_n (( priv->runner_tx_hash - ? (const char *const*) priv->runner_tx_hash->pdata - : NULL), - ( priv->runner_tx_hash - ? (gssize) priv->runner_tx_hash->len - : (gssize) -1), - NM_CAST_STRV_CC (strv), - -1) != 0) { - nm_clear_pointer (&priv->runner_tx_hash, g_ptr_array_unref); - if (strv) { - priv->runner_tx_hash = g_ptr_array_new_full (NM_PTRARRAY_LEN (strv), g_free); - for (i = 0; strv[i]; i++) - g_ptr_array_add (priv->runner_tx_hash, strv[i]); - nm_clear_g_free (&strv); - } - } - nm_clear_pointer (&strv, g_strfreev); + _maybe_changed (NM_SETTING_TEAM (dst), + nm_team_setting_reset (NM_SETTING_TEAM_GET_PRIVATE (dst)->team_setting, + NM_SETTING_TEAM_GET_PRIVATE (src)->team_setting)); +} + +static gboolean +init_from_dbus (NMSetting *setting, + GHashTable *keys, + GVariant *setting_dict, + GVariant *connection_dict, + guint /* NMSettingParseFlags */ parse_flags, + GError **error) +{ + guint32 changed = 0; + gboolean success; - g_ptr_array_unref (priv->link_watchers); - priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS); + if (keys) + g_hash_table_remove (keys, "interface-name"); + + success = nm_team_setting_reset_from_dbus (NM_SETTING_TEAM_GET_PRIVATE (setting)->team_setting, + setting_dict, + keys, + &changed, + parse_flags, + error); + _maybe_changed (NM_SETTING_TEAM (setting), changed); + return success; } /*****************************************************************************/ @@ -1493,58 +1344,47 @@ get_property (GObject *object, guint prop_id, { NMSettingTeam *setting = NM_SETTING_TEAM (object); NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + const GPtrArray *v_ptrarr; switch (prop_id) { - case PROP_CONFIG: - g_value_set_string (value, nm_setting_team_get_config (setting)); - break; - case PROP_NOTIFY_PEERS_COUNT: - g_value_set_int (value, priv->notify_peers_count); + case NM_TEAM_ATTRIBUTE_CONFIG: + g_value_set_string (value, + nm_team_setting_config_get (priv->team_setting)); break; - case PROP_NOTIFY_PEERS_INTERVAL: - g_value_set_int (value, priv->notify_peers_interval); + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE: + g_value_set_boolean (value, + nm_team_setting_value_get_bool (priv->team_setting, + prop_id)); break; - case PROP_MCAST_REJOIN_COUNT: - g_value_set_int (value, priv->mcast_rejoin_count); + case NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT: + case NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL: + case NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT: + case NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS: + g_value_set_int (value, + nm_team_setting_value_get_int32 (priv->team_setting, + prop_id)); break; - case PROP_MCAST_REJOIN_INTERVAL: - g_value_set_int (value, priv->mcast_rejoin_interval); + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY: + g_value_set_string (value, + nm_team_setting_value_get_string (priv->team_setting, + prop_id)); break; - case PROP_RUNNER: - g_value_set_string (value, nm_setting_team_get_runner (setting)); - break; - case PROP_RUNNER_HWADDR_POLICY: - g_value_set_string (value, nm_setting_team_get_runner_hwaddr_policy (setting)); - break; - case PROP_RUNNER_TX_HASH: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH: + v_ptrarr = priv->team_setting->d.master.runner_tx_hash; g_value_take_boxed (value, - priv->runner_tx_hash - ? _nm_utils_ptrarray_to_strv (priv->runner_tx_hash) + v_ptrarr + ? _nm_utils_ptrarray_to_strv ((GPtrArray *) v_ptrarr) : NULL); break; - case PROP_RUNNER_TX_BALANCER: - g_value_set_string (value, nm_setting_team_get_runner_tx_balancer (setting)); - break; - case PROP_RUNNER_TX_BALANCER_INTERVAL: - g_value_set_int (value, priv->runner_tx_balancer_interval); - break; - case PROP_RUNNER_ACTIVE: - g_value_set_boolean (value, nm_setting_team_get_runner_active (setting)); - break; - case PROP_RUNNER_FAST_RATE: - g_value_set_boolean (value, nm_setting_team_get_runner_fast_rate (setting)); - break; - case PROP_RUNNER_SYS_PRIO: - g_value_set_int (value, priv->runner_sys_prio); - break; - case PROP_RUNNER_MIN_PORTS: - g_value_set_int (value, priv->runner_min_ports); - break; - case PROP_RUNNER_AGG_SELECT_POLICY: - g_value_set_string (value, nm_setting_team_get_runner_agg_select_policy (setting)); - break; - case PROP_LINK_WATCHERS: - g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers, + case NM_TEAM_ATTRIBUTE_LINK_WATCHERS: + g_value_take_boxed (value, _nm_utils_copy_array (priv->team_setting->d.link_watchers, (NMUtilsCopyFunc) _nm_team_link_watcher_ref, (GDestroyNotify) nm_team_link_watcher_unref)); break; @@ -1560,141 +1400,56 @@ set_property (GObject *object, guint prop_id, { NMSettingTeam *setting = NM_SETTING_TEAM (object); NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object); - const GValue *align_value = NULL; - gboolean align_config = FALSE; - char **strv; + guint32 changed; + const GPtrArray *v_ptrarr; switch (prop_id) { - case PROP_CONFIG: - g_free (priv->config); - priv->config = g_value_dup_string (value); - _align_team_properties (setting); + case NM_TEAM_ATTRIBUTE_CONFIG: + changed = nm_team_setting_config_set (priv->team_setting, g_value_get_string (value)); break; - case PROP_NOTIFY_PEERS_COUNT: - if (priv->notify_peers_count == g_value_get_int (value)) - break; - priv->notify_peers_count = g_value_get_int (value); - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE: + changed = nm_team_setting_value_set_bool (priv->team_setting, + prop_id, + g_value_get_boolean (value)); break; - case PROP_NOTIFY_PEERS_INTERVAL: - if (priv->notify_peers_interval == g_value_get_int (value)) - break; - priv->notify_peers_interval = g_value_get_int (value); - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT: + case NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL: + case NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT: + case NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS: + changed = nm_team_setting_value_set_int32 (priv->team_setting, + prop_id, + g_value_get_int (value)); break; - case PROP_MCAST_REJOIN_COUNT: - if (priv->mcast_rejoin_count == g_value_get_int (value)) - break; - priv->mcast_rejoin_count = g_value_get_int (value); - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY: + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY: + changed = nm_team_setting_value_set_string (priv->team_setting, + prop_id, + g_value_get_string (value)); break; - case PROP_MCAST_REJOIN_INTERVAL: - if (priv->mcast_rejoin_interval == g_value_get_int (value)) - break; - priv->mcast_rejoin_interval = g_value_get_int (value); - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH: + v_ptrarr = g_value_get_boxed (value); + changed = nm_team_setting_value_master_runner_tx_hash_set_list (priv->team_setting, + v_ptrarr ? (const char *const*) v_ptrarr->pdata : NULL, + v_ptrarr ? v_ptrarr->len : 0u); break; - case PROP_RUNNER: - if ( !g_value_get_string (value) - || nm_streq (priv->runner, g_value_get_string (value))) - break; - g_free (priv->runner); - priv->runner = g_value_dup_string (value); - _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], value); - _align_team_properties (setting); - break; - case PROP_RUNNER_HWADDR_POLICY: - if (nm_streq0 (priv->runner_hwaddr_policy, g_value_get_string (value))) - break; - g_free (priv->runner_hwaddr_policy); - priv->runner_hwaddr_policy = g_value_dup_string (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_TX_HASH: - if (priv->runner_tx_hash) - g_ptr_array_unref (priv->runner_tx_hash); - strv = g_value_get_boxed (value); - if (strv && strv[0]) { - priv->runner_tx_hash = _nm_utils_strv_to_ptrarray (strv); - align_value = value; - } else - priv->runner_tx_hash = NULL; - align_config = TRUE; - break; - case PROP_RUNNER_TX_BALANCER: - if (nm_streq0 (priv->runner_tx_balancer, g_value_get_string (value))) - break; - g_free (priv->runner_tx_balancer); - priv->runner_tx_balancer = g_value_dup_string (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_TX_BALANCER_INTERVAL: - if (priv->runner_tx_balancer_interval == g_value_get_int (value)) - break; - priv->runner_tx_balancer_interval = g_value_get_int (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_ACTIVE: - if (priv->runner_active == g_value_get_boolean (value)) - break; - priv->runner_active = g_value_get_boolean (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_FAST_RATE: - if (priv->runner_fast_rate == g_value_get_boolean (value)) - break; - priv->runner_fast_rate = g_value_get_boolean (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_SYS_PRIO: - if (priv->runner_sys_prio == g_value_get_int (value)) - break; - priv->runner_sys_prio = g_value_get_int (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_MIN_PORTS: - if (priv->runner_min_ports == g_value_get_int (value)) - break; - priv->runner_min_ports = g_value_get_int (value); - align_value = value; - align_config = TRUE; - break; - case PROP_RUNNER_AGG_SELECT_POLICY: - if (nm_streq0 (priv->runner_agg_select_policy, g_value_get_string (value))) - break; - g_free (priv->runner_agg_select_policy); - priv->runner_agg_select_policy = g_value_dup_string (value); - align_value = value; - align_config = TRUE; - break; - case PROP_LINK_WATCHERS: - g_ptr_array_unref (priv->link_watchers); - priv->link_watchers = _nm_utils_copy_array (g_value_get_boxed (value), - (NMUtilsCopyFunc) _nm_team_link_watcher_ref, - (GDestroyNotify) nm_team_link_watcher_unref); - if (priv->link_watchers->len) - align_value = value; - align_config = TRUE; + case NM_TEAM_ATTRIBUTE_LINK_WATCHERS: + v_ptrarr = g_value_get_boxed (value); + changed = nm_team_setting_value_link_watchers_set_list (priv->team_setting, + v_ptrarr ? (const NMTeamLinkWatcher *const*) v_ptrarr->pdata : NULL, + v_ptrarr ? v_ptrarr->len : 0u); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + return; } - if (align_config) { - _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value); - _align_team_properties (setting); - } + _maybe_changed (setting, changed & ~(((guint32) 1) << prop_id)); } /*****************************************************************************/ @@ -1704,11 +1459,7 @@ nm_setting_team_init (NMSettingTeam *setting) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); - priv->runner = g_strdup (NM_SETTING_TEAM_RUNNER_ROUNDROBIN); - priv->runner_tx_balancer_interval = -1; - priv->runner_sys_prio = -1; - priv->runner_min_ports = -1; - priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + priv->team_setting = nm_team_setting_new (FALSE, NULL); } /** @@ -1729,14 +1480,7 @@ finalize (GObject *object) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object); - g_free (priv->config); - g_free (priv->runner); - g_free (priv->runner_hwaddr_policy); - g_free (priv->runner_tx_balancer); - g_free (priv->runner_agg_select_policy); - if (priv->runner_tx_hash) - g_ptr_array_unref (priv->runner_tx_hash); - g_ptr_array_unref (priv->link_watchers); + nm_team_setting_free (priv->team_setting); G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object); } @@ -1754,8 +1498,10 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) object_class->set_property = set_property; object_class->finalize = finalize; - setting_class->compare_property = compare_property; - setting_class->verify = verify; + setting_class->compare_property = compare_property; + setting_class->verify = verify; + setting_class->duplicate_copy_properties = duplicate_copy_properties; + setting_class->init_from_dbus = init_from_dbus; /** * NMSettingTeam:config: @@ -1771,7 +1517,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * description: Team configuration in JSON. See man teamd.conf for details. * ---end--- */ - obj_properties[PROP_CONFIG] = + obj_properties[NM_TEAM_ATTRIBUTE_CONFIG] = g_param_spec_string (NM_SETTING_TEAM_CONFIG, "", "", NULL, G_PARAM_READWRITE | @@ -1785,7 +1531,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_NOTIFY_PEERS_COUNT] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT] = g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1798,7 +1544,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_NOTIFY_PEERS_INTERVAL] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL] = g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1811,7 +1557,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_MCAST_REJOIN_COUNT] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT] = g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_COUNT, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1824,7 +1570,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_MCAST_REJOIN_INTERVAL] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL] = g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1836,14 +1582,10 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * Corresponds to the teamd runner.name. * Permitted values are: "roundrobin", "broadcast", "activebackup", * "loadbalance", "lacp", "random". - * When setting the runner, all the properties specific to the runner - * will be reset to the default value; all the properties specific to - * other runners will be set to an empty value (or if not possible to - * a default value). * * Since: 1.12 **/ - obj_properties[PROP_RUNNER] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER] = g_param_spec_string (NM_SETTING_TEAM_RUNNER, "", "", NULL, G_PARAM_READWRITE | @@ -1856,7 +1598,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_HWADDR_POLICY] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY] = g_param_spec_string (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, "", "", NULL, G_PARAM_READWRITE | @@ -1869,7 +1611,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_TX_HASH] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH] = g_param_spec_boxed (NM_SETTING_TEAM_RUNNER_TX_HASH, "", "", G_TYPE_STRV, G_PARAM_READWRITE | @@ -1883,7 +1625,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_TX_BALANCER] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER] = g_param_spec_string (NM_SETTING_TEAM_RUNNER_TX_BALANCER, "", "", NULL, G_PARAM_READWRITE | @@ -1896,7 +1638,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_TX_BALANCER_INTERVAL] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL] = g_param_spec_int (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1909,7 +1651,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_ACTIVE] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE] = g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_ACTIVE, "", "", FALSE, G_PARAM_READWRITE | @@ -1922,7 +1664,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_FAST_RATE] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE] = g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_FAST_RATE, "", "", FALSE, G_PARAM_READWRITE | @@ -1935,7 +1677,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_SYS_PRIO] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO] = g_param_spec_int (NM_SETTING_TEAM_RUNNER_SYS_PRIO, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1948,7 +1690,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_MIN_PORTS] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS] = g_param_spec_int (NM_SETTING_TEAM_RUNNER_MIN_PORTS, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READWRITE | @@ -1961,7 +1703,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_RUNNER_AGG_SELECT_POLICY] = + obj_properties[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY] = g_param_spec_string (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, "", "", NULL, G_PARAM_READWRITE | @@ -1981,14 +1723,14 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) * * Since: 1.12 **/ - obj_properties[PROP_LINK_WATCHERS] = + obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS] = g_param_spec_boxed (NM_SETTING_TEAM_LINK_WATCHERS, "", "", G_TYPE_PTR_ARRAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); _properties_override_add_transform (properties_override, - obj_properties[PROP_LINK_WATCHERS], + obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS], G_VARIANT_TYPE ("aa{sv}"), team_link_watchers_to_dbus, team_link_watchers_from_dbus); @@ -2007,7 +1749,7 @@ nm_setting_team_class_init (NMSettingTeamClass *klass) _nm_setting_get_deprecated_virtual_interface_name, NULL); - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_properties), obj_properties); _nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TEAM, NULL, properties_override); diff --git a/libnm-core/nm-team-utils.c b/libnm-core/nm-team-utils.c index 95aa4691d2..9d437f8a1f 100644 --- a/libnm-core/nm-team-utils.c +++ b/libnm-core/nm-team-utils.c @@ -17,6 +17,1996 @@ * Copyright 2019 Red Hat, Inc. */ +#define NM_VALUE_TYPE_DEFINE_FUNCTIONS + #include "nm-default.h" #include "nm-team-utils.h" + +#include "nm-errors.h" +#include "nm-utils-private.h" +#include "nm-json.h" +#include "nm-glib-aux/nm-json-aux.h" +#include "nm-core-internal.h" +#include "nm-setting-team.h" +#include "nm-setting-team-port.h" + +/*****************************************************************************/ + +/* we rely on "config" being the first. At various places we iterate over attribute types, + * starting after "config".*/ +G_STATIC_ASSERT (_NM_TEAM_ATTRIBUTE_0 == 0); +G_STATIC_ASSERT (NM_TEAM_ATTRIBUTE_CONFIG == 1); + +typedef struct { + const char *const*js_keys; + const char *dbus_name; + NMValueTypUnion default_val; + NMTeamAttribute team_attr; + NMValueType value_type; + guint8 field_offset; + guint8 js_keys_len; + bool for_master:1; + bool for_port:1; +} TeamAttrData; + +#define TEAM_ATTR_IDX(_is_port, _team_attr) \ + (( (!(_is_port) || (_team_attr) < _NM_TEAM_ATTRIBUTE_START) \ + ? (int) (_team_attr) \ + : (((int) (_NM_TEAM_ATTRIBUTE_MASTER_NUM - _NM_TEAM_ATTRIBUTE_START)) + ((int) (_team_attr)))) - 1) + +#define TEAM_ATTR_IDX_CONFIG (TEAM_ATTR_IDX (FALSE, NM_TEAM_ATTRIBUTE_CONFIG)) + +static const TeamAttrData team_attr_datas[] = { + +#define _JS_KEYS(...) \ + .js_keys = NM_MAKE_STRV (__VA_ARGS__), \ + .js_keys_len = NM_NARG (__VA_ARGS__) + +#define _INIT(_is_port, _team_attr, field, _value_type, _dbus_name, ...) \ + [TEAM_ATTR_IDX (_is_port, _team_attr)] = { \ + .for_master = (_team_attr) < _NM_TEAM_ATTRIBUTE_START || !(_is_port), \ + .for_port = (_team_attr) < _NM_TEAM_ATTRIBUTE_START || (_is_port), \ + .team_attr = (_team_attr), \ + .field_offset = G_STRUCT_OFFSET (NMTeamSetting, _data_priv.field), \ + .value_type = (_value_type), \ + .dbus_name = ""_dbus_name"", \ + __VA_ARGS__ \ + } + + _INIT (0, NM_TEAM_ATTRIBUTE_CONFIG, _js_str, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_CONFIG, ), + + _INIT (0, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, link_watchers, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_LINK_WATCHERS, _JS_KEYS ("link_watch"), ), + + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT, master.notify_peers_count, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, _JS_KEYS ("notify_peers", "count"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL, master.notify_peers_interval, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, _JS_KEYS ("notify_peers", "interval"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT, master.mcast_rejoin_count, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_MCAST_REJOIN_COUNT, _JS_KEYS ("mcast_rejoin", "count"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL, master.mcast_rejoin_interval, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, _JS_KEYS ("mcast_rejoin", "interval"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER, master.runner, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER, _JS_KEYS ("runner", "name"), .default_val.v_string = NM_SETTING_TEAM_RUNNER_DEFAULT, ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY, master.runner_hwaddr_policy, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, _JS_KEYS ("runner", "hwaddr_policy"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, master.runner_tx_hash, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_RUNNER_TX_HASH, _JS_KEYS ("runner", "tx_hash"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER, master.runner_tx_balancer, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER_TX_BALANCER, _JS_KEYS ("runner", "tx_balancer", "name"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL, master.runner_tx_balancer_interval, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, _JS_KEYS ("runner", "tx_balancer", "balancing_interval"), .default_val.v_int32 = -1 ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE, master.runner_active, NM_VALUE_TYPE_BOOL, NM_SETTING_TEAM_RUNNER_ACTIVE, _JS_KEYS ("runner", "active"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE, master.runner_fast_rate, NM_VALUE_TYPE_BOOL, NM_SETTING_TEAM_RUNNER_FAST_RATE, _JS_KEYS ("runner", "fast_rate"), ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO, master.runner_sys_prio, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_RUNNER_SYS_PRIO, _JS_KEYS ("runner", "sys_prio"), .default_val.v_int32 = -1, ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS, master.runner_min_ports, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_RUNNER_MIN_PORTS, _JS_KEYS ("runner", "min_ports"), .default_val.v_int32 = -1, ), + _INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY, master.runner_agg_select_policy, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, _JS_KEYS ("runner", "agg_select_policy"), ), + + _INIT (1, NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID, port.queue_id, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_QUEUE_ID, _JS_KEYS ("queue_id"), .default_val.v_int32 = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT, ), + _INIT (1, NM_TEAM_ATTRIBUTE_PORT_PRIO, port.prio, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_PRIO, _JS_KEYS ("prio"), ), + _INIT (1, NM_TEAM_ATTRIBUTE_PORT_STICKY, port.sticky, NM_VALUE_TYPE_BOOL, NM_SETTING_TEAM_PORT_STICKY, _JS_KEYS ("sticky"), ), + _INIT (1, NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO, port.lacp_prio, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_LACP_PRIO, _JS_KEYS ("lacp_prio"), .default_val.v_int32 = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT, ), + _INIT (1, NM_TEAM_ATTRIBUTE_PORT_LACP_KEY, port.lacp_key, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_LACP_KEY, _JS_KEYS ("lacp_key"), ), + +#undef _INIT + +}; + +/*****************************************************************************/ + +typedef enum { + LINK_WATCHER_ATTRIBUTE_NAME, + LINK_WATCHER_ATTRIBUTE_TARGET_HOST, + LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, + LINK_WATCHER_ATTRIBUTE_DELAY_UP, + LINK_WATCHER_ATTRIBUTE_DELAY_DOWN, + LINK_WATCHER_ATTRIBUTE_INIT_WAIT, + LINK_WATCHER_ATTRIBUTE_INTERVAL, + LINK_WATCHER_ATTRIBUTE_MISSED_MAX, + LINK_WATCHER_ATTRIBUTE_VLANID, + LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, + LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, + LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS, +} LinkWatcherAttribute; + +typedef struct { + const char *js_key; + NMValueTypUnion default_val; + LinkWatcherAttribute link_watcher_attr; + NMValueType value_type; +} LinkWatcherAttrData; + +static const LinkWatcherAttrData link_watcher_attr_datas[] = { +#define _INIT(_link_watcher_attr, _js_key, _value_type, ...) \ + [_link_watcher_attr] = { \ + .link_watcher_attr = (_link_watcher_attr), \ + .value_type = (_value_type), \ + .js_key = (""_js_key""), \ + __VA_ARGS__ \ + } + _INIT (LINK_WATCHER_ATTRIBUTE_NAME, "name", NM_VALUE_TYPE_STRING, ), + _INIT (LINK_WATCHER_ATTRIBUTE_TARGET_HOST, "target_host", NM_VALUE_TYPE_STRING, ), + _INIT (LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, "source_host", NM_VALUE_TYPE_STRING, ), + _INIT (LINK_WATCHER_ATTRIBUTE_DELAY_UP, "delay_up", NM_VALUE_TYPE_INT, ), + _INIT (LINK_WATCHER_ATTRIBUTE_DELAY_DOWN, "delay_down", NM_VALUE_TYPE_INT, ), + _INIT (LINK_WATCHER_ATTRIBUTE_INIT_WAIT, "init_wait", NM_VALUE_TYPE_INT, ), + _INIT (LINK_WATCHER_ATTRIBUTE_INTERVAL, "interval", NM_VALUE_TYPE_INT, ), + _INIT (LINK_WATCHER_ATTRIBUTE_MISSED_MAX, "missed_max", NM_VALUE_TYPE_INT, .default_val.v_int = 3, ), + _INIT (LINK_WATCHER_ATTRIBUTE_VLANID, "vlanid", NM_VALUE_TYPE_INT, .default_val.v_int = -1, ), + _INIT (LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, "validate_active", NM_VALUE_TYPE_BOOL, ), + _INIT (LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, "validate_inactive", NM_VALUE_TYPE_BOOL, ), + _INIT (LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS, "send_always", NM_VALUE_TYPE_BOOL, ), +#undef _INIT +}; + +/*****************************************************************************/ + +static const TeamAttrData *_team_attr_data_get (gboolean is_port, + NMTeamAttribute team_attr); +static gpointer _team_setting_get_field (const NMTeamSetting *self, + const TeamAttrData *attr_data); +static gboolean _team_setting_verify (const NMTeamSetting *self, + GError **error); +static void _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher, + GString *gstr); + +/*****************************************************************************/ + +static void +_team_attr_data_ASSERT (const TeamAttrData *attr_data) +{ +#if NM_MORE_ASSERTS > 5 + nm_assert (attr_data); + if (attr_data->for_port) + nm_assert (attr_data == _team_attr_data_get (TRUE, attr_data->team_attr)); + if (attr_data->for_master) + nm_assert (attr_data == _team_attr_data_get (FALSE, attr_data->team_attr)); + nm_assert ((attr_data - team_attr_datas) == TEAM_ATTR_IDX (attr_data->for_port, attr_data->team_attr)); + nm_assert (attr_data->value_type > 0); + nm_assert (attr_data->field_offset < sizeof (NMTeamSetting)); + nm_assert (attr_data->js_keys_len == NM_PTRARRAY_LEN (attr_data->js_keys)); + nm_assert (attr_data->dbus_name); + { + static int checked = 0; + + if (checked == 0) { + checked = 1; + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) + _team_attr_data_ASSERT (attr_data); + } + } +#endif +} + +static gboolean +_team_attr_data_is_relevant (const TeamAttrData *attr_data, + gboolean is_port) +{ + return is_port + ? attr_data->for_port + : attr_data->for_master; +} + +static const TeamAttrData * +_team_attr_data_get (gboolean is_port, + NMTeamAttribute team_attr) +{ + const int idx = TEAM_ATTR_IDX (is_port, team_attr); + + nm_assert ( idx >= 0 + && idx < G_N_ELEMENTS (team_attr_datas)); + nm_assert (team_attr_datas[idx].team_attr == team_attr); + nm_assert (_team_attr_data_is_relevant (&team_attr_datas[idx], is_port)); + + return &team_attr_datas[idx]; +} + +static const TeamAttrData * +_team_attr_data_find_for_dbus_name (gboolean is_port, + const char *dbus_name) +{ + const TeamAttrData *attr_data; + + for (attr_data = team_attr_datas; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + if ( _team_attr_data_is_relevant (attr_data, is_port) + && nm_streq (dbus_name, attr_data->dbus_name)) + return attr_data; + } + return NULL; +} + +static const NMValueTypUnion * +_team_attr_data_get_default (const TeamAttrData *attr_data, + gboolean is_port, + const char *v_master_runner, + NMValueTypUnion *value_tmp) +{ + GPtrArray *v_ptrarray; + + /* unfortunately, the default certain values depends on other values :( + * + * For examle, master attributes depend on the "runner" setting. + * and port settings default to the ethtool link-watcher. */ + + if (is_port) { + + switch (attr_data->team_attr) { + case NM_TEAM_ATTRIBUTE_LINK_WATCHERS: { + static GPtrArray *volatile gl_arr = NULL; + +again_port_link_watchers: + v_ptrarray = g_atomic_pointer_get (&gl_arr); + if (G_UNLIKELY (!v_ptrarray)) { + v_ptrarray = g_ptr_array_new_full (1, (GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (v_ptrarray, nm_team_link_watcher_new_ethtool (0, 0, NULL)); + if (!g_atomic_pointer_compare_and_exchange (&gl_arr, NULL, v_ptrarray)) { + g_ptr_array_unref (v_ptrarray); + goto again_port_link_watchers; + } + } + return NM_VALUE_TYP_UNION_SET (value_tmp, v_ptrarray, v_ptrarray); + } + default: + break; + } + + } else { + + if (NM_IN_STRSET (v_master_runner, NULL, + NM_SETTING_TEAM_RUNNER_DEFAULT)) { + /* a runner %NULL is the same as NM_SETTING_TEAM_RUNNER_DEFAULT ("roundrobin"). + * In this case, the settings in attr_data are accurate. */ + return &attr_data->default_val; + } + + switch (attr_data->team_attr) { + case NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT); + break; + case NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT); + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_string, "same_all"); + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH: + if (NM_IN_STRSET (v_master_runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NM_SETTING_TEAM_RUNNER_LACP)) { + static GPtrArray *volatile gl_arr = NULL; + +again_master_runner_tx_hash: + v_ptrarray = g_atomic_pointer_get (&gl_arr); + if (G_UNLIKELY (!v_ptrarray)) { + v_ptrarray = g_ptr_array_sized_new (3); + g_ptr_array_add (v_ptrarray, "eth"); + g_ptr_array_add (v_ptrarray, "ipv4"); + g_ptr_array_add (v_ptrarray, "ipv6"); + if (!g_atomic_pointer_compare_and_exchange (&gl_arr, NULL, v_ptrarray)) { + g_ptr_array_unref (v_ptrarray); + goto again_master_runner_tx_hash; + } + } + return NM_VALUE_TYP_UNION_SET (value_tmp, v_ptrarray, v_ptrarray); + } + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL: + if (NM_IN_STRSET (v_master_runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NM_SETTING_TEAM_RUNNER_LACP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT); + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_bool, TRUE); + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT); + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, 0); + break; + case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY: + if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP)) + return NM_VALUE_TYP_UNION_SET (value_tmp, v_string, NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT); + break; + default: + break; + } + } + + return &attr_data->default_val; +} +static int +_team_attr_data_cmp (const TeamAttrData *attr_data, + gboolean is_port, + gconstpointer val_a, + gconstpointer val_b) +{ + const GPtrArray *v_ptrarray_a; + const GPtrArray *v_ptrarray_b; + guint len; + + _team_attr_data_ASSERT (attr_data); + nm_assert (val_a); + nm_assert (val_b); + + if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) + NM_CMP_RETURN (nm_value_type_cmp (attr_data->value_type, val_a, val_b)); + else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) { + v_ptrarray_a = *((const GPtrArray *const*) val_a); + v_ptrarray_b = *((const GPtrArray *const*) val_b); + len = v_ptrarray_a ? v_ptrarray_a->len : 0u; + NM_CMP_DIRECT (len, (v_ptrarray_b ? v_ptrarray_b->len : 0u)); + if (len > 0) { + NM_CMP_RETURN (nm_team_link_watchers_cmp ((const NMTeamLinkWatcher *const*) v_ptrarray_a->pdata, + (const NMTeamLinkWatcher *const*) v_ptrarray_b->pdata, + len, + FALSE)); + } + } else if ( !is_port + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) { + v_ptrarray_a = *((const GPtrArray *const*) val_a); + v_ptrarray_b = *((const GPtrArray *const*) val_b); + NM_CMP_RETURN (_nm_utils_strv_cmp_n (v_ptrarray_a ? (const char *const*) v_ptrarray_a->pdata : NULL, + v_ptrarray_a ? v_ptrarray_a->len : 0u, + v_ptrarray_b ? (const char *const*) v_ptrarray_b->pdata : NULL, + v_ptrarray_b ? v_ptrarray_b->len : 0u)); + } else + nm_assert_not_reached (); + return 0; +} + +static gboolean +_team_attr_data_equal (const TeamAttrData *attr_data, + gboolean is_port, + gconstpointer val_a, + gconstpointer val_b) +{ + return _team_attr_data_cmp (attr_data, is_port, val_a, val_b) == 0; +} + +static void +_team_attr_data_copy (const TeamAttrData *attr_data, + gboolean is_port, + gpointer dst, + gconstpointer src) +{ + GPtrArray *v_ptrarray_dst; + const GPtrArray *v_ptrarray_src; + GPtrArray *dst_array; + guint i, len; + + if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) + nm_value_type_copy (attr_data->value_type, dst, src); + else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) { + v_ptrarray_src = *((const GPtrArray *const *) src); + v_ptrarray_dst = *((GPtrArray **) dst); + len = (v_ptrarray_src ? v_ptrarray_src->len : 0u); + + if (len == 0) { + if (v_ptrarray_dst) + g_ptr_array_set_size (v_ptrarray_dst, 0); + } else { + dst_array = g_ptr_array_new_full (len, (GDestroyNotify) nm_team_link_watcher_unref); + for (i = 0; i < len; i++) { + if (v_ptrarray_src->pdata[i]) { + nm_team_link_watcher_ref (v_ptrarray_src->pdata[i]); + g_ptr_array_add (dst_array,v_ptrarray_src->pdata[i]); + } + } + if (v_ptrarray_dst) + g_ptr_array_unref (v_ptrarray_dst); + *((GPtrArray **) dst) = dst_array; + } + } else if ( !is_port + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) { + v_ptrarray_src = *((const GPtrArray *const *) src); + v_ptrarray_dst = *((GPtrArray **) dst); + len = (v_ptrarray_src ? v_ptrarray_src->len : 0u); + + if ( v_ptrarray_src + && v_ptrarray_src->len > 0) { + dst_array = g_ptr_array_new_full (v_ptrarray_src->len, g_free); + for (i = 0; i < v_ptrarray_src->len; i++) + g_ptr_array_add (dst_array, g_strdup (v_ptrarray_src->pdata[i])); + } else + dst_array = NULL; + if (v_ptrarray_dst) + g_ptr_array_unref (v_ptrarray_dst); + *((GPtrArray **) dst) = dst_array; + } else + nm_assert_not_reached (); +} + +static gboolean +_team_attr_data_is_default (const TeamAttrData *attr_data, + gboolean is_port, + const char *v_master_runner, + gconstpointer p_field) +{ + const NMValueTypUnion *default_value; + NMValueTypUnion value_tmp; + + _team_attr_data_ASSERT (attr_data); + nm_assert (p_field); + + default_value = _team_attr_data_get_default (attr_data, + is_port, + v_master_runner, + &value_tmp); + if (_team_attr_data_equal (attr_data, + is_port, + default_value, + p_field)) + return TRUE; + + if ( attr_data->value_type == NM_VALUE_TYPE_STRING + && default_value->v_string) { + const char *str0 = NULL; + + /* this is a string value, whose default is not NULL. In such a case, + * NULL is also treated like the default. */ + if (_team_attr_data_equal (attr_data, + is_port, + &str0, + p_field)) + return TRUE; + } + + return FALSE; +} + +static void +_team_attr_data_to_json (const TeamAttrData *attr_data, + gboolean is_port, + GString *gstr, + gconstpointer p_field) +{ + guint i; + + _team_attr_data_ASSERT (attr_data); + nm_assert (p_field); + + nm_json_aux_gstr_append_obj_name (gstr, + attr_data->js_keys[attr_data->js_keys_len - 1], + '\0'); + + if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) { + nm_value_type_to_json (attr_data->value_type, gstr, p_field); + return; + } + + if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) { + const GPtrArray *v_ptrarray = *((const GPtrArray *const*) p_field); + + if (!v_ptrarray) + g_string_append (gstr, "null"); + else if (v_ptrarray->len == 0) + g_string_append (gstr, "[ ]"); + else if (v_ptrarray->len == 1) + _link_watcher_to_json (v_ptrarray->pdata[0], gstr); + else { + g_string_append (gstr, "[ "); + for (i = 0; i < v_ptrarray->len; i++) { + if (i > 0) + nm_json_aux_gstr_append_delimiter (gstr); + _link_watcher_to_json (v_ptrarray->pdata[i], gstr); + } + g_string_append (gstr, " ]"); + } + return; + } + + if ( !is_port + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) { + const GPtrArray *v_ptrarray = *((const GPtrArray *const*) p_field); + + if (!v_ptrarray) + g_string_append (gstr, "null"); + else { + g_string_append (gstr, "[ "); + for (i = 0; i < v_ptrarray->len; i++) { + if (i > 0) + nm_json_aux_gstr_append_delimiter (gstr); + nm_json_aux_gstr_append_string (gstr, v_ptrarray->pdata[i]); + } + g_string_append (gstr, i > 0 ? " ]" : "]"); + } + return; + } + + nm_assert_not_reached (); +} + +/*****************************************************************************/ + +static void +_team_setting_ASSERT (const NMTeamSetting *self) +{ + nm_assert (self); + nm_assert (!self->d._js_str_need_synthetize || !self->d._js_str); +#if NM_MORE_ASSERTS > 2 + if (!self->d.strict_validated) { + nm_assert (!self->d._js_str_need_synthetize); + nm_assert (self->d._js_str); + } + nm_assert (self->d.link_watchers); + nm_assert ( self->d.is_port + || !self->d.master.runner_tx_hash + || self->d.master.runner_tx_hash->len > 0); +#endif +} + +static gpointer +_team_setting_get_field (const NMTeamSetting *self, + const TeamAttrData *attr_data) +{ + _team_setting_ASSERT (self); + _team_attr_data_ASSERT (attr_data); + nm_assert (_team_attr_data_is_relevant (attr_data, self->d.is_port)); + +#if NM_MORE_ASSERTS > 5 + if ( attr_data->for_master + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO ) + nm_assert ((gpointer) (((char *) self) + attr_data->field_offset) == &self->d.master.runner_sys_prio); +#endif + + return (((char *) self) + attr_data->field_offset); +} + +static guint32 +_team_setting_attribute_changed (NMTeamSetting *self, + NMTeamAttribute team_attr, + gboolean changed) +{ + guint32 changed_flags; + + nm_assert (_team_attr_data_get (self->d.is_port, team_attr)); + + if (!changed) { + /* a regular attribute was set, but the value did not change. + * + * If we previously were in non-strict mode, then + * + * - switch to strict-mode. Clearly the user set a regular attribute + * and hence now we want to validate the setting. + * + * - clear the JSON string. We need to regenerate it. + */ + if (self->_data_priv.strict_validated) + return 0; + changed_flags = nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); + } else { + changed_flags = nm_team_attribute_to_flags (team_attr) + | nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); + } + + nm_clear_g_free ((char **) &self->_data_priv._js_str); + self->_data_priv.strict_validated = TRUE; + self->_data_priv._js_str_need_synthetize = TRUE; + + return changed_flags; +} + +static void +_team_setting_field_to_json (const NMTeamSetting *self, + GString *gstr, + gboolean prepend_delimiter, + NMTeamAttribute team_attr) +{ + const TeamAttrData *attr_data = _team_attr_data_get (self->d.is_port, team_attr); + + if (prepend_delimiter) + nm_json_aux_gstr_append_delimiter (gstr); + _team_attr_data_to_json (attr_data, + self->d.is_port, + gstr, + _team_setting_get_field (self, attr_data)); +} + +static gboolean +_team_setting_fields_to_json_maybe (const NMTeamSetting *self, + GString *gstr, + gboolean prepend_delimiter, + const bool is_default_lst[static _NM_TEAM_ATTRIBUTE_NUM], + const NMTeamAttribute *team_attrs_lst, + gsize team_attrs_lst_len) +{ + gsize i; + gboolean any_added = FALSE; + + for (i = 0; i < team_attrs_lst_len; i++) { + NMTeamAttribute team_attr = team_attrs_lst[i]; + + if (is_default_lst[team_attr]) + continue; + + _team_setting_field_to_json (self, gstr, prepend_delimiter, team_attr); + any_added = TRUE; + prepend_delimiter = TRUE; + } + return any_added; +} + +static guint32 +_team_setting_set (NMTeamSetting *self, + gboolean modify, + const bool *has_lst, + const NMValueTypUnion *val_lst) +{ + guint32 changed_flags = 0; + const TeamAttrData *attr_data; + const char *v_master_runner; + + nm_assert ((!has_lst) == (!val_lst)); + + if (!self->d.is_port) { + if ( has_lst + && has_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER]) + v_master_runner = val_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER].v_string; + else { + nm_assert (nm_streq0 (_team_attr_data_get (FALSE, NM_TEAM_ATTRIBUTE_MASTER_RUNNER)->default_val.v_string, + NM_SETTING_TEAM_RUNNER_DEFAULT)); + v_master_runner = NM_SETTING_TEAM_RUNNER_DEFAULT; + } + } else + v_master_runner = NULL; + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + NMValueTypUnion value_tmp; + const NMValueTypUnion *p_val; + gconstpointer p_field; + + if (!_team_attr_data_is_relevant (attr_data, self->d.is_port)) + continue; + + if ( has_lst + && has_lst[attr_data->team_attr]) + p_val = &val_lst[attr_data->team_attr]; + else { + p_val = _team_attr_data_get_default (attr_data, + self->d.is_port, + v_master_runner, + &value_tmp); + } + + p_field = _team_setting_get_field (self, attr_data); + + if (!_team_attr_data_equal (attr_data, + self->d.is_port, + p_val, + p_field)) { + if (modify) { + _team_attr_data_copy (attr_data, + self->d.is_port, + (gpointer) p_field, + p_val); + } + changed_flags |= nm_team_attribute_to_flags (attr_data->team_attr); + } + } + + return changed_flags; +} + +static guint32 +_team_setting_check_default (const NMTeamSetting *self) +{ + return _team_setting_set ((NMTeamSetting *) self, FALSE, NULL, NULL); +} + +static guint32 +_team_setting_set_default (NMTeamSetting *self) +{ + return _team_setting_set (self, TRUE, NULL, NULL); +} + +/*****************************************************************************/ + +gconstpointer +_nm_team_setting_value_get (const NMTeamSetting *self, + NMTeamAttribute team_attr, + NMValueType value_type) +{ + const TeamAttrData *attr_data = _team_attr_data_get (self->d.is_port, team_attr); + + nm_assert (value_type == attr_data->value_type); + + return _team_setting_get_field (self, attr_data); +} + +static guint32 +_team_setting_value_set (NMTeamSetting *self, + NMTeamAttribute team_attr, + NMValueType value_type, + gconstpointer val) +{ + const TeamAttrData *attr_data; + gpointer p_field; + + nm_assert (self); + + attr_data = _team_attr_data_get (self->d.is_port, team_attr); + + nm_assert (val); + nm_assert (value_type == attr_data->value_type); + + p_field = _team_setting_get_field (self, attr_data); + + if (nm_value_type_equal (attr_data->value_type, p_field, val)) + return 0u; + nm_value_type_copy (attr_data->value_type, p_field, val); + return nm_team_attribute_to_flags (team_attr); +} + +guint32 +_nm_team_setting_value_set (NMTeamSetting *self, + NMTeamAttribute team_attr, + NMValueType value_type, + gconstpointer val) +{ + return _team_setting_attribute_changed (self, + team_attr, + (_team_setting_value_set (self, + team_attr, + value_type, + val) != 0u)); +} + +guint32 +nm_team_setting_value_link_watchers_add (NMTeamSetting *self, + const NMTeamLinkWatcher *link_watcher) +{ + guint i; + + for (i = 0; i < self->d.link_watchers->len; i++) { + if (nm_team_link_watcher_equal (self->d.link_watchers->pdata[i], link_watcher)) + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, FALSE); + } + g_ptr_array_add ((GPtrArray *) self->d.link_watchers, + _nm_team_link_watcher_ref ((NMTeamLinkWatcher *) link_watcher)); + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, TRUE); +} + +guint32 +nm_team_setting_value_link_watchers_remove_by_value (NMTeamSetting *self, + const NMTeamLinkWatcher *link_watcher) +{ + guint i; + + for (i = 0; i < self->d.link_watchers->len; i++) { + if (nm_team_link_watcher_equal (self->d.link_watchers->pdata[i], + link_watcher)) + return nm_team_setting_value_link_watchers_remove (self, i); + } + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, FALSE); +} + +guint32 +nm_team_setting_value_link_watchers_remove (NMTeamSetting *self, + guint idx) +{ + g_ptr_array_remove_index ((GPtrArray *) self->d.link_watchers, idx); + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, TRUE); +} + +static guint32 +_team_setting_value_link_watchers_set_list (NMTeamSetting *self, + const NMTeamLinkWatcher *const*arr, + guint len) +{ + if ( self->d.link_watchers->len == len + && nm_team_link_watchers_cmp ((const NMTeamLinkWatcher *const*) self->d.link_watchers->pdata, + arr, + len, + FALSE) == 0) + return 0; + + if (len == 0) + g_ptr_array_set_size ((GPtrArray *) self->d.link_watchers, 0); + else { + _nm_unused gs_unref_ptrarray GPtrArray *old_val_destroy = NULL; + guint i; + + old_val_destroy = (GPtrArray *) g_steal_pointer (&self->_data_priv.link_watchers); + + self->_data_priv.link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + + for (i = 0; i < len; i++) { + if (arr[i]) { + g_ptr_array_add ((GPtrArray *) self->d.link_watchers, + _nm_team_link_watcher_ref ((NMTeamLinkWatcher *) arr[i])); + } + } + } + + return nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_LINK_WATCHERS); +} + +guint32 +nm_team_setting_value_link_watchers_set_list (NMTeamSetting *self, + const NMTeamLinkWatcher *const*arr, + guint len) +{ + return _team_setting_attribute_changed (self, + NM_TEAM_ATTRIBUTE_LINK_WATCHERS, + (_team_setting_value_link_watchers_set_list (self, + arr, + len) != 0u)); +} + +/*****************************************************************************/ + +guint32 +nm_team_setting_value_master_runner_tx_hash_add (NMTeamSetting *self, + const char *txhash) +{ + guint i; + + if (!self->d.master.runner_tx_hash) + self->_data_priv.master.runner_tx_hash = g_ptr_array_new_with_free_func (g_free); + else { + for (i = 0; i < self->d.master.runner_tx_hash->len; i++) { + if (nm_streq (txhash, self->d.master.runner_tx_hash->pdata[i])) + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, FALSE); + } + } + g_ptr_array_add ((GPtrArray *) self->d.master.runner_tx_hash, g_strdup (txhash)); + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, TRUE); +} + +guint32 +nm_team_setting_value_master_runner_tx_hash_remove (NMTeamSetting *self, + guint idx) +{ + g_ptr_array_remove_index ((GPtrArray *) self->d.master.runner_tx_hash, idx); + return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, TRUE); +} + +static guint32 +_team_setting_value_master_runner_tx_hash_set_list (NMTeamSetting *self, + const char *const*arr, + guint len) +{ + _nm_unused gs_unref_ptrarray GPtrArray *old_val_destroy = NULL; + guint i; + + if (_nm_utils_strv_cmp_n (self->d.master.runner_tx_hash ? (const char *const*) self->d.master.runner_tx_hash->pdata : NULL, + self->d.master.runner_tx_hash ? self->d.master.runner_tx_hash->len : 0u, + arr, + len) == 0) + return 0u; + + old_val_destroy = (GPtrArray *) g_steal_pointer (&self->_data_priv.master.runner_tx_hash); + + for (i = 0; i < len; i++) { + if (!arr[i]) + continue; + if (!self->d.master.runner_tx_hash) + self->_data_priv.master.runner_tx_hash = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add ((GPtrArray *) self->d.master.runner_tx_hash, g_strdup (arr[i])); + } + + return nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH); +} + +guint32 +nm_team_setting_value_master_runner_tx_hash_set_list (NMTeamSetting *self, + const char *const*arr, + guint len) +{ + return _team_setting_attribute_changed (self, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, + (_team_setting_value_master_runner_tx_hash_set_list (self, + arr, + len) != 0u)); +} + +/*****************************************************************************/ + +#define _LINK_WATCHER_ATTR_GET(args, link_watcher_attribute, _value_type) \ + ({ \ + const NMValueTypUnioMaybe *const _args = (args); \ + \ + nm_assert (link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \ + \ + _args[(link_watcher_attribute)].has \ + ? &_args[(link_watcher_attribute)].val \ + : &link_watcher_attr_datas[(link_watcher_attribute)].default_val; \ + }) +#define _LINK_WATCHER_ATTR_GET_BOOL(args, link_watcher_attribute) (_LINK_WATCHER_ATTR_GET (args, link_watcher_attribute, NM_VALUE_TYPE_BOOL )->v_bool) +#define _LINK_WATCHER_ATTR_GET_INT(args, link_watcher_attribute) (_LINK_WATCHER_ATTR_GET (args, link_watcher_attribute, NM_VALUE_TYPE_INT )->v_int) +#define _LINK_WATCHER_ATTR_GET_STRING(args, link_watcher_attribute) (_LINK_WATCHER_ATTR_GET (args, link_watcher_attribute, NM_VALUE_TYPE_STRING )->v_string) + +#define _LINK_WATCHER_ATTR_SET(args, link_watcher_attribute, _value_type, c_type, val) \ + ({ \ + nm_assert (link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \ + \ + NM_VALUE_TYP_UNIO_MAYBE_SET (&(args)[(link_watcher_attribute)], c_type, (val)); \ + }) +#define _LINK_WATCHER_ATTR_SET_BOOL(args, link_watcher_attribute, val) _LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_BOOL, v_bool, (val)) +#define _LINK_WATCHER_ATTR_SET_INT(args, link_watcher_attribute, val) _LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_INT, v_int, (val)) +#define _LINK_WATCHER_ATTR_SET_STRING(args, link_watcher_attribute, val) _LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_STRING, v_string, (val)) + +static void +_link_watcher_to_json (const NMTeamLinkWatcher *link_watcher, + GString *gstr) +{ + NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)] = { }; + NMTeamLinkWatcherArpPingFlags v_arp_ping_flags; + const char *v_name; + int i; + + if (!link_watcher) { + g_string_append (gstr, "null"); + return; + } + + v_name = nm_team_link_watcher_get_name (link_watcher); + + g_string_append (gstr, "{ "); + + nm_json_aux_gstr_append_obj_name (gstr, "name", '\0'); + nm_json_aux_gstr_append_string (gstr, v_name); + + if (nm_streq (v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { + _LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_UP, nm_team_link_watcher_get_delay_up (link_watcher)); + _LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN, nm_team_link_watcher_get_delay_down (link_watcher)); + } else if (NM_IN_STRSET (v_name, NM_TEAM_LINK_WATCHER_NSNA_PING, + NM_TEAM_LINK_WATCHER_ARP_PING)) { + _LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT, nm_team_link_watcher_get_init_wait (link_watcher)); + _LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_INTERVAL, nm_team_link_watcher_get_interval (link_watcher)); + _LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX, nm_team_link_watcher_get_missed_max (link_watcher)); + _LINK_WATCHER_ATTR_SET_STRING (args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST, nm_team_link_watcher_get_target_host (link_watcher)); + if (nm_streq (v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) { + v_arp_ping_flags = nm_team_link_watcher_get_flags (link_watcher); + _LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_VLANID, nm_team_link_watcher_get_vlanid (link_watcher)); + _LINK_WATCHER_ATTR_SET_STRING (args, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, nm_team_link_watcher_get_source_host (link_watcher)); + _LINK_WATCHER_ATTR_SET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, NM_FLAGS_HAS (v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE)); + _LINK_WATCHER_ATTR_SET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, NM_FLAGS_HAS (v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE)); + _LINK_WATCHER_ATTR_SET_BOOL (args, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS, NM_FLAGS_HAS (v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS)); + } + } + + for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++) { + const NMValueTypUnioMaybe *p_val = &args[i]; + const LinkWatcherAttrData *attr_data = &link_watcher_attr_datas[i]; + + if (!p_val->has) + continue; + if (nm_value_type_equal (attr_data->value_type, &attr_data->default_val, &p_val->val)) + continue; + nm_json_aux_gstr_append_delimiter (gstr); + nm_json_aux_gstr_append_obj_name (gstr, attr_data->js_key, '\0'); + nm_value_type_to_json (attr_data->value_type, gstr, &p_val->val); + } + + g_string_append (gstr, "}"); +} + +#if WITH_JSON_VALIDATION +static NMTeamLinkWatcher * +_link_watcher_from_json (const json_t *root_js_obj, + gboolean *out_unrecognized_content) +{ + NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)] = { }; + const char *j_key; + json_t *j_val; + const char *v_name; + NMTeamLinkWatcher *result = NULL; + + if (!json_is_object (root_js_obj)) + goto fail; + + json_object_foreach ((json_t *) root_js_obj, j_key, j_val) { + const LinkWatcherAttrData *attr_data = NULL; + NMValueTypUnioMaybe *parse_result; + + if (j_key) { + int i; + + for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++) { + if (nm_streq (link_watcher_attr_datas[i].js_key, j_key)) { + attr_data = &link_watcher_attr_datas[i]; + break; + } + } + } + if (!attr_data) { + *out_unrecognized_content = TRUE; + continue; + } + + parse_result = &args[attr_data->link_watcher_attr]; + + if (parse_result->has) + *out_unrecognized_content = TRUE; + + if (!nm_value_type_from_json (attr_data->value_type, j_val, &parse_result->val)) + *out_unrecognized_content = TRUE; + else + parse_result->has = TRUE; + } + +#define _PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES(_parse_results, ...) \ + ({ \ + int _i; \ + \ + for (_i = 0; _i < (int) G_N_ELEMENTS ((_parse_results)); _i++) { \ + if ( (_parse_results)[_i].has \ + && !NM_IN_SET ((LinkWatcherAttribute) _i, LINK_WATCHER_ATTRIBUTE_NAME, \ + __VA_ARGS__)) \ + break; \ + } \ + \ + (_i == (int) G_N_ELEMENTS ((_parse_results))); \ + }) + + v_name = _LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_NAME); + + if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { + if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES (args, + LINK_WATCHER_ATTRIBUTE_DELAY_UP, + LINK_WATCHER_ATTRIBUTE_DELAY_DOWN)) + *out_unrecognized_content = TRUE; + result = nm_team_link_watcher_new_ethtool (_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_UP), + _LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN), + NULL); + } else if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_NSNA_PING)) { + if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES (args, + LINK_WATCHER_ATTRIBUTE_INIT_WAIT, + LINK_WATCHER_ATTRIBUTE_INTERVAL, + LINK_WATCHER_ATTRIBUTE_MISSED_MAX, + LINK_WATCHER_ATTRIBUTE_TARGET_HOST)) + *out_unrecognized_content = TRUE; + result = nm_team_link_watcher_new_nsna_ping (_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT), + _LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INTERVAL), + _LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX), + _LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST), + NULL); + } else if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) { + NMTeamLinkWatcherArpPingFlags v_flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE; + + if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES (args, + LINK_WATCHER_ATTRIBUTE_INIT_WAIT, + LINK_WATCHER_ATTRIBUTE_INTERVAL, + LINK_WATCHER_ATTRIBUTE_MISSED_MAX, + LINK_WATCHER_ATTRIBUTE_VLANID, + LINK_WATCHER_ATTRIBUTE_TARGET_HOST, + LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, + LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, + LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, + LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS)) + *out_unrecognized_content = TRUE; + + if (_LINK_WATCHER_ATTR_GET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE)) + v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; + if (_LINK_WATCHER_ATTR_GET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE)) + v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE; + if (_LINK_WATCHER_ATTR_GET_BOOL (args, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS)) + v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; + + result = nm_team_link_watcher_new_arp_ping2 (_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT), + _LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INTERVAL), + _LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX), + _LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_VLANID), + _LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST), + _LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST), + v_flags, + NULL); + } + + if (result) + return result; +fail: + *out_unrecognized_content = TRUE; + return NULL; +} +#endif + +/*****************************************************************************/ + +const char * +nm_team_setting_config_get (const NMTeamSetting *self) +{ + char *js_str; + + nm_assert (self); + + if (G_LIKELY (!self->d._js_str_need_synthetize)) + return self->d._js_str; + + nm_assert (!self->d._js_str); + nm_assert (self->d.strict_validated); + + if (_team_setting_check_default (self) == 0) { + /* the default is set. We signal this as a NULL JSON string. + * Nothing to do. */ + js_str = NULL; + } else { + const TeamAttrData *attr_data; + GString *gstr; + bool is_default_lst[_NM_TEAM_ATTRIBUTE_NUM] = { FALSE, }; + gboolean list_is_empty = TRUE; + const char *v_master_runner; + + gstr = g_string_new (NULL); + + g_string_append (gstr, "{ "); + + v_master_runner = self->d.is_port + ? NULL + : self->d.master.runner; + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + if (_team_attr_data_is_relevant (attr_data, self->d.is_port)) { + is_default_lst[attr_data->team_attr] = _team_attr_data_is_default (attr_data, + self->d.is_port, + v_master_runner, + _team_setting_get_field (self, attr_data)); + } + } + + if (self->d.is_port) { + static const NMTeamAttribute attr_lst_port[] = { + NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID, + NM_TEAM_ATTRIBUTE_PORT_PRIO, + NM_TEAM_ATTRIBUTE_PORT_STICKY, + NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO, + NM_TEAM_ATTRIBUTE_PORT_LACP_KEY, + }; + + if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty, is_default_lst, attr_lst_port, G_N_ELEMENTS (attr_lst_port))) + list_is_empty = FALSE; + } else { + + if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY]) { + static const NMTeamAttribute attr_lst_runner_pt1[] = { + NM_TEAM_ATTRIBUTE_MASTER_RUNNER, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, + }; + static const NMTeamAttribute attr_lst_runner_pt2[] = { + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL, + }; + static const NMTeamAttribute attr_lst_runner_pt3[] = { + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY, + }; + gboolean list_is_empty2 = TRUE; + + if (!list_is_empty) + nm_json_aux_gstr_append_delimiter (gstr); + nm_json_aux_gstr_append_obj_name (gstr, "runner", '{'); + + if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty2, is_default_lst, attr_lst_runner_pt1, G_N_ELEMENTS (attr_lst_runner_pt1))) + list_is_empty2 = FALSE; + + if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL]) { + if (!list_is_empty2) + nm_json_aux_gstr_append_delimiter (gstr); + nm_json_aux_gstr_append_obj_name (gstr, "tx_balancer", '{'); + if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, is_default_lst, attr_lst_runner_pt2, G_N_ELEMENTS (attr_lst_runner_pt2))) + nm_assert_not_reached (); + g_string_append (gstr, " }"); + list_is_empty2 = FALSE; + } + + if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty2, is_default_lst, attr_lst_runner_pt3, G_N_ELEMENTS (attr_lst_runner_pt3))) + list_is_empty2 = FALSE; + + nm_assert (!list_is_empty2); + g_string_append (gstr, " }"); + list_is_empty = FALSE; + } + + if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL]) { + static const NMTeamAttribute attr_lst_notify_peers[] = { + NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT, + NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL, + }; + + if (!list_is_empty) + nm_json_aux_gstr_append_delimiter (gstr); + nm_json_aux_gstr_append_obj_name (gstr, "notify_peers", '{'); + if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, is_default_lst, attr_lst_notify_peers, G_N_ELEMENTS (attr_lst_notify_peers))) + nm_assert_not_reached (); + g_string_append (gstr, " }"); + list_is_empty = FALSE; + } + + if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT] + || !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL]) { + static const NMTeamAttribute attr_lst_notify_peers[] = { + NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT, + NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL, + }; + + if (!list_is_empty) + nm_json_aux_gstr_append_delimiter (gstr); + nm_json_aux_gstr_append_obj_name (gstr, "mcast_rejoin", '{'); + if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, is_default_lst, attr_lst_notify_peers, G_N_ELEMENTS (attr_lst_notify_peers))) + nm_assert_not_reached (); + g_string_append (gstr, " }"); + list_is_empty = FALSE; + } + } + + if (!is_default_lst[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) { + _team_setting_field_to_json (self, gstr, !list_is_empty, NM_TEAM_ATTRIBUTE_LINK_WATCHERS); + list_is_empty = FALSE; + } + if (!list_is_empty) + g_string_append (gstr, " }"); + + js_str = g_string_free (gstr, list_is_empty);; + } + + /* mutate the constant object. In C++ speak, these fields are "mutable". + * That is because we construct the JSON string lazily/on-demand. */ + *((char **) &self->_data_priv._js_str) = js_str; + *((bool *) &self->_data_priv._js_str_need_synthetize) = FALSE; + + return self->d._js_str; +} + +/*****************************************************************************/ + +#if WITH_JSON_VALIDATION +static gboolean +_attr_data_match_keys (const TeamAttrData *attr_data, + const char *const*keys, + guint8 n_keys) +{ + guint8 i; + + _team_attr_data_ASSERT (attr_data); + nm_assert (keys); + nm_assert (n_keys > 0); + nm_assert (({ + gboolean all_non_null = TRUE; + + for (i = 0; i < n_keys; i++) + all_non_null = all_non_null && keys[i] && keys[i][0] != '\0'; + all_non_null; + })); + + if (attr_data->js_keys_len < n_keys) + return FALSE; + for (i = 0; i < n_keys; i++) { + if (!nm_streq (keys[i], attr_data->js_keys[i])) + return FALSE; + } + return TRUE; +} + +static const TeamAttrData * +_attr_data_find_by_json_key (gboolean is_port, + const char *const*keys, + guint8 n_keys) +{ + const TeamAttrData *attr_data; + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + if ( _team_attr_data_is_relevant (attr_data, is_port) + && _attr_data_match_keys (attr_data, keys, n_keys)) + return attr_data; + } + + return NULL; +} + +static void +_js_parse_locate_keys (NMTeamSetting *self, + json_t *root_js_obj, + json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], + gboolean *out_unrecognized_content) +{ + const char *keys[3]; + const char *cur_key1; + const char *cur_key2; + const char *cur_key3; + json_t *cur_val1; + json_t *cur_val2; + json_t *cur_val3; + +#define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content) \ + ({ \ + const TeamAttrData *_attr_data; \ + gboolean _handled = FALSE; \ + \ + (_keys)[(_level) - 1] = (_cur_key); \ + _attr_data = _attr_data_find_by_json_key ((_self)->d.is_port, (_keys), (_level)); \ + if ( _attr_data \ + && _attr_data->js_keys_len == (_level)) { \ + if ((_found_keys)[_attr_data->team_attr]) \ + *(_out_unrecognized_content) = TRUE; \ + (_found_keys)[_attr_data->team_attr] = (_cur_val); \ + _handled = TRUE; \ + } else if ( !_attr_data \ + || !json_is_object ((_cur_val))) { \ + *(_out_unrecognized_content) = TRUE; \ + _handled = TRUE; \ + } \ + _handled; \ + }) + + json_object_foreach (root_js_obj, cur_key1, cur_val1) { + if (!_handle (self, cur_key1, cur_val1, keys, 1, found_keys, out_unrecognized_content)) { + json_object_foreach (cur_val1, cur_key2, cur_val2) { + if (!_handle (self, cur_key2, cur_val2, keys, 2, found_keys, out_unrecognized_content)) { + json_object_foreach (cur_val2, cur_key3, cur_val3) { + if (!_handle (self, cur_key3, cur_val3, keys, 3, found_keys, out_unrecognized_content)) + *out_unrecognized_content = TRUE; + } + } + } + } + } + +#undef _handle +} + +static void +_js_parse_unpack (gboolean is_port, + json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM], + bool out_has_lst[static _NM_TEAM_ATTRIBUTE_NUM], + NMValueTypUnion out_val_lst[static _NM_TEAM_ATTRIBUTE_NUM], + gboolean *out_unrecognized_content, + GPtrArray **out_ptr_array_link_watchers_free, + GPtrArray **out_ptr_array_master_runner_tx_hash_free) +{ + const TeamAttrData *attr_data; + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + NMValueTypUnion *p_out_val; + gboolean valid = FALSE; + json_t *arg_js_obj; + + if (!_team_attr_data_is_relevant (attr_data, is_port)) + continue; + + nm_assert (!out_has_lst[attr_data->team_attr]); + + arg_js_obj = found_keys[attr_data->team_attr]; + if (!arg_js_obj) + continue; + + p_out_val = &out_val_lst[attr_data->team_attr]; + + if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) + valid = nm_value_type_from_json (attr_data->value_type, arg_js_obj, p_out_val); + else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) { + GPtrArray *link_watchers = NULL; + NMTeamLinkWatcher *link_watcher; + + nm_assert (out_ptr_array_link_watchers_free && !*out_ptr_array_link_watchers_free); + if (json_is_array (arg_js_obj)) { + gsize i, len; + + len = json_array_size (arg_js_obj); + link_watchers = g_ptr_array_new_full (len, (GDestroyNotify) nm_team_link_watcher_unref); + for (i = 0; i < len; i++) { + link_watcher = _link_watcher_from_json (json_array_get (arg_js_obj, i), + out_unrecognized_content); + if (link_watcher) + g_ptr_array_add (link_watchers, link_watcher); + } + } else { + link_watcher = _link_watcher_from_json (arg_js_obj, + out_unrecognized_content); + if (link_watcher) { + link_watchers = g_ptr_array_new_full (1, (GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, link_watcher); + } + } + if (link_watchers) { + valid = TRUE; + p_out_val->v_ptrarray = link_watchers; + *out_ptr_array_link_watchers_free = link_watchers; + } + } else if ( !is_port + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) { + GPtrArray *strv = NULL; + + nm_assert (out_ptr_array_master_runner_tx_hash_free && !*out_ptr_array_master_runner_tx_hash_free); + if (json_is_array (arg_js_obj)) { + gsize i, len; + + len = json_array_size (arg_js_obj); + if (len > 0) { + strv = g_ptr_array_sized_new (len); + for (i = 0; i < len; i++) { + const char *v_string; + + if ( nm_jansson_json_as_string (json_array_get (arg_js_obj, i), + &v_string) <= 0 + || !v_string + || v_string[0] == '\0') { + /* we remember that there was some invalid content, but parts of the + * list could still be parsed. */ + *out_unrecognized_content = TRUE; + continue; + } + g_ptr_array_add (strv, (char *) v_string); + } + } + valid = TRUE; + *out_ptr_array_master_runner_tx_hash_free = strv; + } + p_out_val->v_ptrarray = strv; + } else + nm_assert_not_reached (); + + out_has_lst[attr_data->team_attr] = valid; + if (!valid) + *out_unrecognized_content = TRUE; + } +} +#endif + +guint32 +nm_team_setting_config_set (NMTeamSetting *self, const char *js_str) +{ + guint32 changed_flags = 0; + gboolean do_set_default = TRUE; + gboolean new_strict_validated = FALSE; + gboolean new_js_str_invalid = FALSE; + + _team_setting_ASSERT (self); + + if ( !js_str + || js_str[0] == '\0') { + changed_flags = _team_setting_set_default (self); + if ( changed_flags != 0 + || !nm_streq0 (js_str, self->d._js_str)) + changed_flags |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); + nm_clear_g_free ((char **) &self->_data_priv._js_str); + self->_data_priv._js_str = g_strdup (js_str); + self->_data_priv._js_str_need_synthetize = FALSE; + self->_data_priv.strict_validated = TRUE; + self->_data_priv.js_str_invalid = FALSE; + return changed_flags; + } + + if ( self->d._js_str + && nm_streq (js_str, self->d._js_str)) + return 0; + + changed_flags |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); + +#if WITH_JSON_VALIDATION + if (js_str[0] != '\0') { + nm_auto_decref_json json_t *root_js_obj = NULL; + + if (nm_jansson_load ()) + root_js_obj = json_loads (js_str, 0, NULL); + + if ( !root_js_obj + || !json_is_object (root_js_obj)) + new_js_str_invalid = TRUE; + else { + gboolean unrecognized_content = FALSE; + bool has_lst[_NM_TEAM_ATTRIBUTE_NUM] = { FALSE, }; + NMValueTypUnion val_lst[_NM_TEAM_ATTRIBUTE_NUM]; + json_t *found_keys[_NM_TEAM_ATTRIBUTE_NUM] = { NULL, }; + gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL; + gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free = NULL; + + _js_parse_locate_keys (self, + root_js_obj, + found_keys, + &unrecognized_content); + + _js_parse_unpack (self->d.is_port, + found_keys, + has_lst, + val_lst, + &unrecognized_content, + &ptr_array_link_watchers_free, + &ptr_array_master_runner_tx_hash_free); + + do_set_default = FALSE; + + changed_flags |= _team_setting_set (self, + TRUE, + has_lst, + val_lst); + + if ( !unrecognized_content + && _team_setting_verify (self, NULL)) { + /* if we could parse everything without unexpected/unknown data, + * we switch into strictly validating mode. */ + new_strict_validated = TRUE; + } + } + } + +#endif + + if (do_set_default) + changed_flags |= _team_setting_set_default (self); + + self->_data_priv.strict_validated = new_strict_validated; + self->_data_priv._js_str_need_synthetize = FALSE; + self->_data_priv.js_str_invalid = new_js_str_invalid; + g_free ((char *) self->_data_priv._js_str); + self->_data_priv._js_str = g_strdup (js_str); + + return changed_flags; +} + +/*****************************************************************************/ + +static void +_team_setting_prefix_error (const NMTeamSetting *self, + GError **error, + const char *prop_name_master, + const char *prop_name_port) +{ + _team_setting_ASSERT (self); + nm_assert ( self->d.is_port + ? (!!prop_name_port) + : (!!prop_name_master)); + g_prefix_error (error, + "%s.%s: ", + self->d.is_port + ? NM_SETTING_TEAM_PORT_SETTING_NAME + : NM_SETTING_TEAM_SETTING_NAME, + self->d.is_port + ? prop_name_master + : prop_name_port); +} + +static gboolean +_team_setting_verify (const NMTeamSetting *self, + GError **error) +{ + guint i; + const char *js_str; + + if (!self->d.is_port) { + if (!self->d.master.runner) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, + _("missing runner")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_RUNNER, NULL); + return FALSE; + } + if ( self->d.master.runner + && g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_BROADCAST) != 0 + && g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_ROUNDROBIN) != 0 + && g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_RANDOM) != 0 + && g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP) != 0 + && g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) != 0 + && g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_LACP) != 0) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, + _("invalid runner \"%s\""), self->d.master.runner); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_RUNNER, NULL); + return FALSE; + } + + if (self->d.master.runner_tx_hash) { + for (i = 0; i < self->d.master.runner_tx_hash->len; i++) { + const char *val = self->d.master.runner_tx_hash->pdata[i]; + + if (!val[0]) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, + _("invalid runner.tx-hash")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_RUNNER_TX_HASH, NULL); + return FALSE; + } + } + } + } + + for (i = 0; i < self->d.link_watchers->len; i++) { + NMTeamLinkWatcher *link_watcher = self->d.link_watchers->pdata[i]; + const char *name = nm_team_link_watcher_get_name (link_watcher); + + if (!NM_IN_STRSET (name, + NM_TEAM_LINK_WATCHER_ETHTOOL, + NM_TEAM_LINK_WATCHER_ARP_PING, + NM_TEAM_LINK_WATCHER_NSNA_PING)) { + if (!name) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing link watcher name")); + } else { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, + _("unknown link watcher \"%s\""), name); + } + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_LINK_WATCHERS, NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return FALSE; + } + + if ( NM_IN_STRSET (name, + NM_TEAM_LINK_WATCHER_ARP_PING, + NM_TEAM_LINK_WATCHER_NSNA_PING) + && !nm_team_link_watcher_get_target_host (link_watcher)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing target host")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_LINK_WATCHERS, NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return FALSE; + } + if ( nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) + && !nm_team_link_watcher_get_source_host (link_watcher)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing source address")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_LINK_WATCHERS, NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return FALSE; + } + } + + /* we always materialize the JSON string. That is because we want to validate the + * string length of the resulting JSON. */ + js_str = nm_team_setting_config_get (self); + + if (js_str) { + if (strlen (js_str) > 1*1024*1024) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("team config exceeds size limit")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_CONFIG, NM_SETTING_TEAM_PORT_CONFIG); + return FALSE; + } + if (!g_utf8_validate (js_str, -1, NULL)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("team config is not valid UTF-8")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_CONFIG, NM_SETTING_TEAM_PORT_CONFIG); + return FALSE; + } + if (self->d.js_str_invalid) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid json")); + _team_setting_prefix_error (self, error, NM_SETTING_TEAM_CONFIG, NM_SETTING_TEAM_PORT_CONFIG); + return FALSE; + } + } + + return TRUE; +} + +gboolean +nm_team_setting_verify (const NMTeamSetting *self, + GError **error) +{ + return _team_setting_verify (self, error); +} + +/*****************************************************************************/ + +int +nm_team_setting_cmp (const NMTeamSetting *self_a, + const NMTeamSetting *self_b, + gboolean ignore_js_str) +{ + const TeamAttrData *attr_data; + + NM_CMP_SELF (self_a, self_b); + + NM_CMP_FIELD_UNSAFE (self_a, self_b, d.is_port); + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + if (_team_attr_data_is_relevant (attr_data, self_a->d.is_port)) { + NM_CMP_RETURN (_team_attr_data_cmp (attr_data, + self_a->d.is_port, + _team_setting_get_field (self_a, attr_data), + _team_setting_get_field (self_b, attr_data))); + } + } + + if (!ignore_js_str) { + NM_CMP_DIRECT_STRCMP0 (nm_team_setting_config_get (self_a), + nm_team_setting_config_get (self_b)); + } + + return 0; +} + +guint32 +nm_team_setting_reset (NMTeamSetting *self, + const NMTeamSetting *src) +{ + const TeamAttrData *attr_data; + guint32 changed; + + _team_setting_ASSERT (self); + _team_setting_ASSERT (src); + nm_assert (self->d.is_port == src->d.is_port); + + if (self == src) + return 0; + + changed = 0; + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + if (!_team_attr_data_is_relevant (attr_data, self->d.is_port)) + continue; + if (_team_attr_data_equal (attr_data, + self->d.is_port, + _team_setting_get_field (self, attr_data), + _team_setting_get_field (src, attr_data))) + continue; + _team_attr_data_copy (attr_data, + self->d.is_port, + _team_setting_get_field (self, attr_data), + _team_setting_get_field (src, attr_data)); + changed |= nm_team_attribute_to_flags (attr_data->team_attr); + } + + if (!nm_streq0 (self->d._js_str, src->d._js_str)) { + g_free ((char *) self->_data_priv._js_str); + self->_data_priv._js_str = g_strdup (src->d._js_str); + changed |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); + } else if (changed != 0) + changed |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG); + + self->_data_priv._js_str_need_synthetize = src->d._js_str_need_synthetize; + self->_data_priv.strict_validated = src->d.strict_validated; + self->_data_priv.js_str_invalid = src->d.js_str_invalid; + + return changed; +} + +static void +_variants_list_unref_auto (GVariant *(*p_variants)[]) +{ + int i; + + for (i = 0; i < _NM_TEAM_ATTRIBUTE_NUM; i++) + nm_g_variant_unref ((*p_variants)[i]); +} + +gboolean +nm_team_setting_reset_from_dbus (NMTeamSetting *self, + GVariant *setting_dict, + GHashTable *keys, + guint32 *out_changed, + guint /* NMSettingParseFlags */ parse_flags, + GError **error) +{ + nm_auto (_variants_list_unref_auto) GVariant *variants[_NM_TEAM_ATTRIBUTE_NUM] = { NULL, }; + gs_unref_ptrarray GPtrArray *v_link_watchers = NULL; + const TeamAttrData *attr_data; + GVariantIter iter; + const char *v_key; + GVariant *v_val; + + *out_changed = 0; + + g_variant_iter_init (&iter, setting_dict); + while (g_variant_iter_next (&iter, "{&sv}", &v_key, &v_val)) { + _nm_unused gs_unref_variant GVariant *v_val_free = v_val; + const GVariantType *variant_type = NULL; + + attr_data = _team_attr_data_find_for_dbus_name (self->d.is_port, v_key); + if (!attr_data) { + /* _nm_setting_new_from_dbus() already checks for unknown keys. Don't + * do that here. */ + continue; + } + + if (keys) + g_hash_table_remove (keys, v_key); + + if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) + variant_type = nm_value_type_get_variant_type (attr_data->value_type); + else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_CONFIG) + variant_type = G_VARIANT_TYPE_STRING; + else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) + variant_type = G_VARIANT_TYPE ("aa{sv}"); + else if ( !self->d.is_port + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) + variant_type = G_VARIANT_TYPE_STRING_ARRAY; + else + nm_assert_not_reached (); + + if (!g_variant_is_of_type (v_val, variant_type)) { + if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid D-Bus type \"%s\""), + g_variant_get_type_string (v_val)); + _team_setting_prefix_error (self, + error, + attr_data->dbus_name, + attr_data->dbus_name); + return FALSE; + } + continue; + } + + /* _nm_setting_new_from_dbus() already checks for duplicate keys. Don't + * do that here. */ + nm_g_variant_unref (variants[attr_data->team_attr]); + variants[attr_data->team_attr] = g_steal_pointer (&v_val_free); + } + + *out_changed |= nm_team_setting_config_set (self, + variants[NM_TEAM_ATTRIBUTE_CONFIG] + ? g_variant_get_string (variants[NM_TEAM_ATTRIBUTE_CONFIG], NULL) + : NULL); + + if ( variants[NM_TEAM_ATTRIBUTE_CONFIG] + && WITH_JSON_VALIDATION) { + /* for team settings, the JSON must be able to express all possible options. That means, + * if the GVariant contains both the JSON "config" and other options, then the other options + * are silently ignored. */ + } else { + guint32 extra_changed = 0u; + + if (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) { + /* FIXME: handle errors for NM_SETTING_PARSE_FLAGS_STRICT. + * + * But then also move the check before starting to modify the setting so we fail + * early. */ + v_link_watchers = _nm_utils_team_link_watchers_from_variant (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]); + } + + for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) { + NMValueTypUnion val; + guint32 changed = 0u; + + if (!_team_attr_data_is_relevant (attr_data, self->d.is_port)) + continue; + if (!variants[attr_data->team_attr]) + continue; + + if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) { + nm_value_type_get_from_variant (attr_data->value_type, &val, variants[attr_data->team_attr], FALSE); + changed = _team_setting_value_set (self, + attr_data->team_attr, + attr_data->value_type, + &val); + } else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) { + changed = _team_setting_value_link_watchers_set_list (self, + v_link_watchers ? (const NMTeamLinkWatcher *const *) v_link_watchers->pdata : NULL, + v_link_watchers ? v_link_watchers->len : 0u); + } else if ( !self->d.is_port + && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) { + gs_free const char **strv = NULL; + gsize len; + + strv = g_variant_get_strv (variants[attr_data->team_attr], &len); + changed = _team_setting_value_master_runner_tx_hash_set_list (self, + strv, + NM_MIN (len, (gsize) G_MAXUINT)); + } else + nm_assert_not_reached (); + + extra_changed |= changed; + } + + if ( !variants[NM_TEAM_ATTRIBUTE_CONFIG] + && extra_changed) { + /* clear the JSON string so it can be regenerated. But only if we didn't set + * it above. */ + self->_data_priv.strict_validated = TRUE; + self->_data_priv._js_str_need_synthetize = TRUE; + } + + *out_changed |= extra_changed; + } + + return TRUE; +} + +/*****************************************************************************/ + +gboolean +nm_team_setting_maybe_changed (NMSetting *source, + const GParamSpec *const*obj_properties, + guint32 changed_flags) +{ + NMTeamAttribute team_attr; + int count_flags; + guint32 ch; + + if (changed_flags == 0u) + return FALSE; + + count_flags = 0; + for (ch = changed_flags; ch != 0u; ch >>= 1) { + if (NM_FLAGS_HAS (ch, 0x1u)) + count_flags++; + } + + if (count_flags > 1) + g_object_freeze_notify (G_OBJECT (source)); + + ch = changed_flags; + for (team_attr = 0; team_attr < _NM_TEAM_ATTRIBUTE_NUM; team_attr++) { + if (!NM_FLAGS_ANY (ch, nm_team_attribute_to_flags (team_attr))) + continue; + g_object_notify_by_pspec (G_OBJECT (source), + (GParamSpec *) obj_properties[team_attr]); + ch &= ~nm_team_attribute_to_flags (team_attr); + if (ch == 0) + break; + } + + if (count_flags > 1) + g_object_thaw_notify (G_OBJECT (source)); + + return TRUE; +} + +/*****************************************************************************/ + +NMTeamSetting * +nm_team_setting_new (gboolean is_port, + const char *js_str) +{ + NMTeamSetting *self; + gsize l; + + G_STATIC_ASSERT_EXPR (sizeof (*self) == sizeof (self->_data_priv)); + G_STATIC_ASSERT_EXPR (sizeof (*self) == NM_CONST_MAX (nm_offsetofend (NMTeamSetting, d.master), nm_offsetofend (NMTeamSetting, d.port))); + + l = is_port + ? nm_offsetofend (NMTeamSetting, d.port) + : nm_offsetofend (NMTeamSetting, d.master); + + self = g_malloc0 (l); + + self->_data_priv.is_port = is_port; + self->_data_priv.strict_validated = TRUE; + self->_data_priv._js_str_need_synthetize = FALSE; + self->_data_priv.link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + + _team_setting_ASSERT (self); + + nm_team_setting_config_set (self, js_str); + + _team_setting_ASSERT (self); + + return self; +} + +void +nm_team_setting_free (NMTeamSetting *self) +{ + if (!self) + return; + + _team_setting_ASSERT (self); + + if (!self->d.is_port) { + nm_clear_pointer (((GPtrArray **) &self->_data_priv.master.runner_tx_hash), g_ptr_array_unref); + g_free ((char *) self->_data_priv.master.runner); + g_free ((char *) self->_data_priv.master.runner_hwaddr_policy); + g_free ((char *) self->_data_priv.master.runner_tx_balancer); + g_free ((char *) self->_data_priv.master.runner_agg_select_policy); + } + g_ptr_array_unref ((GPtrArray *) self->_data_priv.link_watchers); + g_free ((char *) self->_data_priv._js_str); + g_free (self); +} diff --git a/libnm-core/nm-team-utils.h b/libnm-core/nm-team-utils.h index 6b3550000d..dad74468f1 100644 --- a/libnm-core/nm-team-utils.h +++ b/libnm-core/nm-team-utils.h @@ -24,4 +24,250 @@ #error Cannot use this header. #endif +#include "nm-glib-aux/nm-value-type.h" + +struct _NMSetting; + +struct NMTeamLinkWatcher; + +typedef enum { + + _NM_TEAM_ATTRIBUTE_0 = 0, + NM_TEAM_ATTRIBUTE_CONFIG = 1, + NM_TEAM_ATTRIBUTE_LINK_WATCHERS = 2, + + _NM_TEAM_ATTRIBUTE_START = 3, + + NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT = _NM_TEAM_ATTRIBUTE_START, + NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL, + NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT, + NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS, + NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY, + _NM_TEAM_ATTRIBUTE_MASTER_NUM, + + NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID = _NM_TEAM_ATTRIBUTE_START, + NM_TEAM_ATTRIBUTE_PORT_PRIO, + NM_TEAM_ATTRIBUTE_PORT_STICKY, + NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO, + NM_TEAM_ATTRIBUTE_PORT_LACP_KEY, + _NM_TEAM_ATTRIBUTE_PORT_NUM, + + _NM_TEAM_ATTRIBUTE_NUM = NM_CONST_MAX (_NM_TEAM_ATTRIBUTE_MASTER_NUM, _NM_TEAM_ATTRIBUTE_PORT_NUM), + +} NMTeamAttribute; + +static inline guint32 +nm_team_attribute_to_flags (NMTeamAttribute team_attr) +{ + nm_assert (_NM_INT_NOT_NEGATIVE (team_attr)); + nm_assert (team_attr < _NM_TEAM_ATTRIBUTE_NUM); + G_STATIC_ASSERT_EXPR (_NM_TEAM_ATTRIBUTE_NUM < 32); + + return ((guint32) 1) << team_attr; +} + +struct _NMTeamSettingData { + + const char *_js_str; + + const GPtrArray *link_watchers; + + /* this means that @_js_str is unset and needs to be created by + * converting the properties to JSON. This flag indicates that + * we need to re-generate the JSON string on-demand (lazily). */ + bool _js_str_need_synthetize; + + bool strict_validated:1; + + /* indicates tha the JSON is invalid. Usually, we do a very relaxed validation of + * the JSON config, in case !@strict_validated and accept all unknown fields. This + * flag indicates that the JSON value is not even parsable as JSON. nm_connection_verify() + * would reject such a setting. */ + bool js_str_invalid:1; + + bool is_port:1; + + union { + struct { + const GPtrArray *runner_tx_hash; + const char *runner; + const char *runner_hwaddr_policy; + const char *runner_tx_balancer; + const char *runner_agg_select_policy; + gint32 notify_peers_count; + gint32 notify_peers_interval; + gint32 mcast_rejoin_count; + gint32 mcast_rejoin_interval; + gint32 runner_sys_prio; + gint32 runner_min_ports; + gint32 runner_tx_balancer_interval; + bool runner_active; + bool runner_fast_rate; + } master; + struct { + gint32 queue_id; + gint32 prio; + gint32 lacp_prio; + gint32 lacp_key; + bool sticky; + } port; + }; +}; + +/*****************************************************************************/ + +typedef struct { + union { + const struct _NMTeamSettingData d; + + struct _NMTeamSettingData _data_priv; + }; +} NMTeamSetting; + +NMTeamSetting *nm_team_setting_new (gboolean is_port, + const char *js_str); + +void nm_team_setting_free (NMTeamSetting *self); + +NM_AUTO_DEFINE_FCN0 (NMTeamSetting *, _nm_auto_free_team_setting, nm_team_setting_free) +#define nm_auto_free_team_setting nm_auto (_nm_auto_free_team_setting) + +/*****************************************************************************/ + +const char *nm_team_setting_config_get (const NMTeamSetting *self); + +guint32 nm_team_setting_config_set (NMTeamSetting *self, const char *js_str); + +/*****************************************************************************/ + +gconstpointer _nm_team_setting_value_get (const NMTeamSetting *self, + NMTeamAttribute team_attr, + NMValueType value_type); + +static inline gboolean +nm_team_setting_value_get_bool (const NMTeamSetting *self, + NMTeamAttribute team_attr) +{ + const bool *p; + + p = _nm_team_setting_value_get (self, team_attr, NM_VALUE_TYPE_BOOL); + return p ? *p : 0; +} + +static inline gint32 +nm_team_setting_value_get_int32 (const NMTeamSetting *self, + NMTeamAttribute team_attr) +{ + const gint32 *p; + + p = _nm_team_setting_value_get (self, team_attr, NM_VALUE_TYPE_INT32); + return p ? *p : 0; +} + +static inline const char * +nm_team_setting_value_get_string (const NMTeamSetting *self, + NMTeamAttribute team_attr) +{ + const char *const*p; + + p = _nm_team_setting_value_get (self, team_attr, NM_VALUE_TYPE_STRING); + return p ? *p : NULL; +} + +/*****************************************************************************/ + +guint32 _nm_team_setting_value_set (NMTeamSetting *self, + NMTeamAttribute team_attr, + NMValueType value_type, + gconstpointer val); + +static inline guint32 +nm_team_setting_value_set_bool (NMTeamSetting *self, + NMTeamAttribute team_attr, + gboolean val) +{ + const bool bool_val = val; + + return _nm_team_setting_value_set (self, team_attr, NM_VALUE_TYPE_BOOL, &bool_val); +} + +static inline guint32 +nm_team_setting_value_set_int32 (NMTeamSetting *self, + NMTeamAttribute team_attr, + gint32 val) +{ + return _nm_team_setting_value_set (self, team_attr, NM_VALUE_TYPE_INT32, &val); +} + +static inline guint32 +nm_team_setting_value_set_string (NMTeamSetting *self, + NMTeamAttribute team_attr, + const char *arg) +{ + return _nm_team_setting_value_set (self, team_attr, NM_VALUE_TYPE_STRING, &arg); +} + +/*****************************************************************************/ + +guint32 nm_team_setting_value_link_watchers_add (NMTeamSetting *self, + const struct NMTeamLinkWatcher *link_watcher); + +guint32 nm_team_setting_value_link_watchers_remove (NMTeamSetting *self, + guint idx); + +guint32 nm_team_setting_value_link_watchers_remove_by_value (NMTeamSetting *self, + const struct NMTeamLinkWatcher *link_watcher); + +guint32 nm_team_setting_value_link_watchers_set_list (NMTeamSetting *self, + const struct NMTeamLinkWatcher *const*arr, + guint len); + +/*****************************************************************************/ + +guint32 nm_team_setting_value_master_runner_tx_hash_add (NMTeamSetting *self, + const char *txhash); + +guint32 nm_team_setting_value_master_runner_tx_hash_remove (NMTeamSetting *self, + guint idx); + +guint32 nm_team_setting_value_master_runner_tx_hash_set_list (NMTeamSetting *self, + const char *const*arr, + guint len); + +/*****************************************************************************/ + +gboolean nm_team_setting_verify (const NMTeamSetting *self, + GError **error); + +/*****************************************************************************/ + +int nm_team_setting_cmp (const NMTeamSetting *self_a, + const NMTeamSetting *self_b, + gboolean ignore_js_str); + +guint32 nm_team_setting_reset (NMTeamSetting *self, + const NMTeamSetting *src); + +gboolean nm_team_setting_reset_from_dbus (NMTeamSetting *self, + GVariant *setting_dict, + GHashTable *keys, + guint32 *out_changed, + guint /* NMSettingParseFlags */ parse_flags, + GError **error); + +/*****************************************************************************/ + +gboolean nm_team_setting_maybe_changed (struct _NMSetting *source, + const GParamSpec *const*obj_properties, + guint32 changed); + #endif /* __NM_TEAM_UITLS_H__ */ diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 8d953191d3..ee949d8834 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -131,116 +131,4 @@ gboolean _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, const char *setting, const char *property); -/* JSON to GValue conversion macros */ - -static inline void -_nm_auto_unset_and_free_gvalue (GValue **ptr) -{ - if (*ptr) { - g_value_unset (*ptr); - g_free (*ptr); - } -} -#define nm_auto_unset_and_free_gvalue nm_auto(_nm_auto_unset_and_free_gvalue) - -typedef struct { - const char *key1; - const char *key2; - const char *key3; - union { - int default_int; - gboolean default_bool; - const char *default_str; - }; -} _NMUtilsTeamPropertyKeys; - -static inline int -_nm_utils_json_extract_int (char *conf, - _NMUtilsTeamPropertyKeys key, - gboolean is_port) -{ - nm_auto_unset_and_free_gvalue GValue *t_value = NULL; - - t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); - if ( !t_value - || !G_VALUE_HOLDS_INT (t_value)) - return key.default_int; - return g_value_get_int (t_value); -} - -static inline gboolean -_nm_utils_json_extract_boolean (char *conf, - _NMUtilsTeamPropertyKeys key, - gboolean is_port) -{ - nm_auto_unset_and_free_gvalue GValue *t_value = NULL; - - t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); - if ( !t_value - || !G_VALUE_HOLDS_BOOLEAN (t_value)) - return key.default_bool; - return g_value_get_boolean (t_value); -} - -static inline char * -_nm_utils_json_extract_string (char *conf, - _NMUtilsTeamPropertyKeys key, - gboolean is_port) -{ - nm_auto_unset_and_free_gvalue GValue *t_value = NULL; - - t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); - if ( !t_value - || !G_VALUE_HOLDS_STRING (t_value)) - return g_strdup (key.default_str); - return g_value_dup_string (t_value); -} - -static inline char ** -_nm_utils_json_extract_strv (char *conf, - _NMUtilsTeamPropertyKeys key, - gboolean is_port) -{ - nm_auto_unset_and_free_gvalue GValue *t_value = NULL; - - t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); - if ( !t_value - || !G_TYPE_CHECK_VALUE_TYPE (t_value, G_TYPE_STRV)) - return NULL; - return g_strdupv (g_value_get_boxed (t_value)) - ?: g_new0 (char *, 1); -} - -static inline GPtrArray * -_nm_utils_json_extract_ptr_array (char *conf, - _NMUtilsTeamPropertyKeys key, - gboolean is_port) -{ - nm_auto_unset_and_free_gvalue GValue *t_value = NULL; - GPtrArray *data, *ret; - guint i; - - ret = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); - - t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); - if ( !t_value - || !G_TYPE_CHECK_VALUE_TYPE (t_value, G_TYPE_PTR_ARRAY)) - return ret; - - data = g_value_get_boxed (t_value); - if (!data) - return ret; - for (i = 0; i < data->len; i++) - g_ptr_array_add (ret, _nm_team_link_watcher_ref (data->pdata[i])); - return ret; -} - -static inline void -_nm_utils_json_append_gvalue (char **conf, - _NMUtilsTeamPropertyKeys key, - const GValue *val) -{ - _nm_utils_team_config_set (conf, key.key1, key.key2, key.key3, val); -} - #endif diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 13bf626e9b..aa75ed42be 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -5378,429 +5378,6 @@ _nm_utils_is_json_object_no_validation (const char *str, GError **error) return FALSE; } -#if WITH_JSON_VALIDATION - -static void -_json_add_object (json_t *json, - const char *key1, - const char *key2, - const char *key3, - json_t *value) -{ - json_t *json_element, *json_link; - - json_element = json_object_get (json, key1); - if (!json_element) { - json_element = value; - if (key2) { - if (key3) { - json_element = json_object (); - json_object_set_new (json_element, key3, value); - } - json_link = json_object (); - json_object_set_new (json_link, key2, json_element); - json_element = json_link; - } - json_object_set_new (json, key1, json_element); - return; - } - - if (!key2) - goto key_already_there; - - json_link = json_element; - json_element = json_object_get (json_element, key2); - if (!json_element) { - json_element = value; - if (key3) { - json_element = json_object (); - json_object_set_new (json_element, key3, value); - } - json_object_set_new (json_link, key2, json_element); - return; - } - - if (!key3) - goto key_already_there; - - json_link = json_element; - json_element = json_object_get (json_element, key3); - if (!json_element) { - json_object_set_new (json_link, key3, value); - return; - } - -key_already_there: - json_decref (value); -} - -/* - * Removes the specified key1[.key2.key3] from json. - * Returns TRUE if json has been modified, FALSE otherwise. */ -static gboolean -_json_del_object (json_t *json, - const char *key1, - const char *key2, - const char *key3) -{ - json_t *json_element = json; - json_t *json_link = NULL; - const char *iter_key = key1; - - if (key2) { - json_link = json; - json_element = json_object_get (json, key1); - if (!json_element) - return FALSE; - iter_key = key2; - } - if (key3) { - json_link = json_element; - json_element = json_object_get (json_element, key2); - if (!json_element) - return FALSE; - iter_key = key3; - } - - if (json_object_del (json_element, iter_key) != 0) - return FALSE; - - /* 1st level key only */ - if (!json_link) - return TRUE; - - if (json_object_size (json_element) == 0) - json_object_del (json_link, (key3 ? key2 : key1)); - - if (key3 && json_object_size (json_link) == 0) - json_object_del (json, key1); - - return TRUE; -} - -/* Adds in place to json the defaults for missing properties; - * the "add_implicit" allows to add to the json also the default - * values used but not shown with teamdctl */ -static void -_json_team_add_defaults (json_t *json, - gboolean port_config, - gboolean add_implicit) -{ - json_t *json_element; - const char *runner = NULL; - - if (port_config) { - _json_add_object (json, "link_watch", "name", NULL, - json_string (NM_TEAM_LINK_WATCHER_ETHTOOL)); - return; - } - - /* Retrieve runner or add default one */ - json_element = json_object_get (json, "runner"); - if (json_element) { - runner = json_string_value (json_object_get (json_element, "name")); - } else { - json_element = json_object (); - json_object_set_new (json, "runner", json_element); - } - if (!runner) { - runner = NM_SETTING_TEAM_RUNNER_DEFAULT; - json_object_set_new (json_element, "name", json_string (runner)); - } - - if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) { - _json_add_object (json, "notify_peers", "count", NULL, - json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT)); - _json_add_object (json, "mcast_rejoin", "count", NULL, - json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT)); - } else if ( nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) - || nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { - json_element = json_array (); - json_array_append_new (json_element, json_string ("eth")); - json_array_append_new (json_element, json_string ("ipv4")); - json_array_append_new (json_element, json_string ("ipv6")); - _json_add_object (json, "runner", "tx_hash", NULL, json_element); - } - - if (!add_implicit) - return; - - if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) - _json_add_object (json, "runner", "hwaddr_policy", NULL, json_string ("same_all")); - else if (NM_IN_STRSET (runner, - NM_SETTING_TEAM_RUNNER_LOADBALANCE, - NM_SETTING_TEAM_RUNNER_LACP)) { - _json_add_object (json, "runner", "tx_balancer", "balancing_interval", - json_integer (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT)); - if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { - _json_add_object (json, "runner", "active", NULL, json_boolean (TRUE)); - _json_add_object (json, "runner", "sys_prio", NULL, - json_integer (NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT)); - _json_add_object (json, "runner", "min_ports", NULL, json_integer (0)); - _json_add_object (json, "runner", "agg_select_policy", NULL, - json_string (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT)); - } - } -} - -static json_t * -_json_find_object (json_t *json, - const char *key1, - const char *key2, - const char *key3) -{ - json_t *json_element; - - if (!key1) - return NULL; - json_element = json_object_get (json, key1); - if (!key2 || !json_element) - return json_element; - - json_element = json_object_get (json_element, key2); - if (!key3 || !json_element) - return json_element; - - json_element = json_object_get (json_element, key3); - return json_element; -} - -static void -_json_delete_object_on_int_match (json_t *json, - const char *key1, - const char *key2, - const char *key3, - int val) -{ - json_t *json_element; - - json_element = _json_find_object (json, key1, key2, key3); - if (!json_element || !json_is_integer (json_element)) - return; - if (json_integer_value (json_element) == val) - _json_del_object (json, key1, key2, key3); -} - -static void -_json_delete_object_on_bool_match (json_t *json, - const char *key1, - const char *key2, - const char *key3, - gboolean val) -{ - json_t *json_element; - - json_element = _json_find_object (json, key1, key2, key3); - if (!json_element || !json_is_boolean (json_element)) - return; - if (json_boolean_value (json_element) == val) - _json_del_object (json, key1, key2, key3); -} - -static void -_json_delete_object_on_string_match (json_t *json, - const char *key1, - const char *key2, - const char *key3, - const char *val) -{ - json_t *json_element; - - json_element = _json_find_object (json, key1, key2, key3); - if (!json_element || !json_is_string (json_element)) - return; - if (nm_streq0 (json_string_value (json_element), val)) - _json_del_object (json, key1, key2, key3); -} - -static void -_json_team_normalize_defaults (json_t *json, gboolean reset) -{ - json_t *json_element; - const char *runner = NM_SETTING_TEAM_RUNNER_DEFAULT; - gs_free char *runner_free = NULL; - int notify_peers_count = 0, notify_peers_interval = 0; - int mcast_rejoin_count = 0, mcast_rejoin_interval = 0; - int runner_tx_balancer_interval = -1; - gboolean runner_active = FALSE, runner_fast_rate = FALSE; - int runner_sys_prio = -1, runner_min_ports = -1; - - json_element = _json_find_object (json, "runner", "name", NULL); - if (json_element) { - runner_free = g_strdup (json_string_value (json_element)); - runner = runner_free; - _json_delete_object_on_string_match (json, "runner", "name", NULL, - NM_SETTING_TEAM_RUNNER_DEFAULT); - } - - /* the runner changed: clear all the properties. Then team.config will be saved - * and reloaded triggering the reset of the values through _nm_utils_team_config_get - */ - if (reset) { - _json_del_object (json, "notify_peers", "count", NULL); - _json_del_object (json, "notify_peers", "interval", NULL); - _json_del_object (json, "mcast_rejoin", "count", NULL); - _json_del_object (json, "mcast_rejoin", "interval", NULL); - _json_del_object (json, "runner", "hwaddr_policy", NULL); - _json_del_object (json, "runner", "tx_hash", NULL); - _json_del_object (json, "runner", "tx_balancer", "name"); - _json_del_object (json, "runner", "tx_balancer", "balancing_interval"); - _json_del_object (json, "runner", "active", NULL); - _json_del_object (json, "runner", "fast_rate", NULL); - _json_del_object (json, "runner", "sys_prio", NULL); - _json_del_object (json, "runner", "min_ports", NULL); - _json_del_object (json, "runner", "agg_select_policy", NULL); - return; - } - - if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) { - notify_peers_count = 1; - mcast_rejoin_count = 1; - _json_delete_object_on_string_match (json, "runner", "hwaddr_policy", NULL, - NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT); - } else if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { - runner_tx_balancer_interval = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT; - runner_active = TRUE; - runner_sys_prio = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT; - runner_min_ports = 0; - _json_delete_object_on_string_match (json, "runner", "agg_select_policy", NULL, - NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT); - } else if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE)) - runner_tx_balancer_interval = 50; - - _json_delete_object_on_int_match (json, "notify_peers", "count", NULL, notify_peers_count); - _json_delete_object_on_int_match (json, "notify_peers", "interval", NULL, notify_peers_interval); - _json_delete_object_on_int_match (json, "mcast_rejoin", "count", NULL, mcast_rejoin_count); - _json_delete_object_on_int_match (json, "macst_rejoin", "interval", NULL, mcast_rejoin_interval); - _json_delete_object_on_int_match (json, "runner", "tx_balancer", "balancing_interval", - runner_tx_balancer_interval); - _json_delete_object_on_int_match (json, "runner", "sys_prio", NULL, runner_sys_prio); - _json_delete_object_on_int_match (json, "runner", "min_ports", NULL, runner_min_ports); - _json_delete_object_on_bool_match (json, "runner", "active", NULL, runner_active); - _json_delete_object_on_bool_match (json, "runner", "active", NULL, runner_active); - _json_delete_object_on_bool_match (json, "runner", "fast_rate", NULL, runner_fast_rate); -} - -static NMTeamLinkWatcher * -_nm_utils_team_link_watcher_from_json (json_t *json_element) -{ - const char *j_key; - json_t *j_val; - gs_free char *name = NULL, *target_host = NULL, *source_host = NULL; - int val1 = 0, val2 = 0, val3 = 3, val4 = -1; - NMTeamLinkWatcherArpPingFlags flags = 0; - - g_return_val_if_fail (json_element, NULL); - - json_object_foreach (json_element, j_key, j_val) { - if (nm_streq (j_key, "name")) { - g_free (name); - name = strdup (json_string_value (j_val)); - } else if (nm_streq (j_key, "target_host")) { - g_free (target_host); - target_host = strdup (json_string_value (j_val)); - } else if (nm_streq (j_key, "source_host")) { - g_free (source_host); - source_host = strdup (json_string_value (j_val)); - } else if (NM_IN_STRSET (j_key, "delay_up", "init_wait")) - val1 = json_integer_value (j_val); - else if (NM_IN_STRSET (j_key, "delay_down", "interval")) - val2 = json_integer_value (j_val); - else if (nm_streq (j_key, "missed_max")) - val3 = json_integer_value (j_val); - else if (nm_streq (j_key, "vlanid")) - val4 = json_integer_value (j_val); - else if (nm_streq (j_key, "validate_active")) { - if (json_is_true (j_val)) - flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; - } else if (nm_streq (j_key, "validate_inactive")) { - if (json_is_true (j_val)) - flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE; - } else if (nm_streq (j_key, "send_always")) { - if (json_is_true (j_val)) - flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; - } - } - - if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) - return nm_team_link_watcher_new_ethtool (val1, val2, NULL); - else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) - return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, NULL); - else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING)) { - return nm_team_link_watcher_new_arp_ping2 (val1, val2, val3, val4, target_host, - source_host, flags, NULL); - } else - return NULL; -} - -static json_t * -_nm_utils_team_link_watcher_to_json (NMTeamLinkWatcher *watcher) -{ - const char *name; - int int_val; - const char *str_val; - NMTeamLinkWatcherArpPingFlags flags = 0; - json_t *json_element; - - g_return_val_if_fail (watcher, NULL); - - json_element = json_object (); - name = nm_team_link_watcher_get_name (watcher); - if (!name) - goto fail; - - json_object_set_new (json_element, "name", json_string (name)); - - if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { - int_val = nm_team_link_watcher_get_delay_up (watcher); - if (int_val) - json_object_set_new (json_element, "delay_up", json_integer (int_val)); - int_val = nm_team_link_watcher_get_delay_down (watcher); - if (int_val) - json_object_set_new (json_element, "delay_down", json_integer (int_val)); - return json_element; - } - - int_val = nm_team_link_watcher_get_init_wait (watcher); - if (int_val) - json_object_set_new (json_element, "init_wait", json_integer (int_val)); - int_val = nm_team_link_watcher_get_interval (watcher); - if (int_val) - json_object_set_new (json_element, "interval", json_integer (int_val)); - int_val = nm_team_link_watcher_get_missed_max (watcher); - if (int_val != 3) - json_object_set_new (json_element, "missed_max", json_integer (int_val)); - str_val = nm_team_link_watcher_get_target_host (watcher); - if (!str_val) - goto fail; - json_object_set_new (json_element, "target_host", json_string (str_val)); - - if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) - return json_element; - - int_val = nm_team_link_watcher_get_vlanid (watcher); - if (int_val != -1) - json_object_set_new (json_element, "vlanid", json_integer (int_val)); - str_val = nm_team_link_watcher_get_source_host (watcher); - if (!str_val) - goto fail; - json_object_set_new (json_element, "source_host", json_string (str_val)); - - flags = nm_team_link_watcher_get_flags (watcher); - if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE) - json_object_set_new (json_element, "validate_active", json_string ("true")); - if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE) - json_object_set_new (json_element, "validate_inactive", json_string ("true")); - if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS) - json_object_set_new (json_element, "send_always", json_string ("true")); - - return json_element; - -fail: - json_decref (json_element); - return NULL; -} - /** * nm_utils_is_json_object: * @str: the JSON string to test @@ -5816,6 +5393,7 @@ fail: gboolean nm_utils_is_json_object (const char *str, GError **error) { +#if WITH_JSON_VALIDATION json_t *json; json_error_t jerror; @@ -5855,305 +5433,7 @@ nm_utils_is_json_object (const char *str, GError **error) json_decref (json); return TRUE; -} - -gboolean -_nm_utils_team_config_equal (const char *conf1, - const char *conf2, - gboolean port_config) -{ - json_t *json1 = NULL, *json2 = NULL, *json; - gs_free char *dump1 = NULL, *dump2 = NULL; - json_t *value; - json_error_t jerror; - const char *key; - gboolean ret; - void *tmp; - int i; - - if (nm_streq0 (conf1, conf2)) - return TRUE; - else if (!nm_jansson_load ()) - return FALSE; - - /* A NULL configuration is equivalent to default value '{}' */ - json1 = json_loads (conf1 ?: "{}", JSON_REJECT_DUPLICATES, &jerror); - if (json1) - json2 = json_loads (conf2 ?: "{}", JSON_REJECT_DUPLICATES, &jerror); - - if (!json1 || !json2) { - ret = FALSE; - goto out; - } - - /* Some properties are added by teamd when missing from the initial - * configuration. Add them with the default value if necessary, depending - * on the configuration type. - */ - for (i = 0, json = json1; i < 2; i++, json = json2) - _json_team_add_defaults (json, port_config, FALSE); - - /* Only consider a given subset of nodes, others can change depending on - * current state */ - for (i = 0, json = json1; i < 2; i++, json = json2) { - json_object_foreach_safe (json, tmp, key, value) { - if (!NM_IN_STRSET (key, "runner", "link_watch")) - json_object_del (json, key); - } - } - - dump1 = json_dumps (json1, JSON_INDENT(0) | JSON_ENSURE_ASCII | JSON_SORT_KEYS); - dump2 = json_dumps (json2, JSON_INDENT(0) | JSON_ENSURE_ASCII | JSON_SORT_KEYS); - - ret = nm_streq0 (dump1, dump2); -out: - - if (json1) - json_decref (json1); - if (json2) - json_decref (json2); - - return ret; -} - -GValue * -_nm_utils_team_config_get (const char *conf, - const char *key, - const char *key2, - const char *key3, - gboolean port_config) -{ - json_t *json; - json_t *json_element; - GValue *value = NULL; - json_error_t jerror; - - if (!key) - return NULL; - - if (!nm_jansson_load ()) - return NULL; - - json = json_loads (conf ?: "{}", JSON_REJECT_DUPLICATES, &jerror); - - /* Invalid json in conf */ - if (!json) - return NULL; - - /* Some properties are added by teamd when missing from the initial - * configuration. Add them with the default value if necessary, depending - * on the configuration type. - * Skip this for port config, as some properties change on the basis of the - * runner specified in the master connection... but we don't want to check - * against properties in another connection. Moreover, for team-port we have - * the link-watchers property only here: and for this compound property it is - * fine to show the default value only if explicitly set. - */ - if (!port_config) - _json_team_add_defaults (json, port_config, TRUE); - - /* Now search the property to retrieve */ - json_element = json_object_get (json, key); - if (json_element && key2) - json_element = json_object_get (json_element, key2); - if (json_element && key3) - json_element = json_object_get (json_element, key3); - - if (json_element) { - value = g_new0 (GValue, 1); - if (json_is_string (json_element)) { - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, json_string_value (json_element)); - } else if (json_is_integer (json_element)) { - g_value_init (value, G_TYPE_INT); - g_value_set_int (value, json_integer_value (json_element)); - } else if (json_is_boolean (json_element)) { - g_value_init (value, G_TYPE_BOOLEAN); - g_value_set_boolean (value, json_boolean_value (json_element)); - } else if (nm_streq (key, "link_watch")) { - NMTeamLinkWatcher *watcher; - GPtrArray *data = g_ptr_array_new_with_free_func - ((GDestroyNotify) nm_team_link_watcher_unref); - - if (json_is_array (json_element)) { - json_t *j_watcher; - int index; - - json_array_foreach (json_element, index, j_watcher) { - watcher = _nm_utils_team_link_watcher_from_json (j_watcher); - if (watcher) - g_ptr_array_add (data, watcher); - } - } else { - watcher = _nm_utils_team_link_watcher_from_json (json_element); - if (watcher) - g_ptr_array_add (data, watcher); - } - if (data->len) { - g_value_init (value, G_TYPE_PTR_ARRAY); - g_value_take_boxed (value, data); - } else - g_ptr_array_free (data, TRUE); - - } else if (json_is_array (json_element)) { - GPtrArray *data = g_ptr_array_new_with_free_func (g_free); - json_t *str_element; - int index; - - json_array_foreach (json_element, index, str_element) { - if (json_is_string (str_element)) - g_ptr_array_add (data, g_strdup (json_string_value (str_element))); - } - g_ptr_array_add (data, NULL); - g_value_init (value, G_TYPE_STRV); - g_value_take_boxed (value, g_ptr_array_free (data, FALSE)); - } else { - g_assert_not_reached (); - g_free (value); - value = NULL; - } - } - - if (json) - json_decref (json); - - return value; -} - -/* if conf is updated in place returns TRUE */ -gboolean -_nm_utils_team_config_set (char **conf, - const char *key, - const char *key2, - const char *key3, - const GValue *value) -{ - nm_auto_decref_json json_t *json = NULL; - nm_auto_decref_json json_t *json_value = NULL; - json_t *json_element; - json_t *json_link; - json_error_t jerror; - const char *iter_key = key; - gs_free char *conf_new = NULL; - - g_return_val_if_fail (key, FALSE); - - if (!nm_jansson_load ()) - return FALSE; - - json = json_loads (*conf?: "{}", JSON_REJECT_DUPLICATES, &jerror); - if (!json) - return FALSE; - - if (!value) { - if (!_json_del_object (json, key, key2, key3)) - return FALSE; - goto done; - } - - if (G_VALUE_HOLDS_STRING (value)) - json_value = json_string (g_value_get_string (value)); - else if (G_VALUE_HOLDS_INT (value)) - json_value = json_integer (g_value_get_int (value)); - else if (G_VALUE_HOLDS_BOOLEAN (value)) - json_value = json_boolean (g_value_get_boolean (value)); - else if (G_VALUE_HOLDS_BOXED (value)) { - if (nm_streq (key, "link_watch")) { - gboolean has_array = FALSE; - GPtrArray *array; - guint i; - - array = g_value_get_boxed (value); - if (!array || !array->len) - return FALSE; - - for (i = 0; i < array->len; i++) { - json_t *el; - - el = _nm_utils_team_link_watcher_to_json (array->pdata[i]); - if (!el) - continue; - /* if there is only one watcher, it is added as-is. If there - * are multiple watchers, they are added in an array. */ - if (!json_value) { - json_value = el; - continue; - } - if (!has_array) { - json_t *el_arr; - - has_array = TRUE; - el_arr = json_array(); - json_array_append_new (el_arr, json_value); - json_value = el_arr; - } - json_array_append_new (json_value, el); - } - } else if ( nm_streq (key, "runner") - && nm_streq0 (key2, "tx_hash")) { - const char *const*strv; - gsize i; - - strv = g_value_get_boxed (value); - if (!strv) - return FALSE; - - json_value = json_array (); - for (i = 0; strv[i]; i++) - json_array_append_new (json_value, json_string (strv[i])); - } else { - nm_assert_not_reached (); - return FALSE; - } - - } else { /* G_VALUE_HOLDS_? */ - nm_assert_not_reached (); - return FALSE; - } - - /* Simplest case: first level key only */ - json_element = json; - json_link = NULL; - - if (key2) { - json_link = json; - json_element = json_object_get (json, iter_key); - if (!json_element) { - json_element = json_object (); - json_object_set_new (json_link, iter_key, json_element); - } - iter_key = key2; - } - if (key3) { - json_link = json_element; - json_element = json_object_get (json_link, iter_key); - if (!json_element) { - json_element = json_object (); - json_object_set_new (json_link, iter_key, json_element); - } - iter_key = key3; - } - - json_object_set_new (json_element, iter_key, g_steal_pointer (&json_value)); - -done: - _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") - && nm_streq0 (key2, "name"))); - conf_new = json_dumps (json, JSON_PRESERVE_ORDER); - if (nm_streq0 (conf_new, "{}")) - nm_clear_g_free (&conf_new); - if (nm_streq0 (conf_new, *conf)) - return FALSE; - g_free (*conf); - *conf = g_steal_pointer (&conf_new); - return TRUE; -} - #else /* !WITH_JSON_VALIDATION */ - -gboolean -nm_utils_is_json_object (const char *str, GError **error) -{ g_return_val_if_fail (!error || !*error, FALSE); if (!str || !str[0]) { @@ -6165,36 +5445,8 @@ nm_utils_is_json_object (const char *str, GError **error) } return _nm_utils_is_json_object_no_validation (str, error); -} - -gboolean -_nm_utils_team_config_equal (const char *conf1, - const char *conf2, - gboolean port_config) -{ - return nm_streq0 (conf1, conf2); -} - -GValue * -_nm_utils_team_config_get (const char *conf, - const char *key, - const char *key2, - const char *key3, - gboolean port_config) -{ - return NULL; -} - -gboolean -_nm_utils_team_config_set (char **conf, - const char *key, - const char *key2, - const char *key3, - const GValue *value) -{ - return FALSE; -} #endif +} /** * _nm_utils_team_link_watchers_to_variant: diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 20736fb2d8..a8d182c457 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -32,6 +32,7 @@ #include "nm-utils-private.h" #include "nm-core-internal.h" #include "nm-core-tests-enum-types.h" +#include "nm-team-utils.h" #include "nm-setting-8021x.h" #include "nm-setting-adsl.h" @@ -6807,19 +6808,42 @@ _team_config_equal_check (const char *conf1, gboolean port_config, gboolean expected) { + nm_auto_free_team_setting NMTeamSetting *team_a = NULL; + nm_auto_free_team_setting NMTeamSetting *team_b = NULL; + gboolean is_same; + + if (nmtst_get_rand_bool ()) + NMTST_SWAP (conf1, conf2); + if (!nm_streq0 (conf1, conf2)) { _team_config_equal_check (conf1, conf1, port_config, TRUE); _team_config_equal_check (conf2, conf2, port_config, TRUE); } - g_assert_cmpint (_nm_utils_team_config_equal (conf1, conf2, port_config), ==, expected); + team_a = nm_team_setting_new (port_config, conf1); + team_b = nm_team_setting_new (port_config, conf2); + + is_same = (nm_team_setting_cmp (team_a, team_b, TRUE) == 0); + g_assert_cmpint (is_same, ==, expected); + + if (nm_streq0 (conf1, conf2)) { + g_assert_cmpint (nm_team_setting_cmp (team_a, team_b, FALSE), ==, 0); + g_assert (expected); + } else + g_assert_cmpint (nm_team_setting_cmp (team_a, team_b, FALSE), !=, 0); } static void test_nm_utils_team_config_equal (void) { -#if WITH_JSON_VALIDATION - _team_config_equal_check ("", "", TRUE, TRUE); + _team_config_equal_check ("", + "", + TRUE, + TRUE); + _team_config_equal_check ("", + " ", + TRUE, + TRUE); _team_config_equal_check ("{}", "{ }", TRUE, @@ -6827,7 +6851,15 @@ test_nm_utils_team_config_equal (void) _team_config_equal_check ("{}", "{", TRUE, - FALSE); + TRUE); + _team_config_equal_check ("{ \"a\": 1 }", + "{ \"a\": 1 }", + TRUE, + TRUE); + _team_config_equal_check ("{ \"a\": 1 }", + "{ \"a\": 1 }", + TRUE, + TRUE); /* team config */ _team_config_equal_check ("{ }", @@ -6837,11 +6869,11 @@ test_nm_utils_team_config_equal (void) _team_config_equal_check ("{ }", "{ \"runner\" : { \"name\" : \"random\"} }", FALSE, - FALSE); + !WITH_JSON_VALIDATION); _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }", "{ \"runner\" : { \"name\" : \"random\"} }", FALSE, - FALSE); + !WITH_JSON_VALIDATION); _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"} }", "{ \"runner\" : { \"name\" : \"random\"} }", FALSE, @@ -6861,11 +6893,11 @@ test_nm_utils_team_config_equal (void) _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }", "{ \"runner\" : { \"name\" : \"roundrobin\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }", FALSE, - FALSE); + !WITH_JSON_VALIDATION); _team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }", "{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\" ] } }", FALSE, - FALSE); + !WITH_JSON_VALIDATION); /* team port config */ _team_config_equal_check ("{ }", @@ -6875,11 +6907,11 @@ test_nm_utils_team_config_equal (void) _team_config_equal_check ("{ }", "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", TRUE, - FALSE); + TRUE); _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"ethtool\"} }", "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", TRUE, - FALSE); + TRUE); _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }", TRUE, @@ -6888,13 +6920,6 @@ test_nm_utils_team_config_equal (void) "{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth1\" : {} } }", TRUE, TRUE); -#else - /* Without JSON library, strings are compared for equality */ - _team_config_equal_check ("", "", TRUE, TRUE); - _team_config_equal_check ("", " ", TRUE, FALSE); - _team_config_equal_check ("{ \"a\": 1 }", "{ \"a\": 1 }", TRUE, TRUE); - _team_config_equal_check ("{ \"a\": 1 }", "{ \"a\": 1 }", TRUE, FALSE); -#endif } /*****************************************************************************/ diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 7844b1aecd..a3be0bc8fa 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -1378,9 +1378,64 @@ test_team_port_full_config (void) static void _check_team_setting (NMSetting *setting) { + gs_unref_object NMSetting *setting2 = NULL; + gs_unref_object NMSetting *setting_clone = NULL; gboolean is_port = NM_IS_SETTING_TEAM_PORT (setting); + gs_unref_variant GVariant *variant2 = NULL; + gs_unref_variant GVariant *variant3 = NULL; g_assert (NM_IS_SETTING_TEAM (setting) || is_port); + + setting_clone = nm_setting_duplicate (setting); + + if (!is_port) { + if (nm_setting_team_get_runner (NM_SETTING_TEAM (setting)) == NULL) { + /* such a setting is invalid. We must first coerce it so that it becomes + * valid. */ + setting = setting_clone; + g_object_set (setting, + NM_SETTING_TEAM_RUNNER, + NM_SETTING_TEAM_RUNNER_DEFAULT, + NULL); + } + } + + setting2 = g_object_new (G_OBJECT_TYPE (setting), + is_port + ? NM_SETTING_TEAM_PORT_CONFIG + : NM_SETTING_TEAM_CONFIG, + is_port + ? nm_setting_team_port_get_config (NM_SETTING_TEAM_PORT (setting)) + : nm_setting_team_get_config (NM_SETTING_TEAM (setting)), + NULL); + + if (WITH_JSON_VALIDATION) + nmtst_assert_setting_is_equal (setting, setting2, NM_SETTING_COMPARE_FLAG_EXACT); + + g_clear_object (&setting2); + + nmtst_assert_setting_dbus_roundtrip (setting); + + /* OK, now parse the setting only from the D-Bus variant, but removing the JSON config. + * For that, we have to "drop" the JSON and we do that by resetting the property. + * This causes JSON to be regenerated and it's in a normalized form that will compare + * equal. */ + setting = setting_clone; + if (is_port) { + g_object_set (setting, + NM_SETTING_TEAM_PORT_STICKY, + nm_setting_team_port_get_sticky (NM_SETTING_TEAM_PORT (setting)), + NULL); + } else { + g_object_set (setting, + NM_SETTING_TEAM_RUNNER_SYS_PRIO, + nm_setting_team_get_runner_sys_prio (NM_SETTING_TEAM (setting)), + NULL); + } + variant2 = _nm_setting_to_dbus (setting, NULL, NM_CONNECTION_SERIALIZE_ALL); + variant3 = nm_utils_gvariant_vardict_filter_drop_one (variant2, "config"); + setting2 = nmtst_assert_setting_dbus_new (G_OBJECT_TYPE (setting), variant3); + nmtst_assert_setting_is_equal (setting, setting2, NM_SETTING_COMPARE_FLAG_EXACT); } static void @@ -1393,11 +1448,6 @@ test_team_setting (void) nm_auto_unref_team_link_watcher NMTeamLinkWatcher *watcher1 = nm_team_link_watcher_new_nsna_ping (1, 3, 4, "bbb", NULL); nm_auto_unref_team_link_watcher NMTeamLinkWatcher *watcher2 = nm_team_link_watcher_new_arp_ping2 (1, 3, 4, -1, "ccc", "ddd", 0, NULL); - if (!WITH_JSON_VALIDATION) { - g_test_skip ("disabled test without json-validation"); - return; - } - g_assert (watcher1); g_assert (watcher2); @@ -1410,7 +1460,6 @@ test_team_setting (void) _check_team_setting (setting); g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{\"link_watch\": {\"name\": \"ethtool\"}}"); - g_assert_cmpint (nm_setting_team_get_num_link_watchers (NM_SETTING_TEAM (setting)), ==, 1); g_object_set (setting, @@ -1420,19 +1469,36 @@ test_team_setting (void) _check_team_setting (setting); g_assert_cmpint (nm_setting_team_get_num_link_watchers (NM_SETTING_TEAM (setting)), ==, 1); - g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{\"link_watch\": {\"name\": \"ethtool\"}, \"runner\": {\"sys_prio\": 10}}"); + g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{ \"runner\": { \"sys_prio\": 10 }, \"link_watch\": { \"name\": \"ethtool\"} }"); nm_setting_team_remove_link_watcher (NM_SETTING_TEAM (setting), 0); + _check_team_setting (setting); - g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{\"link_watch\": {\"name\": \"ethtool\"}, \"runner\": {\"sys_prio\": 10}}"); + g_assert_cmpint (nm_setting_team_get_num_link_watchers (NM_SETTING_TEAM (setting)), ==, 0); + g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{ \"runner\": { \"sys_prio\": 10 } }"); nm_setting_team_add_link_watcher (NM_SETTING_TEAM (setting), watcher1); _check_team_setting (setting); - g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{\"link_watch\": {\"name\": \"ethtool\"}, \"runner\": {\"sys_prio\": 10}}"); + g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{ \"runner\": { \"sys_prio\": 10 }, \"link_watch\": { \"name\": \"nsna_ping\", \"target_host\": \"bbb\", \"init_wait\": 1, \"interval\": 3, \"missed_max\": 4} }"); nm_setting_team_add_link_watcher (NM_SETTING_TEAM (setting), watcher2); _check_team_setting (setting); - g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{\"link_watch\": {\"name\": \"ethtool\"}, \"runner\": {\"sys_prio\": 10}}"); + g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{ \"runner\": { \"sys_prio\": 10 }, \"link_watch\": [ { \"name\": \"nsna_ping\", \"target_host\": \"bbb\", \"init_wait\": 1, \"interval\": 3, \"missed_max\": 4}, { \"name\": \"arp_ping\", \"target_host\": \"ccc\", \"source_host\": \"ddd\", \"init_wait\": 1, \"interval\": 3, \"missed_max\": 4} ] }"); + + nm_setting_team_remove_link_watcher (NM_SETTING_TEAM (setting), 0); + nm_setting_team_remove_link_watcher (NM_SETTING_TEAM (setting), 0); + g_object_set (setting, + NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, + (int) 5, + NULL); + g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{ \"runner\": { \"tx_balancer\": { \"balancing_interval\": 5 }, \"sys_prio\": 10 } }"); + + g_object_set (setting, + NM_SETTING_TEAM_RUNNER, + NULL, + NULL); + _check_team_setting (setting); + g_assert_cmpstr (nm_setting_team_get_config (NM_SETTING_TEAM (setting)), ==, "{ \"runner\": { \"tx_balancer\": { \"balancing_interval\": 5 }, \"sys_prio\": 10 } }"); } /*****************************************************************************/ |